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 if (copyfile_free(s
) < 0) {
211 copyfile_state_t
copyfile_init(void)
213 copyfile_state_t s
= (copyfile_state_t
) calloc(1, sizeof(struct _copyfile_state
));
219 s
->fsec
= filesec_init();
225 int copyfile_free(copyfile_state_t s
)
230 filesec_free(s
->fsec
);
236 if (copyfile_close(s
) < 0)
238 copyfile_warn("error closing files");
246 static int copyfile_close(copyfile_state_t s
)
251 if (s
->dst_fd
>= 0 && close(s
->dst_fd
))
253 copyfile_warn("close on %s", s
->dst
);
259 static int copyfile_fix_perms(copyfile_state_t s
, filesec_t
*fsec
, int on
)
268 if(fstatx_np(s
->dst_fd
, &sb
, *fsec
))
271 tmp_fsec
= filesec_dup(*fsec
);
273 if (!filesec_get_property(tmp_fsec
, FILESEC_ACL
, &acl
))
276 acl_permset_t permset
;
279 if (mbr_uid_to_uuid(getuid(), qual
) != 0)
282 if (acl_create_entry_np(&acl
, &entry
, ACL_FIRST_ENTRY
) == -1)
284 if (acl_get_permset(entry
, &permset
) == -1)
286 if (acl_clear_perms(permset
) == -1)
288 if (acl_add_perm(permset
, ACL_WRITE_DATA
) == -1)
290 if (acl_add_perm(permset
, ACL_WRITE_ATTRIBUTES
) == -1)
292 if (acl_add_perm(permset
, ACL_WRITE_EXTATTRIBUTES
) == -1)
294 if (acl_set_tag_type(entry
, ACL_EXTENDED_ALLOW
) == -1)
297 if(acl_set_permset(entry
, permset
) == -1)
299 if(acl_set_qualifier(entry
, qual
) == -1)
302 if (filesec_set_property(tmp_fsec
, FILESEC_ACL
, &acl
) != 0)
306 if (filesec_get_property(tmp_fsec
, FILESEC_MODE
, &mode
) == 0)
311 if (filesec_set_property(tmp_fsec
, FILESEC_MODE
, &mode
) != 0)
315 if (fchmodx_np(s
->dst_fd
, tmp_fsec
) < 0 && errno
!= ENOTSUP
)
316 copyfile_warn("setting security information");
317 filesec_free(tmp_fsec
);
319 if (fchmodx_np(s
->dst_fd
, *fsec
) < 0 && errno
!= ENOTSUP
)
320 copyfile_warn("setting security information");
329 static int copyfile_open(copyfile_state_t s
)
331 int oflags
= O_EXCL
| O_CREAT
;
333 if (s
->src
&& s
->src_fd
== -2)
335 if ((COPYFILE_NOFOLLOW_SRC
& s
->flags
? lstatx_np
: statx_np
)
336 (s
->src
, &s
->sb
, s
->fsec
))
338 copyfile_warn("stat on %s", s
->src
);
341 if ((s
->src_fd
= open(s
->src
, O_RDONLY
, 0)) < 0)
343 copyfile_warn("open on %s", s
->src
);
348 if (s
->dst
&& s
->dst_fd
== -2)
350 if (COPYFILE_DATA
& s
->flags
|| COPYFILE_PACK
& s
->flags
)
353 if (COPYFILE_ACL
& ~s
->flags
)
355 if (filesec_set_property(s
->fsec
, FILESEC_ACL
, NULL
) == -1)
357 copyfile_debug(1, "unsetting acl attribute on %s", s
->dst
);
360 if (COPYFILE_STAT
& ~s
->flags
)
362 if (filesec_set_property(s
->fsec
, FILESEC_MODE
, NULL
) == -1)
364 copyfile_debug(1, "unsetting mode attribute on %s", s
->dst
);
367 if (COPYFILE_PACK
& s
->flags
)
370 if (filesec_set_property(s
->fsec
, FILESEC_MODE
, &m
) == -1)
372 mode_t m
= S_IRUSR
| S_IWUSR
;
373 if (filesec_set_property(s
->fsec
, FILESEC_MODE
, &m
) == -1)
375 copyfile_debug(1, "setting mode attribute on %s", s
->dst
);
378 if (filesec_set_property(s
->fsec
, FILESEC_OWNER
, NULL
) == -1)
380 copyfile_debug(1, "unsetting uid attribute on %s", s
->dst
);
382 if (filesec_set_property(s
->fsec
, FILESEC_UUID
, NULL
) == -1)
384 copyfile_debug(1, "unsetting uuid attribute on %s", s
->dst
);
386 if (filesec_set_property(s
->fsec
, FILESEC_GROUP
, NULL
) == -1)
388 copyfile_debug(1, "unsetting gid attribute on %s", s
->dst
);
392 if (COPYFILE_UNLINK
& s
->flags
&& unlink(s
->dst
) < 0)
394 copyfile_warn("%s: remove", s
->dst
);
398 while((s
->dst_fd
= open(s
->dst
, oflags
, s
->sb
.st_mode
| S_IWUSR
)) < 0)
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.
409 copyfile_debug(3, "open failed, retrying (%s)", s
->dst
);
410 if (s
->flags
& COPYFILE_EXCL
)
412 oflags
= oflags
& ~O_CREAT
;
413 if (s
->flags
& (COPYFILE_PACK
| COPYFILE_DATA
))
415 copyfile_debug(4, "truncating existing file (%s)", s
->dst
);
420 if(chmod(s
->dst
, (s
->sb
.st_mode
| S_IWUSR
) & ~S_IFMT
) == 0)
426 copyfile_warn("open on %s", s
->dst
);
433 static copyfile_flags_t
copyfile_check(copyfile_state_t s
)
436 copyfile_flags_t ret
= 0;
442 if (COPYFILE_XATTR
& s
->flags
)
443 if (listxattr(s
->src
, 0, 0, 0) > 0)
444 ret
|= COPYFILE_XATTR
;
446 if (COPYFILE_ACL
& s
->flags
)
448 (COPYFILE_NOFOLLOW_SRC
& s
->flags
? lstatx_np
: statx_np
)
449 (s
->src
, &s
->sb
, s
->fsec
);
451 if (filesec_get_property(s
->fsec
, FILESEC_ACL
, &acl
) == 0)
458 static int copyfile_data(copyfile_state_t s
)
465 if ((bp
= malloc((size_t)s
->sb
.st_blksize
)) == NULL
)
468 warnx("malloc failed");
471 blen
= s
->sb
.st_blksize
;
473 while ((nread
= read(s
->src_fd
, bp
, (size_t)blen
)) > 0)
475 if (write(s
->dst_fd
, bp
, (size_t)nread
) != nread
)
477 copyfile_warn("writing to %s", s
->dst
);
483 copyfile_warn("reading from %s", s
->src
);
489 if (ftruncate(s
->dst_fd
, s
->sb
.st_size
) < 0)
495 static int copyfile_security(copyfile_state_t s
)
497 filesec_t fsec_dst
= filesec_init();
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;
506 if (COPYFILE_ACL
& s
->flags
)
508 if(fstatx_np(s
->dst_fd
, &sb
, fsec_dst
))
513 if (filesec_get_property(fsec_dst
, FILESEC_ACL
, &acl_dst
))
517 acl_dst
= acl_init(4);
527 if (filesec_get_property(s
->fsec
, FILESEC_ACL
, &acl_src
))
533 acl_dst
= acl_init(4);
543 for (;acl_get_entry(acl_src
,
544 entry_src
== NULL
? ACL_FIRST_ENTRY
: ACL_NEXT_ENTRY
,
547 acl_get_flagset_np(entry_src
, &flags
);
548 if (!acl_get_flag_np(flags
, ACL_ENTRY_INHERITED
))
550 if ((ret
= acl_create_entry(&acl_dst
, &entry_dst
)) == -1)
553 if ((ret
= acl_copy_entry(entry_dst
, entry_src
)) == -1)
556 copyfile_debug(1, "copied acl entry from %s to %s", s
->src
, s
->dst
);
561 if (!filesec_set_property(fsec_dst
, FILESEC_ACL
, &acl_dst
))
563 copyfile_debug(1, "altered acl");
567 if (fchmodx_np(s
->dst_fd
, fsec_dst
) < 0 && errno
!= ENOTSUP
)
569 copyfile_warn("setting security information: %s", s
->dst
);
574 filesec_free(fsec_dst
);
575 if (inited_src
) acl_free(acl_src
);
576 if (inited_dst
) acl_free(acl_dst
);
581 static int copyfile_stat(copyfile_state_t s
)
583 struct timeval tval
[2];
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.)
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
);
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
);
602 static int copyfile_xattr(copyfile_state_t s
)
608 size_t bufsize
= 4096;
614 if (COPYFILE_NOFOLLOW_SRC
& s
->flags
)
615 flags
|= XATTR_NOFOLLOW
;
617 /* delete EAs on destination */
618 if ((nsize
= flistxattr(s
->dst_fd
, 0, 0, 0)) > 0)
620 if ((namebuf
= (char *) malloc(nsize
)) == NULL
)
623 nsize
= flistxattr(s
->dst_fd
, namebuf
, nsize
, 0);
626 for (name
= namebuf
; name
< namebuf
+ nsize
; name
+= strlen(name
) + 1)
627 fremovexattr(s
->dst_fd
, name
,flags
);
630 } else if (nsize
< 0)
632 if (errno
== ENOTSUP
)
638 /* get name list of EAs on source */
639 if ((nsize
= flistxattr(s
->src_fd
, 0, 0, 0)) < 0)
641 if (errno
== ENOTSUP
)
645 } else if (nsize
== 0)
648 if ((namebuf
= (char *) malloc(nsize
)) == NULL
)
651 nsize
= flistxattr(s
->src_fd
, namebuf
, nsize
, 0);
656 if ((xa_dataptr
= (void *) malloc(bufsize
)) == NULL
)
659 for (name
= namebuf
; name
< namebuf
+ nsize
; name
+= strlen(name
) + 1)
661 if ((xa_size
= fgetxattr(s
->src_fd
, name
, 0, 0, 0, flags
)) < 0)
667 if (xa_size
> bufsize
)
671 (void *) realloc((void *) xa_dataptr
, bufsize
)) == NULL
)
678 if ((asize
= fgetxattr(s
->src_fd
, name
, xa_dataptr
, xa_size
, 0, flags
)) < 0)
684 if (xa_size
!= asize
)
687 if (fsetxattr(s
->dst_fd
, name
, xa_dataptr
, xa_size
, 0, flags
) < 0)
693 free((void *) xa_dataptr
);
698 #ifdef _COPYFILE_TEST
699 #define COPYFILE_OPTION(x) { #x, COPYFILE_ ## x },
701 struct {char *s
; int v
;} opts
[] = {
703 COPYFILE_OPTION(STAT
)
704 COPYFILE_OPTION(XATTR
)
705 COPYFILE_OPTION(DATA
)
706 COPYFILE_OPTION(SECURITY
)
707 COPYFILE_OPTION(METADATA
)
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
)
723 int main(int c
, char *v
[])
729 errx(1, "insufficient arguments");
733 for (i
= 0; opts
[i
].s
!= NULL
; ++i
)
735 if (strcasecmp(opts
[i
].s
, v
[c
]) == 0)
737 printf("option %d: %s <- %d\n", c
, opts
[i
].s
, opts
[i
].v
);
744 return copyfile(v
[1], v
[2], NULL
, flags
);
748 * Apple Double Create
750 * Create an Apple Double "._" file from a file's extented attributes
752 * Copyright (c) 2004 Apple Computer, Inc. All rights reserved.
756 #define offsetof(type, member) ((size_t)(&((type *)0)->member))
758 #define XATTR_MAXATTRLEN (4*1024)
762 Typical "._" AppleDouble Header File layout:
763 ------------------------------------------------------------
768 .-- AD ENTRY[0] Finder Info Entry (must be first)
769 .--+-- AD ENTRY[1] Resource Fork Entry (must be last)
771 | ///////////// Fixed Size Data (32 bytes)
775 | ATTR ENTRY[1] --+--.
776 | ATTR ENTRY[2] --+--+--.
778 | ATTR ENTRY[N] --+--+--+--.
779 | ATTR DATA 0 <-' | | |
781 | ATTR DATA 1 <----' | |
783 | ATTR DATA 2 <-------' |
786 | ATTR DATA N <----------'
788 | Attribute Free Space
791 ///////////// Variable Sized Data
800 ------------------------------------------------------------
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.
810 * On Disk Data Structures
812 * Note: Motorola 68K alignment and big-endian.
814 * See RFC 1740 for additional information about the AppleDouble file format.
818 #define ADH_MAGIC 0x00051607
819 #define ADH_VERSION 0x00020000
820 #define ADH_MACOSX "Mac OS X "
823 * AppleDouble Entry ID's
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
843 #define ATTR_FILE_PREFIX "._"
844 #define ATTR_HDR_MAGIC 0x41545452 /* 'ATTR' */
846 #define ATTR_BUF_SIZE 4096 /* default size of the attr file and how much we'll grow by */
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)
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.
860 #pragma options align=mac68k
862 #define FINDERINFOSIZE 32
864 typedef struct apple_double_entry
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
;
872 typedef struct apple_double_header
874 u_int32_t magic
; /* == ADH_MAGIC */
875 u_int32_t version
; /* format version: 2 = 0x00020000 */
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
;
884 /* Entries are aligned on 4 byte boundaries */
885 typedef struct attr_entry
887 u_int32_t offset
; /* file offset to data */
888 u_int32_t length
; /* size of attribute data */
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
;
895 /* Header + entries must fit into 64K */
896 typedef struct attr_header
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];
907 } __attribute__((packed
)) attr_header_t
;
910 #pragma options align=reset
912 #define SWAP16(x) OSSwapBigToHostInt16(x)
913 #define SWAP32(x) OSSwapBigToHostInt32(x)
914 #define SWAP64(x) OSSwapBigToHostInt64(x)
916 #define ATTR_ALIGN 3L /* Use four-byte alignment */
918 #define ATTR_ENTRY_LENGTH(namelen) \
919 ((sizeof(attr_entry_t) - 1 + (namelen) + ATTR_ALIGN) & (~ATTR_ALIGN))
921 #define ATTR_NEXT(ae) \
922 (attr_entry_t *)((u_int8_t *)(ae) + ATTR_ENTRY_LENGTH((ae)->namelen))
924 #define XATTR_SECURITY_NAME "com.apple.acl.text"
927 * Endian swap Apple Double header
930 swap_adhdr(apple_double_header_t
*adh
)
932 #if BYTE_ORDER == LITTLE_ENDIAN
936 count
= (adh
->magic
== ADH_MAGIC
) ? adh
->numEntries
: SWAP16(adh
->numEntries
);
938 adh
->magic
= SWAP32 (adh
->magic
);
939 adh
->version
= SWAP32 (adh
->version
);
940 adh
->numEntries
= SWAP16 (adh
->numEntries
);
942 for (i
= 0; i
< count
; i
++)
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
);
952 * Endian swap extended attributes header
955 swap_attrhdr(attr_header_t
*ah
)
957 #if BYTE_ORDER == LITTLE_ENDIAN
962 count
= (ah
->magic
== ATTR_HDR_MAGIC
) ? ah
->num_attrs
: SWAP16(ah
->num_attrs
);
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
);
972 ae
= (attr_entry_t
*)(&ah
[1]);
973 for (i
= 0; i
< count
; i
++, ae
++)
975 ae
->offset
= SWAP32 (ae
->offset
);
976 ae
->length
= SWAP32 (ae
->length
);
977 ae
->flags
= SWAP16 (ae
->flags
);
982 static const u_int32_t emptyfinfo
[8] = {0};
984 static int copyfile_unpack(copyfile_state_t s
)
987 void * buffer
, * endptr
;
988 apple_double_header_t
*adhdr
;
992 if (s
->sb
.st_size
< ATTR_MAX_HDR_SIZE
)
993 hdrsize
= s
->sb
.st_size
;
995 hdrsize
= ATTR_MAX_HDR_SIZE
;
997 buffer
= calloc(1, hdrsize
);
998 if (buffer
== NULL
) {
999 copyfile_debug(1, "copyfile_unpack: calloc(1, %u) returned NULL", hdrsize
);
1003 endptr
= (char*)buffer
+ hdrsize
;
1005 bytes
= pread(s
->src_fd
, buffer
, hdrsize
, 0);
1009 copyfile_debug(1, "pread returned: %d", bytes
);
1013 if (bytes
< hdrsize
)
1016 "pread couldn't read entire header: %d of %d",
1017 (int)bytes
, (int)s
->sb
.st_size
);
1021 adhdr
= (apple_double_header_t
*)buffer
;
1024 * Check for Apple Double file.
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
)
1032 if (COPYFILE_VERBOSE
& s
->flags
)
1033 copyfile_warn("Not a valid Apple Double header");
1040 * Extract the extended attributes.
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.
1047 if (adhdr
->entries
[0].length
> FINDERINFOSIZE
)
1049 attr_header_t
*attrhdr
;
1050 attr_entry_t
*entry
;
1054 if (hdrsize
< sizeof(attr_header_t
)) {
1055 copyfile_warn("bad attribute header: %u < %u", hdrsize
, sizeof(attr_header_t
));
1060 attrhdr
= (attr_header_t
*)buffer
;
1061 swap_attrhdr(attrhdr
);
1062 if (attrhdr
->magic
!= ATTR_HDR_MAGIC
)
1064 if (COPYFILE_VERBOSE
& s
->flags
)
1065 copyfile_warn("bad attribute header");
1069 count
= attrhdr
->num_attrs
;
1070 entry
= (attr_entry_t
*)&attrhdr
[1];
1072 for (i
= 0; i
< count
; i
++)
1077 * First we do some simple sanity checking.
1078 * +) See if entry is within the buffer's range;
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.)
1084 * +) If, given the name length, it goes beyond the end of
1085 * the buffer, error out.
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.)
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.
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.)
1106 if ((void*)entry
>= endptr
|| (void*)entry
< buffer
) {
1107 if (COPYFILE_VERBOSE
& s
->flags
)
1108 copyfile_warn("Incomplete or corrupt attribute entry");
1113 if (((void*)entry
+ sizeof(*entry
)) > endptr
) {
1114 if (COPYFILE_VERBOSE
& s
->flags
)
1115 copyfile_warn("Incomplete or corrupt attribute entry");
1120 if (entry
->namelen
< 2) {
1121 if (COPYFILE_VERBOSE
& s
->flags
)
1122 copyfile_warn("Corrupt attribute entry (only %d bytes)", entry
->namelen
);
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
);
1132 if ((void*)(entry
->name
+ entry
->namelen
) >= endptr
) {
1133 if (COPYFILE_VERBOSE
& s
->flags
)
1134 copyfile_warn("Incomplete or corrupt attribute entry");
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)");
1147 copyfile_debug(3, "extracting \"%s\" (%d bytes) at offset %u",
1148 entry
->name
, entry
->length
, entry
->offset
);
1150 dataptr
= (char *)attrhdr
+ entry
->offset
;
1152 if (dataptr
>= endptr
|| dataptr
< buffer
) {
1153 copyfile_debug(1, "Entry %d overflows: offset = %u", entry
->offset
);
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
);
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
;
1173 if (COPYFILE_ACL
& s
->flags
&& strncmp(entry
->name
, XATTR_SECURITY_NAME
, strlen(XATTR_SECURITY_NAME
)) == 0)
1176 char *tacl
= strdup(dataptr
);
1179 tacl
[entry
->length
] = 0; /* Ensure it is NUL-terminated */
1180 if (acl
= acl_from_text(tacl
))
1182 filesec_t tfsec
= filesec_init();
1185 if (filesec_set_property(tfsec
, FILESEC_ACL
, &acl
) < 0)
1187 copyfile_debug(1, "setting acl");
1190 else if (fchmodx_np(s
->dst_fd
, tfsec
) < 0 && errno
!= ENOTSUP
)
1193 copyfile_debug(1, "applying acl to file");
1195 filesec_free(tfsec
);
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
);
1209 entry
= ATTR_NEXT(entry
);
1214 * Extract the Finder Info.
1216 if (adhdr
->entries
[0].offset
> (hdrsize
- sizeof(emptyfinfo
))) {
1221 if (bcmp((u_int8_t
*)buffer
+ adhdr
->entries
[0].offset
, emptyfinfo
, sizeof(emptyfinfo
)) != 0)
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);
1230 * Extract the Resource Fork.
1232 if (adhdr
->entries
[1].type
== AD_RESOURCE
&&
1233 adhdr
->entries
[1].length
> 0)
1235 void * rsrcforkdata
= NULL
;
1239 struct timeval tval
[2];
1241 length
= adhdr
->entries
[1].length
;
1242 offset
= adhdr
->entries
[1].offset
;
1243 rsrcforkdata
= malloc(length
);
1245 if (rsrcforkdata
== NULL
) {
1246 copyfile_debug(1, "could not allocate %u bytes for rsrcforkdata",
1252 if (fstat(s
->dst_fd
, &sb
) < 0)
1254 copyfile_debug(1, "couldn't stat destination file");
1259 bytes
= pread(s
->src_fd
, rsrcforkdata
, length
, offset
);
1264 copyfile_debug(1, "couldn't read resource fork");
1269 "couldn't read resource fork (only read %d bytes of %d)",
1270 (int)bytes
, (int)length
);
1275 error
= fsetxattr(s
->dst_fd
, XATTR_RESOURCEFORK_NAME
, rsrcforkdata
, bytes
, 0, 0);
1278 copyfile_debug(1, "error %d setting resource fork attribute", error
);
1282 copyfile_debug(1, "extracting \"%s\" (%d bytes)",
1283 XATTR_RESOURCEFORK_NAME
, (int)length
);
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;
1289 if (futimes(s
->dst_fd
, tval
))
1291 copyfile_warn("cannot set time on destination file %s", s
->dst
? s
->dst
: "<no filename>");
1299 if (buffer
) free(buffer
);
1303 static int copyfile_pack_acl(copyfile_state_t s
, void **buf
, ssize_t
*len
)
1309 if (filesec_get_property(s
->fsec
, FILESEC_ACL
, &acl
) < 0)
1311 if (errno
!= ENOENT
)
1314 if (COPYFILE_VERBOSE
& s
->flags
)
1315 copyfile_warn("getting acl");
1320 if ((acl_text
= acl_to_text(acl
, len
)) != NULL
)
1322 *buf
= malloc(*len
);
1323 memcpy(*buf
, acl_text
, *len
);
1326 copyfile_debug(1, "copied acl (%ld) %p", *len
, *buf
);
1331 static int copyfile_pack_rsrcfork(copyfile_state_t s
, attr_header_t
*filehdr
)
1336 /* Get the resource fork size */
1337 if ((datasize
= fgetxattr(s
->src_fd
, XATTR_RESOURCEFORK_NAME
, NULL
, 0, 0, 0)) < 0)
1339 if (COPYFILE_VERBOSE
& s
->flags
)
1340 copyfile_warn("skipping attr \"%s\" due to error %d", XATTR_RESOURCEFORK_NAME
, errno
);
1344 if ((databuf
= malloc(datasize
)) == NULL
)
1346 copyfile_warn("malloc");
1350 if (fgetxattr(s
->src_fd
, XATTR_RESOURCEFORK_NAME
, databuf
, datasize
, 0, 0) != datasize
)
1352 if (COPYFILE_VERBOSE
& s
->flags
)
1353 copyfile_warn("couldn't read entire resource fork");
1357 /* Write the resource fork to disk. */
1358 if (pwrite(s
->dst_fd
, databuf
, datasize
, filehdr
->appledouble
.entries
[1].offset
) != datasize
)
1360 if (COPYFILE_VERBOSE
& s
->flags
)
1361 copyfile_warn("couldn't write resource fork");
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
;
1372 static int copyfile_pack(copyfile_state_t s
)
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;
1384 int hasrsrcfork
= 0;
1387 filehdr
= (attr_header_t
*) calloc(1, ATTR_MAX_SIZE
);
1388 if (filehdr
== NULL
) {
1392 endfilehdr
= ((void*)filehdr
) + ATTR_MAX_SIZE
;
1395 attrnamebuf
= calloc(1, ATTR_MAX_HDR_SIZE
);
1396 if (attrnamebuf
== NULL
) {
1400 endnamebuf
= ((char*)attrnamebuf
) + ATTR_MAX_HDR_SIZE
;
1404 * Fill in the Apple Double Header defaults.
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
));
1418 * Fill in the initial Attribute Header.
1420 filehdr
->magic
= ATTR_HDR_MAGIC
;
1421 filehdr
->debug_tag
= s
->sb
.st_ino
;
1422 filehdr
->data_start
= sizeof(attr_header_t
);
1425 * Collect the attribute names.
1427 entry
= (attr_entry_t
*)((char *)filehdr
+ sizeof(attr_header_t
));
1430 * Test if there are acls to copy
1432 if (COPYFILE_ACL
& s
->flags
)
1434 if (filesec_get_property(s
->fsec
, FILESEC_ACL
, &datasize
) < 0)
1436 copyfile_debug(1, "no acl entries found (%d)", datasize
< 0 ? errno
: 0);
1439 offset
= strlen(XATTR_SECURITY_NAME
) + 1;
1440 strcpy(attrnamebuf
, XATTR_SECURITY_NAME
);
1444 if (COPYFILE_XATTR
& s
->flags
)
1446 ssize_t left
= ATTR_MAX_HDR_SIZE
- offset
;
1447 if ((listsize
= flistxattr(s
->src_fd
, attrnamebuf
+ offset
, left
, 0)) <= 0)
1449 copyfile_debug(1, "no extended attributes found (%d)", errno
);
1451 if (listsize
> left
)
1453 copyfile_debug(1, "extended attribute list too long");
1454 listsize
= ATTR_MAX_HDR_SIZE
;
1458 endnamebuf
= attrnamebuf
+ listsize
;
1459 if (endnamebuf
> (attrnamebuf
+ ATTR_MAX_HDR_SIZE
)) {
1464 for (nameptr
= attrnamebuf
; nameptr
<endnamebuf
; nameptr
+= namelen
)
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)
1472 /* The system should prevent this from happening, but... */
1473 if (namelen
> XATTR_MAXNAMELEN
+ 1) {
1474 namelen
= XATTR_MAXNAMELEN
+ 1;
1476 entry
->namelen
= namelen
;
1478 bcopy(nameptr
, &entry
->name
[0], namelen
);
1479 copyfile_debug(2, "copied name [%s]", entry
->name
);
1481 entrylen
= ATTR_ENTRY_LENGTH(namelen
);
1482 entry
= (attr_entry_t
*)(((char *)entry
) + entrylen
);
1484 if ((void*)entry
> (void*)endfilehdr
) {
1489 /* Update the attributes header. */
1490 filehdr
->num_attrs
++;
1491 filehdr
->data_start
+= entrylen
;
1496 * Collect the attribute data.
1498 entry
= (attr_entry_t
*)((char *)filehdr
+ sizeof(attr_header_t
));
1500 for (nameptr
= attrnamebuf
; nameptr
< attrnamebuf
+ listsize
; nameptr
+= namelen
+ 1)
1502 namelen
= strlen(nameptr
);
1504 if (strcmp(nameptr
, XATTR_SECURITY_NAME
) == 0)
1505 copyfile_pack_acl(s
, &databuf
, &datasize
);
1507 /* Check for Finder Info. */
1508 if (strcmp(nameptr
, XATTR_FINDERINFO_NAME
) == 0)
1510 datasize
= fgetxattr(s
->src_fd
, nameptr
, (u_int8_t
*)filehdr
+ filehdr
->appledouble
.entries
[0].offset
, 32, 0, 0);
1513 if (COPYFILE_VERBOSE
& s
->flags
)
1514 copyfile_warn("skipping attr \"%s\" due to error %d", nameptr
, errno
);
1515 } else if (datasize
!= 32)
1517 if (COPYFILE_VERBOSE
& s
->flags
)
1518 copyfile_warn("unexpected size (%ld) for \"%s\"", datasize
, nameptr
);
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
);
1525 continue; /* finder info doesn't have an attribute entry */
1527 /* Check for Resource Fork. */
1528 if (strcmp(nameptr
, XATTR_RESOURCEFORK_NAME
) == 0)
1534 /* Just a normal attribute. */
1535 datasize
= fgetxattr(s
->src_fd
, nameptr
, NULL
, 0, 0, 0);
1540 if (COPYFILE_VERBOSE
& s
->flags
)
1541 copyfile_warn("skipping attr \"%s\" due to error %d", nameptr
, errno
);
1544 if (datasize
> XATTR_MAXATTRLEN
)
1546 if (COPYFILE_VERBOSE
& s
->flags
)
1547 copyfile_warn("skipping attr \"%s\" (too big)", nameptr
);
1550 databuf
= malloc(datasize
);
1551 if (databuf
== NULL
) {
1555 datasize
= fgetxattr(s
->src_fd
, nameptr
, databuf
, datasize
, 0, 0);
1558 entry
->length
= datasize
;
1559 entry
->offset
= filehdr
->data_start
+ filehdr
->data_length
;
1561 filehdr
->data_length
+= datasize
;
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.
1568 if (entry
->offset
> ATTR_MAX_SIZE
||
1569 (entry
->offset
+ datasize
> ATTR_MAX_SIZE
)) {
1572 bcopy(databuf
, (char*)filehdr
+ entry
->offset
, datasize
);
1576 copyfile_debug(1, "copied %ld bytes of \"%s\" data @ offset 0x%08x", datasize
, nameptr
, entry
->offset
);
1578 /* bump to next entry */
1579 entrylen
= ATTR_ENTRY_LENGTH(entry
->namelen
);
1580 entry
= (attr_entry_t
*)((char *)entry
+ entrylen
);
1583 if (filehdr
->data_length
> 0)
1585 /* Now we know where the resource fork data starts. */
1586 filehdr
->appledouble
.entries
[1].offset
= (filehdr
->data_start
+ filehdr
->data_length
);
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
;
1592 filehdr
->total_size
= filehdr
->appledouble
.entries
[1].offset
;
1595 /* Copy Resource Fork. */
1596 if (hasrsrcfork
&& (error
= copyfile_pack_rsrcfork(s
, filehdr
)))
1599 /* Write the header to disk. */
1600 datasize
= filehdr
->appledouble
.entries
[1].offset
;
1602 swap_adhdr(&filehdr
->appledouble
);
1603 swap_attrhdr(filehdr
);
1605 if (pwrite(s
->dst_fd
, filehdr
, datasize
, 0) != datasize
)
1607 if (COPYFILE_VERBOSE
& s
->flags
)
1608 copyfile_warn("couldn't write file header");
1613 if (filehdr
) free(filehdr
);
1614 if (attrnamebuf
) free(attrnamebuf
);
1619 return copyfile_stat(s
);