libarchive/test/test_ustar_filenames.c \
libarchive/test/test_ustar_filename_encoding.c \
libarchive/test/test_write_disk.c \
+ libarchive/test/test_write_disk_appledouble.c \
libarchive/test/test_write_disk_failures.c \
libarchive/test/test_write_disk_hardlink.c \
libarchive/test/test_write_disk_hfs_compression.c \
libarchive/test/test_write_disk_lookup.c \
+ libarchive/test/test_write_disk_mac_metadata.c \
libarchive/test/test_write_disk_no_hfs_compression.c \
libarchive/test/test_write_disk_perms.c \
libarchive/test/test_write_disk_secure.c \
libarchive/test/test_splitted_rar_seek_support_aa.uu \
libarchive/test/test_splitted_rar_seek_support_ab.uu \
libarchive/test/test_splitted_rar_seek_support_ac.uu \
+ libarchive/test/test_write_disk_hfs_appledouble.cpio.gz.uu \
libarchive/test/test_write_disk_hfs_compression.tgz.uu \
+ libarchive/test/test_write_disk_mac_metaata.tar.gz.uu \
libarchive/test/test_write_disk_no_hfs_compression.tgz.uu \
libarchive/test/CMakeLists.txt \
libarchive/test/README
#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
+#ifdef HAVE_SYS_ACL_H
+#include <sys/acl.h>
+#endif
#ifdef HAVE_SYS_EXTATTR_H
#include <sys/extattr.h>
#endif
* On Mac OS, we use copyfile() to unpack the metadata and
* apply it to the target file.
*/
+
+static int
+copy_xattrs(struct archive_write_disk *a, int tmpfd, int dffd)
+{
+ ssize_t xattr_size;
+ char *xattr_names = NULL, *xattr_val = NULL;
+ int ret = ARCHIVE_OK, xattr_i;
+
+ xattr_size = flistxattr(tmpfd, NULL, 0, 0);
+ if (xattr_size == -1) {
+ archive_set_error(&a->archive, errno,
+ "Failed to read metadata(xattr)");
+ ret = ARCHIVE_WARN;
+ goto exit_xattr;
+ }
+ xattr_names = malloc(xattr_size);
+ if (xattr_names == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for metadata(xattr)");
+ ret = ARCHIVE_FATAL;
+ goto exit_xattr;
+ }
+ xattr_size = flistxattr(tmpfd, xattr_names, xattr_size, 0);
+ if (xattr_size == -1) {
+ archive_set_error(&a->archive, errno,
+ "Failed to read metadata(xattr)");
+ ret = ARCHIVE_WARN;
+ goto exit_xattr;
+ }
+ for (xattr_i = 0; xattr_i < xattr_size;
+ xattr_i += strlen(xattr_names + xattr_i) + 1) {
+ ssize_t s;
+ int f;
+
+ s = fgetxattr(tmpfd, xattr_names + xattr_i, NULL, 0, 0, 0);
+ if (s == -1) {
+ archive_set_error(&a->archive, errno,
+ "Failed to get metadata(xattr)");
+ ret = ARCHIVE_WARN;
+ goto exit_xattr;
+ }
+ xattr_val = realloc(xattr_val, s);
+ if (xattr_val == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Failed to get metadata(xattr)");
+ ret = ARCHIVE_WARN;
+ goto exit_xattr;
+ }
+ s = fgetxattr(tmpfd, xattr_names + xattr_i, xattr_val, s, 0, 0);
+ if (s == -1) {
+ archive_set_error(&a->archive, errno,
+ "Failed to get metadata(xattr)");
+ ret = ARCHIVE_WARN;
+ goto exit_xattr;
+ }
+ f = fsetxattr(dffd, xattr_names + xattr_i, xattr_val, s, 0, 0);
+ if (f == -1) {
+ archive_set_error(&a->archive, errno,
+ "Failed to get metadata(xattr)");
+ ret = ARCHIVE_WARN;
+ goto exit_xattr;
+ }
+ }
+exit_xattr:
+ free(xattr_names);
+ free(xattr_val);
+ return (ret);
+}
+
+static int
+copy_acls(struct archive_write_disk *a, int tmpfd, int dffd)
+{
+ acl_t acl, dfacl = NULL;
+ int acl_r, ret = ARCHIVE_OK;
+
+ acl = acl_get_fd(tmpfd);
+ if (acl == NULL) {
+ if (errno == ENOENT)
+ /* There are not any ACLs. */
+ return (ret);
+ archive_set_error(&a->archive, errno,
+ "Failed to get metadata(acl)");
+ ret = ARCHIVE_WARN;
+ goto exit_acl;
+ }
+ dfacl = acl_dup(acl);
+ acl_r = acl_set_fd(dffd, dfacl);
+ if (acl_r == -1) {
+ archive_set_error(&a->archive, errno,
+ "Failed to get metadata(acl)");
+ ret = ARCHIVE_WARN;
+ goto exit_acl;
+ }
+exit_acl:
+ if (acl)
+ acl_free(acl);
+ if (dfacl)
+ acl_free(dfacl);
+ return (ret);
+}
+
+static int
+create_tempdatafork(struct archive_write_disk *a, const char *pathname)
+{
+ struct archive_string tmpdatafork;
+ int tmpfd;
+
+ archive_string_init(&tmpdatafork);
+ archive_strcpy(&tmpdatafork, "tar.md.XXXXXX");
+ tmpfd = mkstemp(tmpdatafork.s);
+ if (tmpfd < 0) {
+ archive_set_error(&a->archive, errno,
+ "Failed to mkstemp");
+ archive_string_free(&tmpdatafork);
+ return (-1);
+ }
+ if (copyfile(pathname, tmpdatafork.s, 0,
+ COPYFILE_UNPACK | COPYFILE_NOFOLLOW
+ | COPYFILE_ACL | COPYFILE_XATTR) < 0) {
+ archive_set_error(&a->archive, errno,
+ "Failed to restore metadata");
+ close(tmpfd);
+ tmpfd = -1;
+ }
+ unlink(tmpdatafork.s);
+ archive_string_free(&tmpdatafork);
+ return (tmpfd);
+}
+
+static int
+copy_metadata(struct archive_write_disk *a, const char *metadata,
+ const char *datafork, int datafork_compressed)
+{
+ int ret = ARCHIVE_OK;
+
+ if (datafork_compressed) {
+ int dffd, tmpfd;
+
+ tmpfd = create_tempdatafork(a, metadata);
+ if (tmpfd == -1)
+ return (ARCHIVE_WARN);
+
+ /*
+ * Do not open the data fork compressed by HFS+ compression
+ * with at least a writing mode(O_RDWR or O_WRONLY). it
+ * makes the data fork uncompressed.
+ */
+ dffd = open(datafork, 0);
+ if (dffd == -1) {
+ archive_set_error(&a->archive, errno,
+ "Failed to open the data fork for metadata");
+ close(tmpfd);
+ return (ARCHIVE_WARN);
+ }
+
+ ret = copy_xattrs(a, tmpfd, dffd);
+ if (ret == ARCHIVE_OK)
+ ret = copy_acls(a, tmpfd, dffd);
+ close(tmpfd);
+ close(dffd);
+ } else {
+ if (copyfile(metadata, datafork, 0,
+ COPYFILE_UNPACK | COPYFILE_NOFOLLOW
+ | COPYFILE_ACL | COPYFILE_XATTR) < 0) {
+ archive_set_error(&a->archive, errno,
+ "Failed to restore metadata");
+ ret = ARCHIVE_WARN;
+ }
+ }
+ return (ret);
+}
+
static int
set_mac_metadata(struct archive_write_disk *a, const char *pathname,
const void *metadata, size_t metadata_size)
}
written = write(fd, metadata, metadata_size);
close(fd);
- if ((size_t)written != metadata_size
- || copyfile(tmp.s, pathname, 0,
- COPYFILE_UNPACK | COPYFILE_NOFOLLOW
- | COPYFILE_ACL | COPYFILE_XATTR)) {
+ if ((size_t)written != metadata_size) {
archive_set_error(&a->archive, errno,
"Failed to restore metadata");
ret = ARCHIVE_WARN;
+ } else {
+ int compressed;
+
+ if ((a->todo & TODO_HFS_COMPRESSION) != 0 &&
+ (ret = lazy_stat(a)) == ARCHIVE_OK)
+ compressed = a->st.st_flags & UF_COMPRESSED;
+ else
+ compressed = 0;
+ ret = copy_metadata(a, tmp.s, pathname, compressed);
}
unlink(tmp.s);
archive_string_free(&tmp);
*/
archive_strncpy(&datafork, pathname, p - pathname);
archive_strcat(&datafork, p + 2);
- if (lstat(datafork.s, &st) == -1 &&
+ if (lstat(datafork.s, &st) == -1 ||
(st.st_mode & AE_IFMT) != AE_IFREG)
goto skip_appledouble;
/*
* Check if the file is in the AppleDouble form.
*/
- fd = open(a->name, O_RDONLY | O_BINARY | O_CLOEXEC);
+ fd = open(pathname, O_RDONLY | O_BINARY | O_CLOEXEC);
__archive_ensure_cloexec_flag(fd);
if (fd == -1) {
archive_set_error(&a->archive, errno,
if (read(fd, buff, 8) == -1) {
archive_set_error(&a->archive, errno,
"Failed to read a restoring file");
+ close(fd);
ret = ARCHIVE_WARN;
goto skip_appledouble;
}
+ close(fd);
/* Check AppleDouble Magic Code. */
if (archive_be32dec(buff) != 0x00051607)
goto skip_appledouble;
if (archive_be32dec(buff+4) != 0x00020000)
goto skip_appledouble;
- if (copyfile(pathname, datafork.s, 0,
- COPYFILE_UNPACK | COPYFILE_NOFOLLOW
- | COPYFILE_ACL | COPYFILE_XATTR) < 0) {
- archive_set_error(&a->archive, errno,
- "Failed to restore metadata");
- ret = ARCHIVE_WARN;
- goto skip_appledouble;
+ ret = copy_metadata(a, pathname, datafork.s,
+ st.st_flags & UF_COMPRESSED);
+ if (ret == ARCHIVE_OK) {
+ unlink(pathname);
+ ret = ARCHIVE_EOF;
}
- unlink(pathname);
- ret = ARCHIVE_EOF;
skip_appledouble:
- if (fd >= 0)
- close(fd);
archive_string_free(&datafork);
return (ret);
}
test_ustar_filenames.c
test_ustar_filename_encoding.c
test_write_disk.c
+ test_write_disk_appledouble.c
test_write_disk_failures.c
test_write_disk_hardlink.c
test_write_disk_hfs_compression.c
test_write_disk_lookup.c
+ test_write_disk_mac_metadata.c
test_write_disk_no_hfs_compression.c
test_write_disk_perms.c
test_write_disk_secure.c
--- /dev/null
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * Copyright (c) 2012 Michihiro NAKAJIMA
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "test.h"
+__FBSDID("$FreeBSD$");
+
+#ifdef HAVE_SYS_ACL_H
+#include <sys/acl.h>
+#endif
+#ifdef HAVE_SYS_XATTR_H
+#include <sys/xattr.h>
+#endif
+
+#if defined(__APPLE__) && defined(UF_COMPRESSED)
+static int
+has_xattr(const char *filename, const char *xattrname)
+{
+ char *nl, *nlp;
+ ssize_t r;
+ int exisiting;
+
+ r = listxattr(filename, NULL, 0, XATTR_SHOWCOMPRESSION);
+ if (r < 0)
+ return (0);
+ if (r == 0)
+ return (0);
+
+ nl = malloc(r);
+ if (!assert(nl != NULL))
+ return (0);
+
+ r = listxattr(filename, nl, r, XATTR_SHOWCOMPRESSION);
+ if (r < 0) {
+ free(nl);
+ return (0);
+ }
+
+ exisiting = 0;
+ for (nlp = nl; nlp < nl + r; nlp += strlen(nlp) + 1) {
+ if (strcmp(nlp, xattrname) == 0) {
+ exisiting = 1;
+ break;
+ }
+ }
+ free(nl);
+ return (exisiting);
+}
+
+#endif
+
+/*
+ * Exercise HFS+ Compression.
+ */
+DEFINE_TEST(test_write_disk_appledouble)
+{
+#if !defined(__APPLE__) || !defined(UF_COMPRESSED)
+ skipping("MacOS-specific AppleDouble test");
+#else
+ const char *refname = "test_write_disk_appledouble.cpio.gz";
+ struct archive *ad, *a;
+ struct archive_entry *ae;
+ struct stat st;
+ acl_t acl;
+
+ extract_reference_file(refname);
+
+ /*
+ * Extract an archive to disk with HFS+ Compression.
+ */
+ assert((ad = archive_write_disk_new()) != NULL);
+ assertEqualIntA(ad, ARCHIVE_OK,
+ archive_write_disk_set_standard_lookup(ad));
+ assertEqualIntA(ad, ARCHIVE_OK,
+ archive_write_disk_set_options(ad,
+ ARCHIVE_EXTRACT_TIME |
+ ARCHIVE_EXTRACT_SECURE_SYMLINKS |
+ ARCHIVE_EXTRACT_SECURE_NODOTDOT |
+ ARCHIVE_EXTRACT_HFS_COMPRESSION_FORCED));
+
+ assert((a = archive_read_new()) != NULL);
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a));
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a));
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a,
+ refname, 512 * 20));
+
+ assertMakeDir("hfscmp", 0755);
+ assertChdir("hfscmp");
+
+ /* Skip "." */
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+ assertEqualString(".", archive_entry_pathname(ae));
+ /* Extract file3. */
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+ assertEqualString("./file3", archive_entry_pathname(ae));
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_extract2(a, ae, ad));
+ /* Extract ._file3 which will be merged into file3 as medtadata. */
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+ assertEqualString("./._file3", archive_entry_pathname(ae));
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_extract2(a, ae, ad));
+
+ assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae));
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
+ assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+ assertEqualIntA(ad, ARCHIVE_OK, archive_write_free(ad));
+
+ /* Test file3. */
+ assertEqualInt(0, stat("file3", &st));
+ assertEqualInt(UF_COMPRESSED, st.st_flags & UF_COMPRESSED);
+ assertFileSize("file3", 8);
+ failure("'%s' should not have Resource Fork", "file3");
+ assertEqualInt(0, has_xattr("file3", "com.apple.ResourceFork"));
+ failure("'%s' should have decompfs xattr", "file3");
+ assertEqualInt(1, has_xattr("file3", "com.apple.decmpfs"));
+ assert(NULL != (acl = acl_get_file("file3", ACL_TYPE_EXTENDED)));
+ assertEqualString(acl_to_text(acl, NULL),
+ "!#acl 1\n"
+ "user:FFFFEEEE-DDDD-CCCC-BBBB-AAAA000000C9:Guest:201:deny:read\n"
+ "group:ABCDEFAB-CDEF-ABCD-EFAB-CDEF00000050:admin:80:allow:write\n"
+ );
+ if (acl) acl_free(acl);
+ /* Test ._file3. */
+ failure("'file3' should be merged and removed");
+ assertFileNotExists("._file3");
+
+ assertChdir("..");
+
+ /*
+ * Extract an archive to disk without HFS+ Compression.
+ */
+ assert((ad = archive_write_disk_new()) != NULL);
+ assertEqualIntA(ad, ARCHIVE_OK,
+ archive_write_disk_set_standard_lookup(ad));
+ assertEqualIntA(ad, ARCHIVE_OK,
+ archive_write_disk_set_options(ad,
+ ARCHIVE_EXTRACT_TIME |
+ ARCHIVE_EXTRACT_SECURE_SYMLINKS |
+ ARCHIVE_EXTRACT_SECURE_NODOTDOT));
+
+ assert((a = archive_read_new()) != NULL);
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a));
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a));
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a,
+ refname, 512 * 20));
+
+ assertMakeDir("nocmp", 0755);
+ assertChdir("nocmp");
+
+ /* Skip "." */
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+ assertEqualString(".", archive_entry_pathname(ae));
+ /* Extract file3. */
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+ assertEqualString("./file3", archive_entry_pathname(ae));
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_extract2(a, ae, ad));
+ /* Extract ._file3 which will be merged into file3 as medtadata. */
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+ assertEqualString("./._file3", archive_entry_pathname(ae));
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_extract2(a, ae, ad));
+
+ assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae));
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
+ assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+ assertEqualIntA(ad, ARCHIVE_OK, archive_write_free(ad));
+
+ /* Test file3. */
+ assertEqualInt(0, stat("file3", &st));
+ assertEqualInt(0, st.st_flags & UF_COMPRESSED);
+ assertFileSize("file3", 8);
+ failure("'%s' should not have Resource Fork", "file3");
+ assertEqualInt(0, has_xattr("file3", "com.apple.ResourceFork"));
+ failure("'%s' should not have decmpfs", "file3");
+ assertEqualInt(0, has_xattr("file3", "com.apple.decmpfs"));
+ assert(NULL != (acl = acl_get_file("file3", ACL_TYPE_EXTENDED)));
+ assertEqualString(acl_to_text(acl, NULL),
+ "!#acl 1\n"
+ "user:FFFFEEEE-DDDD-CCCC-BBBB-AAAA000000C9:Guest:201:deny:read\n"
+ "group:ABCDEFAB-CDEF-ABCD-EFAB-CDEF00000050:admin:80:allow:write\n"
+ );
+ if (acl) acl_free(acl);
+ /* Test ._file3. */
+ failure("'file3' should be merged and removed");
+ assertFileNotExists("._file3");
+
+ assertChdir("..");
+
+ assertEqualFile("hfscmp/file3", "nocmp/file3");
+#endif
+}
--- /dev/null
+begin 644 test_write_disk_appledouble.cpio.gz
+M'XL("#N]E5```W1E<W1?=W)I=&5?9&ES:U]H9G-?8V]M<')E<W-I;VY?;6%C
+M;65T861A=&$N8W!I;P#MD4%/@S`4QSL38^1JXE6(YW9M*:"]`6/&1&."'+P9
+MA&XA88,PR/03^!7\DGX''\-,PG:8%T_^FM>^_VO?/X52A[:CCZ".9<'JV.U,
+MN6AG9G6;C%-A,2X<T]YHWFLDB`[=&*/4%F+H1EG?C9F"V[UJEY+Q+,N5B>*7
+M)%6SN;;CS7_GO;VI,!TR)L^=.SH^/T%'"-W'B?[PJ#_IW[0U=`K!(3XA0(\N
+MT$&X411VV:;C`^)]<&3T4S]+B@6)RS)7)$YR4JO7&C:,2Q`ZTYJ5JN04"``\
+M`;`/8`_`+M!]DW\M;QJUJB6G3*9J^28K%:?:O"J:4KJ>/PFFKH?;!;<*;V77
+M;E$9IXML*:\@R?-B+==55BMM]T%-NA^VO]H_'H7N[5T0&H9QV&_\YP_X`B-U
+&WD<`!```
+`
+end
--- /dev/null
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * Copyright (c) 2012 Michihiro NAKAJIMA
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "test.h"
+__FBSDID("$FreeBSD$");
+
+#ifdef HAVE_SYS_ACL_H
+#include <sys/acl.h>
+#endif
+#ifdef HAVE_SYS_XATTR_H
+#include <sys/xattr.h>
+#endif
+
+#if defined(__APPLE__) && defined(UF_COMPRESSED)
+static int
+has_xattr(const char *filename, const char *xattrname)
+{
+ char *nl, *nlp;
+ ssize_t r;
+ int exisiting;
+
+ r = listxattr(filename, NULL, 0, XATTR_SHOWCOMPRESSION);
+ if (r < 0)
+ return (0);
+ if (r == 0)
+ return (0);
+
+ nl = malloc(r);
+ if (!assert(nl != NULL))
+ return (0);
+
+ r = listxattr(filename, nl, r, XATTR_SHOWCOMPRESSION);
+ if (r < 0) {
+ free(nl);
+ return (0);
+ }
+
+ exisiting = 0;
+ for (nlp = nl; nlp < nl + r; nlp += strlen(nlp) + 1) {
+ if (strcmp(nlp, xattrname) == 0) {
+ exisiting = 1;
+ break;
+ }
+ }
+ free(nl);
+ return (exisiting);
+}
+
+#endif
+
+/*
+ * Exercise HFS+ Compression.
+ */
+DEFINE_TEST(test_write_disk_mac_metadata)
+{
+#if !defined(__APPLE__) || !defined(UF_COMPRESSED)
+ skipping("MacOS-specific Mac Metadata test");
+#else
+ const char *refname = "test_write_disk_mac_metadata.tar.gz";
+ struct archive *ad, *a;
+ struct archive_entry *ae;
+ struct stat st;
+ acl_t acl;
+
+ extract_reference_file(refname);
+
+ /*
+ * Extract an archive to disk with HFS+ Compression.
+ */
+ assert((ad = archive_write_disk_new()) != NULL);
+ assertEqualIntA(ad, ARCHIVE_OK,
+ archive_write_disk_set_standard_lookup(ad));
+ assertEqualIntA(ad, ARCHIVE_OK,
+ archive_write_disk_set_options(ad,
+ ARCHIVE_EXTRACT_TIME |
+ ARCHIVE_EXTRACT_SECURE_SYMLINKS |
+ ARCHIVE_EXTRACT_SECURE_NODOTDOT |
+ ARCHIVE_EXTRACT_MAC_METADATA));
+
+ assert((a = archive_read_new()) != NULL);
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a));
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a));
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a,
+ refname, 512 * 20));
+
+ assertMakeDir("hfscmp", 0755);
+ assertChdir("hfscmp");
+
+ /* Extract file3. */
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+ assertEqualString("file3", archive_entry_pathname(ae));
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_extract2(a, ae, ad));
+
+ assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae));
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
+ assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+ assertEqualIntA(ad, ARCHIVE_OK, archive_write_free(ad));
+
+ /* Test file3. */
+ assertEqualInt(0, stat("file3", &st));
+ assertEqualInt(UF_COMPRESSED, st.st_flags & UF_COMPRESSED);
+ assertFileSize("file3", 8);
+ failure("'%s' should not have Resource Fork", "file3");
+ assertEqualInt(0, has_xattr("file3", "com.apple.ResourceFork"));
+ failure("'%s' should have decompfs xattr", "file3");
+ assertEqualInt(1, has_xattr("file3", "com.apple.decmpfs"));
+ assert(NULL != (acl = acl_get_file("file3", ACL_TYPE_EXTENDED)));
+ assertEqualString(acl_to_text(acl, NULL),
+ "!#acl 1\n"
+ "user:FFFFEEEE-DDDD-CCCC-BBBB-AAAA000000C9:Guest:201:deny:read\n"
+ "group:ABCDEFAB-CDEF-ABCD-EFAB-CDEF00000050:admin:80:allow:write\n"
+ );
+ if (acl) acl_free(acl);
+
+ assertChdir("..");
+
+ /*
+ * Extract an archive to disk without HFS+ Compression.
+ */
+ assert((ad = archive_write_disk_new()) != NULL);
+ assertEqualIntA(ad, ARCHIVE_OK,
+ archive_write_disk_set_standard_lookup(ad));
+ assertEqualIntA(ad, ARCHIVE_OK,
+ archive_write_disk_set_options(ad,
+ ARCHIVE_EXTRACT_TIME |
+ ARCHIVE_EXTRACT_SECURE_SYMLINKS |
+ ARCHIVE_EXTRACT_SECURE_NODOTDOT |
+ ARCHIVE_EXTRACT_MAC_METADATA |
+ ARCHIVE_EXTRACT_NO_HFS_COMPRESSION));
+
+ assert((a = archive_read_new()) != NULL);
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a));
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a));
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a,
+ refname, 512 * 20));
+
+ assertMakeDir("nocmp", 0755);
+ assertChdir("nocmp");
+
+ /* Extract file3. */
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+ assertEqualString("file3", archive_entry_pathname(ae));
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_extract2(a, ae, ad));
+
+ assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae));
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
+ assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+ assertEqualIntA(ad, ARCHIVE_OK, archive_write_free(ad));
+
+ /* Test file3. */
+ assertEqualInt(0, stat("file3", &st));
+ assertEqualInt(0, st.st_flags & UF_COMPRESSED);
+ assertFileSize("file3", 8);
+ failure("'%s' should not have Resource Fork", "file3");
+ assertEqualInt(0, has_xattr("file3", "com.apple.ResourceFork"));
+ failure("'%s' should not have decmpfs", "file3");
+ assertEqualInt(0, has_xattr("file3", "com.apple.decmpfs"));
+ assert(NULL != (acl = acl_get_file("file3", ACL_TYPE_EXTENDED)));
+ assertEqualString(acl_to_text(acl, NULL),
+ "!#acl 1\n"
+ "user:FFFFEEEE-DDDD-CCCC-BBBB-AAAA000000C9:Guest:201:deny:read\n"
+ "group:ABCDEFAB-CDEF-ABCD-EFAB-CDEF00000050:admin:80:allow:write\n"
+ );
+ if (acl) acl_free(acl);
+
+ assertChdir("..");
+
+ assertEqualFile("hfscmp/file3", "nocmp/file3");
+#endif
+}
--- /dev/null
+begin 644 test_write_disk_mac_metadata.tar.gz
+M'XL(")$=EU```W1E<W1?=W)I=&5?9&ES:U]M86-?;65T861A=&$N=&%R`.V5
+MS4K#0!#'4T'$7`6OKGA.W-U\-0L]I%^V4%':(GJ2-=F40-J&?-#Z!+Z"+^D[
+M.&U**SVT'HPHS0^&V9F="9,-_ZSZ[`>AT*0BP1B;NHX6WC*-I<<TCY?HFH4(
+MQ;I!B*93$V%"B4XDA`N=:D66I#R&4=Q,[*R#,M_?L9^_"EK[_\+Q^8ET)$FW
+MW$5W`_2(5BQRTBD8!?L`@[AR\;U'.L-A/U\M.][!WK9**IO\F3L=JSR*0J%R
+M-U13,4]AX_(*`D3D+!$Q:P,M0&D"2@-0ZH#B`/EY-VQVDXDD9103YHG)*XL%
+M]^11/,TBYM0;S5;;J2L+IRPB91WF[09FW!L'$U:%11A.9VP6!ZF0?^*`_S;W
+M?-Z!DQ+Q=7'_@7WZ)P;=TK]FFI:$YH5,L\6!ZY]BY*;!6-2(9A`;OD35E"''
+M-SF;F)3*6A7UNG6GW^AT'UJJ"^I*@^GD:R<Q;9E6T0`J>D^J[X=\E-1`VU$L
+MDD1X!Z"E_TCQM_]^_6."M_1/+&R5]_]OP%]<3_BC4ITE)24E!\8G]#K>=@`0
+"````
+`
+end