]> git.ipfire.org Git - thirdparty/libarchive.git/commitdiff
Big decompression refactor, Phase 2.
authorTim Kientzle <kientzle@gmail.com>
Sun, 4 Jan 2009 02:02:36 +0000 (21:02 -0500)
committerTim Kientzle <kientzle@gmail.com>
Sun, 4 Jan 2009 02:02:36 +0000 (21:02 -0500)
First, I've changed terminology: the objects that were called
"sources" and "readers" in libarchive 2.6 are now called "filters" and
"filter bidders."  I think this reads a lot better.  It also more
cleanly conveys that filters can be stacked and are not limited
to decompression.

Filter objects are now created and owned by the archive_read core,
which attaches reblocking logic to the output of each one.  This
allows filters to use the read_ahead/consume protocol to pull data
from upstream filters (the client itself is also handled as a filter,
one with no upstream).  Peek/consume read semantics greatly simplify
bidding and should make it easier to handle other lookahead scenarios
such as concatenated gzip streams.

Bid protocol:
  * Filters install bidders into the core.
  * Bidder has a "bid" function which is handled an upstream filter to taste.
  * Core creates a filter object for the winner and allows the filter
    to initialize that object.
  * Core repeats the bid with the new filter to build out filter streams.

Filter protocol:
  * read() can use peek/consume to pull from upstream filter, returns
    arbitrary blocks
  * close() releases private data
  * Optional skip() seeks forward in the stream.

The core obviously provides a lot more support in the way of reblocking
logic so that downstream filters can use simple peek/consume semantics
while upstream filters can provide arbitrary blocks without regard
for downstream needs.  The lazy reblocking makes this pretty efficient
in practice.

Upcoming:  I think the core should provide a "prepare" stage that's called
before the first read.  Concatenated gzip streams would go back to prepare
after close.  It will require a little thought to properly separate the
init() and prepare() operations.

SVN-Revision: 364

libarchive/archive_read.c
libarchive/archive_read_private.h
libarchive/archive_read_support_compression_bzip2.c
libarchive/archive_read_support_compression_compress.c
libarchive/archive_read_support_compression_gzip.c
libarchive/archive_read_support_compression_lzma.c
libarchive/archive_read_support_compression_program.c
libarchive/test/test_read_position.c

index a1f40238f6a8ff58b00baed321091d607bb9cd58..a2af41726fdf198e0636d872370ffe698c6ea5f2 100644 (file)
@@ -75,16 +75,6 @@ archive_read_new(void)
        a->archive.state = ARCHIVE_STATE_NEW;
        a->entry = archive_entry_new();
 
-       /* Initialize reblocking logic. */
-       a->buffer_size = 64 * 1024; /* 64k */
-       a->buffer = (char *)malloc(a->buffer_size);
-       a->next = a->buffer;
-       if (a->buffer == NULL) {
-               archive_entry_free(a->entry);
-               free(a);
-               return (NULL);
-       }
-
        return (&a->archive);
 }
 
@@ -117,21 +107,27 @@ archive_read_open(struct archive *a, void *client_data,
 }
 
 static ssize_t
