libarchive/test/test_compat_gzip.c \
libarchive/test/test_compat_lzip.c \
libarchive/test/test_compat_lzma.c \
+ libarchive/test/test_compat_mac_gnutar.c \
libarchive/test/test_compat_solaris_tar_acl.c \
libarchive/test/test_compat_tar_hardlink.c \
libarchive/test/test_compat_xz.c \
libarchive/test/test_compat_lzma_1.tlz.uu \
libarchive/test/test_compat_lzma_2.tlz.uu \
libarchive/test/test_compat_lzma_3.tlz.uu \
+ libarchive/test/test_compat_mac_gnutar.tgz.uu \
libarchive/test/test_compat_solaris_tar_acl.tar.uu \
libarchive/test/test_compat_tar_hardlink_1.tar.uu \
libarchive/test/test_compat_xz_1.txz.uu \
aes_clean(&entry->ae_sourcepath);
aes_clean(&entry->ae_symlink);
aes_clean(&entry->ae_uname);
+ archive_entry_copy_mac_metadata(entry, NULL, 0);
archive_entry_acl_clear(entry);
archive_entry_xattr_clear(entry);
archive_entry_sparse_clear(entry);
struct ae_acl *ap, *ap2;
struct ae_xattr *xp;
struct ae_sparse *sp;
+ size_t s;
+ const void *p;
/* Allocate new structure and copy over all of the fields. */
entry2 = archive_entry_new();
ap = ap->next;
}
+ /* Copy Mac OS metadata. */
+ p = archive_entry_mac_metadata(entry, &s);
+ archive_entry_copy_mac_metadata(entry2, p, s);
+
/* Copy xattr data over. */
xp = entry->xattr_head;
while (xp != NULL) {
return (aes_update_utf8(&entry->ae_uname, name));
}
+const void *
+archive_entry_mac_metadata(struct archive_entry *entry, size_t *s)
+{
+ *s = entry->mac_metadata_size;
+ return entry->mac_metadata;
+}
+
+void
+archive_entry_copy_mac_metadata(struct archive_entry *entry,
+ const void *p, size_t s)
+{
+ free(entry->mac_metadata);
+ if (p == NULL || s == 0) {
+ entry->mac_metadata = NULL;
+ entry->mac_metadata_size = 0;
+ } else {
+ entry->mac_metadata_size = s;
+ entry->mac_metadata = malloc(s);
+ if (entry->mac_metadata == NULL)
+ abort();
+ memcpy(entry->mac_metadata, p, s);
+ }
+}
+
/*
* ACL management. The following would, of course, be a lot simpler
* if: 1) the last draft of POSIX.1e were a really thorough and
__LA_DECL const struct stat *archive_entry_stat(struct archive_entry *);
__LA_DECL void archive_entry_copy_stat(struct archive_entry *, const struct stat *);
+/*
+ * Storage for Mac OS-specific AppleDouble metadata information.
+ * Apple-format tar files store a separate binary blob containing
+ * encoded metadata with ACL, extended attributes, etc.
+ * This provides a place to store that blob.
+ */
+
+__LA_DECL const void * archive_entry_mac_metadata(struct archive_entry *, size_t *);
+__LA_DECL void archive_entry_copy_mac_metadata(struct archive_entry *, const void *, size_t);
/*
* ACL routines. This used to simply store and return text-format ACL
/* Not used within libarchive; useful for some clients. */
struct aes ae_sourcepath; /* Path this entry is sourced from. */
+ void *mac_metadata;
+ size_t mac_metadata_size;
+
/* ACL support. */
struct ae_acl *acl_head;
struct ae_acl *acl_p;
struct archive_entry *, const void *h);
static int header_longname(struct archive_read *, struct tar *,
struct archive_entry *, const void *h);
+static int read_mac_metadata_blob(struct archive_read *, struct tar *,
+ struct archive_entry *, const void *h);
static int header_volume(struct archive_read *, struct tar *,
struct archive_entry *, const void *h);
static int header_ustar(struct archive_read *, struct tar *,
}
}
--tar->header_recursion_depth;
+ /* Yuck. Apple's design here ends up storing long pathname
+ * extensions for both the AppleDouble extension entry and the
+ * regular entry.
+ */
+ // TODO: Should this be disabled on non-Mac platforms?
+ if ((err == ARCHIVE_WARN || err == ARCHIVE_OK) &&
+ tar->header_recursion_depth == 0) {
+ int err2 = read_mac_metadata_blob(a, tar, entry, h);
+ if (err2 < err)
+ err = err2;
+ }
+
/* We return warnings or success as-is. Anything else is fatal. */
if (err == ARCHIVE_WARN || err == ARCHIVE_OK) {
if (tar->sparse_gnu_pending) {
return (0);
}
+/*
+ * Read a Mac AppleDouble-encoded blob of file metadata,
+ * if there is one.
+ */
+static int
+read_mac_metadata_blob(struct archive_read *a, struct tar *tar,
+ struct archive_entry *entry, const void *h)
+{
+ size_t size, padded_size;
+ const void *data;
+ const char *p, *name;
+
+ // Find the last path element.
+ name = p = archive_entry_pathname(entry);
+ for (; *p != '\0'; ++p) {
+ if (p[0] == '/' && p[1] != '\0')
+ name = p + 1;
+ }
+ // If last path element starts with "._", then
+ // this is a Mac extension.
+ if (name[0] != '.' || name[1] != '_' || name[2] == '\0')
+ return ARCHIVE_OK;
+
+ /* Read the body as a Mac OS metadata blob. */
+ size = archive_entry_size(entry);
+ padded_size = (size + 511) & ~ 511;
+ /*
+ * TODO: Look beyond the body here to peek at the next header.
+ * If it's a regular header (not an extension header)
+ * that has the wrong name, just return the current
+ * entry as-is, without consuming the body here.
+ * That would reduce the risk of us mis-identifying
+ * an ordinary file that just happened to have
+ * a name starting with "._".
+ */
+ data = __archive_read_ahead(a, size, NULL);
+ if (data == NULL)
+ return (ARCHIVE_FATAL);
+ archive_entry_copy_mac_metadata(entry, data, size);
+ if (padded_size != __archive_read_consume(a, padded_size))
+ return (ARCHIVE_FATAL);
+ return (tar_read_header(a, tar, entry));
+}
+
/*
* Parse a file header for a pax extended archive entry.
*/
test_compat_gzip.c
test_compat_lzip.c
test_compat_lzma.c
+ test_compat_mac_gnutar.c
test_compat_solaris_tar_acl.c
test_compat_tar_hardlink.c
test_compat_xz.c
--- /dev/null
+/*-
+ * Copyright (c) 2003-2010 Tim Kientzle
+ * 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$");
+
+/* The sample has some files in a directory with a very long name. */
+#define TESTPATH "abcdefghijklmnopqrstuvwxyz/" \
+ "abcdefghijklmnopqrstuvwxyz/" \
+ "abcdefghijklmnopqrstuvwxyz/" \
+ "abcdefghijklmnopqrstuvwxyz/" \
+ "abcdefghijklmnopqrstuvwxyz/" \
+ "abcdefghijklmnopqrstuvwxyz/" \
+ "abcdefghijklmnopqrstuvwxyz/"
+
+/*
+ * Apple shipped an extended version of GNU tar with Mac OX X 10.5
+ * and earlier.
+ */
+DEFINE_TEST(test_compat_mac_gnutar)
+{
+ char name[] = "test_compat_mac_gnutar.tgz";
+ struct archive_entry *ae;
+ struct archive *a;
+ const void *attr;
+ size_t attrSize;
+ int r;
+
+ assert((a = archive_read_new()) != NULL);
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_support_compression_all(a));
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a));
+ extract_reference_file(name);
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a, name, 10240));
+
+ assertEqualIntA(a, ARCHIVE_OK, r = archive_read_next_header(a, &ae));
+ assertEqualString(TESTPATH, archive_entry_pathname(ae));
+ assertEqualInt(1275688109, archive_entry_mtime(ae));
+ assertEqualInt(95594, archive_entry_uid(ae));
+ assertEqualString("kientzle", archive_entry_uname(ae));
+ assertEqualInt(5000, archive_entry_gid(ae));
+ assertEqualString("", archive_entry_gname(ae));
+ assertEqualInt(040755, archive_entry_mode(ae));
+
+ attr = archive_entry_mac_metadata(ae, &attrSize);
+ assert(attr == NULL);
+ assertEqualInt(0, attrSize);
+
+ assertEqualIntA(a, ARCHIVE_OK, r = archive_read_next_header(a, &ae));
+ assertEqualString(TESTPATH "dir/", archive_entry_pathname(ae));
+ assertEqualInt(1275687611, archive_entry_mtime(ae));
+ assertEqualInt(95594, archive_entry_uid(ae));
+ assertEqualString("kientzle", archive_entry_uname(ae));
+ assertEqualInt(5000, archive_entry_gid(ae));
+ assertEqualString("", archive_entry_gname(ae));
+ assertEqualInt(040755, archive_entry_mode(ae));
+
+ attr = archive_entry_mac_metadata(ae, &attrSize);
+ assert(attr != NULL);
+ assertEqualInt(225, attrSize);
+
+ assertEqualIntA(a, ARCHIVE_OK, r = archive_read_next_header(a, &ae));
+ assertEqualString(TESTPATH "file", archive_entry_pathname(ae));
+ assertEqualInt(1275687588, archive_entry_mtime(ae));
+ assertEqualInt(95594, archive_entry_uid(ae));
+ assertEqualString("kientzle", archive_entry_uname(ae));
+ assertEqualInt(5000, archive_entry_gid(ae));
+ assertEqualString("", archive_entry_gname(ae));
+ assertEqualInt(0100644, archive_entry_mode(ae));
+
+ attr = archive_entry_mac_metadata(ae, &attrSize);
+ assert(attr != NULL);
+ assertEqualInt(225, attrSize);
+
+ assertEqualIntA(a, ARCHIVE_OK, r = archive_read_next_header(a, &ae));
+ assertEqualString("dir/", archive_entry_pathname(ae));
+ assertEqualInt(1275688064, archive_entry_mtime(ae));
+ assertEqualInt(95594, archive_entry_uid(ae));
+ assertEqualString("kientzle", archive_entry_uname(ae));
+ assertEqualInt(5000, archive_entry_gid(ae));
+ assertEqualString("", archive_entry_gname(ae));
+ assertEqualInt(040755, archive_entry_mode(ae));
+
+ attr = archive_entry_mac_metadata(ae, &attrSize);
+ assert(attr != NULL);
+ assertEqualInt(225, attrSize);
+
+ assertEqualIntA(a, ARCHIVE_OK, r = archive_read_next_header(a, &ae));
+ assertEqualString("file", archive_entry_pathname(ae));
+ assertEqualInt(1275625860, archive_entry_mtime(ae));
+ assertEqualInt(95594, archive_entry_uid(ae));
+ assertEqualString("kientzle", archive_entry_uname(ae));
+ assertEqualInt(5000, archive_entry_gid(ae));
+ assertEqualString("", archive_entry_gname(ae));
+ assertEqualInt(0100644, archive_entry_mode(ae));
+
+ attr = archive_entry_mac_metadata(ae, &attrSize);
+ assert(attr != NULL);
+ assertEqualInt(225, attrSize);
+
+ /* Verify the end-of-archive. */
+ assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae));
+
+ /* Verify that the format detection worked. */
+ assertEqualInt(archive_compression(a), ARCHIVE_COMPRESSION_GZIP);
+ assertEqualInt(archive_format(a), ARCHIVE_FORMAT_TAR_GNUTAR);
+
+ assertEqualInt(ARCHIVE_OK, archive_read_close(a));
+ assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+}
+
--- /dev/null
+begin 644 test_compat_mac_gnutar.tgz
+M'XL(`.UT"4P``^V9WV[3,!3)"[!:)2Q3$=5/;<1+4*SK^2$A%D[9=<(="
+MFW59TZ1DZ3KV),`-C\`;<+,GX4VPUW9=J6"E6T[-]OTD*W'LM)&;\QU_IV[=
+MK3]OY5FWE60]5@U\S)^.7(;!^?G9=2%"X3&G5='SS#$\+*/"<5B1Y^7?YHWV
+MXSBE>"!:H@_M3KS7W4\.>FD_RP<?B\-R>#0Z_G12MW]HW8MW`Z#XN4Q0A[[/
+M=*!+WY<ZUH4(!)^+>2$4EWI<FGF>4H%DCD^Q`-/X[R5Q5IZD,<5W6H1K@?Y[
+MW%O0?RZA_Q18HN2K#;GO.TFQ[A7\O[%!_STEIOHO9.B-]5\QAU,LP"W7?W;O
+MT7UVE[&W4=O9VG'>.1/,-?9`-ZG;=]U,_^=R']G<W=UFC[^<3N[XK-N;WZ;<
+MF5U_V,[[;C08I+$;M5.WC(^-#C]YJCN.V.@6^7#0:&Z^>/GJ=7.S9@XUTZN=
+M=\?OD,\;4:>?9(UG^B1-\U%C5"1EO'$]BW1SL2/_R\7\+Y#_*;`DDZ\VI+,_
+M/.#5L"'_7_!_L_POX/\HL$/_U:+^H_Y'@B5*OJK_VTMN7<1>+U3Z'RBUG/\S
+M\[3^^R'\'PG5^;\?#/[/>NS(__!_Z\*23+[:$++_E;$A_\_YOTG^5\C_)+@$
+M?Z)<YO\O[/^D/--_'@C4_VFH;/_WM<.P_[,>BA+J/]3_IO'OB0#U/PI<@B+*
+MTOY?Z'FF%BCTZ\*A_R14IO\'WQCTWWHH+-32^_]9_'M!@/@'````````````
+7``````````````#@,GX!9EY(W`!0````
+`
+end