2 * Copyright (c) 2004 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
26 #include <sys/types.h>
35 #include <sys/errno.h>
37 #include <sys/xattr.h>
38 #include <sys/syscall.h>
39 #include <sys/param.h>
41 #include <libkern/OSByteOrder.h>
42 #include <membership.h>
46 struct _copyfile_state
54 copyfile_flags_t flags
;
59 static int copyfile_open (copyfile_state_t
);
60 static int copyfile_close (copyfile_state_t
);
61 static int copyfile_data (copyfile_state_t
);
62 static int copyfile_stat (copyfile_state_t
);
63 static int copyfile_security (copyfile_state_t
);
64 static int copyfile_xattr (copyfile_state_t
);
65 static int copyfile_pack (copyfile_state_t
);
66 static int copyfile_unpack (copyfile_state_t
);
68 static copyfile_flags_t
copyfile_check (copyfile_state_t
);
69 static int copyfile_fix_perms(copyfile_state_t
, filesec_t
*, int);
71 #define COPYFILE_DEBUG (1<<31)
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__); \
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__); \
88 int copyfile(const char *src
, const char *dst
, copyfile_state_t state
, copyfile_flags_t flags
)
91 copyfile_state_t s
= state
;
92 filesec_t original_fsec
;
95 original_fsec
= filesec_init();
96 if (s
== NULL
&& (s
= copyfile_init()) == NULL
)
101 if (s
->src_fd
!= -2 && s
->src
&& !strncmp(src
, s
->src
, MAXPATHLEN
))
103 s
->src
= strdup(src
);
108 if (s
->dst_fd
!= -2 && s
->dst
&& !strncmp(dst
, s
->dst
, MAXPATHLEN
))
110 s
->dst
= strdup(dst
);
115 if (COPYFILE_DEBUG
& s
->flags
)
118 if ((e
= getenv("COPYFILE_DEBUG")))
120 s
->debug
= strtol(e
, NULL
, 0);
124 copyfile_debug(1, "debug value set to: %d\n", s
->debug
);
127 if (COPYFILE_CHECK
& flags
)
128 return copyfile_check(s
);
130 if (copyfile_open(s
) < 0)
134 if (s
->dst_fd
== -2 || s
->src_fd
== -2)
137 if (COPYFILE_PACK
& flags
)
139 if (copyfile_pack(s
) < 0)
144 } else if (COPYFILE_UNPACK
& flags
)
146 if (!(COPYFILE_STAT
& flags
|| COPYFILE_ACL
& flags
))
147 fix_perms
= !copyfile_fix_perms(s
, &original_fsec
, 1);
149 if (copyfile_unpack(s
) < 0)
153 if (COPYFILE_SECURITY
& flags
)
155 if (copyfile_security(s
) < 0)
157 copyfile_warn("error processing security information");
160 } else if (COPYFILE_UNPACK
& flags
)
162 fix_perms
= !copyfile_fix_perms(s
, &original_fsec
, 1);
163 if (copyfile_unpack(s
) < 0)
167 if (COPYFILE_SECURITY
& flags
)
169 copyfile_warn("error processing stat information");
173 fix_perms
= !copyfile_fix_perms(s
, &original_fsec
, 1);
175 if (COPYFILE_XATTR
& flags
)
177 if (copyfile_xattr(s
) < 0)
179 copyfile_warn("error processing extended attributes");
183 if (COPYFILE_DATA
& flags
)
185 if (copyfile_data(s
) < 0)
187 copyfile_warn("error processing data");
189 if (s
->dst
&& unlink(s
->dst
))
190 copyfile_warn("%s: remove", s
->src
);
198 copyfile_fix_perms(s
, &original_fsec
, 0);
200 filesec_free(original_fsec
);
203 ret
-= copyfile_free(s
);
207 copyfile_state_t
copyfile_init(void)
209 copyfile_state_t s
= (copyfile_state_t
) calloc(1, sizeof(struct _copyfile_state
));
215 s
->fsec
= filesec_init();
221 int copyfile_free(copyfile_state_t s
)
226 filesec_free(s
->fsec
);
232 if (copyfile_close(s
) < 0)
234 copyfile_warn("error closing files");
242 static int copyfile_close(copyfile_state_t s
)
247 if (s
->dst_fd
!= -2 && close(s
->dst_fd
))
249 copyfile_warn("close on %s", s
->dst
);
255 static int copyfile_fix_perms(copyfile_state_t s
, filesec_t
*fsec
, int on
)
264 if(statx_np(s
->dst
, &sb
, *fsec
))
267 tmp_fsec
= filesec_dup(*fsec
);
269 if (!filesec_get_property(tmp_fsec
, FILESEC_ACL
, &acl
))
272 acl_permset_t permset
;
275 if (mbr_uid_to_uuid(getuid(), qual
) != 0)
278 if (acl_create_entry_np(&acl
, &entry
, ACL_FIRST_ENTRY
) == -1)
280 if (acl_get_permset(entry
, &permset
) == -1)
282 if (acl_clear_perms(permset
) == -1)
284 if (acl_add_perm(permset
, ACL_WRITE_DATA
) == -1)
286 if (acl_add_perm(permset
, ACL_WRITE_ATTRIBUTES
) == -1)
288 if (acl_add_perm(permset
, ACL_WRITE_EXTATTRIBUTES
) == -1)
290 if (acl_set_tag_type(entry
, ACL_EXTENDED_ALLOW
) == -1)
293 if(acl_set_permset(entry
, permset
) == -1)
295 if(acl_set_qualifier(entry
, qual
) == -1)
298 if (filesec_set_property(tmp_fsec
, FILESEC_ACL
, &acl
) != 0)
302 if (filesec_get_property(tmp_fsec
, FILESEC_MODE
, &mode
) == 0)
307 if (filesec_set_property(tmp_fsec
, FILESEC_MODE
, &mode
) != 0)
311 if (fchmodx_np(s
->dst_fd
, tmp_fsec
) < 0 && errno
!= ENOTSUP
)
312 copyfile_warn("setting security information");
313 filesec_free(tmp_fsec
);
315 if (fchmodx_np(s
->dst_fd
, *fsec
) < 0 && errno
!= ENOTSUP
)
316 copyfile_warn("setting security information");
325 static int copyfile_open(copyfile_state_t s
)
327 int oflags
= O_EXCL
| O_CREAT
;
329 if (s
->src
&& s
->src_fd
== -2)
331 if ((COPYFILE_NOFOLLOW_SRC
& s
->flags
? lstatx_np
: statx_np
)
332 (s
->src
, &s
->sb
, s
->fsec
))
334 copyfile_warn("stat on %s", s
->src
);
337 if ((s
->src_fd
= open(s
->src
, O_RDONLY
, 0)) < 0)
339 copyfile_warn("open on %s", s
->src
);
344 if (s
->dst
&& s
->dst_fd
== -2)
346 if (COPYFILE_DATA
& s
->flags
|| COPYFILE_PACK
& s
->flags
)
349 if (COPYFILE_ACL
& ~s
->flags
)
351 if (filesec_set_property(s
->fsec
, FILESEC_ACL
, NULL
) == -1)
353 copyfile_debug(1, "unsetting acl attribute on %s", s
->dst
);
356 if (COPYFILE_STAT
& ~s
->flags
)
358 if (filesec_set_property(s
->fsec
, FILESEC_MODE
, NULL
) == -1)
360 copyfile_debug(1, "unsetting mode attribute on %s", s
->dst
);
363 if (COPYFILE_PACK
& s
->flags
)
366 if (filesec_set_property(s
->fsec
, FILESEC_MODE
, &m
) == -1)
368 mode_t m
= S_IRUSR
| S_IWUSR
;
369 if (filesec_set_property(s
->fsec
, FILESEC_MODE
, &m
) == -1)
371 copyfile_debug(1, "setting mode attribute on %s", s
->dst
);
374 if (filesec_set_property(s
->fsec
, FILESEC_OWNER
, NULL
) == -1)
376 copyfile_debug(1, "unsetting uid attribute on %s", s
->dst
);
378 if (filesec_set_property(s
->fsec
, FILESEC_UUID
, NULL
) == -1)
380 copyfile_debug(1, "unsetting uuid attribute on %s", s
->dst
);
382 if (filesec_set_property(s
->fsec
, FILESEC_GROUP
, NULL
) == -1)
384 copyfile_debug(1, "unsetting gid attribute on %s", s
->dst
);
388 if (COPYFILE_UNLINK
& s
->flags
&& unlink(s
->dst
) < 0)
390 copyfile_warn("%s: remove", s
->dst
);
394 while((s
->dst_fd
= openx_np(s
->dst
, oflags
, s
->fsec
)) < 0)
398 oflags
= oflags
& ~O_CREAT
;
401 copyfile_warn("open on %s", s
->dst
);
408 static copyfile_flags_t
copyfile_check(copyfile_state_t s
)
411 copyfile_flags_t ret
= 0;
417 if (COPYFILE_XATTR
& s
->flags
)
418 if (listxattr(s
->src
, 0, 0, 0) > 0)
419 ret
|= COPYFILE_XATTR
;
421 if (COPYFILE_ACL
& s
->flags
)
423 (COPYFILE_NOFOLLOW_SRC
& s
->flags
? lstatx_np
: statx_np
)
424 (s
->src
, &s
->sb
, s
->fsec
);
426 if (filesec_get_property(s
->fsec
, FILESEC_ACL
, &acl
) == 0)
433 static int copyfile_data(copyfile_state_t s
)
440 if ((bp
= malloc((size_t)s
->sb
.st_blksize
)) == NULL
)
443 warnx("malloc failed");
446 blen
= s
->sb
.st_blksize
;
448 while ((nread
= read(s
->src_fd
, bp
, (size_t)blen
)) > 0)
450 if (write(s
->dst_fd
, bp
, (size_t)nread
) != nread
)
452 copyfile_warn("writing to %s", s
->dst
);
458 copyfile_warn("reading from %s", s
->src
);
464 if (ftruncate(s
->dst_fd
, s
->sb
.st_size
) < 0)
470 static int copyfile_security(copyfile_state_t s
)
472 filesec_t fsec_dst
= filesec_init();
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;
481 if (COPYFILE_ACL
& s
->flags
)
483 if(fstatx_np(s
->dst_fd
, &sb
, fsec_dst
))
488 if (filesec_get_property(fsec_dst
, FILESEC_ACL
, &acl_dst
))
492 acl_dst
= acl_init(4);
502 if (filesec_get_property(s
->fsec
, FILESEC_ACL
, &acl_src
))
508 acl_dst
= acl_init(4);
518 for (;acl_get_entry(acl_src
,
519 entry_src
== NULL
? ACL_FIRST_ENTRY
: ACL_NEXT_ENTRY
,
522 acl_get_flagset_np(entry_src
, &flags
);
523 if (!acl_get_flag_np(flags
, ACL_ENTRY_INHERITED
))
525 if ((ret
= acl_create_entry(&acl_dst
, &entry_dst
)) == -1)
528 if ((ret
= acl_copy_entry(entry_dst
, entry_src
)) == -1)
531 copyfile_debug(1, "copied acl entry from %s to %s", s
->src
, s
->dst
);
536 if (!filesec_set_property(s
->fsec
, FILESEC_ACL
, &acl_dst
))
538 copyfile_debug(1, "altered acl");
542 if (fchmodx_np(s
->dst_fd
, s
->fsec
) < 0 && errno
!= ENOTSUP
)
543 copyfile_warn("setting security information: %s", s
->dst
);
546 filesec_free(fsec_dst
);
547 if (inited_src
) acl_free(acl_src
);
548 if (inited_dst
) acl_free(acl_dst
);
553 static int copyfile_stat(copyfile_state_t s
)
555 struct timeval tval
[2];
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.)
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
);
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
);
574 static int copyfile_xattr(copyfile_state_t s
)
580 size_t bufsize
= 4096;
586 if (COPYFILE_NOFOLLOW_SRC
& s
->flags
)
587 flags
|= XATTR_NOFOLLOW
;
589 /* delete EAs on destination */
590 if ((nsize
= flistxattr(s
->dst_fd
, 0, 0, 0)) > 0)
592 if ((namebuf
= (char *) malloc(nsize
)) == NULL
)
595 nsize
= flistxattr(s
->dst_fd
, namebuf
, nsize
, 0);
598 for (name
= namebuf
; name
< namebuf
+ nsize
; name
+= strlen(name
) + 1)
599 fremovexattr(s
->dst_fd
, name
,flags
);
602 } else if (nsize
< 0)
604 if (errno
== ENOTSUP
)
610 /* get name list of EAs on source */
611 if ((nsize
= flistxattr(s
->src_fd
, 0, 0, 0)) < 0)
613 if (errno
== ENOTSUP
)
617 } else if (nsize
== 0)
620 if ((namebuf
= (char *) malloc(nsize
)) == NULL
)
623 nsize
= flistxattr(s
->src_fd
, namebuf
, nsize
, 0);
628 if ((xa_dataptr
= (void *) malloc(bufsize
)) == NULL
)
631 for (name
= namebuf
; name
< namebuf
+ nsize
; name
+= strlen(name
) + 1)
633 if ((xa_size
= fgetxattr(s
->src_fd
, name
, 0, 0, 0, flags
)) < 0)
639 if (xa_size
> bufsize
)
643 (void *) realloc((void *) xa_dataptr
, bufsize
)) == NULL
)
650 if ((asize
= fgetxattr(s
->src_fd
, name
, xa_dataptr
, xa_size
, 0, flags
)) < 0)
656 if (xa_size
!= asize
)
659 if (fsetxattr(s
->dst_fd
, name
, xa_dataptr
, xa_size
, 0, flags
) < 0)
665 free((void *) xa_dataptr
);
670 #ifdef _COPYFILE_TEST
671 #define COPYFILE_OPTION(x) { #x, COPYFILE_ ## x },
673 struct {char *s
; int v
;} opts
[] = {
675 COPYFILE_OPTION(STAT
)
676 COPYFILE_OPTION(XATTR
)
677 COPYFILE_OPTION(DATA
)
678 COPYFILE_OPTION(SECURITY
)
679 COPYFILE_OPTION(METADATA
)
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
)
695 int main(int c
, char *v
[])
701 errx(1, "insufficient arguments");
705 for (i
= 0; opts
[i
].s
!= NULL
; ++i
)
707 if (strcasecmp(opts
[i
].s
, v
[c
]) == 0)
709 printf("option %d: %s <- %d\n", c
, opts
[i
].s
, opts
[i
].v
);
716 return copyfile(v
[1], v
[2], NULL
, flags
);
720 * Apple Double Create
722 * Create an Apple Double "._" file from a file's extented attributes
724 * Copyright (c) 2004 Apple Computer, Inc. All rights reserved.
728 #define offsetof(type, member) ((size_t)(&((type *)0)->member))
730 #define XATTR_MAXATTRLEN (4*1024)
734 Typical "._" AppleDouble Header File layout:
735 ------------------------------------------------------------
740 .-- AD ENTRY[0] Finder Info Entry (must be first)
741 .--+-- AD ENTRY[1] Resource Fork Entry (must be last)
743 | ///////////// Fixed Size Data (32 bytes)
747 | ATTR ENTRY[1] --+--.
748 | ATTR ENTRY[2] --+--+--.
750 | ATTR ENTRY[N] --+--+--+--.
751 | ATTR DATA 0 <-' | | |
753 | ATTR DATA 1 <----' | |
755 | ATTR DATA 2 <-------' |
758 | ATTR DATA N <----------'
760 | Attribute Free Space
763 ///////////// Variable Sized Data
772 ------------------------------------------------------------
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.
782 * On Disk Data Structures
784 * Note: Motorola 68K alignment and big-endian.
786 * See RFC 1740 for additional information about the AppleDouble file format.
790 #define ADH_MAGIC 0x00051607
791 #define ADH_VERSION 0x00020000
792 #define ADH_MACOSX "Mac OS X "
795 * AppleDouble Entry ID's
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
815 #define ATTR_FILE_PREFIX "._"
816 #define ATTR_HDR_MAGIC 0x41545452 /* 'ATTR' */
818 #define ATTR_BUF_SIZE 4096 /* default size of the attr file and how much we'll grow by */
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)
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.
832 #pragma options align=mac68k
834 #define FINDERINFOSIZE 32
836 typedef struct apple_double_entry
838 u_int32_t type
; /* entry type: see list, 0 invalid */
839 u_int32_t offset
; /* entry data offset from the beginning of the file. */
840 u_int32_t length
; /* entry data length in bytes. */
841 } apple_double_entry_t
;
844 typedef struct apple_double_header
846 u_int32_t magic
; /* == ADH_MAGIC */
847 u_int32_t version
; /* format version: 2 = 0x00020000 */
849 u_int16_t numEntries
; /* number of entries which follow */
850 apple_double_entry_t entries
[2]; /* 'finfo' & 'rsrc' always exist */
851 u_int8_t finfo
[FINDERINFOSIZE
]; /* Must start with Finder Info (32 bytes) */
852 u_int8_t pad
[2]; /* get better alignment inside attr_header */
853 } apple_double_header_t
;
856 /* Entries are aligned on 4 byte boundaries */
857 typedef struct attr_entry
859 u_int32_t offset
; /* file offset to data */
860 u_int32_t length
; /* size of attribute data */
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) */
867 /* Header + entries must fit into 64K */
868 typedef struct attr_header
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];
882 #pragma options align=reset
884 #define SWAP16(x) OSSwapBigToHostInt16(x)
885 #define SWAP32(x) OSSwapBigToHostInt32(x)
886 #define SWAP64(x) OSSwapBigToHostInt64(x)
888 #define ATTR_ALIGN 3L /* Use four-byte alignment */
890 #define ATTR_ENTRY_LENGTH(namelen) \
891 ((sizeof(attr_entry_t) - 1 + (namelen) + ATTR_ALIGN) & (~ATTR_ALIGN))
893 #define ATTR_NEXT(ae) \
894 (attr_entry_t *)((u_int8_t *)(ae) + ATTR_ENTRY_LENGTH((ae)->namelen))
896 #define XATTR_SECURITY_NAME "com.apple.acl.text"
899 * Endian swap Apple Double header
902 swap_adhdr(apple_double_header_t
*adh
)
904 #if BYTE_ORDER == LITTLE_ENDIAN
908 count
= (adh
->magic
== ADH_MAGIC
) ? adh
->numEntries
: SWAP16(adh
->numEntries
);
910 adh
->magic
= SWAP32 (adh
->magic
);
911 adh
->version
= SWAP32 (adh
->version
);
912 adh
->numEntries
= SWAP16 (adh
->numEntries
);
914 for (i
= 0; i
< count
; i
++)
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
);
924 * Endian swap extended attributes header
927 swap_attrhdr(attr_header_t
*ah
)
929 #if BYTE_ORDER == LITTLE_ENDIAN
934 count
= (ah
->magic
== ATTR_HDR_MAGIC
) ? ah
->num_attrs
: SWAP16(ah
->num_attrs
);
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
);
944 ae
= (attr_entry_t
*)(&ah
[1]);
945 for (i
= 0; i
< count
; i
++, ae
++)
947 ae
->offset
= SWAP32 (ae
->offset
);
948 ae
->length
= SWAP32 (ae
->length
);
949 ae
->flags
= SWAP16 (ae
->flags
);
954 static u_int32_t emptyfinfo
[8] = {0};
956 static int copyfile_unpack(copyfile_state_t s
)
960 apple_double_header_t
*adhdr
;
964 if (s
->sb
.st_size
< ATTR_MAX_HDR_SIZE
)
965 hdrsize
= s
->sb
.st_size
;
967 hdrsize
= ATTR_MAX_HDR_SIZE
;
969 buffer
= calloc(1, hdrsize
);
970 bytes
= pread(s
->src_fd
, buffer
, hdrsize
, 0);
974 copyfile_debug(1, "pread returned: %d", bytes
);
981 "pread couldn't read entire header: %d of %d",
982 (int)bytes
, (int)s
->sb
.st_size
);
986 adhdr
= (apple_double_header_t
*)buffer
;
989 * Check for Apple Double file.
991 if (bytes
< sizeof(apple_double_header_t
) - 2 ||
992 SWAP32(adhdr
->magic
) != ADH_MAGIC
||
993 SWAP32(adhdr
->version
) != ADH_VERSION
||
994 SWAP16(adhdr
->numEntries
) != 2 ||
995 SWAP32(adhdr
->entries
[0].type
) != AD_FINDERINFO
)
997 if (COPYFILE_VERBOSE
& s
->flags
)
998 copyfile_warn("Not a valid Apple Double header");
1005 * Extract the extended attributes.
1008 * This assumes that the data is already in memory (not
1009 * the case when there are lots of attributes or one of
1010 * the attributes is very large.
1012 if (adhdr
->entries
[0].length
> FINDERINFOSIZE
)
1014 attr_header_t
*attrhdr
;
1015 attr_entry_t
*entry
;
1019 attrhdr
= (attr_header_t
*)buffer
;
1020 swap_attrhdr(attrhdr
);
1021 if (attrhdr
->magic
!= ATTR_HDR_MAGIC
)
1023 if (COPYFILE_VERBOSE
& s
->flags
)
1024 copyfile_warn("bad attribute header");
1028 count
= attrhdr
->num_attrs
;
1029 entry
= (attr_entry_t
*)&attrhdr
[1];
1030 for (i
= 0; i
< count
; i
++)
1034 copyfile_debug(2, "extracting \"%s\" (%d bytes)",
1035 entry
->name
, entry
->length
);
1036 dataptr
= (char *)attrhdr
+ entry
->offset
;
1038 if (COPYFILE_ACL
& s
->flags
&& strncmp(entry
->name
, XATTR_SECURITY_NAME
, strlen(XATTR_SECURITY_NAME
)) == 0)
1041 if ((acl
= acl_from_text(dataptr
)) != NULL
)
1043 if (filesec_set_property(s
->fsec
, FILESEC_ACL
, &acl
) < 0)
1046 if ((acl
= acl_from_text(dataptr
)) != NULL
)
1048 if (filesec_set_property(s
->fsec
, FILESEC_ACL
, &acl
) < 0)
1050 copyfile_debug(1, "setting acl");
1052 else if (fchmodx_np(s
->dst_fd
, s
->fsec
) < 0 && errno
!= ENOTSUP
)
1053 copyfile_warn("setting security information");
1057 if (COPYFILE_XATTR
& s
->flags
&& (fsetxattr(s
->dst_fd
, entry
->name
, dataptr
, entry
->length
, 0, 0))) {
1058 if (COPYFILE_VERBOSE
& s
->flags
)
1059 copyfile_warn("error %d setting attribute %s", error
, entry
->name
);
1062 else if (fchmodx_np(s
->dst_fd
, s
->fsec
) < 0 && errno
!= ENOTSUP
)
1063 copyfile_warn("setting security information");
1067 if (COPYFILE_XATTR
& s
->flags
&& (fsetxattr(s
->dst_fd
, entry
->name
, dataptr
, entry
->length
, 0, 0))) {
1068 if (COPYFILE_VERBOSE
& s
->flags
)
1069 copyfile_warn("error %d setting attribute %s", error
, entry
->name
);
1072 entry
= ATTR_NEXT(entry
);
1077 * Extract the Finder Info.
1079 if (bcmp((u_int8_t
*)buffer
+ adhdr
->entries
[0].offset
, emptyfinfo
, sizeof(emptyfinfo
)) != 0)
1081 copyfile_debug(1, " extracting \"%s\" (32 bytes)", XATTR_FINDERINFO_NAME
);
1082 error
= fsetxattr(s
->dst_fd
, XATTR_FINDERINFO_NAME
, (u_int8_t
*)buffer
+ adhdr
->entries
[0].offset
, sizeof(emptyfinfo
), 0, 0);
1088 * Extract the Resource Fork.
1090 if (adhdr
->entries
[1].type
== AD_RESOURCE
&&
1091 adhdr
->entries
[1].length
> 0)
1093 void * rsrcforkdata
;
1097 length
= adhdr
->entries
[1].length
;
1098 offset
= adhdr
->entries
[1].offset
;
1099 rsrcforkdata
= malloc(length
);
1101 bytes
= pread(s
->src_fd
, rsrcforkdata
, length
, offset
);
1106 copyfile_debug(1, "couldn't read resource fork");
1111 "couldn't read resource fork (only read %d bytes of %d)",
1112 (int)bytes
, (int)length
);
1117 error
= fsetxattr(s
->dst_fd
, XATTR_RESOURCEFORK_NAME
, rsrcforkdata
, bytes
, 0, 0);
1120 copyfile_debug(1, "error %d setting resource fork attribute", error
);
1124 copyfile_debug(1, "extracting \"%s\" (%d bytes)",
1125 XATTR_RESOURCEFORK_NAME
, (int)length
);
1133 static int copyfile_pack_acl(copyfile_state_t s
, void **buf
, ssize_t
*len
)
1139 if (filesec_get_property(s
->fsec
, FILESEC_ACL
, &acl
) < 0)
1141 if (errno
!= ENOENT
)
1144 if (COPYFILE_VERBOSE
& s
->flags
)
1145 copyfile_warn("getting acl");
1150 if ((acl_text
= acl_to_text(acl
, len
)) != NULL
)
1152 *buf
= malloc(*len
);
1153 memcpy(*buf
, acl_text
, *len
);
1156 copyfile_debug(1, "copied acl (%ld) %p", *len
, *buf
);
1161 static int copyfile_pack_rsrcfork(copyfile_state_t s
, attr_header_t
*filehdr
)
1166 /* Get the resource fork size */
1167 if ((datasize
= fgetxattr(s
->src_fd
, XATTR_RESOURCEFORK_NAME
, NULL
, 0, 0, 0)) < 0)
1169 if (COPYFILE_VERBOSE
& s
->flags
)
1170 copyfile_warn("skipping attr \"%s\" due to error %d", XATTR_RESOURCEFORK_NAME
, errno
);
1174 if ((databuf
= malloc(datasize
)) == NULL
)
1176 copyfile_warn("malloc");
1180 if (fgetxattr(s
->src_fd
, XATTR_RESOURCEFORK_NAME
, databuf
, datasize
, 0, 0) != datasize
)
1182 if (COPYFILE_VERBOSE
& s
->flags
)
1183 copyfile_warn("couldn't read entire resource fork");
1187 /* Write the resource fork to disk. */
1188 if (pwrite(s
->dst_fd
, databuf
, datasize
, filehdr
->appledouble
.entries
[1].offset
) != datasize
)
1190 if (COPYFILE_VERBOSE
& s
->flags
)
1191 copyfile_warn("couldn't write resource fork");
1193 copyfile_debug(1, "copied %d bytes of \"%s\" data @ offset 0x%08x",
1194 datasize
, XATTR_RESOURCEFORK_NAME
, filehdr
->appledouble
.entries
[1].offset
);
1195 filehdr
->appledouble
.entries
[1].length
= datasize
;
1202 static int copyfile_pack(copyfile_state_t s
)
1206 attr_header_t
*filehdr
;
1207 attr_entry_t
*entry
;
1214 int hasrsrcfork
= 0;
1217 filehdr
= (attr_header_t
*) calloc(1, ATTR_MAX_SIZE
);
1218 attrnamebuf
= calloc(1, ATTR_MAX_HDR_SIZE
);
1221 * Fill in the Apple Double Header defaults.
1223 filehdr
->appledouble
.magic
= SWAP32 (ADH_MAGIC
);
1224 filehdr
->appledouble
.version
= SWAP32 (ADH_VERSION
);
1225 filehdr
->appledouble
.numEntries
= SWAP16 (2);
1226 filehdr
->appledouble
.entries
[0].type
= SWAP32 (AD_FINDERINFO
);
1227 filehdr
->appledouble
.entries
[0].offset
= SWAP32 (offsetof(apple_double_header_t
, finfo
));
1228 filehdr
->appledouble
.entries
[0].length
= SWAP32 (FINDERINFOSIZE
);
1229 filehdr
->appledouble
.entries
[1].type
= SWAP32 (AD_RESOURCE
);
1230 filehdr
->appledouble
.entries
[1].offset
= SWAP32 (offsetof(apple_double_header_t
, pad
));
1231 filehdr
->appledouble
.entries
[1].length
= 0;
1232 bcopy(ADH_MACOSX
, filehdr
->appledouble
.filler
, sizeof(filehdr
->appledouble
.filler
));
1235 * Fill in the initial Attribute Header.
1237 filehdr
->magic
= SWAP32 (ATTR_HDR_MAGIC
);
1238 filehdr
->debug_tag
= SWAP32 (s
->sb
.st_ino
);
1239 filehdr
->data_start
= SWAP32 (sizeof(attr_header_t
));
1242 * Collect the attribute names.
1244 entry
= (attr_entry_t
*)((char *)filehdr
+ sizeof(attr_header_t
));
1247 * Test if there are acls to copy
1249 if (COPYFILE_ACL
& s
->flags
)
1251 if (filesec_get_property(s
->fsec
, FILESEC_ACL
, &datasize
) < 0)
1253 copyfile_debug(1, "no acl entries found (%d)", datasize
< 0 ? errno
: 0);
1256 offset
= strlen(XATTR_SECURITY_NAME
) + 1;
1257 strcpy(attrnamebuf
, XATTR_SECURITY_NAME
);
1261 if (COPYFILE_XATTR
& s
->flags
)
1263 if ((listsize
= flistxattr(s
->src_fd
, attrnamebuf
+ offset
, ATTR_MAX_HDR_SIZE
, 0)) <= 0)
1265 copyfile_debug(1, "no extended attributes found (%d)", errno
);
1267 if (listsize
> ATTR_MAX_HDR_SIZE
)
1269 copyfile_debug(1, "extended attribute list too long");
1270 listsize
= ATTR_MAX_HDR_SIZE
;
1275 for (nameptr
= attrnamebuf
; nameptr
< attrnamebuf
+ listsize
; nameptr
+= namelen
)
1277 namelen
= strlen(nameptr
) + 1;
1278 /* Skip over FinderInfo or Resource Fork names */
1279 if (strncmp(nameptr
, XATTR_FINDERINFO_NAME
, strlen(XATTR_FINDERINFO_NAME
)) == 0 ||
1280 strncmp(nameptr
, XATTR_RESOURCEFORK_NAME
, strlen(XATTR_RESOURCEFORK_NAME
)) == 0)
1283 entry
->namelen
= namelen
;
1285 bcopy(nameptr
, &entry
->name
[0], namelen
);
1286 copyfile_debug(2, "copied name [%s]", entry
->name
);
1288 entrylen
= ATTR_ENTRY_LENGTH(namelen
);
1289 entry
= (attr_entry_t
*)(((char *)entry
) + entrylen
);
1291 /* Update the attributes header. */
1292 filehdr
->num_attrs
++;
1293 filehdr
->data_start
+= entrylen
;
1298 * Collect the attribute data.
1300 entry
= (attr_entry_t
*)((char *)filehdr
+ sizeof(attr_header_t
));
1302 for (nameptr
= attrnamebuf
; nameptr
< attrnamebuf
+ listsize
; nameptr
+= namelen
+ 1)
1305 namelen
= strlen(nameptr
);
1307 if (strncmp(nameptr
, XATTR_SECURITY_NAME
, strlen(XATTR_SECURITY_NAME
)) == 0)
1308 copyfile_pack_acl(s
, &databuf
, &datasize
);
1310 /* Check for Finder Info. */
1311 if (strncmp(nameptr
, XATTR_FINDERINFO_NAME
, strlen(XATTR_FINDERINFO_NAME
)) == 0)
1313 datasize
= fgetxattr(s
->src_fd
, nameptr
, (u_int8_t
*)filehdr
+ filehdr
->appledouble
.entries
[0].offset
, 32, 0, 0);
1316 if (COPYFILE_VERBOSE
& s
->flags
)
1317 copyfile_warn("skipping attr \"%s\" due to error %d", nameptr
, errno
);
1318 } else if (datasize
!= 32)
1320 if (COPYFILE_VERBOSE
& s
->flags
)
1321 copyfile_warn("unexpected size (%ld) for \"%s\"", datasize
, nameptr
);
1324 if (COPYFILE_VERBOSE
& s
->flags
)
1325 copyfile_warn(" copied 32 bytes of \"%s\" data @ offset 0x%08x",
1326 XATTR_FINDERINFO_NAME
, filehdr
->appledouble
.entries
[0].offset
);
1328 continue; /* finder info doesn't have an attribute entry */
1330 /* Check for Resource Fork. */
1331 if (strncmp(nameptr
, XATTR_RESOURCEFORK_NAME
, strlen(XATTR_RESOURCEFORK_NAME
)) == 0)
1337 /* Just a normal attribute. */
1338 datasize
= fgetxattr(s
->src_fd
, nameptr
, NULL
, 0, 0, 0);
1343 if (COPYFILE_VERBOSE
& s
->flags
)
1344 copyfile_warn("skipping attr \"%s\" due to error %d", nameptr
, errno
);
1347 if (datasize
> XATTR_MAXATTRLEN
)
1349 if (COPYFILE_VERBOSE
& s
->flags
)
1350 copyfile_warn("skipping attr \"%s\" (too big)", nameptr
);
1353 databuf
= malloc(datasize
);
1354 datasize
= fgetxattr(s
->src_fd
, nameptr
, databuf
, datasize
, 0, 0);
1357 entry
->length
= datasize
;
1358 entry
->offset
= filehdr
->data_start
+ filehdr
->data_length
;
1360 filehdr
->data_length
+= datasize
;
1363 * This assumes that the data is fits in memory (not
1364 * the case when there are lots of attributes or one of
1365 * the attributes is very large.
1367 bcopy(databuf
, (char*)filehdr
+ entry
->offset
, datasize
);
1370 copyfile_debug(1, "copied %ld bytes of \"%s\" data @ offset 0x%08x", datasize
, nameptr
, entry
->offset
);
1372 /* bump to next entry */
1373 entrylen
= ATTR_ENTRY_LENGTH(entry
->namelen
);
1374 entry
= (attr_entry_t
*)((char *)entry
+ entrylen
);
1377 if (filehdr
->data_length
> 0)
1379 /* Now we know where the resource fork data starts. */
1380 filehdr
->appledouble
.entries
[1].offset
= (filehdr
->data_start
+ filehdr
->data_length
);
1382 /* We also know the size of the "Finder Info entry. */
1383 filehdr
->appledouble
.entries
[0].length
=
1384 filehdr
->appledouble
.entries
[1].offset
- filehdr
->appledouble
.entries
[0].offset
;
1386 filehdr
->total_size
= SWAP32 (filehdr
->appledouble
.entries
[1].offset
);
1389 /* Copy Resource Fork. */
1390 if (hasrsrcfork
&& (error
= copyfile_pack_rsrcfork(s
, filehdr
)))
1393 /* Write the header to disk. */
1394 datasize
= filehdr
->appledouble
.entries
[1].offset
;
1396 if (pwrite(s
->dst_fd
, filehdr
, datasize
, 0) != datasize
)
1398 if (COPYFILE_VERBOSE
& s
->flags
)
1399 copyfile_warn("couldn't write file header");
1410 return copyfile_stat(s
);