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>
38 #include <sys/xattr.h>
39 #include <sys/syscall.h>
40 #include <sys/param.h>
42 #include <libkern/OSByteOrder.h>
43 #include <membership.h>
47 struct _copyfile_state
55 copyfile_flags_t flags
;
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
);
69 static copyfile_flags_t
copyfile_check (copyfile_state_t
);
70 static int copyfile_fix_perms(copyfile_state_t
, filesec_t
*, int);
72 #define COPYFILE_DEBUG (1<<31)
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__); \
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__); \
89 int copyfile(const char *src
, const char *dst
, copyfile_state_t state
, copyfile_flags_t flags
)
92 copyfile_state_t s
= state
;
93 filesec_t original_fsec
;
96 original_fsec
= filesec_init();
97 if (s
== NULL
&& (s
= copyfile_init()) == NULL
)
102 if (s
->src_fd
!= -2 && s
->src
&& !strncmp(src
, s
->src
, MAXPATHLEN
))
104 s
->src
= strdup(src
);
109 if (s
->dst_fd
!= -2 && s
->dst
&& !strncmp(dst
, s
->dst
, MAXPATHLEN
))
111 s
->dst
= strdup(dst
);
116 if (COPYFILE_DEBUG
& s
->flags
)
119 if ((e
= getenv("COPYFILE_DEBUG")))
121 s
->debug
= strtol(e
, NULL
, 0);
125 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 } __attribute__((packed
)) 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 } __attribute__((packed
)) 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) */
864 } __attribute__((packed
)) attr_entry_t
;
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];
879 } __attribute__((packed
)) attr_header_t
;
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 const u_int32_t emptyfinfo
[8] = {0};
956 static int copyfile_unpack(copyfile_state_t s
)
959 void * buffer
, * endptr
;
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 if (buffer
== NULL
) {
971 copyfile_debug(1, "copyfile_unpack: calloc(1, %u) returned NULL", hdrsize
);
975 endptr
= (char*)buffer
+ hdrsize
;
977 bytes
= pread(s
->src_fd
, buffer
, hdrsize
, 0);
981 copyfile_debug(1, "pread returned: %d", bytes
);
988 "pread couldn't read entire header: %d of %d",
989 (int)bytes
, (int)s
->sb
.st_size
);
993 adhdr
= (apple_double_header_t
*)buffer
;
996 * Check for Apple Double file.
998 if (bytes
< sizeof(apple_double_header_t
) - 2 ||
999 SWAP32(adhdr
->magic
) != ADH_MAGIC
||
1000 SWAP32(adhdr
->version
) != ADH_VERSION
||
1001 SWAP16(adhdr
->numEntries
) != 2 ||
1002 SWAP32(adhdr
->entries
[0].type
) != AD_FINDERINFO
)
1004 if (COPYFILE_VERBOSE
& s
->flags
)
1005 copyfile_warn("Not a valid Apple Double header");
1012 * Extract the extended attributes.
1015 * This assumes that the data is already in memory (not
1016 * the case when there are lots of attributes or one of
1017 * the attributes is very large.
1019 if (adhdr
->entries
[0].length
> FINDERINFOSIZE
)
1021 attr_header_t
*attrhdr
;
1022 attr_entry_t
*entry
;
1026 if (hdrsize
< sizeof(attr_header_t
)) {
1027 copyfile_warn("bad attribute header: %u < %u", hdrsize
, sizeof(attr_header_t
));
1032 attrhdr
= (attr_header_t
*)buffer
;
1033 swap_attrhdr(attrhdr
);
1034 if (attrhdr
->magic
!= ATTR_HDR_MAGIC
)
1036 if (COPYFILE_VERBOSE
& s
->flags
)
1037 copyfile_warn("bad attribute header");
1041 count
= attrhdr
->num_attrs
;
1042 entry
= (attr_entry_t
*)&attrhdr
[1];
1044 for (i
= 0; i
< count
; i
++)
1049 * First we do some simple sanity checking.
1050 * +) See if entry is within the buffer's range;
1052 * +) Check the attribute name length; if it's longer than the
1053 * maximum, we truncate it down. (We could error out as well;
1054 * I'm not sure which is the better way to go here.)
1056 * +) If, given the name length, it goes beyond the end of
1057 * the buffer, error out.
1059 * +) If the last byte isn't a NUL, make it a NUL. (Since we
1060 * truncated the name length above, we truncate the name here.)
1062 * +) If entry->offset is so large that it causes dataptr to
1063 * go beyond the end of the buffer -- or, worse, so large that
1064 * it wraps around! -- we error out.
1066 * +) If entry->length would cause the entry to go beyond the
1067 * end of the buffer (or, worse, wrap around to before it),
1068 * *or* if the length is larger than the hdrsize, we error out.
1069 * (An explanation of that: what we're checking for there is
1070 * the small range of values such that offset+length would cause
1071 * it to go beyond endptr, and then wrap around past buffer. We
1072 * care about this because we are passing entry->length down to
1073 * fgetxattr() below, and an erroneously large value could cause
1074 * problems there. By making sure that it's less than hdrsize,
1075 * which has already been sanity-checked above, we're safe.
1076 * That may mean that the check against < buffer is unnecessary.)
1078 if ((void*)entry
>= endptr
|| (void*)entry
< buffer
) {
1079 if (COPYFILE_VERBOSE
& s
->flags
)
1080 copyfile_warn("Incomplete or corrupt attribute entry");
1085 if (((void*)entry
+ sizeof(*entry
)) > endptr
) {
1086 if (COPYFILE_VERBOSE
& s
->flags
)
1087 copyfile_warn("Incomplete or corrupt attribute entry");
1092 if (entry
->namelen
> ATTR_MAX_NAME_LEN
) {
1093 entry
->namelen
= ATTR_MAX_NAME_LEN
;
1095 if ((void*)(entry
->name
+ entry
->namelen
) >= endptr
) {
1096 if (COPYFILE_VERBOSE
& s
->flags
)
1097 copyfile_warn("Incomplete or corrupt attribute entry");
1102 if (entry
->name
[entry
->namelen
] != 0) {
1103 entry
->name
[entry
->namelen
] = 0;
1106 copyfile_debug(3, "extracting \"%s\" (%d bytes) at offset %u",
1107 entry
->name
, entry
->length
, entry
->offset
);
1109 dataptr
= (char *)attrhdr
+ entry
->offset
;
1111 if (dataptr
>= endptr
|| dataptr
< buffer
) {
1112 copyfile_debug(1, "Entry %d overflows: offset = %u", entry
->offset
);
1116 if ((dataptr
+ entry
->length
) > endptr
||
1117 ((dataptr
+ entry
->length
) < buffer
) ||
1118 (entry
->length
> hdrsize
)) {
1119 if (COPYFILE_VERBOSE
& s
->flags
)
1120 copyfile_warn("Incomplete or corrupt attribute entry");
1121 copyfile_debug(1, "Entry %d length overflows: dataptr = %u, offset = %u, length = %u, buffer = %u, endptr = %u",
1122 i
, dataptr
, entry
->offset
, entry
->length
, buffer
, endptr
);
1127 if (COPYFILE_ACL
& s
->flags
&& strcmp((char*)entry
->name
, XATTR_SECURITY_NAME
) == 0)
1128 copyfile_debug(2, "extracting \"%s\" (%d bytes)",
1129 entry
->name
, entry
->length
);
1130 dataptr
= (char *)attrhdr
+ entry
->offset
;
1132 if (COPYFILE_ACL
& s
->flags
&& strncmp(entry
->name
, XATTR_SECURITY_NAME
, strlen(XATTR_SECURITY_NAME
)) == 0)
1135 if ((acl
= acl_from_text(dataptr
)) != NULL
)
1137 if (filesec_set_property(s
->fsec
, FILESEC_ACL
, &acl
) < 0)
1140 if ((acl
= acl_from_text(dataptr
)) != NULL
)
1142 if (filesec_set_property(s
->fsec
, FILESEC_ACL
, &acl
) < 0)
1144 copyfile_debug(1, "setting acl");
1146 else if (fchmodx_np(s
->dst_fd
, s
->fsec
) < 0 && errno
!= ENOTSUP
)
1147 copyfile_warn("setting security information");
1150 } else if (COPYFILE_XATTR
& s
->flags
&& (fsetxattr(s
->dst_fd
, entry
->name
, dataptr
, entry
->length
, 0, 0))) {
1151 if (COPYFILE_VERBOSE
& s
->flags
)
1152 copyfile_warn("error %d setting attribute %s", error
, entry
->name
);
1155 else if (fchmodx_np(s
->dst_fd
, s
->fsec
) < 0 && errno
!= ENOTSUP
)
1156 copyfile_warn("setting security information");
1160 if (COPYFILE_XATTR
& s
->flags
&& (fsetxattr(s
->dst_fd
, entry
->name
, dataptr
, entry
->length
, 0, 0))) {
1161 if (COPYFILE_VERBOSE
& s
->flags
)
1162 copyfile_warn("error %d setting attribute %s", error
, entry
->name
);
1165 entry
= ATTR_NEXT(entry
);
1170 * Extract the Finder Info.
1172 if (adhdr
->entries
[0].offset
> (hdrsize
- sizeof(emptyfinfo
))) {
1177 if (bcmp((u_int8_t
*)buffer
+ adhdr
->entries
[0].offset
, emptyfinfo
, sizeof(emptyfinfo
)) != 0)
1179 copyfile_debug(1, " extracting \"%s\" (32 bytes)", XATTR_FINDERINFO_NAME
);
1180 error
= fsetxattr(s
->dst_fd
, XATTR_FINDERINFO_NAME
, (u_int8_t
*)buffer
+ adhdr
->entries
[0].offset
, sizeof(emptyfinfo
), 0, 0);
1186 * Extract the Resource Fork.
1188 if (adhdr
->entries
[1].type
== AD_RESOURCE
&&
1189 adhdr
->entries
[1].length
> 0)
1191 void * rsrcforkdata
= NULL
;
1195 length
= adhdr
->entries
[1].length
;
1196 offset
= adhdr
->entries
[1].offset
;
1197 rsrcforkdata
= malloc(length
);
1199 if (rsrcforkdata
== NULL
) {
1200 copyfile_debug(1, "could not allocate %u bytes for rsrcforkdata",
1206 bytes
= pread(s
->src_fd
, rsrcforkdata
, length
, offset
);
1211 copyfile_debug(1, "couldn't read resource fork");
1216 "couldn't read resource fork (only read %d bytes of %d)",
1217 (int)bytes
, (int)length
);
1222 error
= fsetxattr(s
->dst_fd
, XATTR_RESOURCEFORK_NAME
, rsrcforkdata
, bytes
, 0, 0);
1225 copyfile_debug(1, "error %d setting resource fork attribute", error
);
1229 copyfile_debug(1, "extracting \"%s\" (%d bytes)",
1230 XATTR_RESOURCEFORK_NAME
, (int)length
);
1236 if (buffer
) free(buffer
);
1240 static int copyfile_pack_acl(copyfile_state_t s
, void **buf
, ssize_t
*len
)
1246 if (filesec_get_property(s
->fsec
, FILESEC_ACL
, &acl
) < 0)
1248 if (errno
!= ENOENT
)
1251 if (COPYFILE_VERBOSE
& s
->flags
)
1252 copyfile_warn("getting acl");
1257 if ((acl_text
= acl_to_text(acl
, len
)) != NULL
)
1259 *buf
= malloc(*len
);
1260 memcpy(*buf
, acl_text
, *len
);
1263 copyfile_debug(1, "copied acl (%ld) %p", *len
, *buf
);
1268 static int copyfile_pack_rsrcfork(copyfile_state_t s
, attr_header_t
*filehdr
)
1273 /* Get the resource fork size */
1274 if ((datasize
= fgetxattr(s
->src_fd
, XATTR_RESOURCEFORK_NAME
, NULL
, 0, 0, 0)) < 0)
1276 if (COPYFILE_VERBOSE
& s
->flags
)
1277 copyfile_warn("skipping attr \"%s\" due to error %d", XATTR_RESOURCEFORK_NAME
, errno
);
1281 if ((databuf
= malloc(datasize
)) == NULL
)
1283 copyfile_warn("malloc");
1287 if (fgetxattr(s
->src_fd
, XATTR_RESOURCEFORK_NAME
, databuf
, datasize
, 0, 0) != datasize
)
1289 if (COPYFILE_VERBOSE
& s
->flags
)
1290 copyfile_warn("couldn't read entire resource fork");
1294 /* Write the resource fork to disk. */
1295 if (pwrite(s
->dst_fd
, databuf
, datasize
, filehdr
->appledouble
.entries
[1].offset
) != datasize
)
1297 if (COPYFILE_VERBOSE
& s
->flags
)
1298 copyfile_warn("couldn't write resource fork");
1300 copyfile_debug(1, "copied %d bytes of \"%s\" data @ offset 0x%08x",
1301 datasize
, XATTR_RESOURCEFORK_NAME
, filehdr
->appledouble
.entries
[1].offset
);
1302 filehdr
->appledouble
.entries
[1].length
= datasize
;
1309 static int copyfile_pack(copyfile_state_t s
)
1311 char *attrnamebuf
= NULL
, *endnamebuf
;
1312 void *databuf
= NULL
;
1313 attr_header_t
*filehdr
, *endfilehdr
;
1314 attr_entry_t
*entry
;
1315 ssize_t listsize
= 0;
1321 int hasrsrcfork
= 0;
1324 filehdr
= (attr_header_t
*) calloc(1, ATTR_MAX_SIZE
);
1325 if (filehdr
== NULL
) {
1329 endfilehdr
= ((void*)filehdr
) + ATTR_MAX_SIZE
;
1332 attrnamebuf
= calloc(1, ATTR_MAX_HDR_SIZE
);
1333 if (attrnamebuf
== NULL
) {
1337 endnamebuf
= ((char*)attrnamebuf
) + ATTR_MAX_HDR_SIZE
;
1341 * Fill in the Apple Double Header defaults.
1343 filehdr
->appledouble
.magic
= ADH_MAGIC
;
1344 filehdr
->appledouble
.version
= ADH_VERSION
;
1345 filehdr
->appledouble
.numEntries
= 2;
1346 filehdr
->appledouble
.entries
[0].type
= AD_FINDERINFO
;
1347 filehdr
->appledouble
.entries
[0].offset
= offsetof(apple_double_header_t
, finfo
);
1348 filehdr
->appledouble
.entries
[0].length
= FINDERINFOSIZE
;
1349 filehdr
->appledouble
.entries
[1].type
= AD_RESOURCE
;
1350 filehdr
->appledouble
.entries
[1].offset
= offsetof(apple_double_header_t
, pad
);
1351 filehdr
->appledouble
.entries
[1].length
= 0;
1352 bcopy(ADH_MACOSX
, filehdr
->appledouble
.filler
, sizeof(filehdr
->appledouble
.filler
));
1355 * Fill in the initial Attribute Header.
1357 filehdr
->magic
= ATTR_HDR_MAGIC
;
1358 filehdr
->debug_tag
= s
->sb
.st_ino
;
1359 filehdr
->data_start
= sizeof(attr_header_t
);
1362 * Collect the attribute names.
1364 entry
= (attr_entry_t
*)((char *)filehdr
+ sizeof(attr_header_t
));
1367 * Test if there are acls to copy
1369 if (COPYFILE_ACL
& s
->flags
)
1371 if (filesec_get_property(s
->fsec
, FILESEC_ACL
, &datasize
) < 0)
1373 copyfile_debug(1, "no acl entries found (%d)", datasize
< 0 ? errno
: 0);
1376 offset
= strlen(XATTR_SECURITY_NAME
) + 1;
1377 strcpy(attrnamebuf
, XATTR_SECURITY_NAME
);
1381 if (COPYFILE_XATTR
& s
->flags
)
1383 ssize_t left
= ATTR_MAX_HDR_SIZE
- offset
;
1384 if ((listsize
= flistxattr(s
->src_fd
, attrnamebuf
+ offset
, left
, 0)) <= 0)
1386 copyfile_debug(1, "no extended attributes found (%d)", errno
);
1388 if (listsize
> left
)
1390 copyfile_debug(1, "extended attribute list too long");
1391 listsize
= ATTR_MAX_HDR_SIZE
;
1395 endnamebuf
= attrnamebuf
+ listsize
;
1396 if (endnamebuf
> (attrnamebuf
+ ATTR_MAX_HDR_SIZE
)) {
1401 for (nameptr
= attrnamebuf
; nameptr
<endnamebuf
; nameptr
+= namelen
)
1403 namelen
= strlen(nameptr
) + 1;
1404 /* Skip over FinderInfo or Resource Fork names */
1405 if (strcmp(nameptr
, XATTR_FINDERINFO_NAME
) == 0 ||
1406 strcmp(nameptr
, XATTR_RESOURCEFORK_NAME
) == 0)
1409 /* The system should prevent this from happening, but... */
1410 if (namelen
> XATTR_MAXNAMELEN
+ 1) {
1411 namelen
= XATTR_MAXNAMELEN
+ 1;
1413 entry
->namelen
= namelen
;
1415 bcopy(nameptr
, &entry
->name
[0], namelen
);
1416 copyfile_debug(2, "copied name [%s]", entry
->name
);
1418 entrylen
= ATTR_ENTRY_LENGTH(namelen
);
1419 entry
= (attr_entry_t
*)(((char *)entry
) + entrylen
);
1421 if ((void*)entry
> (void*)endfilehdr
) {
1426 /* Update the attributes header. */
1427 filehdr
->num_attrs
++;
1428 filehdr
->data_start
+= entrylen
;
1433 * Collect the attribute data.
1435 entry
= (attr_entry_t
*)((char *)filehdr
+ sizeof(attr_header_t
));
1437 for (nameptr
= attrnamebuf
; nameptr
< attrnamebuf
+ listsize
; nameptr
+= namelen
+ 1)
1439 namelen
= strlen(nameptr
);
1441 if (strcmp(nameptr
, XATTR_SECURITY_NAME
) == 0)
1442 copyfile_pack_acl(s
, &databuf
, &datasize
);
1444 /* Check for Finder Info. */
1445 if (strcmp(nameptr
, XATTR_FINDERINFO_NAME
) == 0)
1447 datasize
= fgetxattr(s
->src_fd
, nameptr
, (u_int8_t
*)filehdr
+ filehdr
->appledouble
.entries
[0].offset
, 32, 0, 0);
1450 if (COPYFILE_VERBOSE
& s
->flags
)
1451 copyfile_warn("skipping attr \"%s\" due to error %d", nameptr
, errno
);
1452 } else if (datasize
!= 32)
1454 if (COPYFILE_VERBOSE
& s
->flags
)
1455 copyfile_warn("unexpected size (%ld) for \"%s\"", datasize
, nameptr
);
1458 if (COPYFILE_VERBOSE
& s
->flags
)
1459 copyfile_warn(" copied 32 bytes of \"%s\" data @ offset 0x%08x",
1460 XATTR_FINDERINFO_NAME
, filehdr
->appledouble
.entries
[0].offset
);
1462 continue; /* finder info doesn't have an attribute entry */
1464 /* Check for Resource Fork. */
1465 if (strcmp(nameptr
, XATTR_RESOURCEFORK_NAME
) == 0)
1471 /* Just a normal attribute. */
1472 datasize
= fgetxattr(s
->src_fd
, nameptr
, NULL
, 0, 0, 0);
1477 if (COPYFILE_VERBOSE
& s
->flags
)
1478 copyfile_warn("skipping attr \"%s\" due to error %d", nameptr
, errno
);
1481 if (datasize
> XATTR_MAXATTRLEN
)
1483 if (COPYFILE_VERBOSE
& s
->flags
)
1484 copyfile_warn("skipping attr \"%s\" (too big)", nameptr
);
1487 databuf
= malloc(datasize
);
1488 if (databuf
== NULL
) {
1492 datasize
= fgetxattr(s
->src_fd
, nameptr
, databuf
, datasize
, 0, 0);
1495 entry
->length
= datasize
;
1496 entry
->offset
= filehdr
->data_start
+ filehdr
->data_length
;
1498 filehdr
->data_length
+= datasize
;
1501 * This assumes that the data is fits in memory (not
1502 * the case when there are lots of attributes or one of
1503 * the attributes is very large.
1505 if (entry
->offset
> ATTR_MAX_SIZE
||
1506 (entry
->offset
+ datasize
> ATTR_MAX_SIZE
)) {
1509 bcopy(databuf
, (char*)filehdr
+ entry
->offset
, datasize
);
1513 copyfile_debug(1, "copied %ld bytes of \"%s\" data @ offset 0x%08x", datasize
, nameptr
, entry
->offset
);
1515 /* bump to next entry */
1516 entrylen
= ATTR_ENTRY_LENGTH(entry
->namelen
);
1517 entry
= (attr_entry_t
*)((char *)entry
+ entrylen
);
1520 if (filehdr
->data_length
> 0)
1522 /* Now we know where the resource fork data starts. */
1523 filehdr
->appledouble
.entries
[1].offset
= (filehdr
->data_start
+ filehdr
->data_length
);
1525 /* We also know the size of the "Finder Info entry. */
1526 filehdr
->appledouble
.entries
[0].length
=
1527 filehdr
->appledouble
.entries
[1].offset
- filehdr
->appledouble
.entries
[0].offset
;
1529 filehdr
->total_size
= filehdr
->appledouble
.entries
[1].offset
;
1532 /* Copy Resource Fork. */
1533 if (hasrsrcfork
&& (error
= copyfile_pack_rsrcfork(s
, filehdr
)))
1536 /* Write the header to disk. */
1537 datasize
= filehdr
->appledouble
.entries
[1].offset
;
1539 swap_adhdr(&filehdr
->appledouble
);
1540 swap_attrhdr(filehdr
);
1542 if (pwrite(s
->dst_fd
, filehdr
, datasize
, 0) != datasize
)
1544 if (COPYFILE_VERBOSE
& s
->flags
)
1545 copyfile_warn("couldn't write file header");
1550 if (filehdr
) free(filehdr
);
1551 if (attrnamebuf
) free(attrnamebuf
);
1556 return copyfile_stat(s
);