-client_read_proxy(struct archive_read_source *self, const void **buff)
+client_read_proxy(struct archive_read_filter *self, const void **buff)
 {
-       return (self->archive->client.reader)((struct archive *)self->archive,
+       ssize_t r;
+       r = (self->archive->client.reader)(&self->archive->archive,
            self->data, buff);
+       self->archive->archive.raw_position += r;
+       return (r);
 }
 
 static int64_t
-client_skip_proxy(struct archive_read_source *self, int64_t request)
+client_skip_proxy(struct archive_read_filter *self, int64_t request)
 {
-       return (self->archive->client.skipper)((struct archive *)self->archive,
+       int64_t r;
+       r = (self->archive->client.skipper)(&self->archive->archive,
            self->data, request);
+       self->archive->archive.raw_position += r;
+       return (r);
 }
 
 static int
-client_close_proxy(struct archive_read_source *self)
+client_close_proxy(struct archive_read_filter *self)
 {
        int r = ARCHIVE_OK;
 
@@ -151,6 +147,7 @@ archive_read_open2(struct archive *_a, void *client_data,
     archive_close_callback *client_closer)
 {
        struct archive_read *a = (struct archive_read *)_a;
+       struct archive_read_filter *filter;
        int e;
 
        __archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW,
@@ -172,27 +169,21 @@ archive_read_open2(struct archive *_a, void *client_data,
        }
 
        /* Save the client functions and mock up the initial source. */
-       a->client.opener = client_opener; /* Do we need to remember this? */
        a->client.reader = client_reader;
        a->client.skipper = client_skipper;
        a->client.closer = client_closer;
-       a->client.data = client_data;
 
-       {
-               struct archive_read_source *source;
-
-               source = calloc(1, sizeof(*source));
-               if (source == NULL)
-                       return (ARCHIVE_FATAL);
-               source->reader = NULL;
-               source->upstream = NULL;
-               source->archive = a;
-               source->data = client_data;
-               source->read = client_read_proxy;
-               source->skip = client_skip_proxy;
-               source->close = client_close_proxy;
-               a->source = source;
-       }
+       filter = calloc(1, sizeof(*filter));
+       if (filter == NULL)
+               return (ARCHIVE_FATAL);
+       filter->bidder = NULL;
+       filter->upstream = NULL;
+       filter->archive = a;
+       filter->data = client_data;
+       filter->read = client_read_proxy;
+       filter->skip = client_skip_proxy;
+       filter->close = client_close_proxy;
+       a->filter = filter;
 
        /* In case there's no filter. */
        a->archive.compression_code = ARCHIVE_COMPRESSION_NONE;
@@ -214,60 +205,49 @@ archive_read_open2(struct archive *_a, void *client_data,
 static int
 build_stream(struct archive_read *a)
 {
-       int number_readers, i, bid, best_bid;
-       struct archive_reader *reader, *best_reader;
-       struct archive_read_source *source;
-       const void *block;
-       ssize_t bytes_read;
+       int number_bidders, i, bid, best_bid;
+       struct archive_read_filter_bidder *bidder, *best_bidder;
+       struct archive_read_filter *filter;
+       int r;
 
-       /* Read first block now for compress format detection. */
-       bytes_read = (a->source->read)(a->source, &block);
-       if (bytes_read < 0) {
-               /* If the first read fails, close before returning error. */
-               if (a->source->close != NULL) {
-                       (a->source->close)(a->source);
-                       a->source = NULL;
+       for (;;) {
+               number_bidders = sizeof(a->bidders) / sizeof(a->bidders[0]);
+
+               best_bid = 0;
+               best_bidder = NULL;
+
+               bidder = a->bidders;
+               for (i = 0; i < number_bidders; i++, bidder++) {
+                       if (bidder->bid != NULL) {
+                               bid = (bidder->bid)(bidder, a->filter);
+                               if (bid > best_bid) {
+                                       best_bid = bid;
+                                       best_bidder = bidder;
+                               }
+                       }
                }
-               /* source->read should have already set error information. */
-               return (ARCHIVE_FATAL);
-       }
-
-       number_readers = sizeof(a->readers) / sizeof(a->readers[0]);
-
-       best_bid = 0;
-       best_reader = NULL;
 
-       reader = a->readers;
-       for (i = 0, reader = a->readers; i < number_readers; i++, reader++) {
-               if (reader->bid != NULL) {
-                       bid = (reader->bid)(reader, block, bytes_read);
-                       if (bid > best_bid) {
-                               best_bid = bid;
-                               best_reader = reader;
-                       }
+               /* If no bidder, we're done. */
+               if (best_bidder == NULL) {
+                       a->archive.compression_name = a->filter->name;
+                       a->archive.compression_code = a->filter->code;
+                       return (ARCHIVE_OK);
                }
-       }
 
-       /*
-        * If we have a winner, it becomes the next stage in the pipeline.
-        */
-       if (best_reader != NULL) {
-               source = (best_reader->init)(a, best_reader, a->source,
-                   block, bytes_read);
-               if (source == NULL)
+               filter
+                   = (struct archive_read_filter *)calloc(1, sizeof(*filter));
+               if (filter == NULL)
                        return (ARCHIVE_FATAL);
-               /* Record the best decompressor for this stream. */
-               a->source = source;
-               /* Recurse to get next pipeline stage. */
-               return (build_stream(a));
+               filter->bidder = best_bidder;
+               filter->archive = a;
+               filter->upstream = a->filter;
+               r = (best_bidder->init)(filter);
+               if (r != ARCHIVE_OK) {
+                       free(filter);
+                       return (r);
+               }
+               a->filter = filter;
        }
-
-       /* Save first block of data. */
-       a->client_buff = block;
-       a->client_total = bytes_read;
-       a->client_next = a->client_buff;
-       a->client_avail = a->client_total;
-       return (ARCHIVE_OK);
 }
 
 /*
@@ -594,20 +574,20 @@ archive_read_close(struct archive *_a)
 
        /* TODO: Clean up the formatters. */
 
-       /* Clean up the stream pipeline. */
-       while (a->source != NULL) {
-               struct archive_read_source *t = a->source->upstream;
-               r1 = (a->source->close)(a->source);
+       /* Clean up the filter pipeline. */
+       while (a->filter != NULL) {
+               struct archive_read_filter *t = a->filter->upstream;
+               r1 = (a->filter->close)(a->filter);
                if (r1 < r)
                        r = r1;
-               a->source = t;
+               a->filter = t;
        }
 
-       /* Release the reader objects. */
-       n = sizeof(a->readers)/sizeof(a->readers[0]);
+       /* Release the bidder objects. */
+       n = sizeof(a->bidders)/sizeof(a->bidders[0]);
        for (i = 0; i < n; i++) {
-               if (a->readers[i].free != NULL) {
-                       r1 = (a->readers[i].free)(&a->readers[i]);
+               if (a->bidders[i].free != NULL) {
+                       r1 = (a->bidders[i].free)(&a->bidders[i]);
                        if (r1 < r)
                                r = r1;
                }
@@ -649,7 +629,6 @@ archive_read_finish(struct archive *_a)
        if (a->entry)
                archive_entry_free(a->entry);
        a->archive.magic = 0;
-       free(a->buffer);
        free(a);
 #if ARCHIVE_API_VERSION > 1
        return (r);
@@ -699,20 +678,20 @@ __archive_read_register_format(struct archive_read *a,
  * Used internally by decompression routines to register their bid and
  * initialization functions.
  */
-struct archive_reader *
-__archive_read_get_reader(struct archive_read *a)
+struct archive_read_filter_bidder *
+__archive_read_get_bidder(struct archive_read *a)
 {
        int i, number_slots;
 
        __archive_check_magic(&a->archive,
            ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW,
-           "__archive_read_get_reader");
+           "__archive_read_get_bidder");
 
-       number_slots = sizeof(a->readers) / sizeof(a->readers[0]);
+       number_slots = sizeof(a->bidders) / sizeof(a->bidders[0]);
 
        for (i = 0; i < number_slots; i++) {
-               if (a->readers[i].bid == NULL)
-                       return (a->readers + i);
+               if (a->bidders[i].bid == NULL)
+                       return (a->bidders + i);
        }
 
        __archive_errx(1, "Not enough slots for compression registration");
@@ -725,7 +704,7 @@ __archive_read_get_reader(struct archive_read *a)
  * flexible read-ahead and allows the I/O code to operate in a
  * zero-copy manner most of the time.
  *
- * In the ideal case, block providers give the I/O code blocks of data
+ * In the ideal case, filters generate blocks of data
  * and __archive_read_ahead() just returns pointers directly into
  * those blocks.  Then __archive_read_consume() just bumps those
  * pointers.  Only if your request would span blocks does the I/O
@@ -738,7 +717,7 @@ __archive_read_get_reader(struct archive_read *a)
  *  * "I just want some data."  Ask for 1 byte and pay attention to
  *    the "number of bytes available" from __archive_read_ahead().
  *    You can consume more than you asked for; you just can't consume
- *    more than is available right now.  If you consume everything that's
+ *    more than is available.  If you consume everything that's
  *    immediately available, the next read_ahead() call will pull
  *    the next block.
  *  * "I want to output a large block of data."  As above, ask for 1 byte,
@@ -789,11 +768,18 @@ __archive_read_get_reader(struct archive_read *a)
  */
 const void *
 __archive_read_ahead(struct archive_read *a, size_t min, ssize_t *avail)
+{
+       return (__archive_read_filter_ahead(a->filter, min, avail));
+}
+
+const void *
+__archive_read_filter_ahead(struct archive_read_filter *filter,
+    size_t min, ssize_t *avail)
 {
        ssize_t bytes_read;
        size_t tocopy;
 
-       if (a->fatal) {
+       if (filter->fatal) {
                if (avail)
                        *avail = ARCHIVE_FATAL;
                return (NULL);
@@ -807,68 +793,68 @@ __archive_read_ahead(struct archive_read *a, size_t min, ssize_t *avail)
                /*
                 * If we can satisfy from the copy buffer, we're done.
                 */
-               if (a->avail >= min) {
+               if (filter->avail >= min) {
                        if (avail != NULL)
-                               *avail = a->avail;
-                       return (a->next);
+                               *avail = filter->avail;
+                       return (filter->next);
                }
 
                /*
                 * We can satisfy directly from client buffer if everything
                 * currently in the copy buffer is still in the client buffer.
                 */
-               if (a->client_total >= a->client_avail + a->avail
-                   && a->client_avail + a->avail >= min) {
+               if (filter->client_total >= filter->client_avail + filter->avail
+                   && filter->client_avail + filter->avail >= min) {
                        /* "Roll back" to client buffer. */
-                       a->client_avail += a->avail;
-                       a->client_next -= a->avail;
+                       filter->client_avail += filter->avail;
+                       filter->client_next -= filter->avail;
                        /* Copy buffer is now empty. */
-                       a->avail = 0;
-                       a->next = a->buffer;
+                       filter->avail = 0;
+                       filter->next = filter->buffer;
                        /* Return data from client buffer. */
                        if (avail != NULL)
-                               *avail = a->client_avail;
-                       return (a->client_next);
+                               *avail = filter->client_avail;
+                       return (filter->client_next);
                }
 
                /* Move data forward in copy buffer if necessary. */
-               if (a->next > a->buffer &&
-                   a->next + min > a->buffer + a->buffer_size) {
-                       if (a->avail > 0)
-                               memmove(a->buffer, a->next, a->avail);
-                       a->next = a->buffer;
+               if (filter->next > filter->buffer &&
+                   filter->next + min > filter->buffer + filter->buffer_size) {
+                       if (filter->avail > 0)
+                               memmove(filter->buffer, filter->next, filter->avail);
+                       filter->next = filter->buffer;
                }
 
                /* If we've used up the client data, get more. */
-               if (a->client_avail <= 0) {
-                       if (a->end_of_file) {
+               if (filter->client_avail <= 0) {
+                       if (filter->end_of_file) {
                                if (avail != NULL)
                                        *avail = 0;
                                return (NULL);
                        }
-                       bytes_read = (a->source->read)(a->source,
-                           &a->client_buff);
+                       bytes_read = (filter->read)(filter,
+                           &filter->client_buff);
                        if (bytes_read < 0) {           /* Read error. */
-                               a->client_total = a->client_avail = 0;
-                               a->client_next = a->client_buff = NULL;
-                               a->fatal = 1;
+                               filter->client_total = filter->client_avail = 0;
+                               filter->client_next = filter->client_buff = NULL;
+                               filter->fatal = 1;
                                if (avail != NULL)
                                        *avail = ARCHIVE_FATAL;
                                return (NULL);
                        }
                        if (bytes_read == 0) {  /* Premature end-of-file. */
-                               a->client_total = a->client_avail = 0;
-                               a->client_next = a->client_buff = NULL;
-                               a->end_of_file = 1;
+                               filter->client_total = filter->client_avail = 0;
+                               filter->client_next = filter->client_buff = NULL;
+                               filter->end_of_file = 1;
                                /* Return whatever we do have. */
                                if (avail != NULL)
-                                       *avail = a->avail;
+                                       *avail = filter->avail;
                                return (NULL);
                        }
-                       a->archive.raw_position += bytes_read;
-                       a->client_total = bytes_read;
-                       a->client_avail = a->client_total;
-                       a->client_next = a->client_buff;
+                       filter->position += bytes_read;
+                       filter->client_total = bytes_read;
+                       filter->client_avail = filter->client_total;
+                       filter->client_next = filter->client_buff;
                }
                else
                {
@@ -880,19 +866,22 @@ __archive_read_ahead(struct archive_read *a, size_t min, ssize_t *avail)
                         */
 
                        /* Ensure the buffer is big enough. */
-                       if (min > a->buffer_size) {
+                       if (min > filter->buffer_size) {
                                size_t s, t;
                                char *p;
 
                                /* Double the buffer; watch for overflow. */
-                               s = t = a->buffer_size;
+                               s = t = filter->buffer_size;
+                               if (s == 0)
+                                       s = min;
                                while (s < min) {
                                        t *= 2;
                                        if (t <= s) { /* Integer overflow! */
-                                               archive_set_error(&a->archive,
-                                                   ENOMEM,
+                                               archive_set_error(
+                                                       &filter->archive->archive,
+                                                       ENOMEM,
                                                    "Unable to allocate copy buffer");
-                                               a->fatal = 1;
+                                               filter->fatal = 1;
                                                if (avail != NULL)
                                                        *avail = ARCHIVE_FATAL;
                                                return (NULL);
@@ -902,39 +891,41 @@ __archive_read_ahead(struct archive_read *a, size_t min, ssize_t *avail)
                                /* Now s >= min, so allocate a new buffer. */
                                p = (char *)malloc(s);
                                if (p == NULL) {
-                                       archive_set_error(&a->archive, ENOMEM,
+                                       archive_set_error(
+                                               &filter->archive->archive,
+                                               ENOMEM,
                                            "Unable to allocate copy buffer");
-                                       a->fatal = 1;
+                                       filter->fatal = 1;
                                        if (avail != NULL)
                                                *avail = ARCHIVE_FATAL;
                                        return (NULL);
                                }
                                /* Move data into newly-enlarged buffer. */
-                               if (a->avail > 0)
-                                       memmove(p, a->next, a->avail);
-                               free(a->buffer);
-                               a->next = a->buffer = p;
-                               a->buffer_size = s;
+                               if (filter->avail > 0)
+                                       memmove(p, filter->next, filter->avail);
+                               free(filter->buffer);
+                               filter->next = filter->buffer = p;
+                               filter->buffer_size = s;
                        }
 
                        /* We can add client data to copy buffer. */
                        /* First estimate: copy to fill rest of buffer. */
-                       tocopy = (a->buffer + a->buffer_size)
-                           - (a->next + a->avail);
+                       tocopy = (filter->buffer + filter->buffer_size)
+                           - (filter->next + filter->avail);
                        /* Don't waste time buffering more than we need to. */
-                       if (tocopy + a->avail > min)
-                               tocopy = min - a->avail;
+                       if (tocopy + filter->avail > min)
+                               tocopy = min - filter->avail;
                        /* Don't copy more than is available. */
-                       if (tocopy > a->client_avail)
-                               tocopy = a->client_avail;
+                       if (tocopy > filter->client_avail)
+                               tocopy = filter->client_avail;
 
-                       memcpy(a->next + a->avail, a->client_next,
+                       memcpy(filter->next + filter->avail, filter->client_next,
                            tocopy);
                        /* Remove this data from client buffer. */
-                       a->client_next += tocopy;
-                       a->client_avail -= tocopy;
+                       filter->client_next += tocopy;
+                       filter->client_avail -= tocopy;
                        /* add it to copy buffer. */
-                       a->avail += tocopy;
+                       filter->avail += tocopy;
                }
        }
 }
@@ -953,16 +944,25 @@ __archive_read_ahead(struct archive_read *a, size_t min, ssize_t *avail)
 ssize_t
 __archive_read_consume(struct archive_read *a, size_t request)
 {
-       if (a->avail > 0) {
+       ssize_t r;
+       r = __archive_read_filter_consume(a->filter, request);
+       a->archive.file_position += r;
+       return (r);
+}
+
+ssize_t
+__archive_read_filter_consume(struct archive_read_filter * filter,
+    size_t request)
+{
+       if (filter->avail > 0) {
                /* Read came from copy buffer. */
-               a->next += request;
-               a->avail -= request;
+               filter->next += request;
+               filter->avail -= request;
        } else {
                /* Read came from client buffer. */
-               a->client_next += request;
-               a->client_avail -= request;
+               filter->client_next += request;
+               filter->client_avail -= request;
        }
-       a->archive.file_position += request;
        return (request);
 }
 
@@ -975,24 +975,30 @@ __archive_read_consume(struct archive_read *a, size_t request)
  */
 int64_t
 __archive_read_skip(struct archive_read *a, int64_t request)
+{
+       return (__archive_read_filter_skip(a->filter, request));
+}
+
+int64_t
+__archive_read_filter_skip(struct archive_read_filter *filter, int64_t request)
 {
        off_t bytes_skipped, total_bytes_skipped = 0;
        size_t min;
 
-       if (a->fatal)
+       if (filter->fatal)
                return (-1);
        /*
         * If there is data in the buffers already, use that first.
         */
-       if (a->avail > 0) {
-               min = minimum(request, (off_t)a->avail);
-               bytes_skipped = __archive_read_consume(a, min);
+       if (filter->avail > 0) {
+               min = minimum(request, (off_t)filter->avail);
+               bytes_skipped = __archive_read_consume(filter->archive, min);
                request -= bytes_skipped;
                total_bytes_skipped += bytes_skipped;
        }
-       if (a->client_avail > 0) {
-               min = minimum(request, (off_t)a->client_avail);
-               bytes_skipped = __archive_read_consume(a, min);
+       if (filter->client_avail > 0) {
+               min = minimum(request, (off_t)filter->client_avail);
+               bytes_skipped = __archive_read_consume(filter->archive, min);
                request -= bytes_skipped;
                total_bytes_skipped += bytes_skipped;
        }
@@ -1002,23 +1008,22 @@ __archive_read_skip(struct archive_read *a, int64_t request)
         * If a client_skipper was provided, try that first.
         */
 #if ARCHIVE_API_VERSION < 2
-       if ((a->source->skip != NULL) && (request < SSIZE_MAX)) {
+       if ((filter->skip != NULL) && (request < SSIZE_MAX)) {
 #else
-       if (a->source->skip != NULL) {
+       if (filter->skip != NULL) {
 #endif
-               bytes_skipped = (a->source->skip)(a->source, request);
+               bytes_skipped = (filter->skip)(filter, request);
                if (bytes_skipped < 0) {        /* error */
-                       a->client_total = a->client_avail = 0;
-                       a->client_next = a->client_buff = NULL;
-                       a->fatal = 1;
+                       filter->client_total = filter->client_avail = 0;
+                       filter->client_next = filter->client_buff = NULL;
+                       filter->fatal = 1;
                        return (bytes_skipped);
                }
+               filter->archive->archive.file_position += bytes_skipped;
                total_bytes_skipped += bytes_skipped;
-               a->archive.file_position += bytes_skipped;
                request -= bytes_skipped;
-               a->client_next = a->client_buff;
-               a->archive.raw_position += bytes_skipped;
-               a->client_avail = a->client_total = 0;
+               filter->client_next = filter->client_buff;
+               filter->client_avail = filter->client_total = 0;
        }
        /*
         * Note that client_skipper will usually not satisfy the
@@ -1029,18 +1034,20 @@ __archive_read_skip(struct archive_read *a, int64_t request)
        while (request > 0) {
                const void* dummy_buffer;
                ssize_t bytes_read;
-               dummy_buffer = __archive_read_ahead(a, 1, &bytes_read);
+               dummy_buffer = __archive_read_ahead(filter->archive,
+                   1, &bytes_read);
                if (bytes_read < 0)
                        return (bytes_read);
                if (bytes_read == 0) {
                        /* We hit EOF before we satisfied the skip request. */
-                       archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+                       archive_set_error(&filter->archive->archive,
+                           ARCHIVE_ERRNO_MISC,
                            "Truncated input file (need to skip %jd bytes)",
                            (intmax_t)request);
                        return (ARCHIVE_FATAL);
                }
                min = (size_t)(minimum(bytes_read, request));
-               bytes_read = __archive_read_consume(a, min);
+               bytes_read = __archive_read_consume(filter->archive, min);
                total_bytes_skipped += bytes_read;
                request -= bytes_read;
        }
index df390e63622747353d16ae4e623de749d45098e2..7ec8be88635fe932c0b76a2c7f184b9cbeb26ab3 100644 (file)
 #include "archive_private.h"
 
 struct archive_read;
-struct archive_reader;
-struct archive_read_source;
+struct archive_read_filter_bidder;
+struct archive_read_filter;
 
 /*
- * A "reader" knows how to provide blocks.  That can include something
- * that reads blocks from disk or socket or a transformation layer
- * that reads blocks from another source and transforms them.  This
- * includes decompression and decryption filters.
- *
- * How bidding works:
+ * How bidding works for filters:
  *   * The bid manager reads the first block from the current source.
  *   * It shows that block to each registered bidder.
- *   * The winning bidder is initialized (with the block and information
- *     about the source)
- *   * The winning bidder becomes the new source and the process repeats
- * This ends only when no reader provides a non-zero bid.
+ *   * The bid manager creates a new filter structure for the winning
+ *     bidder and gives the winning bidder a chance to initialize it.
+ *   * The new filter becomes the top filter in the archive_read structure
+ *     and we repeat the process.
+ * This ends only when no bidder provides a non-zero bid.
  */
-struct archive_reader {
-       /* Configuration data for the reader. */
+struct archive_read_filter_bidder {
+       /* Configuration data for the bidder. */
        void *data;
-       /* Bidder is handed the initial block from its source. */
-       int (*bid)(struct archive_reader *, const void *buff, size_t);
-       /* Init() is given the archive, upstream source, and the initial
-        * block above.  It returns a populated source structure. */
-       struct archive_read_source *(*init)(struct archive_read *,
-           struct archive_reader *, struct archive_read_source *source,
-           const void *, size_t);
-       /* Release the reader and any configuration data it allocated. */
-       int (*free)(struct archive_reader *);
+       /* Taste the upstream filter to see if we handle this. */
+       int (*bid)(struct archive_read_filter_bidder *,
+           struct archive_read_filter *);
+       /* Initialize a newly-created filter. */
+       int (*init)(struct archive_read_filter *);
+       /* Release the bidder's configuration data. */
+       int (*free)(struct archive_read_filter_bidder *);
 };
 
 /*
- * A "source" is an instance of a reader.  This structure is
- * allocated and initialized by the init() method of a reader
- * above.
+ * This structure is allocated within the archive_read core
+ * and initialized by archive_read and the init() method of the
+ * corresponding bidder above.
  */
-struct archive_read_source {
-       /* Essentially all sources will need these values, so
+struct archive_read_filter {
+       /* Essentially all filters will need these values, so
         * just declare them here. */
-       struct archive_reader *reader; /* Reader that I'm an instance of. */
-       struct archive_read_source *upstream; /* Who I get blocks from. */
-       struct archive_read *archive; /* associated archive. */
+       struct archive_read_filter_bidder *bidder; /* My bidder. */
+       struct archive_read_filter *upstream; /* Who I read from. */
+       struct archive_read *archive; /* Associated archive. */
        /* Return next block. */
-       ssize_t (*read)(struct archive_read_source *, const void **);
+       ssize_t (*read)(struct archive_read_filter *, const void **);
        /* Skip forward this many bytes. */
-       int64_t (*skip)(struct archive_read_source *self, int64_t request);
-       /* Close (recursively) and free(self). */
-       int (*close)(struct archive_read_source *self);
+       int64_t (*skip)(struct archive_read_filter *self, int64_t request);
+       /* Close (just this filter) and free(self). */
+       int (*close)(struct archive_read_filter *self);
        /* My private data. */
        void *data;
+
+       const char      *name;
+       int              code;
+
+       /* Used by reblocking logic. */
+       char            *buffer;
+       size_t           buffer_size;
+       char            *next;          /* Current read location. */
+       size_t           avail;         /* Bytes in my buffer. */
+       const void      *client_buff;   /* Client buffer information. */
+       size_t           client_total;
+       const char      *client_next;
+       size_t           client_avail;
+       int64_t          position;
+       char             end_of_file;
+       char             fatal;
 };
 
 /*
- * The client source is almost the same as an internal source.
+ * The client looks a lot like a filter, so we just wrap it here.
  *
- * TODO: Make archive_read_source and archive_read_client identical so
+ * TODO: Make archive_read_filter and archive_read_client identical so
  * that users of the library can easily register their own
  * transformation filters.  This will probably break the API/ABI and
- * so should be deferred until libarchive 3.0.
+ * so should be deferred at least until libarchive 3.0.
  */
 struct archive_read_client {
-       archive_open_callback   *opener;
        archive_read_callback   *reader;
        archive_skip_callback   *skipper;
        archive_close_callback  *closer;
-       void                    *data;
 };
 
 struct archive_read {
@@ -122,28 +130,15 @@ struct archive_read {
        /* Callbacks to open/read/write/close client archive stream. */
        struct archive_read_client client;
 
-       /* Registered readers. */
-       struct archive_reader readers[8];
+       /* Registered filter bidders. */
+       struct archive_read_filter_bidder bidders[8];
 
-       /* Source */
-       struct archive_read_source *source;
+       /* Last filter in chain */
+       struct archive_read_filter *filter;
 
        /* File offset of beginning of most recently-read header. */
        off_t             header_position;
 
-
-       /* Used by reblocking logic. */
-       char            *buffer;
-       size_t           buffer_size;
-       char            *next;          /* Current read location. */
-       size_t           avail;         /* Bytes in my buffer. */
-       const void      *client_buff;   /* Client buffer information. */
-       size_t           client_total;
-       const char      *client_next;
-       size_t           client_avail;
-       char             end_of_file;
-       char             fatal;
-
        /*
         * Format detection is mostly the same as compression
         * detection, with one significant difference: The bidders
@@ -177,13 +172,14 @@ int       __archive_read_register_format(struct archive_read *a,
            int (*read_data_skip)(struct archive_read *),
            int (*cleanup)(struct archive_read *));
 
-struct archive_reader
-       *__archive_read_get_reader(struct archive_read *a);
+struct archive_read_filter_bidder
+       *__archive_read_get_bidder(struct archive_read *a);
 
-const void
-       *__archive_read_ahead(struct archive_read *, size_t, ssize_t *);
-ssize_t
-       __archive_read_consume(struct archive_read *, size_t);
-int64_t
-       __archive_read_skip(struct archive_read *, int64_t);
+const void *__archive_read_ahead(struct archive_read *, size_t, ssize_t *);
+const void *__archive_read_filter_ahead(struct archive_read_filter *,
+    size_t, ssize_t *);
+ssize_t        __archive_read_consume(struct archive_read *, size_t);
+ssize_t        __archive_read_filter_consume(struct archive_read_filter *, size_t);
+int64_t        __archive_read_skip(struct archive_read *, int64_t);
+int64_t        __archive_read_filter_skip(struct archive_read_filter *, int64_t);
 #endif
index 89f6c91685ad2a71cbf9ddcaed45626c6780f80c..ddebab5769140c709a608fbe437846a805b2f3f2 100644 (file)
@@ -57,9 +57,9 @@ struct private_data {
        char             eof; /* True = found end of compressed data. */
 };
 
-/* Bzip2 source */
-static ssize_t bzip2_source_read(struct archive_read_source *, const void **);
-static int     bzip2_source_close(struct archive_read_source *);
+/* Bzip2 filter */
+static ssize_t bzip2_filter_read(struct archive_read_filter *, const void **);
+static int     bzip2_filter_close(struct archive_read_filter *);
 #endif
 
 /*
@@ -68,17 +68,15 @@ static int  bzip2_source_close(struct archive_read_source *);
  * error messages.)  So the bid framework here gets compiled even
  * if bzlib is unavailable.
  */
-static int     bzip2_reader_bid(struct archive_reader *, const void *, size_t);
-static struct archive_read_source *bzip2_reader_init(struct archive_read *,
-    struct archive_reader *, struct archive_read_source *,
-    const void *, size_t);
-static int     bzip2_reader_free(struct archive_reader *);
+static int     bzip2_reader_bid(struct archive_read_filter_bidder *, struct archive_read_filter *);
+static int     bzip2_reader_init(struct archive_read_filter *);
+static int     bzip2_reader_free(struct archive_read_filter_bidder *);
 
 int
 archive_read_support_compression_bzip2(struct archive *_a)
 {
        struct archive_read *a = (struct archive_read *)_a;
-       struct archive_reader *reader = __archive_read_get_reader(a);
+       struct archive_read_filter_bidder *reader = __archive_read_get_bidder(a);
 
        if (reader == NULL)
                return (ARCHIVE_FATAL);
@@ -91,7 +89,7 @@ archive_read_support_compression_bzip2(struct archive *_a)
 }
 
 static int
-bzip2_reader_free(struct archive_reader *self){
+bzip2_reader_free(struct archive_read_filter_bidder *self){
        (void)self; /* UNUSED */
        return (ARCHIVE_OK);
 }
@@ -104,61 +102,38 @@ bzip2_reader_free(struct archive_reader *self){
  * from verifying as much as we would like.
  */
 static int
-bzip2_reader_bid(struct archive_reader *self, const void *buff, size_t len)
+bzip2_reader_bid(struct archive_read_filter_bidder *self, struct archive_read_filter *filter)
 {
        const unsigned char *buffer;
+       size_t avail;
        int bits_checked;
 
        (void)self; /* UNUSED */
 
-       if (len < 1)
+       /* Minimal bzip2 archive is 14 bytes. */
+       buffer = __archive_read_filter_ahead(filter, 14, &avail);
+       if (buffer == NULL)
                return (0);
 
-       buffer = (const unsigned char *)buff;
+       /* First three bytes must be "BZh" */
        bits_checked = 0;
-       if (buffer[0] != 'B')   /* Verify first ID byte. */
+       if (buffer[0] != 'B' || buffer[1] != 'Z' || buffer[2] != 'h')
                return (0);
-       bits_checked += 8;
-       if (len < 2)
-               return (bits_checked);
-
-       if (buffer[1] != 'Z')   /* Verify second ID byte. */
-               return (0);
-       bits_checked += 8;
-       if (len < 3)
-               return (bits_checked);
-
-       if (buffer[2] != 'h')   /* Verify third ID byte. */
-               return (0);
-       bits_checked += 8;
-       if (len < 4)
-               return (bits_checked);
+       bits_checked += 24;
 
+       /* Next follows a compression flag which must be an ASCII digit. */
        if (buffer[3] < '1' || buffer[3] > '9')
                return (0);
        bits_checked += 5;
-       if (len < 5)
-               return (bits_checked);
 
        /* After BZh[1-9], there must be either a data block
         * which begins with 0x314159265359 or an end-of-data
         * marker of 0x177245385090. */
-
-       if (buffer[4] == 0x31) {
-               /* Verify the data block signature. */
-               size_t s = len;
-               if (s > 10) s = 10;
-               if (memcmp(buffer + 4, "\x31\x41\x59\x26\x53\x59", s - 4) != 0)
-                       return (0);
-               bits_checked += 8 * (s - 4);
-       } else if (buffer[4] == 0x17) {
-               /* Verify the end-of-data marker. */
-               size_t s = len;
-               if (s > 10) s = 10;
-               if (memcmp(buffer + 4, "\x17\x72\x45\x38\x50\x90", s - 4) != 0)
-                       return (0);
-               bits_checked += 8 * (s - 4);
-       } else
+       if (memcmp(buffer + 4, "\x31\x41\x59\x26\x53\x59", 6) == 0)
+               bits_checked += 48;
+       else if (memcmp(buffer + 4, "\x17\x72\x45\x38\x50\x90", 6) == 0)
+               bits_checked += 48;
+       else
                return (0);
 
        return (bits_checked);
@@ -171,9 +146,10 @@ bzip2_reader_bid(struct archive_reader *self, const void *buff, size_t len)
  * decompression.  We can, however, still detect compressed archives
  * and emit a useful message.
  */
-static struct archive_read_source *
-bzip2_reader_init(struct archive_read *a, struct archive_reader *reader,
-    struct archive_read_source *upstream, const void *buff, size_t n)
+static struct archive_read_filter *
+bzip2_reader_init(struct archive_read *a,
+    struct archive_read_filter_bidder *reader,
+    struct archive_read_filter *upstream, const void *buff, size_t n)
 {
        (void)a;        /* UNUSED */
        (void)reader;   /* UNUSED */
@@ -192,67 +168,45 @@ bzip2_reader_init(struct archive_read *a, struct archive_reader *reader,
 /*
  * Setup the callbacks.
  */
-static struct archive_read_source *
-bzip2_reader_init(struct archive_read *a, struct archive_reader *reader,
-    struct archive_read_source *upstream, const void *buff, size_t n)
+static int
+bzip2_reader_init(struct archive_read_filter *self)
 {
        static const size_t out_block_size = 64 * 1024;
        void *out_block;
-       struct archive_read_source *self;
        struct private_data *state;
 
-       (void)reader; /* UNUSED */
-
-       a->archive.compression_code = ARCHIVE_COMPRESSION_BZIP2;
-       a->archive.compression_name = "bzip2";
+       self->code = ARCHIVE_COMPRESSION_BZIP2;
+       self->name = "bzip2";
 
-       self = calloc(sizeof(*self), 1);
        state = (struct private_data *)calloc(sizeof(*state), 1);
        out_block = (unsigned char *)malloc(out_block_size);
        if (self == NULL || state == NULL || out_block == NULL) {
-               archive_set_error(&a->archive, ENOMEM,
-                   "Can't allocate data for %s decompression",
-                   a->archive.compression_name);
+               archive_set_error(&self->archive->archive, ENOMEM,
+                   "Can't allocate data for bzip2 decompression");
                free(out_block);
                free(state);
-               free(self);
-               return (NULL);
+               return (ARCHIVE_FATAL);
        }
 
-
-       self->archive = a;
        self->data = state;
        state->out_block_size = out_block_size;
        state->out_block = out_block;
-       self->upstream = upstream;
-       self->read = bzip2_source_read;
+       self->read = bzip2_filter_read;
        self->skip = NULL; /* not supported */
-       self->close = bzip2_source_close;
+       self->close = bzip2_filter_close;
 
-       /*
-        * A bug in bzlib.h: stream.next_in should be marked 'const'
-        * but isn't (the library never alters data through the
-        * next_in pointer, only reads it).  The result: this ugly
-        * cast to remove 'const'.
-        */
-       state->stream.next_in = (char *)(uintptr_t)(const void *)buff;
-       state->stream.avail_in = n;
-
-       state->stream.next_out = state->out_block;
-       state->stream.avail_out = state->out_block_size;
-
-       return (self);
+       return (ARCHIVE_OK);
 }
 
 /*
  * Return the next block of decompressed data.
  */
 static ssize_t
-bzip2_source_read(struct archive_read_source *self, const void **p)
+bzip2_filter_read(struct archive_read_filter *self, const void **p)
 {
        struct private_data *state;
        size_t read_avail, decompressed;
-       const void *read_buf;
+       unsigned char *read_buf;
        int ret;
 
        state = (struct private_data *)self->data;
@@ -269,29 +223,8 @@ bzip2_source_read(struct archive_read_source *self, const void **p)
 
        /* Try to fill the output buffer. */
        for (;;) {
-               /* If the last upstream block is done, get another one. */
-               if (state->stream.avail_in == 0) {
-                       ret = (self->upstream->read)(self->upstream,
-                           &read_buf);
-                       /* stream.next_in is really const, but bzlib
-                        * doesn't declare it so. <sigh> */
-                       state->stream.next_in
-                           = (unsigned char *)(uintptr_t)read_buf;
-                       if (ret < 0)
-                               return (ARCHIVE_FATAL);
-                       /* There is no more data, return whatever we have. */
-                       if (ret == 0) {
-                               state->eof = 1;
-                               *p = state->out_block;
-                               decompressed = state->stream.next_out
-                                   - state->out_block;
-                               return (decompressed);
-                       }
-                       state->stream.avail_in = ret;
-               }
-
                if (!state->valid) {
-                       if (state->stream.next_in[0] != 'B') {
+                       if (bzip2_reader_bid(self->bidder, self->upstream) == 0) {
                                state->eof = 1;
                                *p = state->out_block;
                                decompressed = state->stream.next_out
@@ -333,8 +266,28 @@ bzip2_source_read(struct archive_read_source *self, const void **p)
                        state->valid = 1;
                }
 
+               /* stream.next_in is really const, but bzlib
+                * doesn't declare it so. <sigh> */
+               read_buf = (unsigned char *)(uintptr_t)
+                   __archive_read_filter_ahead(self->upstream, 1, &ret);
+               if (read_buf == NULL)
+                       return (ARCHIVE_FATAL);
+               state->stream.next_in = read_buf;
+               state->stream.avail_in = ret;
+               /* There is no more data, return whatever we have. */
+               if (ret == 0) {
+                       state->eof = 1;
+                       *p = state->out_block;
+                       decompressed = state->stream.next_out
+                           - state->out_block;
+                       return (decompressed);
+               }
+
                /* Decompress as much as we can in one pass. */
                ret = BZ2_bzDecompress(&(state->stream));
+               __archive_read_filter_consume(self->upstream,
+                   (unsigned char *)state->stream.next_in - read_buf);
+
                switch (ret) {
                case BZ_STREAM_END: /* Found end of stream. */
                        switch (BZ2_bzDecompressEnd(&(state->stream))) {
@@ -369,7 +322,7 @@ bzip2_source_read(struct archive_read_source *self, const void **p)
  * Clean up the decompressor.
  */
 static int
-bzip2_source_close(struct archive_read_source *self)
+bzip2_filter_close(struct archive_read_filter *self)
 {
        struct private_data *state;
        int ret = ARCHIVE_OK;
index 329173738f3a17a073abb457ada61ebae063ccd4..85bc0701300dad40aa3fbc2406515df6d93c8d95 100644 (file)
@@ -130,31 +130,29 @@ struct private_data {
        unsigned char            stack[65300];
 };
 
-static int     compress_reader_bid(struct archive_reader *, const void *, size_t);
-static struct archive_read_source *compress_reader_init(struct archive_read *,
-    struct archive_reader *, struct archive_read_source *,
-    const void *, size_t);
-static int     compress_reader_free(struct archive_reader *);
+static int     compress_bidder_bid(struct archive_read_filter_bidder *, struct archive_read_filter *);
+static int     compress_bidder_init(struct archive_read_filter *);
+static int     compress_bidder_free(struct archive_read_filter_bidder *);
 
-static ssize_t compress_source_read(struct archive_read_source *, const void **);
-static int     compress_source_close(struct archive_read_source *);
+static ssize_t compress_filter_read(struct archive_read_filter *, const void **);
+static int     compress_filter_close(struct archive_read_filter *);
 
-static int     getbits(struct archive_read_source *, int n);
-static int     next_code(struct archive_read_source *);
+static int     getbits(struct archive_read_filter *, int n);
+static int     next_code(struct archive_read_filter *);
 
 int
 archive_read_support_compression_compress(struct archive *_a)
 {
        struct archive_read *a = (struct archive_read *)_a;
-       struct archive_reader *reader = __archive_read_get_reader(a);
+       struct archive_read_filter_bidder *bidder = __archive_read_get_bidder(a);
 
-       if (reader == NULL)
+       if (bidder == NULL)
                return (ARCHIVE_FATAL);
 
-       reader->data = NULL;
-       reader->bid = compress_reader_bid;
-       reader->init = compress_reader_init;
-       reader->free = compress_reader_free;
+       bidder->data = NULL;
+       bidder->bid = compress_bidder_bid;
+       bidder->init = compress_bidder_init;
+       bidder->free = compress_bidder_free;
        return (ARCHIVE_OK);
 }
 
@@ -166,29 +164,28 @@ archive_read_support_compression_compress(struct archive *_a)
  * from verifying as much as we would like.
  */
 static int
-compress_reader_bid(struct archive_reader *self, const void *buff, size_t len)
+compress_bidder_bid(struct archive_read_filter_bidder *self,
+    struct archive_read_filter *filter)
 {
        const unsigned char *buffer;
+       size_t avail;
        int bits_checked;
 
        (void)self; /* UNUSED */
 
-       if (len < 1)
+       buffer = __archive_read_filter_ahead(filter, 2, &avail);
+
+       if (buffer == NULL)
                return (0);
 
-       buffer = (const unsigned char *)buff;
        bits_checked = 0;
        if (buffer[0] != 037)   /* Verify first ID byte. */
                return (0);
        bits_checked += 8;
-       if (len < 2)
-               return (bits_checked);
 
        if (buffer[1] != 0235)  /* Verify second ID byte. */
                return (0);
        bits_checked += 8;
-       if (len < 3)
-               return (bits_checked);
 
        /*
         * TODO: Verify more.
@@ -200,66 +197,39 @@ compress_reader_bid(struct archive_reader *self, const void *buff, size_t len)
 /*
  * Setup the callbacks.
  */
-static struct archive_read_source *
-compress_reader_init(struct archive_read *a, struct archive_reader *reader,
-    struct archive_read_source *upstream, const void *buff, size_t n)
+static int
+compress_bidder_init(struct archive_read_filter *self)
 {
-       struct archive_read_source *self;
        struct private_data *state;
+       static const size_t out_block_size = 64 * 1024;
+       void *out_block;
        int code;
 
-       (void)reader; /* UNUSED */
-
-       a->archive.compression_code = ARCHIVE_COMPRESSION_COMPRESS;
-       a->archive.compression_name = "compress (.Z)";
-
-       self = calloc(sizeof(*self), 1);
-       if (self == NULL)
-               return (NULL);
-
-       self->read = compress_source_read;
-       self->skip = NULL; /* not supported */
-       self->close = compress_source_close;
-       self->upstream = upstream;
-       self->archive = a;
+       self->code = ARCHIVE_COMPRESSION_COMPRESS;
+       self->name = "compress (.Z)";
 
        state = (struct private_data *)calloc(sizeof(*state), 1);
-       if (state == NULL) {
-               archive_set_error(&a->archive, ENOMEM,
+       out_block = malloc(out_block_size);
+       if (state == NULL || out_block == NULL) {
+               free(out_block);
+               free(state);
+               archive_set_error(&self->archive->archive, ENOMEM,
                    "Can't allocate data for %s decompression",
-                   a->archive.compression_name);
-               free(self);
-               return (NULL);
-       }
-       self->data = state;
-
-       state->out_block_size = 64 * 1024;
-       state->out_block = malloc(state->out_block_size);
-
-       if (state->out_block == NULL) {
-               archive_set_error(&a->archive, ENOMEM,
-                   "Can't allocate %s decompression buffers",
-                   a->archive.compression_name);
-               goto fatal;
+                   self->name);
+               return (ARCHIVE_FATAL);
        }
 
-       state->next_in = (const unsigned char *)buff;
-       state->avail_in = n;
+       self->data = state;
+       state->out_block_size = out_block_size;
+       state->out_block = out_block;
+       self->read = compress_filter_read;
+       self->skip = NULL; /* not supported */
+       self->close = compress_filter_close;
 
-       code = getbits(self, 8);
-       if (code != 037) /* This should be impossible. */
-               goto fatal;
+       /* XXX MOVE THE FOLLOWING OUT OF INIT() XXX */
 
-       code = getbits(self, 8);
-       if (code != 0235) {
-               /* This can happen if the library is receiving 1-byte
-                * blocks and gzip and compress are both enabled.
-                * You can't distinguish gzip and compress only from
-                * the first byte. */
-               archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
-                   "Compress signature did not match.");
-               goto fatal;
-       }
+       code = getbits(self, 8); /* Skip first signature byte. */
+       code = getbits(self, 8); /* Skip second signature byte. */
 
        code = getbits(self, 8);
        state->maxcode_bits = code & 0x1f;
@@ -279,11 +249,8 @@ compress_reader_init(struct archive_read *a, struct archive_reader *reader,
                state->suffix[code] = code;
        }
        next_code(self);
-       return (self);
 
-fatal:
-       compress_source_close(self);
-       return (NULL);
+       return (ARCHIVE_OK);
 }
 
 /*
@@ -291,7 +258,7 @@ fatal:
  * as necessary.
  */
 static ssize_t
-compress_source_read(struct archive_read_source *self, const void **pblock)
+compress_filter_read(struct archive_read_filter *self, const void **pblock)
 {
        struct private_data *state;
        unsigned char *p, *start, *end;
@@ -325,17 +292,17 @@ compress_source_read(struct archive_read_source *self, const void **pblock)
  * Clean up the reader.
  */
 static int
-compress_reader_free(struct archive_reader *self)
+compress_bidder_free(struct archive_read_filter_bidder *self)
 {
        self->data = NULL;
        return (ARCHIVE_OK);
 }
 
 /*
- * Close and release a source.
+ * Close and release the filter.
  */
 static int
-compress_source_close(struct archive_read_source *self)
+compress_filter_close(struct archive_read_filter *self)
 {
        struct private_data *state = (struct private_data *)self->data;
 
@@ -351,7 +318,7 @@ compress_source_close(struct archive_read_source *self)
  * format error, ARCHIVE_EOF if we hit end of data, ARCHIVE_OK otherwise.
  */
 static int
-next_code(struct archive_read_source *self)
+next_code(struct archive_read_filter *self)
 {
        struct private_data *state = (struct private_data *)self->data;
        int code, newcode;
@@ -441,7 +408,7 @@ next_code(struct archive_read_source *self)
  * -1 indicates end of available data.
  */
 static int
-getbits(struct archive_read_source *self, int n)
+getbits(struct archive_read_filter *self, int n)
 {
        struct private_data *state = (struct private_data *)self->data;
        int code, ret;
@@ -449,19 +416,18 @@ getbits(struct archive_read_source *self, int n)
                0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff,
                0x1ff, 0x3ff, 0x7ff, 0xfff, 0x1fff, 0x3fff, 0x7fff, 0xffff
        };
-       const void *read_buf;
 
        while (state->bits_avail < n) {
                if (state->avail_in <= 0) {
-                       read_buf = state->next_in;
-                       ret = (self->upstream->read)(self->upstream, &read_buf);
-                       state->next_in = read_buf;
-                       if (ret < 0)
-                               return (ARCHIVE_FATAL);
+                       state->next_in
+                           = __archive_read_filter_ahead(self->upstream,
+                               1, &ret);
                        if (ret == 0)
                                return (ARCHIVE_EOF);
-/* TODO: Fix this                      a->archive.raw_position += ret; */
+                       if (ret < 0 || state->next_in == NULL)
+                               return (ARCHIVE_FATAL);
                        state->avail_in = ret;
+                       __archive_read_filter_consume(self->upstream, ret);
                }
                state->bit_buffer |= *state->next_in++ << state->bits_avail;
                state->avail_in--;
index c99adf48dfa406abfd810f1ccd550819aefe128a..8cd77f88d6498d9f92b137b1f4fc9cd3d1c8e527 100644 (file)
@@ -62,9 +62,9 @@ struct private_data {
        char             eof; /* True = found end of compressed data. */
 };
 
-/* Gzip Source. */
-static ssize_t gzip_source_read(struct archive_read_source *, const void **);
-static int     gzip_source_close(struct archive_read_source *);
+/* Gzip Filter. */
+static ssize_t gzip_filter_read(struct archive_read_filter *, const void **);
+static int     gzip_filter_close(struct archive_read_filter *);
 #endif
 
 /*
@@ -73,30 +73,28 @@ static int  gzip_source_close(struct archive_read_source *);
  * error messages.)  So the bid framework here gets compiled even
  * if zlib is unavailable.
  */
-static int     gzip_reader_bid(struct archive_reader *, const void *, size_t);
-static struct archive_read_source *gzip_reader_init(struct archive_read *,
-    struct archive_reader *, struct archive_read_source *,
-    const void *, size_t);
-static int     gzip_reader_free(struct archive_reader *);
+static int     gzip_bidder_bid(struct archive_read_filter_bidder *, struct archive_read_filter *);
+static int     gzip_bidder_init(struct archive_read_filter *);
+static int     gzip_bidder_free(struct archive_read_filter_bidder *);
 
 int
 archive_read_support_compression_gzip(struct archive *_a)
 {
        struct archive_read *a = (struct archive_read *)_a;
-       struct archive_reader *reader = __archive_read_get_reader(a);
+       struct archive_read_filter_bidder *bidder = __archive_read_get_bidder(a);
 
-       if (reader == NULL)
+       if (bidder == NULL)
                return (ARCHIVE_FATAL);
 
-       reader->data = NULL;
-       reader->bid = gzip_reader_bid;
-       reader->init = gzip_reader_init;
-       reader->free = gzip_reader_free;
+       bidder->data = NULL;
+       bidder->bid = gzip_bidder_bid;
+       bidder->init = gzip_bidder_init;
+       bidder->free = gzip_bidder_free;
        return (ARCHIVE_OK);
 }
 
 static int
-gzip_reader_free(struct archive_reader *self){
+gzip_bidder_free(struct archive_read_filter_bidder *self){
        (void)self; /* UNUSED */
        return (ARCHIVE_OK);
 }
@@ -109,41 +107,35 @@ gzip_reader_free(struct archive_reader *self){
  * from verifying as much as we would like.
  */
 static int
-gzip_reader_bid(struct archive_reader *self, const void *buff, size_t len)
+gzip_bidder_bid(struct archive_read_filter_bidder *self,
+    struct archive_read_filter *filter)
 {
        const unsigned char *buffer;
+       size_t avail;
        int bits_checked;
 
        (void)self; /* UNUSED */
 
-       if (len < 1)
+       buffer = __archive_read_filter_ahead(filter, 8, &avail);
+       if (buffer == NULL)
                return (0);
 
-       buffer = (const unsigned char *)buff;
        bits_checked = 0;
        if (buffer[0] != 037)   /* Verify first ID byte. */
                return (0);
        bits_checked += 8;
-       if (len < 2)
-               return (bits_checked);
 
        if (buffer[1] != 0213)  /* Verify second ID byte. */
                return (0);
        bits_checked += 8;
-       if (len < 3)
-               return (bits_checked);
 
        if (buffer[2] != 8)     /* Compression must be 'deflate'. */
                return (0);
        bits_checked += 8;
-       if (len < 4)
-               return (bits_checked);
 
        if ((buffer[3] & 0xE0)!= 0)     /* No reserved flags set. */
                return (0);
        bits_checked += 3;
-       if (len < 5)
-               return (bits_checked);
 
        /*
         * TODO: Verify more; in particular, gzip has an optional
@@ -163,15 +155,10 @@ gzip_reader_bid(struct archive_reader *self, const void *buff, size_t len)
  * decompression.  We can, however, still detect compressed archives
  * and emit a useful message.
  */
-static struct archive_read_source *
-gzip_reader_init(struct archive_read *a, struct archive_reader *reader,
-    struct archive_read_source *upstream, const void *buff, size_t n)
+static int
+gzip_reader_init(struct archive_read_filter *filter)
 {
-       (void)a;        /* UNUSED */
-       (void)reader;   /* UNUSED */
-       (void)upstream; /* UNUSED */
-       (void)buff;     /* UNUSED */
-       (void)n;        /* UNUSED */
+       (void)filter;   /* UNUSED */
 
        archive_set_error(&a->archive, -1,
            "This version of libarchive was compiled without gzip support");
@@ -181,61 +168,44 @@ gzip_reader_init(struct archive_read *a, struct archive_reader *reader,
 #else
 
 /*
- * Initialize the source object.
+ * Initialize the filter object.
  */
-static struct archive_read_source *
-gzip_reader_init(struct archive_read *a, struct archive_reader *reader,
-    struct archive_read_source *upstream, const void *buff, size_t n)
+static int
+gzip_bidder_init(struct archive_read_filter *self)
 {
+       struct private_data *state;
        static const size_t out_block_size = 64 * 1024;
        void *out_block;
-       struct archive_read_source *self;
-       struct private_data *state;
 
-       (void)reader; /* UNUSED */
+       self->code = ARCHIVE_COMPRESSION_GZIP;
+       self->name = "gzip";
 
-       a->archive.compression_code = ARCHIVE_COMPRESSION_GZIP;
-       a->archive.compression_name = "gzip";
-
-       self = calloc(sizeof(*self), 1);
        state = (struct private_data *)calloc(sizeof(*state), 1);
        out_block = (unsigned char *)malloc(out_block_size);
-       if (self == NULL || state == NULL || out_block == NULL) {
-               archive_set_error(&a->archive, ENOMEM,
-                   "Can't allocate data for %s decompression",
-                   a->archive.compression_name);
+       if (state == NULL || out_block == NULL) {
                free(out_block);
                free(state);
-               free(self);
-               return (NULL);
+               archive_set_error(&self->archive->archive, ENOMEM,
+                   "Can't allocate data for %s decompression",
+                   self->name);
+               return (ARCHIVE_FATAL);
        }
 
-       self->archive = a;
        self->data = state;
        state->out_block_size = out_block_size;
        state->out_block = out_block;
-       self->upstream = upstream;
-       self->read = gzip_source_read;
+       self->read = gzip_filter_read;
        self->skip = NULL; /* not supported */
-       self->close = gzip_source_close;
+       self->close = gzip_filter_close;
 
        state->crc = crc32(0L, NULL, 0);
        state->header_done = 0; /* We've not yet begun to parse header... */
 
-       /*
-        * A bug in zlib.h: stream.next_in should be marked 'const'
-        * but isn't (the library never alters data through the
-        * next_in pointer, only reads it).  The result: this ugly
-        * cast to remove 'const'.
-        */
-       state->stream.next_in = (Bytef *)(uintptr_t)(const void *)buff;
-       state->stream.avail_in = n;
-
-       return (self);
+       return (ARCHIVE_OK);
 }
 
 static int
-header(struct archive_read_source *self)
+header(struct archive_read_filter *self)
 {
        struct private_data *state;
        int ret, b;
@@ -374,7 +344,7 @@ header(struct archive_read_source *self)
 }
 
 static ssize_t
-gzip_source_read(struct archive_read_source *self, const void **p)
+gzip_filter_read(struct archive_read_filter *self, const void **p)
 {
        struct private_data *state;
        size_t read_avail, decompressed;
@@ -392,20 +362,21 @@ gzip_source_read(struct archive_read_source *self, const void **p)
        while (state->stream.avail_out > 0 && !state->eof) {
                /* If the last upstream block is done, get another one. */
                if (state->stream.avail_in == 0) {
-                       ret = (self->upstream->read)(self->upstream,
-                           &read_buf);
+                       read_buf = __archive_read_filter_ahead(self->upstream,
+                           1, &ret);
+                       if (read_buf == NULL)
+                               return (ARCHIVE_FATAL);
                        /* stream.next_in is really const, but zlib
                         * doesn't declare it so. <sigh> */
                        state->stream.next_in
                            = (unsigned char *)(uintptr_t)read_buf;
-                       if (ret < 0)
-                               return (ARCHIVE_FATAL);
                        state->stream.avail_in = ret;
                        /* There is no more data, return whatever we have. */
                        if (ret == 0) {
                                state->eof = 1;
                                break;
                        }
+                       __archive_read_filter_consume(self->upstream, ret);
                }
 
                /* If we're still parsing header bytes, walk through those. */
@@ -462,7 +433,7 @@ gzip_source_read(struct archive_read_source *self, const void **p)
  * Clean up the decompressor.
  */
 static int
-gzip_source_close(struct archive_read_source *self)
+gzip_filter_close(struct archive_read_filter *self)
 {
        struct private_data *state;
        int ret;
index 2fd8572f1005e3be30a00615b1ff5f17877d9c8f..bd0f8ca32970cb2d1d69ea1210f6418218270e70 100644 (file)
@@ -57,9 +57,9 @@ struct private_data {
        char             eof; /* True = found end of compressed data. */
 };
 
-/* Lzma source */
-static ssize_t lzma_source_read(struct archive_read_source *, const void **);
-static int     lzma_source_close(struct archive_read_source *);
+/* Lzma filter */
+static ssize_t lzma_filter_read(struct archive_read_filter *, const void **);
+static int     lzma_filter_close(struct archive_read_filter *);
 #endif
 
 /*
@@ -68,30 +68,28 @@ static int  lzma_source_close(struct archive_read_source *);
  * error messages.)  So the bid framework here gets compiled even
  * if lzmadec is unavailable.
  */
-static int     lzma_reader_bid(struct archive_reader *, const void *, size_t);
-static struct archive_read_source *lzma_reader_init(struct archive_read *,
-    struct archive_reader *, struct archive_read_source *,
-    const void *, size_t);
-static int     lzma_reader_free(struct archive_reader *);
+static int     lzma_bidder_bid(struct archive_read_filter_bidder *, struct archive_read_filter *);
+static int     lzma_bidder_init(struct archive_read_filter *);
+static int     lzma_bidder_free(struct archive_read_filter_bidder *);
 
 int
 archive_read_support_compression_lzma(struct archive *_a)
 {
        struct archive_read *a = (struct archive_read *)_a;
-       struct archive_reader *reader = __archive_read_get_reader(a);
+       struct archive_read_filter_bidder *bidder = __archive_read_get_bidder(a);
 
-       if (reader == NULL)
+       if (bidder == NULL)
                return (ARCHIVE_FATAL);
 
-       reader->data = NULL;
-       reader->bid = lzma_reader_bid;
-       reader->init = lzma_reader_init;
-       reader->free = lzma_reader_free;
+       bidder->data = NULL;
+       bidder->bid = lzma_bidder_bid;
+       bidder->init = lzma_bidder_init;
+       bidder->free = lzma_bidder_free;
        return (ARCHIVE_OK);
 }
 
 static int
-lzma_reader_free(struct archive_reader *self){
+lzma_bidder_free(struct archive_read_filter_bidder *self){
        (void)self; /* UNUSED */
        return (ARCHIVE_OK);
 }
@@ -113,19 +111,20 @@ lzma_reader_free(struct archive_reader *self){
  * they have other evidence (file name, command-line option) to go on.
  */
 static int
-lzma_reader_bid(struct archive_reader *self, const void *buff, size_t len)
+lzma_bidder_bid(struct archive_read_filter_bidder *self,
+    struct archive_read_filter *filter)
 {
        const unsigned char *buffer;
+       size_t avail;
        int bits_checked;
 
        (void)self; /* UNUSED */
 
-       buffer = (const unsigned char *)buff;
-
+       buffer = __archive_read_filter_ahead(filter, 6, &avail);
+       if (buffer == NULL)
+               return (0);
 
        /* First byte of raw LZMA stream is always 0x5d. */
-       if (len < 1)
-               return (0);
        bits_checked = 0;
        if (buffer[0] != 0x5d)
                return (0);
@@ -134,24 +133,16 @@ lzma_reader_bid(struct archive_reader *self, const void *buff, size_t len)
        /* Second through fifth bytes are dictionary code, stored in
         * little-endian order.  The two least-significant bytes are
         * always zero. */
-       if (len < 2)
-               return (bits_checked);
-       if (buffer[1] != 0)
+       if (buffer[1] != 0 || buffer[2] != 0)
                return (0);
-       bits_checked += 8;
-
-       if (len < 3)
-               return (bits_checked);
-       if (buffer[2] != 0)
-               return (0);
-       bits_checked += 8;
+       bits_checked += 16;
 
-       /* ??? TODO:  Explain this.  ??? */
+       /* ??? TODO:  Fix this. ??? */
        /* NSIS format check uses this, but I've seen tar.lzma
-        * archives where this byte is 0xff, not 0. */
+        * archives where this byte is 0xff, not 0.  Can it
+        * ever be anything other than 0 or 0xff?
+        */
 #if 0
-       if (len < 6)
-               return (bits_checked);
        if (buffer[5] != 0)
                return (0);
        bits_checked += 8;
@@ -170,19 +161,14 @@ lzma_reader_bid(struct archive_reader *self, const void *buff, size_t len)
  * decompression.  We can, however, still detect compressed archives
  * and emit a useful message.
  */
-static struct archive_read_source *
-lzma_reader_init(struct archive_read *a, struct archive_reader *reader,
-    struct archive_read_source *upstream, const void *buff, size_t n)
+static int
+lzma_bidder_init(struct archive_read_filter *filter)
 {
-       (void)a;        /* UNUSED */
-       (void)reader;   /* UNUSED */
-       (void)upstream; /* UNUSED */
-       (void)buff;     /* UNUSED */
-       (void)n;        /* UNUSED */
+       (void)filter;   /* UNUSED */
 
-       archive_set_error(&a->archive, -1,
+       archive_set_error(&filter->archive->archive, -1,
            "This version of libarchive was compiled without lzma support");
-       return (NULL);
+       return (ARCHIVE_FATAL);
 }
 
 
@@ -191,52 +177,50 @@ lzma_reader_init(struct archive_read *a, struct archive_reader *reader,
 /*
  * Setup the callbacks.
  */
-static struct archive_read_source *
-lzma_reader_init(struct archive_read *a, struct archive_reader *reader,
-    struct archive_read_source *upstream, const void *buff, size_t n)
+static int
+lzma_bidder_init(struct archive_read_filter *self)
 {
        static const size_t out_block_size = 64 * 1024;
        void *out_block;
-       struct archive_read_source *self;
+       const char *buff;
        struct private_data *state;
        int ret;
 
-       (void)reader; /* UNUSED */
-
-       a->archive.compression_code = ARCHIVE_COMPRESSION_LZMA;
-       a->archive.compression_name = "lzma";
+       self->code = ARCHIVE_COMPRESSION_LZMA;
+       self->name = "lzma";
 
-       self = calloc(sizeof(*self), 1);
        state = (struct private_data *)calloc(sizeof(*state), 1);
        out_block = (unsigned char *)malloc(out_block_size);
-       if (self == NULL || state == NULL || out_block == NULL) {
-               archive_set_error(&a->archive, ENOMEM,
-                   "Can't allocate data for %s decompression",
-                   a->archive.compression_name);
+       if (state == NULL || out_block == NULL) {
+               archive_set_error(&self->archive->archive, ENOMEM,
+                   "Can't allocate data for lzma decompression");
                free(out_block);
                free(state);
-               free(self);
-               return (NULL);
+               return (ARCHIVE_FATAL);
        }
 
-
-       self->archive = a;
        self->data = state;
        state->out_block_size = out_block_size;
        state->out_block = out_block;
-       self->upstream = upstream;
-       self->read = lzma_source_read;
+       self->read = lzma_filter_read;
        self->skip = NULL; /* not supported */
-       self->close = lzma_source_close;
+       self->close = lzma_filter_close;
 
        /*
-        * A bug in lzmadec.h: stream.next_in should be marked 'const'
-        * but isn't (the library never alters data through the
-        * next_in pointer, only reads it).  The result: this ugly
-        * cast to remove 'const'.
+        * Prime the lzma library with at least 18 bytes
+        * of input.  (But give it as much as is available.)
+        */
+       buff = __archive_read_filter_ahead(self->upstream, 18, &ret);
+       if (buff == NULL)
+               return (ARCHIVE_FATAL);
+       __archive_read_filter_consume(self->upstream, ret);
+       /*
+        * zlib.h made this mistake and people keep copying it.  <sigh>
+        * stream.next_in should be const but isn't, hence this very
+        * ugly cast.
         */
-       state->stream.next_in = (char *)(uintptr_t)(const void *)buff;
-       state->stream.avail_in = n;
+       state->stream.next_in = (unsigned char *)(uintptr_t)buff;
+       state->stream.avail_in = ret;
 
        state->stream.next_out = state->out_block;
        state->stream.avail_out = state->out_block_size;
@@ -245,22 +229,22 @@ lzma_reader_init(struct archive_read *a, struct archive_reader *reader,
        ret = lzmadec_init(&(state->stream));
 
        if (ret == LZMADEC_OK)
-               return (self);
+               return (ARCHIVE_OK);
 
        /* Library setup failed: Clean up. */
-       archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
-           "Internal error initializing %s library",
-           a->archive.compression_name);
+       archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC,
+           "Internal error initializing lzma library");
 
        /* Override the error message if we know what really went wrong. */
        switch (ret) {
        case LZMADEC_HEADER_ERROR:
-               archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+               archive_set_error(&self->archive->archive,
+                   ARCHIVE_ERRNO_MISC,
                    "Internal error initializing compression library: "
                    "invalid header");
                break;
        case LZMADEC_MEM_ERROR:
-               archive_set_error(&a->archive, ENOMEM,
+               archive_set_error(&self->archive->archive, ENOMEM,
                    "Internal error initializing compression library: "
                    "out of memory");
                break;
@@ -268,15 +252,15 @@ lzma_reader_init(struct archive_read *a, struct archive_reader *reader,
 
        free(state->out_block);
        free(state);
-       free(self);
-       return (NULL);
+       self->data = NULL;
+       return (ARCHIVE_FATAL);
 }
 
 /*
  * Return the next block of decompressed data.
  */
 static ssize_t
-lzma_source_read(struct archive_read_source *self, const void **p)
+lzma_filter_read(struct archive_read_filter *self, const void **p)
 {
        struct private_data *state;
        size_t read_avail, decompressed;
@@ -291,26 +275,23 @@ lzma_source_read(struct archive_read_source *self, const void **p)
        state->stream.avail_out = state->out_block_size;
 
        /* Try to fill the output buffer. */
-       for (;;) {
+       while (state->stream.avail_out > 0 && !state->eof) {
                /* If the last upstream block is done, get another one. */
                if (state->stream.avail_in == 0) {
-                       ret = (self->upstream->read)(self->upstream,
-                           &read_buf);
+                       read_buf = __archive_read_filter_ahead(self->upstream,
+                           1, &ret);
+                       if (ret == 0) {
+                               state->eof = 1;
+                               break;
+                       }
+                       if (read_buf == NULL || ret < 0)
+                               return (ARCHIVE_FATAL);
                        /* stream.next_in is really const, but lzmadec
                         * doesn't declare it so. <sigh> */
                        state->stream.next_in
                            = (unsigned char *)(uintptr_t)read_buf;
-                       if (ret < 0)
-                               return (ARCHIVE_FATAL);
-                       /* There is no more data, return whatever we have. */
-                       if (ret == 0) {
-                               *p = state->out_block;
-                               decompressed = state->stream.next_out
-                                   - state->out_block;
-                               state->total_out += decompressed;
-                               return (decompressed);
-                       }
                        state->stream.avail_in = ret;
+                       __archive_read_filter_consume(self->upstream, ret);
                }
 
                /* Decompress as much as we can in one pass. */
@@ -318,20 +299,14 @@ lzma_source_read(struct archive_read_source *self, const void **p)
                    state->stream.avail_in == 0);
                switch (ret) {
                case LZMADEC_STREAM_END: /* Found end of stream. */
-                       /* TODO: Peek ahead to see if there's another
-                        * stream so we can mimic the behavior of gunzip
-                        * on concatenated streams. */
                        state->eof = 1;
                case LZMADEC_OK: /* Decompressor made some progress. */
-                       /* If we filled our buffer, update stats and return. */
-                       if (state->eof || state->stream.avail_out == 0) {
-                               *p = state->out_block;
-                               decompressed = state->stream.next_out
-                                   - state->out_block;
-                               state->total_out += decompressed;
-                               return (decompressed);
-                       }
                        break;
+               case LZMADEC_BUF_ERROR: /* Insufficient input data? */
+                       archive_set_error(&self->archive->archive,
+                           ARCHIVE_ERRNO_MISC,
+                           "Insufficient compressed data");
+                       return (ARCHIVE_FATAL);
                default:
                        /* Return an error. */
                        archive_set_error(&self->archive->archive,
@@ -341,13 +316,18 @@ lzma_source_read(struct archive_read_source *self, const void **p)
                        return (ARCHIVE_FATAL);
                }
        }
+
+       *p = state->out_block;
+       decompressed = state->stream.next_out - state->out_block;
+       state->total_out += decompressed;
+       return (decompressed);
 }
 
 /*
  * Clean up the decompressor.
  */
 static int
-lzma_source_close(struct archive_read_source *self)
+lzma_filter_close(struct archive_read_filter *self)
 {
        struct private_data *state;
        int ret;
@@ -367,7 +347,6 @@ lzma_source_close(struct archive_read_source *self)
 
        free(state->out_block);
        free(state);
-       free(self);
        return (ret);
 }
 
index a2a5eea4a6f8f770239d8bcb114b2831a30e9656..8e381da7f6fe169f7d69335d87dd88db854f4b1d 100644 (file)
@@ -75,12 +75,12 @@ archive_read_support_compression_program(struct archive *_a, const char *cmd)
 
 #include "filter_fork.h"
 
-struct program_reader {
+struct program_bidder {
        char *cmd;
        int bid;
 };
 
-struct program_source {
+struct program_filter {
        char            *description;
        pid_t            child;
        int              child_stdin, child_stdout;
@@ -92,44 +92,42 @@ struct program_source {
        size_t           child_in_buf_avail;
 };
 
-static int     program_reader_bid(struct archive_reader *,
-                   const void *, size_t);
-static struct archive_read_source *program_reader_init(struct archive_read *,
-    struct archive_reader *, struct archive_read_source *,
-    const void *, size_t);
-static int     program_reader_free(struct archive_reader *);
+static int     program_bidder_bid(struct archive_read_filter_bidder *,
+                   struct archive_read_filter *upstream);
+static int     program_bidder_init(struct archive_read_filter *);
+static int     program_bidder_free(struct archive_read_filter_bidder *);
 
-static ssize_t program_source_read(struct archive_read_source *,
-    const void **);
-static int     program_source_close(struct archive_read_source *);
+static ssize_t program_filter_read(struct archive_read_filter *,
+                   const void **);
+static int     program_filter_close(struct archive_read_filter *);
 
 
 int
 archive_read_support_compression_program(struct archive *_a, const char *cmd)
 {
        struct archive_read *a = (struct archive_read *)_a;
-       struct archive_reader *reader = __archive_read_get_reader(a);
-       struct program_reader *state;
+       struct archive_read_filter_bidder *bidder = __archive_read_get_bidder(a);
+       struct program_bidder *state;
 
-       state = (struct program_reader *)calloc(sizeof (*state), 1);
+       state = (struct program_bidder *)calloc(sizeof (*state), 1);
 
        if (state == NULL)
                return (ARCHIVE_FATAL);
-       if (reader == NULL)
+       if (bidder == NULL)
                return (ARCHIVE_FATAL);
 
        state->cmd = strdup(cmd);
        state->bid = INT_MAX;
 
-       reader->data = state;
-       reader->bid = program_reader_bid;
-       reader->init = program_reader_init;
-       reader->free = program_reader_free;
+       bidder->data = state;
+       bidder->bid = program_bidder_bid;
+       bidder->init = program_bidder_init;
+       bidder->free = program_bidder_free;
        return (ARCHIVE_OK);
 }
 
 static int
-program_reader_free(struct archive_reader *self)
+program_bidder_free(struct archive_read_filter_bidder *self)
 {
        free(self->data);
        return (ARCHIVE_OK);
@@ -142,13 +140,13 @@ program_reader_free(struct archive_reader *self)
  * an infinite pipeline.
  */
 static int
-program_reader_bid(struct archive_reader *self, const void *buff, size_t len)
+program_bidder_bid(struct archive_read_filter_bidder *self,
+    struct archive_read_filter *upstream)
 {
-       struct program_reader *state = self->data;
+       struct program_bidder *state = self->data;
        int bid = state->bid;
 
-       (void)buff; /* UNUSED */
-       (void)len; /* UNUSED */
+       (void)upstream; /* UNUSED */
 
        state->bid = 0; /* Don't bid again on this pipeline. */
 
@@ -160,9 +158,9 @@ program_reader_bid(struct archive_reader *self, const void *buff, size_t len)
  */
 
 static ssize_t
-child_read(struct archive_read_source *self, char *buf, size_t buf_len)
+child_read(struct archive_read_filter *self, char *buf, size_t buf_len)
 {
-       struct program_source *state = self->data;
+       struct program_filter *state = self->data;
        ssize_t ret, requested;
        const void *child_buf;
 
@@ -191,7 +189,8 @@ restart_read:
 
        if (state->child_in_buf_avail == 0) {
                child_buf = state->child_in_buf;
-               ret = (self->upstream->read)(self->upstream, &child_buf);
+               child_buf = __archive_read_filter_ahead(self->upstream,
+                           1, &ret);
                state->child_in_buf = (const char *)child_buf;
 
                if (ret < 0) {
@@ -206,6 +205,7 @@ restart_read:
                        fcntl(state->child_stdout, F_SETFL, 0);
                        goto restart_read;
                }
+               __archive_read_filter_consume(self->upstream, ret);
                state->child_in_buf_avail = ret;
        }
 
@@ -240,79 +240,66 @@ restart_read:
        }
 }
 
-static struct archive_read_source *
-program_reader_init(struct archive_read *a, struct archive_reader *reader,
-    struct archive_read_source *upstream, const void *buff, size_t n)
+static int
+program_bidder_init(struct archive_read_filter *self)
 {
-       struct program_source   *state;
-       struct program_reader   *reader_state;
-       struct archive_read_source *self;
+       struct program_filter   *state;
+       struct program_bidder   *bidder_state;
        static const size_t out_buf_len = 65536;
        char *out_buf;
        char *description;
        const char *prefix = "Program: ";
 
 
-       reader_state = (struct program_reader *)reader->data;
+       bidder_state = (struct program_bidder *)self->bidder->data;
 
-       self = (struct archive_read_source *)malloc(sizeof(*self));
-       state = (struct program_source *)malloc(sizeof(*state));
+       state = (struct program_filter *)malloc(sizeof(*state));
        out_buf = (char *)malloc(out_buf_len);
-       description = (char *)malloc(strlen(prefix) + strlen(reader_state->cmd) + 1);
-       if (self == NULL
-           || state == NULL
-           || out_buf == NULL
-           || description == NULL)
-       {
-               archive_set_error(&a->archive, ENOMEM,
+       description = (char *)malloc(strlen(prefix) + strlen(bidder_state->cmd) + 1);
+       if (state == NULL || out_buf == NULL || description == NULL) {
+               archive_set_error(&self->archive->archive, ENOMEM,
                    "Can't allocate input data");
-               free(self);
                free(state);
                free(out_buf);
                free(description);
-               return (NULL);
+               return (ARCHIVE_FATAL);
        }
 
-       a->archive.compression_code = ARCHIVE_COMPRESSION_PROGRAM;
+       self->code = ARCHIVE_COMPRESSION_PROGRAM;
        state->description = description;
        strcpy(state->description, prefix);
-       strcat(state->description, reader_state->cmd);
-       a->archive.compression_name = state->description;
+       strcat(state->description, bidder_state->cmd);
+       self->name = state->description;
 
        state->out_buf = out_buf;
        state->out_buf_len = out_buf_len;
 
-       state->child_in_buf = buff;
-       state->child_in_buf_avail = n;
-
-       if ((state->child = __archive_create_child(reader_state->cmd,
+       if ((state->child = __archive_create_child(bidder_state->cmd,
                 &state->child_stdin, &state->child_stdout)) == -1) {
                free(state->out_buf);
                free(state);
-               archive_set_error(&a->archive, EINVAL,
+               archive_set_error(&self->archive->archive, EINVAL,
                    "Can't initialise filter");
-               return (NULL);
+               return (ARCHIVE_FATAL);
        }
 
        self->data = state;
-       self->read = program_source_read;
+       self->read = program_filter_read;
        self->skip = NULL;
-       self->close = program_source_close;
-       self->upstream = upstream;
-       self->archive = a;
+       self->close = program_filter_close;
 
        /* XXX Check that we can read at least one byte? */
-       return (self);
+       return (ARCHIVE_OK);
 }
 
 static ssize_t
-program_source_read(struct archive_read_source *self, const void **buff)
+program_filter_read(struct archive_read_filter *self, const void **buff)
 {
-       struct program_source *state;
+       struct program_filter *state;
        ssize_t bytes, total;
        char *p;
 
-       state = (struct program_source *)self->data;
+       state = (struct program_filter *)self->data;
 
        total = 0;
        p = state->out_buf;
@@ -323,7 +310,6 @@ program_source_read(struct archive_read_source *self, const void **buff)
                if (bytes == 0)
                        break;
                total += bytes;
-/* TODO: fix this */ /*        a->archive.raw_position += bytes_read; */
        }
 
        *buff = state->out_buf;
@@ -331,12 +317,12 @@ program_source_read(struct archive_read_source *self, const void **buff)
 }
 
 static int
-program_source_close(struct archive_read_source *self)
+program_filter_close(struct archive_read_filter *self)
 {
-       struct program_source   *state;
+       struct program_filter   *state;
        int status;
 
-       state = (struct program_source *)self->data;
+       state = (struct program_filter *)self->data;
 
        /* Shut down the child. */
        if (state->child_stdin != -1)
@@ -350,7 +336,6 @@ program_source_close(struct archive_read_source *self)
        free(state->out_buf);
        free(state->description);
        free(state);
-       free(self);
 
        return (ARCHIVE_OK);
 }
index f55c0a6a85d8404d35249684fcf58ee4d121d708..a4b9216a8ca630d442c02a64eb3ebb702edcf346 100644 (file)
@@ -25,8 +25,8 @@
 #include "test.h"
 __FBSDID("$FreeBSD: src/lib/libarchive/test/test_read_position.c,v 1.4 2008/09/01 05:38:33 kientzle Exp $");
 
-static unsigned char nulls[10000000];
-static unsigned char buff[10000000];
+static unsigned char nulls[10000];
+static unsigned char  buff[10000000];
 
 /* Check that header_position tracks correctly on read. */
 DEFINE_TEST(test_read_position)
@@ -34,42 +34,61 @@ DEFINE_TEST(test_read_position)
        struct archive *a;
        struct archive_entry *ae;
        size_t write_pos;
-       const size_t data_size = 1000000;
+       intmax_t read_position;
+       size_t i, j;
+       size_t data_sizes[] = {0, 5, 511, 512, 513};
 
-       /* Create a simple archive_entry. */
-       assert((ae = archive_entry_new()) != NULL);
-       archive_entry_set_pathname(ae, "testfile");
-       archive_entry_set_mode(ae, S_IFREG);
-       archive_entry_set_size(ae, data_size);
+       /* Sanity test */
+       assert(sizeof(nulls) + 512 + 1024 <= sizeof(buff));
 
+       /* Create an archive. */
        assert(NULL != (a = archive_write_new()));
        assertA(0 == archive_write_set_format_pax_restricted(a));
        assertA(0 == archive_write_set_bytes_per_block(a, 512));
        assertA(0 == archive_write_open_memory(a, buff, sizeof(buff), &write_pos));
-       assertA(0 == archive_write_header(a, ae));
-       archive_entry_free(ae);
-       assertA(data_size == (size_t)archive_write_data(a, nulls, sizeof(nulls)));
-#if ARCHIVE_VERSION_NUMBER < 2000000
+
+       for (i = 0; i < sizeof(data_sizes)/sizeof(data_sizes[0]); ++i) {
+               /* Create a simple archive_entry. */
+               assert((ae = archive_entry_new()) != NULL);
+               archive_entry_set_pathname(ae, "testfile");
+               archive_entry_set_mode(ae, S_IFREG);
+               archive_entry_set_size(ae, data_sizes[i]);
+               assertA(0 == archive_write_header(a, ae));
+               archive_entry_free(ae);
+               assertA(data_sizes[i]
+                   == (size_t)archive_write_data(a, nulls, sizeof(nulls)));
+       }
        assertA(0 == archive_write_close(a));
-       archive_write_finish(a);
-#else
        assertA(0 == archive_write_finish(a));
-#endif
-       /* 512-byte header + data_size (rounded up) + 1024 end-of-archive */
-       assert(write_pos == ((512 + data_size + 1024 + 511)/512)*512);
 
        /* Read the archive back. */
        assert(NULL != (a = archive_read_new()));
        assertA(0 == archive_read_support_format_tar(a));
        assertA(0 == archive_read_open_memory2(a, buff, sizeof(buff), 512));
-       assert((intmax_t)0 == (intmax_t)archive_read_header_position(a));
-       assertA(0 == archive_read_next_header(a, &ae));
-       assert((intmax_t)0 == (intmax_t)archive_read_header_position(a));
-       assertA(0 == archive_read_data_skip(a));
-       assert((intmax_t)0 == (intmax_t)archive_read_header_position(a));
+
+       read_position = 0;
+       /* Initial header position is zero. */
+       assert(read_position == (intmax_t)archive_read_header_position(a));
+       for (j = 0; j < i; ++j) {
+               assertA(0 == archive_read_next_header(a, &ae));
+               assert(read_position
+                   == (intmax_t)archive_read_header_position(a));
+               /* Every other entry: read, then skip */
+               if (j & 1)
+                       assertEqualInt(ARCHIVE_OK,
+                           archive_read_data_into_buffer(a, buff, 1));
+               assertA(0 == archive_read_data_skip(a));
+               /* read_data_skip() doesn't change header_position */
+               assert(read_position
+                   == (intmax_t)archive_read_header_position(a));
+
+               read_position += 512; /* Size of header. */
+               read_position += (data_sizes[j] + 511) & ~511;
+       }
+
        assertA(1 == archive_read_next_header(a, &ae));
-       assert((intmax_t)((data_size + 511 + 512)/512)*512 == (intmax_t)archive_read_header_position(a));
+       assert(read_position == (intmax_t)archive_read_header_position(a));
        assertA(0 == archive_read_close(a));
-       assert((intmax_t)((data_size + 511 + 512)/512)*512 == (intmax_t)archive_read_header_position(a));
+       assert(read_position == (intmax_t)archive_read_header_position(a));
        archive_read_finish(a);
 }