]> git.ipfire.org Git - thirdparty/libarchive.git/commitdiff
Implement functions to manually set the format and filters used.
authorAndres Mejia <amejia004@gmail.com>
Sat, 22 Sep 2012 22:43:23 +0000 (18:43 -0400)
committerAndres Mejia <amejia004@gmail.com>
Fri, 7 Dec 2012 23:39:52 +0000 (18:39 -0500)
14 files changed:
Makefile.am
libarchive/archive.h
libarchive/archive_read.c
libarchive/archive_read_private.h
libarchive/archive_read_support_filter_bzip2.c
libarchive/archive_read_support_filter_compress.c
libarchive/archive_read_support_filter_gzip.c
libarchive/archive_read_support_filter_lrzip.c
libarchive/archive_read_support_filter_program.c
libarchive/archive_read_support_filter_rpm.c
libarchive/archive_read_support_filter_uu.c
libarchive/archive_read_support_filter_xz.c
libarchive/test/CMakeLists.txt
libarchive/test/test_read_set_format.c [new file with mode: 0644]

index c395bb2793e0b93ab99c2f7a7c1acc8efa34d85c..06c8bc06faa5b3598579888853ce511d9b485257 100644 (file)
@@ -409,6 +409,7 @@ libarchive_test_SOURCES=                                    \
        libarchive/test/test_read_large.c                       \
        libarchive/test/test_read_pax_truncated.c               \
        libarchive/test/test_read_position.c                    \
+       libarchive/test/test_read_set_format.c                  \
        libarchive/test/test_read_truncated.c                   \
        libarchive/test/test_read_truncated_filter.c            \
        libarchive/test/test_sparse_basic.c                     \
index 9fdfa626242625ee55df7251191f8d34b47bc995..119690650c44ffed8555de6cddba5ab6926895a2 100644 (file)
@@ -372,6 +372,17 @@ __LA_DECL int archive_read_support_format_tar(struct archive *);
 __LA_DECL int archive_read_support_format_xar(struct archive *);
 __LA_DECL int archive_read_support_format_zip(struct archive *);
 
+/* Functions to manually set the format and filters to be used. This is
+ * useful to bypass the bidding process when the format and filters to use
+ * is known in advance.
+ */
+__LA_DECL int archive_read_set_format(struct archive *, int);
+__LA_DECL int archive_read_append_filter(struct archive *, int);
+__LA_DECL int archive_read_append_filter_program(struct archive *,
+    const char *);
+__LA_DECL int archive_read_append_filter_program_signature
+    (struct archive *, const char *, const void * /* match */, size_t);
+
 /* Set various callbacks. */
 __LA_DECL int archive_read_set_open_callback(struct archive *,
     archive_open_callback *);
