]> git.ipfire.org Git - thirdparty/libarchive.git/commitdiff
Do not directly copy mac metadata to the data fork file if the file
authorMichihiro NAKAJIMA <ggcueroad@gmail.com>
Sun, 4 Nov 2012 00:22:28 +0000 (09:22 +0900)
committerMichihiro NAKAJIMA <ggcueroad@gmail.com>
Mon, 5 Nov 2012 02:27:56 +0000 (11:27 +0900)
is compressed by HFS+ compression through copyfile() because it makes
the file uncompressed.

Makefile.am
libarchive/archive_write_disk_posix.c
libarchive/test/CMakeLists.txt
libarchive/test/test_write_disk_appledouble.c [new file with mode: 0644]
libarchive/test/test_write_disk_appledouble.cpio.gz.uu [new file with mode: 0644]
libarchive/test/test_write_disk_mac_metadata.c [new file with mode: 0644]
libarchive/test/test_write_disk_mac_metadata.tar.gz.uu [new file with mode: 0644]

index ccb8f3ba5b018aa4b59ba1526e5d43eaa71706d0..c81baaf455bbeb90aaf323f6b6451f26f7248a93 100644 (file)
@@ -414,10 +414,12 @@ libarchive_test_SOURCES=                                  \
        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                \
@@ -652,7 +654,9 @@ libarchive_test_EXTRA_DIST=\
        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
index 90c82c51af51b68d386a08685eb609663507d790..3b0f5a5ae574d5fedf2f9db463da72cfb47a22fb 100644 (file)
@@ -33,6 +33,9 @@ __FBSDID("$FreeBSD$");
 #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
@@ -3337,6 +3340,178 @@ fixup_appledouble(struct archive_write_disk *a, const char *pathname)
  * 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)
@@ -3363,13 +3538,19 @@ set_mac_metadata(struct archive_write_disk *a, const char *pathname,
        }
        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);
@@ -3403,14 +3584,14 @@ fixup_appledouble(struct archive_write_disk *a, const char *pathname)
         */
        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,
@@ -3421,9 +3602,11 @@ fixup_appledouble(struct archive_write_disk *a, const char *pathname)
        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;
@@ -3431,19 +3614,13 @@ fixup_appledouble(struct archive_write_disk *a, const char *pathname)
        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);
 }
index 46f1e0c73ed4319c7f5e7144217e093a9295085c..a55a8842a20dad4f19a9ba36e026875df6dbf6e9 100644 (file)
@@ -149,10 +149,12 @@ IF(ENABLE_TEST)
     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
diff --git a/libarchive/test/test_write_disk_appledouble.c b/libarchive/test/test_write_disk_appledouble.c
new file mode 100644 (file)
index 0000000..ed0b1e7
--- /dev/null
@@ -0,0 +1,210 @@
+/*-
+ * 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
+}
diff --git a/libarchive/test/test_write_disk_appledouble.cpio.gz.uu b/libarchive/test/test_write_disk_appledouble.cpio.gz.uu
new file mode 100644 (file)
index 0000000..f2797d9
--- /dev/null
@@ -0,0 +1,12 @@
+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
diff --git a/libarchive/test/test_write_disk_mac_metadata.c b/libarchive/test/test_write_disk_mac_metadata.c
new file mode 100644 (file)
index 0000000..a59f6b0
--- /dev/null
@@ -0,0 +1,192 @@
+/*-
+ * 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
+}
diff --git a/libarchive/test/test_write_disk_mac_metadata.tar.gz.uu b/libarchive/test/test_write_disk_mac_metadata.tar.gz.uu
new file mode 100644 (file)
index 0000000..216b6ef
--- /dev/null
@@ -0,0 +1,14 @@
+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