index 76631534548d18160f5b845014e8df05368e45b0..2a66d4089446dd6df0a04dff6adda9c6925ed7f0 100644 (file)
@@ -455,7 +455,7 @@ int
 archive_read_open1(struct archive *_a)
 {
        struct archive_read *a = (struct archive_read *)_a;
-       struct archive_read_filter *filter;
+       struct archive_read_filter *filter, *tmp;
        int slot, e;
        unsigned int i;
 
@@ -499,25 +499,37 @@ archive_read_open1(struct archive *_a)
        filter->sswitch = client_switch_proxy;
        filter->name = "none";
        filter->code = ARCHIVE_FILTER_NONE;
-       a->filter = filter;
 
-       client_switch_proxy(a->filter, 0);
        a->client.dataset[0].begin_position = 0;
-
-       /* Build out the input pipeline. */
-       e = choose_filters(a);
-       if (e < ARCHIVE_WARN) {
-               a->archive.state = ARCHIVE_STATE_FATAL;
-               return (ARCHIVE_FATAL);
+       if (!a->filter || !a->bypass_filter_bidding)
+       {
+               a->filter = filter;
+               /* Build out the input pipeline. */
+               e = choose_filters(a);
+               if (e < ARCHIVE_WARN) {
+                       a->archive.state = ARCHIVE_STATE_FATAL;
+                       return (ARCHIVE_FATAL);
+               }
+       }
+       else
+       {
+               /* Need to add "NONE" type filter at the end of the filter chain */
+               tmp = a->filter;
+               while (tmp->upstream)
+                       tmp = tmp->upstream;
+               tmp->upstream = filter;
        }
 
-       slot = choose_format(a);
-       if (slot < 0) {
-               close_filters(a);
-               a->archive.state = ARCHIVE_STATE_FATAL;
-               return (ARCHIVE_FATAL);
+       if (!a->format)
+       {
+               slot = choose_format(a);
+               if (slot < 0) {
+                       close_filters(a);
+                       a->archive.state = ARCHIVE_STATE_FATAL;
+                       return (ARCHIVE_FATAL);
+               }
+               a->format = &(a->formats[slot]);
        }
-       a->format = &(a->formats[slot]);
 
        a->archive.state = ARCHIVE_STATE_HEADER;
 
@@ -1622,3 +1634,236 @@ __archive_read_filter_seek(struct archive_read_filter *filter, int64_t offset,
        }
        return r;
 }
+
+int
+archive_read_set_format(struct archive *_a, int code)
+{
+  int r1, r2, slots, i;
+  char str[10];
+  struct archive_read *a = (struct archive_read *)_a;
+
+  if ((r1 = archive_read_support_format_by_code(_a, code)) < (ARCHIVE_OK))
+    return r1;
+
+  r1 = r2 = (ARCHIVE_OK);
+  if (a->format)
+    r2 = (ARCHIVE_WARN);
+  switch (code & ARCHIVE_FORMAT_BASE_MASK)
+  {
+    case ARCHIVE_FORMAT_7ZIP:
+      strcpy(str, "7zip");
+      break;
+    case ARCHIVE_FORMAT_AR:
+      strcpy(str, "ar");
+      break;
+    case ARCHIVE_FORMAT_CAB:
+      strcpy(str, "cab");
+      break;
+    case ARCHIVE_FORMAT_CPIO:
+      strcpy(str, "cpio");
+      break;
+    case ARCHIVE_FORMAT_ISO9660:
+      strcpy(str, "iso9660");
+      break;
+    case ARCHIVE_FORMAT_LHA:
+      strcpy(str, "lha");
+      break;
+    case ARCHIVE_FORMAT_MTREE:
+      strcpy(str, "mtree");
+      break;
+    case ARCHIVE_FORMAT_RAR:
+      strcpy(str, "rar");
+      break;
+    case ARCHIVE_FORMAT_TAR:
+      strcpy(str, "tar");
+      break;
+    case ARCHIVE_FORMAT_XAR:
+      strcpy(str, "xar");
+      break;
+    case ARCHIVE_FORMAT_ZIP:
+      strcpy(str, "zip");
+      break;
+    default:
+      archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
+          "Invalid format code specified");
+      return (ARCHIVE_FATAL);
+  }
+
+  slots = sizeof(a->formats) / sizeof(a->formats[0]);
+  a->format = &(a->formats[0]);
+  for (i = 0; i < slots; i++, a->format++) {
+    if (!a->format->name || !strcmp(a->format->name, str))
+      break;
+  }
+  if (!a->format->name || strcmp(a->format->name, str))
+  {
+    archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
+        "Internal error: Unable to set format");
+    r1 = (ARCHIVE_FATAL);
+  }
+
+  return (r1 < r2) ? r1 : r2;
+}
+
+int
+archive_read_append_filter(struct archive *_a, int code)
+{
+  int r1, r2, number_bidders, i;
+  char str[10];
+  struct archive_read_filter_bidder *bidder;
+  struct archive_read_filter *filter;
+  struct archive_read *a = (struct archive_read *)_a;
+
+  r1 = r2 = (ARCHIVE_OK);
+  switch (code)
+  {
+    case ARCHIVE_FILTER_NONE:
+      /* No filter to add, so do nothing.
+       * NOTE: An initial "NONE" type filter is always set at the end of the
+       * filter chain.
+       */
+      r1 = (ARCHIVE_OK);
+      break;
+    case ARCHIVE_FILTER_GZIP:
+      strcpy(str, "gzip");
+      r1 = archive_read_support_filter_gzip(_a);
+      break;
+    case ARCHIVE_FILTER_BZIP2:
+      strcpy(str, "bzip2");
+      r1 = archive_read_support_filter_bzip2(_a);
+      break;
+    case ARCHIVE_FILTER_COMPRESS:
+      strcpy(str, "compress (.Z)");
+      r1 = archive_read_support_filter_compress(_a);
+      break;
+    case ARCHIVE_FILTER_PROGRAM:
+      archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
+          "Cannot append program filter using archive_read_append_filter");
+      return (ARCHIVE_FATAL);
+    case ARCHIVE_FILTER_LZMA:
+      strcpy(str, "lzma");
+      r1 = archive_read_support_filter_lzma(_a);
+      break;
+    case ARCHIVE_FILTER_XZ:
+      strcpy(str, "xz");
+      r1 = archive_read_support_filter_xz(_a);
+      break;
+    case ARCHIVE_FILTER_UU:
+      strcpy(str, "uu");
+      r1 = archive_read_support_filter_uu(_a);
+      break;
+    case ARCHIVE_FILTER_RPM:
+      strcpy(str, "rpm");
+      r1 = archive_read_support_filter_rpm(_a);
+      break;
+    case ARCHIVE_FILTER_LZIP:
+      strcpy(str, "lzip");
+      r1 = archive_read_support_filter_lzip(_a);
+      break;
+    case ARCHIVE_FILTER_LRZIP:
+      strcpy(str, "lrzip");
+      r1 = archive_read_support_filter_lrzip(_a);
+      break;
+    default:
+      archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
+          "Invalid filter code specified");
+      return (ARCHIVE_FATAL);
+  }
+
+  if (code != ARCHIVE_FILTER_NONE)
+  {
+    number_bidders = sizeof(a->bidders) / sizeof(a->bidders[0]);
+
+    bidder = a->bidders;
+    for (i = 0; i < number_bidders; i++, bidder++)
+    {
+      if (!bidder->name || !strcmp(bidder->name, str))
+        break;
+    }
+    if (!bidder->name || strcmp(bidder->name, str))
+    {
+      archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
+          "Internal error: Unable to append filter");
+      return (ARCHIVE_FATAL);
+    }
+
+    filter
+        = (struct archive_read_filter *)calloc(1, sizeof(*filter));
+    if (filter == NULL)
+    {
+      archive_set_error(&a->archive, ENOMEM, "Out of memory");
+      return (ARCHIVE_FATAL);
+    }
+    filter->bidder = bidder;
+    filter->archive = a;
+    filter->upstream = a->filter;
+    a->filter = filter;
+    r2 = (bidder->init)(a->filter);
+    if (r2 != ARCHIVE_OK) {
+      close_filters(a);
+      free_filters(a);
+      return (ARCHIVE_FATAL);
+    }
+  }
+
+  a->bypass_filter_bidding = 1;
+  return (r1 < r2) ? r1 : r2;
+}
+
+int
+archive_read_append_filter_program(struct archive *_a, const char *cmd)
+{
+  return (archive_read_append_filter_program_signature(_a, cmd, NULL, 0));
+}
+
+int
+archive_read_append_filter_program_signature(struct archive *_a,
+  const char *cmd, const void *signature, size_t signature_len)
+{
+  int r, number_bidders, i;
+  struct archive_read_filter_bidder *bidder;
+  struct archive_read_filter *filter;
+  struct archive_read *a = (struct archive_read *)_a;
+
+  if (archive_read_support_filter_program_signature(_a, cmd, signature,
+    signature_len) != (ARCHIVE_OK))
+    return (ARCHIVE_FATAL);
+
+  number_bidders = sizeof(a->bidders) / sizeof(a->bidders[0]);
+
+  bidder = a->bidders;
+  for (i = 0; i < number_bidders; i++, bidder++)
+  {
+    /* Program bidder name set to filter name after initialization */
+    if (bidder->data && !bidder->name)
+      break;
+  }
+  if (!bidder->data)
+  {
+    archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
+        "Internal error: Unable to append program filter");
+    return (ARCHIVE_FATAL);
+  }
+
+  filter
+      = (struct archive_read_filter *)calloc(1, sizeof(*filter));
+  if (filter == NULL)
+  {
+    archive_set_error(&a->archive, ENOMEM, "Out of memory");
+    return (ARCHIVE_FATAL);
+  }
+  filter->bidder = bidder;
+  filter->archive = a;
+  filter->upstream = a->filter;
+  a->filter = filter;
+  r = (bidder->init)(a->filter);
+  if (r != ARCHIVE_OK) {
+    close_filters(a);
+    free_filters(a);
+    return (ARCHIVE_FATAL);
+  }
+  bidder->name = a->filter->name;
+
+  a->bypass_filter_bidding = 1;
+  return r;
+}
index de3f3cdb566a9a8821c557e68426792cdffa6e93..0ef686f9a7dc1973cac0a41695a1e5dec4e2600b 100644 (file)
@@ -58,6 +58,8 @@ struct archive_read_filter;
 struct archive_read_filter_bidder {
        /* Configuration data for the bidder. */
        void *data;
+       /* Name of the filter */
+       const char *name;
        /* Taste the upstream filter to see if we handle this. */
        int (*bid)(struct archive_read_filter_bidder *,
            struct archive_read_filter *);
@@ -168,6 +170,9 @@ struct archive_read {
        /* Last filter in chain */
        struct archive_read_filter *filter;
 
+       /* Whether to bypass filter bidding process */
+       int bypass_filter_bidding;
+
        /* File offset of beginning of most recently-read header. */
        int64_t           header_position;
 
index 35c6eb01ba5c5f932a65df00a20b2291b4e321a2..3885a7cf615336b733ba17db473d9050ad26a784 100644 (file)
@@ -94,6 +94,7 @@ archive_read_support_filter_bzip2(struct archive *_a)
                return (ARCHIVE_FATAL);
 
        reader->data = NULL;
+       reader->name = "bzip2";
        reader->bid = bzip2_reader_bid;
        reader->init = bzip2_reader_init;
        reader->options = NULL;
index b8b6260308cdfd55d061d60c1ffcfff4dbf14faf..3f5d1f37eab3cec13bc4fb0aea3d1477ddb6edde 100644 (file)
@@ -163,6 +163,7 @@ archive_read_support_filter_compress(struct archive *_a)
                return (ARCHIVE_FATAL);
 
        bidder->data = NULL;
+       bidder->name = "compress (.Z)";
        bidder->bid = compress_bidder_bid;
        bidder->init = compress_bidder_init;
        bidder->options = NULL;
index 475cc1737dd2cb356995265b5ffc693bd5807e35..fa8c675de12453419a414b3a25573556a9d6d144 100644 (file)
@@ -100,6 +100,7 @@ archive_read_support_filter_gzip(struct archive *_a)
                return (ARCHIVE_FATAL);
 
        bidder->data = NULL;
+       bidder->name = "gzip";
        bidder->bid = gzip_bidder_bid;
        bidder->init = gzip_bidder_init;
        bidder->options = NULL;
index 0b2924243207d148652b87c170d11d7fa1e7501c..c82a8e2f13ea02752f86fc490dbe60b383d11214 100644 (file)
@@ -73,6 +73,7 @@ archive_read_support_filter_lrzip(struct archive *_a)
                return (ARCHIVE_FATAL);
 
        reader->data = NULL;
+       reader->name = "lrzip";
        reader->bid = lrzip_bidder_bid;
        reader->init = lrzip_bidder_init;
        reader->options = NULL;
index 850ab45459e8588a22a1850341f0a89cfee2910c..66dc2f424f8587a7ebe7286157306bf510e829dc 100644 (file)
@@ -88,6 +88,7 @@ archive_read_support_filter_program(struct archive *a, const char *cmd)
  * bid twice in the same pipeline.
  */
 struct program_bidder {
+       char *description;
        char *cmd;
        void *signature;
        size_t signature_len;
index 237d9801556185dcb991fb4903c3e9f27f9ab97c..e7e58e51f3b029dd4dd8cc93a0b1edf65a569645 100644 (file)
@@ -85,6 +85,7 @@ archive_read_support_filter_rpm(struct archive *_a)
                return (ARCHIVE_FATAL);
 
        bidder->data = NULL;
+       bidder->name = "rpm";
        bidder->bid = rpm_bidder_bid;
        bidder->init = rpm_bidder_init;
        bidder->options = NULL;
index 493338d0b5e8d389ad53ce3bd28d4eff0b807d91..471771b6f4696ef81dbdb0f0111d849d40a7e1ee 100644 (file)
@@ -89,6 +89,7 @@ archive_read_support_filter_uu(struct archive *_a)
                return (ARCHIVE_FATAL);
 
        bidder->data = NULL;
+       bidder->name = "uu";
        bidder->bid = uudecode_bidder_bid;
        bidder->init = uudecode_bidder_init;
        bidder->options = NULL;
index c3c00fc9edb1f51ca8f7dedc392d7c76cd987525..15824b1d0974c6766c66b5f1f961e1cec29ebda6 100644 (file)
@@ -136,6 +136,7 @@ archive_read_support_filter_xz(struct archive *_a)
                return (ARCHIVE_FATAL);
 
        bidder->data = NULL;
+       bidder->name = "xz";
        bidder->bid = xz_bidder_bid;
        bidder->init = xz_bidder_init;
        bidder->options = NULL;
@@ -170,6 +171,7 @@ archive_read_support_filter_lzma(struct archive *_a)
                return (ARCHIVE_FATAL);
 
        bidder->data = NULL;
+       bidder->name = "lzma";
        bidder->bid = lzma_bidder_bid;
        bidder->init = lzma_bidder_init;
        bidder->options = NULL;
@@ -207,6 +209,7 @@ archive_read_support_filter_lzip(struct archive *_a)
                return (ARCHIVE_FATAL);
 
        bidder->data = NULL;
+       bidder->name = "lzip";
        bidder->bid = lzip_bidder_bid;
        bidder->init = lzip_bidder_init;
        bidder->options = NULL;
index eba089bc6386c85676765f5c9afb06e2ffaf2805..99c00f3b9620d44cdf0938ebb513e801c800d084 100644 (file)
@@ -144,6 +144,7 @@ IF(ENABLE_TEST)
     test_read_large.c
     test_read_pax_truncated.c
     test_read_position.c
+    test_read_set_format.c
     test_read_truncated.c
     test_read_truncated_filter.c
     test_sparse_basic.c
diff --git a/libarchive/test/test_read_set_format.c b/libarchive/test/test_read_set_format.c
new file mode 100644 (file)
index 0000000..2b3ec1b
--- /dev/null
@@ -0,0 +1,219 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * Copyright (c) 2012 Andres Mejia
+ * Copyright (c) 2011-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"
+
+DEFINE_TEST(test_read_set_format)
+{
+  char buff[64];
+  const char reffile[] = "test_read_format_rar.rar";
+  const char test_txt[] = "test text document\r\n";
+  int size = sizeof(test_txt)-1;
+  struct archive_entry *ae;
+  struct archive *a;
+
+  extract_reference_file(reffile);
+  assert((a = archive_read_new()) != NULL);
+  assertA(0 == archive_read_set_format(a, ARCHIVE_FORMAT_RAR));
+  assertA(0 == archive_read_append_filter(a, ARCHIVE_FILTER_NONE));
+  assertA(0 == archive_read_open_filename(a, reffile, 10240));
+
+  /* First header. */
+  assertA(0 == archive_read_next_header(a, &ae));
+  assertEqualString("test.txt", archive_entry_pathname(ae));
+  assertA((int)archive_entry_mtime(ae));
+  assertA((int)archive_entry_ctime(ae));
+  assertA((int)archive_entry_atime(ae));
+  assertEqualInt(20, archive_entry_size(ae));
+  assertEqualInt(33188, archive_entry_mode(ae));
+  assertA(size == archive_read_data(a, buff, size));
+  assertEqualMem(buff, test_txt, size);
+
+  /* Second header. */
+  assertA(0 == archive_read_next_header(a, &ae));
+  assertEqualString("testlink", archive_entry_pathname(ae));
+  assertA((int)archive_entry_mtime(ae));
+  assertA((int)archive_entry_ctime(ae));
+  assertA((int)archive_entry_atime(ae));
+  assertEqualInt(0, archive_entry_size(ae));
+  assertEqualInt(41471, archive_entry_mode(ae));
+  assertEqualString("test.txt", archive_entry_symlink(ae));
+  assertEqualIntA(a, 0, archive_read_data(a, buff, sizeof(buff)));
+
+  /* Third header. */
+  assertA(0 == archive_read_next_header(a, &ae));
+  assertEqualString("testdir/test.txt", archive_entry_pathname(ae));
+  assertA((int)archive_entry_mtime(ae));
+  assertA((int)archive_entry_ctime(ae));
+  assertA((int)archive_entry_atime(ae));
+  assertEqualInt(20, archive_entry_size(ae));
+  assertEqualInt(33188, archive_entry_mode(ae));
+  assertA(size == archive_read_data(a, buff, size));
+  assertEqualMem(buff, test_txt, size);
+
+  /* Fourth header. */
+  assertA(0 == archive_read_next_header(a, &ae));
+  assertEqualString("testdir", archive_entry_pathname(ae));
+  assertA((int)archive_entry_mtime(ae));
+  assertA((int)archive_entry_ctime(ae));
+  assertA((int)archive_entry_atime(ae));
+  assertEqualInt(0, archive_entry_size(ae));
+  assertEqualInt(16877, archive_entry_mode(ae));
+
+  /* Fifth header. */
+  assertA(0 == archive_read_next_header(a, &ae));
+  assertEqualString("testemptydir", archive_entry_pathname(ae));
+  assertA((int)archive_entry_mtime(ae));
+  assertA((int)archive_entry_ctime(ae));
+  assertA((int)archive_entry_atime(ae));
+  assertEqualInt(0, archive_entry_size(ae));
+  assertEqualInt(16877, archive_entry_mode(ae));
+
+  /* Test EOF */
+  assertA(1 == archive_read_next_header(a, &ae));
+  assertEqualInt(5, archive_file_count(a));
+  assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
+  assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+}
+
+DEFINE_TEST(test_read_set_wrong_format)
+{
+  const char reffile[] = "test_read_format_zip.zip";
+  struct archive_entry *ae;
+  struct archive *a;
+
+  extract_reference_file(reffile);
+  assert((a = archive_read_new()) != NULL);
+  assertA(0 == archive_read_set_format(a, ARCHIVE_FORMAT_RAR));
+  assertA(0 == archive_read_append_filter(a, ARCHIVE_FILTER_NONE));
+  assertA(0 == archive_read_open_filename(a, reffile, 10240));
+
+  /* Check that this actually fails, then close the archive. */
+  assertA(archive_read_next_header(a, &ae) < (ARCHIVE_WARN));
+  assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
+  assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+}
+
+static unsigned char archive[] = {
+31,139,8,0,222,'C','p','C',0,3,211,'c',160,'=','0','0','0','0','7','5','U',
+0,210,134,230,166,6,200,'4',28,'(',24,26,24,27,155,24,152,24,154,27,155,')',
+24,24,26,152,154,25,'2','(',152,210,193,'m',12,165,197,'%',137,'E','@',167,
+148,'d',230,226,'U','G','H',30,234,15,'8','=',10,'F',193,'(',24,5,131,28,
+0,0,29,172,5,240,0,6,0,0};
+
+DEFINE_TEST(test_read_append_filter)
+{
+  struct archive_entry *ae;
+  struct archive *a;
+  int r;
+
+  assert((a = archive_read_new()) != NULL);
+  assertA(0 == archive_read_set_format(a, ARCHIVE_FORMAT_TAR));
+  r = archive_read_append_filter(a, ARCHIVE_FILTER_GZIP);
+  if (r == ARCHIVE_WARN) {
+    skipping("gzip reading not fully supported on this platform");
+    assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+    return;
+  }
+  assertEqualInt(ARCHIVE_OK,
+      archive_read_open_memory(a, archive, sizeof(archive)));
+  assertEqualInt(ARCHIVE_OK, archive_read_next_header(a, &ae));
+  assertEqualInt(1, archive_file_count(a));
+  assertEqualInt(archive_filter_code(a, 0), ARCHIVE_COMPRESSION_GZIP);
+  assertEqualInt(archive_format(a), ARCHIVE_FORMAT_TAR_USTAR);
+  assertEqualInt(ARCHIVE_OK, archive_read_close(a));
+  assertEqualInt(ARCHIVE_OK,archive_read_free(a));
+}
+
+DEFINE_TEST(test_read_append_wrong_filter)
+{
+  struct archive_entry *ae;
+  struct archive *a;
+  int r;
+
+  assert((a = archive_read_new()) != NULL);
+  assertA(0 == archive_read_set_format(a, ARCHIVE_FORMAT_TAR));
+  r = archive_read_append_filter(a, ARCHIVE_FILTER_XZ);
+  if (r == ARCHIVE_WARN) {
+    skipping("gzip reading not fully supported on this platform");
+    assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+    return;
+  }
+  assertEqualInt(ARCHIVE_OK,
+      archive_read_open_memory(a, archive, sizeof(archive)));
+  assertA(archive_read_next_header(a, &ae) < (ARCHIVE_WARN));
+  assertEqualInt(ARCHIVE_OK, archive_read_close(a));
+  assertEqualInt(ARCHIVE_OK,archive_read_free(a));
+}
+
+DEFINE_TEST(test_read_append_filter_program)
+{
+  struct archive_entry *ae;
+  struct archive *a;
+
+  /*
+   * If we have "gzip -d", try using that.
+   */
+  if (!canGzip()) {
+    skipping("Can't run gunzip program on this platform");
+    return;
+  }
+  assert((a = archive_read_new()) != NULL);
+  assertA(0 == archive_read_set_format(a, ARCHIVE_FORMAT_TAR));
+  assertEqualIntA(a, ARCHIVE_OK,
+      archive_read_append_filter_program(a, "gunzip"));
+  assertEqualIntA(a, ARCHIVE_OK,
+      archive_read_open_memory(a, archive, sizeof(archive)));
+  assertEqualIntA(a, ARCHIVE_OK,
+      archive_read_next_header(a, &ae));
+  assertEqualInt(archive_filter_code(a, 0), ARCHIVE_FILTER_PROGRAM);
+  assertEqualInt(archive_format(a), ARCHIVE_FORMAT_TAR_USTAR);
+  assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
+  assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+}
+
+DEFINE_TEST(test_read_append_filter_wrong_program)
+{
+  struct archive_entry *ae;
+  struct archive *a;
+
+  /*
+   * If we have "bunzip2 -q", try using that.
+   */
+  if (!canRunCommand("bunzip2 -V")) {
+    skipping("Can't run bunzip2 program on this platform");
+    return;
+  }
+  assert((a = archive_read_new()) != NULL);
+  assertA(0 == archive_read_set_format(a, ARCHIVE_FORMAT_TAR));
+  assertEqualIntA(a, ARCHIVE_OK,
+      archive_read_append_filter_program(a, "bunzip2 -q"));
+  assertEqualIntA(a, ARCHIVE_OK,
+      archive_read_open_memory(a, archive, sizeof(archive)));
+  assertA(archive_read_next_header(a, &ae) < (ARCHIVE_WARN));
+  assertEqualIntA(a, ARCHIVE_WARN, archive_read_close(a));
+  assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+}