diff options
author | kientzle <kientzle@FreeBSD.org> | 2008-12-06 06:45:15 +0000 |
---|---|---|
committer | kientzle <kientzle@FreeBSD.org> | 2008-12-06 06:45:15 +0000 |
commit | e59163f249e40175754ba81c7d87afbe5d1ed3b5 (patch) | |
tree | 07033908f13a75f6e70696ff70c73d1160bb8e74 | |
parent | 01e9b693cc22dbc3f9dcb3d8873312befa9c0e25 (diff) | |
download | FreeBSD-src-e59163f249e40175754ba81c7d87afbe5d1ed3b5.zip FreeBSD-src-e59163f249e40175754ba81c7d87afbe5d1ed3b5.tar.gz |
MfP4: Big read filter refactoring.
This is an attempt to eliminate a lot of redundant
code from the read ("decompression") filters by
changing them to juggle arbitrary-sized blocks
and consolidate reblocking code at a single point
in archive_read.c.
Along the way, I've changed the internal read/consume
API used by the format handlers to a slightly
different style originally suggested by des@. It
does seem to simplify a lot of common cases.
The most dramatic change is, of course, to
archive_read_support_compression_none(), which
has just evaporated into a no-op as the blocking
code this used to hold has all been moved up
a level.
There's at least one more big round of refactoring
yet to come before the individual filters are as
straightforward as I think they should be...
-rw-r--r-- | lib/libarchive/archive_read.c | 598 | ||||
-rw-r--r-- | lib/libarchive/archive_read_private.h | 138 | ||||
-rw-r--r-- | lib/libarchive/archive_read_support_compression_all.c | 6 | ||||
-rw-r--r-- | lib/libarchive/archive_read_support_compression_bzip2.c | 411 | ||||
-rw-r--r-- | lib/libarchive/archive_read_support_compression_compress.c | 213 | ||||
-rw-r--r-- | lib/libarchive/archive_read_support_compression_gzip.c | 633 | ||||
-rw-r--r-- | lib/libarchive/archive_read_support_compression_none.c | 338 | ||||
-rw-r--r-- | lib/libarchive/archive_read_support_compression_program.c | 222 | ||||
-rw-r--r-- | lib/libarchive/archive_read_support_format_ar.c | 56 | ||||
-rw-r--r-- | lib/libarchive/archive_read_support_format_cpio.c | 74 | ||||
-rw-r--r-- | lib/libarchive/archive_read_support_format_empty.c | 5 | ||||
-rw-r--r-- | lib/libarchive/archive_read_support_format_iso9660.c | 40 | ||||
-rw-r--r-- | lib/libarchive/archive_read_support_format_mtree.c | 35 | ||||
-rw-r--r-- | lib/libarchive/archive_read_support_format_tar.c | 103 | ||||
-rw-r--r-- | lib/libarchive/archive_read_support_format_zip.c | 105 |
15 files changed, 1427 insertions, 1550 deletions
diff --git a/lib/libarchive/archive_read.c b/lib/libarchive/archive_read.c index 259fd7c..6973847 100644 --- a/lib/libarchive/archive_read.c +++ b/lib/libarchive/archive_read.c @@ -53,9 +53,10 @@ __FBSDID("$FreeBSD$"); #include "archive_private.h" #include "archive_read_private.h" -static void choose_decompressor(struct archive_read *, const void*, size_t); +#define minimum(a, b) (a < b ? a : b) + +static int build_stream(struct archive_read *); static int choose_format(struct archive_read *); -static off_t dummy_skip(struct archive_read *, off_t); /* * Allocate, initialize and return a struct archive object. @@ -74,8 +75,15 @@ archive_read_new(void) a->archive.state = ARCHIVE_STATE_NEW; a->entry = archive_entry_new(); - /* We always support uncompressed archives. */ - archive_read_support_compression_none(&a->archive); + /* 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); } @@ -108,6 +116,33 @@ archive_read_open(struct archive *a, void *client_data, client_reader, NULL, client_closer); } +static ssize_t +client_read_proxy(struct archive_read_source *self, const void **buff) +{ + return (self->archive->client.reader)((struct archive *)self->archive, + self->data, buff); +} + +static int64_t +client_skip_proxy(struct archive_read_source *self, int64_t request) +{ + return (self->archive->client.skipper)((struct archive *)self->archive, + self->data, request); +} + +static int +client_close_proxy(struct archive_read_source *self) +{ + int r = ARCHIVE_OK; + + if (self->archive->client.closer != NULL) + r = (self->archive->client.closer)((struct archive *)self->archive, + self->data); + free(self); + return (r); +} + + int archive_read_open2(struct archive *_a, void *client_data, archive_open_callback *client_opener, @@ -116,28 +151,15 @@ archive_read_open2(struct archive *_a, void *client_data, archive_close_callback *client_closer) { struct archive_read *a = (struct archive_read *)_a; - const void *buffer; - ssize_t bytes_read; int e; - __archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, "archive_read_open"); + __archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, + "archive_read_open"); if (client_reader == NULL) __archive_errx(1, "No reader function provided to archive_read_open"); - /* - * Set these NULL initially. If the open or initial read fails, - * we'll leave them NULL to indicate that the file is invalid. - * (In particular, this helps ensure that the closer doesn't - * get called more than once.) - */ - a->client_opener = NULL; - a->client_reader = NULL; - a->client_skipper = NULL; - a->client_closer = NULL; - a->client_data = NULL; - /* Open data source. */ if (client_opener != NULL) { e =(client_opener)(&a->archive, client_data); @@ -149,129 +171,103 @@ archive_read_open2(struct archive *_a, void *client_data, } } - /* Read first block now for compress format detection. */ - bytes_read = (client_reader)(&a->archive, client_data, &buffer); - - if (bytes_read < 0) { - /* If the first read fails, close before returning error. */ - if (client_closer) - (client_closer)(&a->archive, client_data); - /* client_reader should have already set error information. */ - return (ARCHIVE_FATAL); - } + /* 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; - /* Now that the client callbacks have worked, remember them. */ - 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; - /* Select a decompression routine. */ - choose_decompressor(a, buffer, (size_t)bytes_read); - if (a->decompressor == NULL) - return (ARCHIVE_FATAL); + 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; + } - /* Initialize decompression routine with the first block of data. */ - e = (a->decompressor->init)(a, buffer, (size_t)bytes_read); + /* In case there's no filter. */ + a->archive.compression_code = ARCHIVE_COMPRESSION_NONE; + a->archive.compression_name = "none"; + /* Build out the input pipeline. */ + e = build_stream(a); if (e == ARCHIVE_OK) a->archive.state = ARCHIVE_STATE_HEADER; - /* - * If the decompressor didn't register a skip function, provide a - * dummy compression-layer skip function. - */ - if (a->decompressor->skip == NULL) - a->decompressor->skip = dummy_skip; - return (e); } /* - * Allow each registered decompression routine to bid on whether it - * wants to handle this stream. Return index of winning bidder. + * Allow each registered stream transform to bid on whether + * it wants to handle this stream. Repeat until we've finished + * building the pipeline. */ -static void -choose_decompressor(struct archive_read *a, - const void *buffer, size_t bytes_read) +static int +build_stream(struct archive_read *a) { - int decompression_slots, i, bid, best_bid; - struct decompressor_t *decompressor, *best_decompressor; + 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; - decompression_slots = sizeof(a->decompressors) / - sizeof(a->decompressors[0]); + /* 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; + } + /* source->read should have already set error information. */ + return (ARCHIVE_FATAL); + } + + number_readers = sizeof(a->readers) / sizeof(a->readers[0]); best_bid = 0; - a->decompressor = NULL; - best_decompressor = NULL; - - decompressor = a->decompressors; - for (i = 0; i < decompression_slots; i++) { - if (decompressor->bid) { - bid = (decompressor->bid)(buffer, bytes_read); - if (bid > best_bid || best_decompressor == NULL) { + 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_decompressor = decompressor; + best_reader = reader; } } - decompressor ++; } /* - * There were no bidders; this is a serious programmer error - * and demands a quick and definitive abort. - */ - if (best_decompressor == NULL) - __archive_errx(1, "No decompressors were registered; you " - "must call at least one " - "archive_read_support_compression_XXX function in order " - "to successfully read an archive."); - - /* - * There were bidders, but no non-zero bids; this means we can't - * support this stream. + * If we have a winner, it becomes the next stage in the pipeline. */ - if (best_bid < 1) { - archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "Unrecognized archive format"); - return; - } - - /* Record the best decompressor for this stream. */ - a->decompressor = best_decompressor; -} - -/* - * Dummy skip function, for use if the compression layer doesn't provide - * one: This code just reads data and discards it. - */ -static off_t -dummy_skip(struct archive_read * a, off_t request) -{ - const void * dummy_buffer; - ssize_t bytes_read; - off_t bytes_skipped; - - for (bytes_skipped = 0; request > 0;) { - bytes_read = (a->decompressor->read_ahead)(a, &dummy_buffer, 1); - if (bytes_read < 0) - return (bytes_read); - if (bytes_read == 0) { - /* Premature EOF. */ - archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "Truncated input file (need to skip %jd bytes)", - (intmax_t)request); + if (best_reader != NULL) { + source = (best_reader->init)(a, best_reader, a->source, + block, bytes_read); + if (source == NULL) return (ARCHIVE_FATAL); - } - if (bytes_read > request) - bytes_read = (ssize_t)request; - (a->decompressor->consume)(a, (size_t)bytes_read); - request -= bytes_read; - bytes_skipped += bytes_read; + /* Record the best decompressor for this stream. */ + a->source = source; + /* Recurse to get next pipeline stage. */ + return (build_stream(a)); } - return (bytes_skipped); + /* 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); } /* @@ -598,23 +594,24 @@ archive_read_close(struct archive *_a) /* TODO: Clean up the formatters. */ - /* Clean up the decompressors. */ - n = sizeof(a->decompressors)/sizeof(a->decompressors[0]); + /* Clean up the stream pipeline. */ + if (a->source != NULL) { + r1 = (a->source->close)(a->source); + if (r1 < r) + r = r1; + a->source = NULL; + } + + /* Release the reader objects. */ + n = sizeof(a->readers)/sizeof(a->readers[0]); for (i = 0; i < n; i++) { - if (a->decompressors[i].finish != NULL) { - r1 = (a->decompressors[i].finish)(a); + if (a->readers[i].free != NULL) { + r1 = (a->readers[i].free)(&a->readers[i]); if (r1 < r) r = r1; } } - /* Close the client stream. */ - if (a->client_closer != NULL) { - r1 = ((a->client_closer)(&a->archive, a->client_data)); - if (r1 < r) - r = r1; - } - return (r); } @@ -651,6 +648,7 @@ 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); @@ -700,40 +698,350 @@ __archive_read_register_format(struct archive_read *a, * Used internally by decompression routines to register their bid and * initialization functions. */ -struct decompressor_t * -__archive_read_register_compression(struct archive_read *a, - int (*bid)(const void *, size_t), - int (*init)(struct archive_read *, const void *, size_t)) +struct archive_reader * +__archive_read_get_reader(struct archive_read *a) { int i, number_slots; __archive_check_magic(&a->archive, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, - "__archive_read_register_compression"); + "__archive_read_get_reader"); - number_slots = sizeof(a->decompressors) / sizeof(a->decompressors[0]); + number_slots = sizeof(a->readers) / sizeof(a->readers[0]); for (i = 0; i < number_slots; i++) { - if (a->decompressors[i].bid == bid) - return (a->decompressors + i); - if (a->decompressors[i].bid == NULL) { - a->decompressors[i].bid = bid; - a->decompressors[i].init = init; - return (a->decompressors + i); - } + if (a->readers[i].bid == NULL) + return (a->readers + i); } __archive_errx(1, "Not enough slots for compression registration"); return (NULL); /* Never actually executed. */ } -/* used internally to simplify read-ahead */ +/* + * The next three functions comprise the peek/consume internal I/O + * system used by archive format readers. This system allows fairly + * 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 + * 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 + * layer use a copy buffer to provide you with a contiguous block of + * data. The __archive_read_skip() is an optimization; it scans ahead + * very quickly (it usually translates into a seek() operation if + * you're reading uncompressed disk files). + * + * A couple of useful idioms: + * * "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 + * 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, + * emit all that's available (up to whatever limit you have), then + * repeat until you're done. + * * "I want to peek ahead by a large amount." Ask for 4k or so, then + * double and repeat until you get an error or have enough. Note + * that the I/O layer will likely end up expanding its copy buffer + * to fit your request, so use this technique cautiously. This + * technique is used, for example, by some of the format tasting + * code that has uncertain look-ahead needs. + * + * TODO: Someday, provide a more generic __archive_read_seek() for + * those cases where it's useful. This is tricky because there are lots + * of cases where seek() is not available (reading gzip data from a + * network socket, for instance), so there needs to be a good way to + * communicate whether seek() is available and users of that interface + * need to use non-seeking strategies whenever seek() is not available. + */ + +/* + * Looks ahead in the input stream: + * * If 'avail' pointer is provided, that returns number of bytes available + * in the current buffer, which may be much larger than requested. + * * If end-of-file, *avail gets set to zero. + * * If error, *avail gets error code. + * * If request can be met, returns pointer to data, returns NULL + * if request is not met. + * + * Note: If you just want "some data", ask for 1 byte and pay attention + * to *avail, which will have the actual amount available. If you + * know exactly how many bytes you need, just ask for that and treat + * a NULL return as an error. + * + * Important: This does NOT move the file pointer. See + * __archive_read_consume() below. + */ + +/* + * This is tricky. We need to provide our clients with pointers to + * contiguous blocks of memory but we want to avoid copying whenever + * possible. + * + * Mostly, this code returns pointers directly into the block of data + * provided by the client_read routine. It can do this unless the + * request would split across blocks. In that case, we have to copy + * into an internal buffer to combine reads. + */ const void * -__archive_read_ahead(struct archive_read *a, size_t len) +__archive_read_ahead(struct archive_read *a, size_t min, ssize_t *avail) { - const void *h; + ssize_t bytes_read; + size_t tocopy; - if ((a->decompressor->read_ahead)(a, &h, len) < (ssize_t)len) + if (a->fatal) { + if (avail) + *avail = ARCHIVE_FATAL; return (NULL); - return (h); + } + + /* + * Keep pulling more data until we can satisfy the request. + */ + for (;;) { + + /* + * If we can satisfy from the copy buffer, we're done. + */ + if (a->avail >= min) { + if (avail != NULL) + *avail = a->avail; + return (a->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) { + /* "Roll back" to client buffer. */ + a->client_avail += a->avail; + a->client_next -= a->avail; + /* Copy buffer is now empty. */ + a->avail = 0; + a->next = a->buffer; + /* Return data from client buffer. */ + if (avail != NULL) + *avail = a->client_avail; + return (a->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 we've used up the client data, get more. */ + if (a->client_avail <= 0) { + if (a->end_of_file) { + if (avail != NULL) + *avail = 0; + return (NULL); + } + bytes_read = (a->source->read)(a->source, + &a->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; + 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; + /* Return whatever we do have. */ + if (avail != NULL) + *avail = a->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; + } + else + { + /* + * We can't satisfy the request from the copy + * buffer or the existing client data, so we + * need to copy more client data over to the + * copy buffer. + */ + + /* Ensure the buffer is big enough. */ + if (min > a->buffer_size) { + size_t s, t; + char *p; + + /* Double the buffer; watch for overflow. */ + s = t = a->buffer_size; + while (s < min) { + t *= 2; + if (t <= s) { /* Integer overflow! */ + archive_set_error(&a->archive, + ENOMEM, + "Unable to allocate copy buffer"); + a->fatal = 1; + if (avail != NULL) + *avail = ARCHIVE_FATAL; + return (NULL); + } + s = t; + } + /* Now s >= min, so allocate a new buffer. */ + p = (char *)malloc(s); + if (p == NULL) { + archive_set_error(&a->archive, ENOMEM, + "Unable to allocate copy buffer"); + a->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; + } + + /* 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); + /* Don't waste time buffering more than we need to. */ + if (tocopy + a->avail > min) + tocopy = min - a->avail; + /* Don't copy more than is available. */ + if (tocopy > a->client_avail) + tocopy = a->client_avail; + + memcpy(a->next + a->avail, a->client_next, + tocopy); + /* Remove this data from client buffer. */ + a->client_next += tocopy; + a->client_avail -= tocopy; + /* add it to copy buffer. */ + a->avail += tocopy; + } + } +} + +/* + * Move the file pointer forward. This should be called after + * __archive_read_ahead() returns data to you. Don't try to move + * ahead by more than the amount of data available according to + * __archive_read_ahead(). + */ +/* + * Mark the appropriate data as used. Note that the request here will + * often be much smaller than the size of the previous read_ahead + * request. + */ +ssize_t +__archive_read_consume(struct archive_read *a, size_t request) +{ + if (a->avail > 0) { + /* Read came from copy buffer. */ + a->next += request; + a->avail -= request; + } else { + /* Read came from client buffer. */ + a->client_next += request; + a->client_avail -= request; + } + a->archive.file_position += request; + return (request); +} + +/* + * Move the file pointer ahead by an arbitrary amount. If you're + * reading uncompressed data from a disk file, this will actually + * translate into a seek() operation. Even in cases where seek() + * isn't feasible, this at least pushes the read-and-discard loop + * down closer to the data source. + */ +int64_t +__archive_read_skip(struct archive_read *a, int64_t request) +{ + off_t bytes_skipped, total_bytes_skipped = 0; + size_t min; + + if (a->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); + 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); + request -= bytes_skipped; + total_bytes_skipped += bytes_skipped; + } + if (request == 0) + return (total_bytes_skipped); + /* + * If a client_skipper was provided, try that first. + */ +#if ARCHIVE_API_VERSION < 2 + if ((a->source->skip != NULL) && (request < SSIZE_MAX)) { +#else + if (a->source->skip != NULL) { +#endif + bytes_skipped = (a->source->skip)(a->source, request); + if (bytes_skipped < 0) { /* error */ + a->client_total = a->client_avail = 0; + a->client_next = a->client_buff = NULL; + a->fatal = 1; + return (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; + } + /* + * Note that client_skipper will usually not satisfy the + * full request (due to low-level blocking concerns), + * so even if client_skipper is provided, we may still + * have to use ordinary reads to finish out the request. + */ + while (request > 0) { + const void* dummy_buffer; + ssize_t bytes_read; + dummy_buffer = __archive_read_ahead(a, 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, + "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); + total_bytes_skipped += bytes_read; + request -= bytes_read; + } + return (total_bytes_skipped); } diff --git a/lib/libarchive/archive_read_private.h b/lib/libarchive/archive_read_private.h index 4353983..da0b83f 100644 --- a/lib/libarchive/archive_read_private.h +++ b/lib/libarchive/archive_read_private.h @@ -32,6 +32,75 @@ #include "archive_string.h" #include "archive_private.h" +struct archive_read; +struct archive_reader; +struct archive_read_source; + +/* + * 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: + * * 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. + */ +struct archive_reader { + /* Configuration data for the reader. */ + 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 *); +}; + +/* + * A "source" is an instance of a reader. This structure is + * allocated and initialized by the init() method of a reader + * above. + */ +struct archive_read_source { + /* Essentially all sources 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. */ + /* Return next block. */ + ssize_t (*read)(struct archive_read_source *, 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); + /* My private data. */ + void *data; +}; + +/* + * The client source is almost the same as an internal source. + * + * TODO: Make archive_read_source 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. + */ +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 { struct archive archive; @@ -50,46 +119,30 @@ struct archive_read { off_t read_data_output_offset; size_t read_data_remaining; - /* Callbacks to open/read/write/close archive stream. */ - archive_open_callback *client_opener; - archive_read_callback *client_reader; - archive_skip_callback *client_skipper; - archive_close_callback *client_closer; - void *client_data; + /* Callbacks to open/read/write/close client archive stream. */ + struct archive_read_client client; + + /* Registered readers. */ + struct archive_reader readers[8]; + + /* Source */ + struct archive_read_source *source; /* File offset of beginning of most recently-read header. */ off_t header_position; - /* - * Decompressors have a very specific lifecycle: - * public setup function initializes a slot in this table - * 'config' holds minimal configuration data - * bid() examines a block of data and returns a bid [1] - * init() is called for successful bidder - * 'data' is initialized by init() - * read() returns a pointer to the next block of data - * consume() indicates how much data is used - * skip() ignores bytes of data - * finish() cleans up and frees 'data' and 'config' - * - * [1] General guideline: bid the number of bits that you actually - * test, e.g., 16 if you test a 2-byte magic value. - */ - struct decompressor_t { - void *config; - void *data; - int (*bid)(const void *buff, size_t); - int (*init)(struct archive_read *, - const void *buff, size_t); - int (*finish)(struct archive_read *); - ssize_t (*read_ahead)(struct archive_read *, - const void **, size_t); - ssize_t (*consume)(struct archive_read *, size_t); - off_t (*skip)(struct archive_read *, off_t); - } decompressors[4]; - - /* Pointer to current decompressor. */ - struct decompressor_t *decompressor; + + /* 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 @@ -124,12 +177,13 @@ int __archive_read_register_format(struct archive_read *a, int (*read_data_skip)(struct archive_read *), int (*cleanup)(struct archive_read *)); -struct decompressor_t - *__archive_read_register_compression(struct archive_read *a, - int (*bid)(const void *, size_t), - int (*init)(struct archive_read *, const void *, size_t)); +struct archive_reader + *__archive_read_get_reader(struct archive_read *a); const void - *__archive_read_ahead(struct archive_read *, size_t); - + *__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); #endif diff --git a/lib/libarchive/archive_read_support_compression_all.c b/lib/libarchive/archive_read_support_compression_all.c index de3d585..967546d 100644 --- a/lib/libarchive/archive_read_support_compression_all.c +++ b/lib/libarchive/archive_read_support_compression_all.c @@ -39,5 +39,11 @@ archive_read_support_compression_all(struct archive *a) #if HAVE_ZLIB_H archive_read_support_compression_gzip(a); #endif +#if HAVE_LZMADEC_H + /* LZMA bidding is subject to false positives because + * the LZMA file format has a very weak signature. It + * may not be feasible to include LZMA detection here. */ + /* archive_read_support_compression_lzma(a); */ +#endif return (ARCHIVE_OK); } diff --git a/lib/libarchive/archive_read_support_compression_bzip2.c b/lib/libarchive/archive_read_support_compression_bzip2.c index b08b6b5..6849771 100644 --- a/lib/libarchive/archive_read_support_compression_bzip2.c +++ b/lib/libarchive/archive_read_support_compression_bzip2.c @@ -51,30 +51,49 @@ __FBSDID("$FreeBSD$"); #if HAVE_BZLIB_H struct private_data { bz_stream stream; - char *uncompressed_buffer; - size_t uncompressed_buffer_size; - char *read_next; - int64_t total_out; + char *out_block; + size_t out_block_size; + char valid; /* True = decompressor is initialized */ char eof; /* True = found end of compressed data. */ }; -static int finish(struct archive_read *); -static ssize_t read_ahead(struct archive_read *, const void **, size_t); -static ssize_t read_consume(struct archive_read *, size_t); -static int drive_decompressor(struct archive_read *a, struct private_data *); +/* Bzip2 source */ +static ssize_t bzip2_source_read(struct archive_read_source *, const void **); +static int bzip2_source_close(struct archive_read_source *); #endif -/* These two functions are defined even if we lack the library. See below. */ -static int bid(const void *, size_t); -static int init(struct archive_read *, const void *, size_t); +/* + * Note that we can detect bzip2 archives even if we can't decompress + * them. (In fact, we like detecting them because we can give better + * 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 *); int archive_read_support_compression_bzip2(struct archive *_a) { struct archive_read *a = (struct archive_read *)_a; - if (__archive_read_register_compression(a, bid, init) != NULL) - return (ARCHIVE_OK); - return (ARCHIVE_FATAL); + struct archive_reader *reader = __archive_read_get_reader(a); + + if (reader == NULL) + return (ARCHIVE_FATAL); + + reader->data = NULL; + reader->bid = bzip2_reader_bid; + reader->init = bzip2_reader_init; + reader->free = bzip2_reader_free; + return (ARCHIVE_OK); +} + +static int +bzip2_reader_free(struct archive_reader *self){ + (void)self; /* UNUSED */ + return (ARCHIVE_OK); } /* @@ -85,11 +104,13 @@ archive_read_support_compression_bzip2(struct archive *_a) * from verifying as much as we would like. */ static int -bid(const void *buff, size_t len) +bzip2_reader_bid(struct archive_reader *self, const void *buff, size_t len) { const unsigned char *buffer; int bits_checked; + (void)self; /* UNUSED */ + if (len < 1) return (0); @@ -150,16 +171,19 @@ bid(const void *buff, size_t len) * decompression. We can, however, still detect compressed archives * and emit a useful message. */ -static int -init(struct archive_read *a, const void *buff, size_t n) +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) { (void)a; /* UNUSED */ + (void)reader; /* UNUSED */ + (void)upstream; /* UNUSED */ (void)buff; /* UNUSED */ (void)n; /* UNUSED */ archive_set_error(&a->archive, -1, "This version of libarchive was compiled without bzip2 support"); - return (ARCHIVE_FATAL); + return (NULL); } @@ -168,37 +192,42 @@ init(struct archive_read *a, const void *buff, size_t n) /* * Setup the callbacks. */ -static int -init(struct archive_read *a, const void *buff, size_t n) +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 const size_t out_block_size = 64 * 1024; + void *out_block; + struct archive_read_source *self; struct private_data *state; - int ret; + + (void)reader; /* UNUSED */ a->archive.compression_code = ARCHIVE_COMPRESSION_BZIP2; a->archive.compression_name = "bzip2"; - state = (struct private_data *)malloc(sizeof(*state)); - if (state == NULL) { + 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); - return (ARCHIVE_FATAL); + free(out_block); + free(state); + free(self); + return (NULL); } - memset(state, 0, sizeof(*state)); - state->uncompressed_buffer_size = 64 * 1024; - state->uncompressed_buffer = (char *)malloc(state->uncompressed_buffer_size); - state->stream.next_out = state->uncompressed_buffer; - state->read_next = state->uncompressed_buffer; - state->stream.avail_out = state->uncompressed_buffer_size; - if (state->uncompressed_buffer == NULL) { - archive_set_error(&a->archive, ENOMEM, - "Can't allocate %s decompression buffers", - a->archive.compression_name); - free(state); - 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->skip = NULL; /* not supported */ + self->close = bzip2_source_close; /* * A bug in bzlib.h: stream.next_in should be marked 'const' @@ -209,218 +238,160 @@ init(struct archive_read *a, const void *buff, size_t n) state->stream.next_in = (char *)(uintptr_t)(const void *)buff; state->stream.avail_in = n; - a->decompressor->read_ahead = read_ahead; - a->decompressor->consume = read_consume; - a->decompressor->skip = NULL; /* not supported */ - a->decompressor->finish = finish; - - /* Initialize compression library. */ - ret = BZ2_bzDecompressInit(&(state->stream), - 0 /* library verbosity */, - 0 /* don't use slow low-mem algorithm */); - - /* If init fails, try using low-memory algorithm instead. */ - if (ret == BZ_MEM_ERROR) { - ret = BZ2_bzDecompressInit(&(state->stream), - 0 /* library verbosity */, - 1 /* do use slow low-mem algorithm */); - } - - if (ret == BZ_OK) { - a->decompressor->data = state; - return (ARCHIVE_OK); - } + state->stream.next_out = state->out_block; + state->stream.avail_out = state->out_block_size; - /* Library setup failed: Clean up. */ - archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "Internal error initializing %s library", - a->archive.compression_name); - free(state->uncompressed_buffer); - free(state); - - /* Override the error message if we know what really went wrong. */ - switch (ret) { - case BZ_PARAM_ERROR: - archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "Internal error initializing compression library: " - "invalid setup parameter"); - break; - case BZ_MEM_ERROR: - archive_set_error(&a->archive, ENOMEM, - "Internal error initializing compression library: " - "out of memory"); - break; - case BZ_CONFIG_ERROR: - archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "Internal error initializing compression library: " - "mis-compiled library"); - break; - } - - return (ARCHIVE_FATAL); + return (self); } /* - * Return a block of data from the decompression buffer. Decompress more - * as necessary. + * Return the next block of decompressed data. */ static ssize_t -read_ahead(struct archive_read *a, const void **p, size_t min) +bzip2_source_read(struct archive_read_source *self, const void **p) { struct private_data *state; - size_t read_avail, was_avail; + size_t read_avail, decompressed; + const void *read_buf; int ret; - state = (struct private_data *)a->decompressor->data; - if (!a->client_reader) { - archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, - "No read callback is registered? " - "This is probably an internal programming error."); - return (ARCHIVE_FATAL); - } - - read_avail = state->stream.next_out - state->read_next; - - if (read_avail + state->stream.avail_out < min) { - memmove(state->uncompressed_buffer, state->read_next, - read_avail); - state->read_next = state->uncompressed_buffer; - state->stream.next_out = state->read_next + read_avail; - state->stream.avail_out - = state->uncompressed_buffer_size - read_avail; - } - - while (read_avail < min && /* Haven't satisfied min. */ - read_avail < state->uncompressed_buffer_size) { /* !full */ - was_avail = read_avail; - if ((ret = drive_decompressor(a, state)) < ARCHIVE_OK) - return (ret); - if (ret == ARCHIVE_EOF) - break; /* Break on EOF even if we haven't met min. */ - read_avail = state->stream.next_out - state->read_next; - if (was_avail == read_avail) /* No progress? */ - break; - } - - *p = state->read_next; - return (read_avail); -} - -/* - * Mark a previously-returned block of data as read. - */ -static ssize_t -read_consume(struct archive_read *a, size_t n) -{ - struct private_data *state; - - state = (struct private_data *)a->decompressor->data; - a->archive.file_position += n; - state->read_next += n; - if (state->read_next > state->stream.next_out) - __archive_errx(1, "Request to consume too many " - "bytes from bzip2 decompressor"); - return (n); -} + state = (struct private_data *)self->data; + read_avail = 0; -/* - * Clean up the decompressor. - */ -static int -finish(struct archive_read *a) -{ - struct private_data *state; - int ret; - - state = (struct private_data *)a->decompressor->data; - ret = ARCHIVE_OK; - switch (BZ2_bzDecompressEnd(&(state->stream))) { - case BZ_OK: - break; - default: - archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "Failed to clean up %s compressor", - a->archive.compression_name); - ret = ARCHIVE_FATAL; + if (state->eof) { + *p = NULL; + return (0); } - free(state->uncompressed_buffer); - free(state); - - a->decompressor->data = NULL; - return (ret); -} - -/* - * Utility function to pull data through decompressor, reading input - * blocks as necessary. - */ -static int -drive_decompressor(struct archive_read *a, struct private_data *state) -{ - ssize_t ret; - int decompressed, total_decompressed; - char *output; - const void *read_buf; + /* Empty our output buffer. */ + state->stream.next_out = state->out_block; + state->stream.avail_out = state->out_block_size; - if (state->eof) - return (ARCHIVE_EOF); - total_decompressed = 0; + /* Try to fill the output buffer. */ for (;;) { + /* If the last upstream block is done, get another one. */ if (state->stream.avail_in == 0) { - read_buf = state->stream.next_in; - ret = (a->client_reader)(&a->archive, a->client_data, + ret = (self->upstream->read)(self->upstream, &read_buf); - state->stream.next_in = (void *)(uintptr_t)read_buf; - if (ret < 0) { - /* - * TODO: Find a better way to handle - * this read failure. - */ - goto fatal; - } - if (ret == 0 && total_decompressed == 0) { - archive_set_error(&a->archive, EIO, - "Premature end of %s compressed data", - a->archive.compression_name); + /* 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); } - a->archive.raw_position += ret; state->stream.avail_in = ret; } - { - output = state->stream.next_out; - - /* Decompress some data. */ - ret = BZ2_bzDecompress(&(state->stream)); - decompressed = state->stream.next_out - output; - - /* Accumulate the total bytes of output. */ - state->total_out += decompressed; - total_decompressed += decompressed; + if (!state->valid) { + if (state->stream.next_in[0] != 'B') { + state->eof = 1; + *p = state->out_block; + decompressed = state->stream.next_out + - state->out_block; + return (decompressed); + } + /* Initialize compression library. */ + ret = BZ2_bzDecompressInit(&(state->stream), + 0 /* library verbosity */, + 0 /* don't use low-mem algorithm */); + + /* If init fails, try low-memory algorithm instead. */ + if (ret == BZ_MEM_ERROR) + ret = BZ2_bzDecompressInit(&(state->stream), + 0 /* library verbosity */, + 1 /* do use low-mem algo */); + + if (ret != BZ_OK) { + const char *detail = NULL; + int err = ARCHIVE_ERRNO_MISC; + switch (ret) { + case BZ_PARAM_ERROR: + detail = "invalid setup parameter"; + break; + case BZ_MEM_ERROR: + err = ENOMEM; + detail = "out of memory"; + break; + case BZ_CONFIG_ERROR: + detail = "mis-compiled library"; + break; + } + archive_set_error(&self->archive->archive, err, + "Internal error initializing decompressor%s%s", + detail == NULL ? "" : ": ", + detail); + return (ARCHIVE_FATAL); + } + state->valid = 1; + } - switch (ret) { - case BZ_OK: /* Decompressor made some progress. */ - if (decompressed > 0) - return (ARCHIVE_OK); + /* Decompress as much as we can in one pass. */ + ret = BZ2_bzDecompress(&(state->stream)); + switch (ret) { + case BZ_STREAM_END: /* Found end of stream. */ + switch (BZ2_bzDecompressEnd(&(state->stream))) { + case BZ_OK: break; - case BZ_STREAM_END: /* Found end of stream. */ - state->eof = 1; - return (ARCHIVE_OK); default: - /* Any other return value is an error. */ - goto fatal; + archive_set_error(&(self->archive->archive), + ARCHIVE_ERRNO_MISC, + "Failed to clean up decompressor"); + return (ARCHIVE_FATAL); } + state->valid = 0; + /* FALLTHROUGH */ + case BZ_OK: /* Decompressor made some progress. */ + /* If we filled our buffer, update stats and return. */ + if (state->stream.avail_out == 0) { + *p = state->out_block; + decompressed = state->stream.next_out + - state->out_block; + return (decompressed); + } + break; + default: /* Return an error. */ + archive_set_error(&self->archive->archive, + ARCHIVE_ERRNO_MISC, "bzip decompression failed"); + return (ARCHIVE_FATAL); } } - return (ARCHIVE_OK); +} + +/* + * Clean up the decompressor. + */ +static int +bzip2_source_close(struct archive_read_source *self) +{ + struct private_data *state; + int ret = ARCHIVE_OK; + + state = (struct private_data *)self->data; - /* Return a fatal error. */ -fatal: - archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "%s decompression failed", a->archive.compression_name); - return (ARCHIVE_FATAL); + if (state->valid) { + switch (BZ2_bzDecompressEnd(&state->stream)) { + case BZ_OK: + break; + default: + archive_set_error(&self->archive->archive, + ARCHIVE_ERRNO_MISC, + "Failed to clean up decompressor"); + ret = ARCHIVE_FATAL; + } + } + + free(state->out_block); + free(state); + free(self); + return (ARCHIVE_OK); } #endif /* HAVE_BZLIB_H */ diff --git a/lib/libarchive/archive_read_support_compression_compress.c b/lib/libarchive/archive_read_support_compression_compress.c index f45b7cd..5ae0b8a 100644 --- a/lib/libarchive/archive_read_support_compression_compress.c +++ b/lib/libarchive/archive_read_support_compression_compress.c @@ -100,11 +100,8 @@ struct private_data { size_t bytes_in_section; /* Output variables. */ - size_t uncompressed_buffer_size; - void *uncompressed_buffer; - unsigned char *read_next; /* Data for client. */ - unsigned char *next_out; /* Where to write new data. */ - size_t avail_out; /* Space at end of buffer. */ + size_t out_block_size; + void *out_block; /* Decompression status variables. */ int use_reset_code; @@ -133,21 +130,32 @@ struct private_data { unsigned char stack[65300]; }; -static int bid(const void *, size_t); -static int finish(struct archive_read *); -static int init(struct archive_read *, const void *, size_t); -static ssize_t read_ahead(struct archive_read *, const void **, size_t); -static ssize_t read_consume(struct archive_read *, size_t); -static int getbits(struct archive_read *, struct private_data *, int n); -static int next_code(struct archive_read *a, struct private_data *state); +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 ssize_t compress_source_read(struct archive_read_source *, const void **); +static int compress_source_close(struct archive_read_source *); + +static int getbits(struct archive_read_source *, int n); +static int next_code(struct archive_read_source *); int archive_read_support_compression_compress(struct archive *_a) { struct archive_read *a = (struct archive_read *)_a; - if (__archive_read_register_compression(a, bid, init) != NULL) - return (ARCHIVE_OK); - return (ARCHIVE_FATAL); + struct archive_reader *reader = __archive_read_get_reader(a); + + if (reader == NULL) + return (ARCHIVE_FATAL); + + reader->data = NULL; + reader->bid = compress_reader_bid; + reader->init = compress_reader_init; + reader->free = compress_reader_free; + return (ARCHIVE_OK); } /* @@ -158,11 +166,13 @@ archive_read_support_compression_compress(struct archive *_a) * from verifying as much as we would like. */ static int -bid(const void *buff, size_t len) +compress_reader_bid(struct archive_reader *self, const void *buff, size_t len) { const unsigned char *buffer; int bits_checked; + (void)self; /* UNUSED */ + if (len < 1) return (0); @@ -190,34 +200,43 @@ bid(const void *buff, size_t len) /* * Setup the callbacks. */ -static int -init(struct archive_read *a, const void *buff, size_t n) +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) { + struct archive_read_source *self; struct private_data *state; int code; + (void)reader; /* UNUSED */ + a->archive.compression_code = ARCHIVE_COMPRESSION_COMPRESS; a->archive.compression_name = "compress (.Z)"; - a->decompressor->read_ahead = read_ahead; - a->decompressor->consume = read_consume; - a->decompressor->skip = NULL; /* not supported */ - a->decompressor->finish = finish; + 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; - state = (struct private_data *)malloc(sizeof(*state)); + state = (struct private_data *)calloc(sizeof(*state), 1); if (state == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate data for %s decompression", a->archive.compression_name); - return (ARCHIVE_FATAL); + free(self); + return (NULL); } - memset(state, 0, sizeof(*state)); - a->decompressor->data = state; + self->data = state; - state->uncompressed_buffer_size = 64 * 1024; - state->uncompressed_buffer = malloc(state->uncompressed_buffer_size); + state->out_block_size = 64 * 1024; + state->out_block = malloc(state->out_block_size); - if (state->uncompressed_buffer == NULL) { + if (state->out_block == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate %s decompression buffers", a->archive.compression_name); @@ -226,14 +245,12 @@ init(struct archive_read *a, const void *buff, size_t n) state->next_in = (const unsigned char *)buff; state->avail_in = n; - state->read_next = state->next_out = (unsigned char *)state->uncompressed_buffer; - state->avail_out = state->uncompressed_buffer_size; - code = getbits(a, state, 8); + code = getbits(self, 8); if (code != 037) /* This should be impossible. */ goto fatal; - code = getbits(a, state, 8); + 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. @@ -244,7 +261,7 @@ init(struct archive_read *a, const void *buff, size_t n) goto fatal; } - code = getbits(a, state, 8); + code = getbits(self, 8); state->maxcode_bits = code & 0x1f; state->maxcode = (1 << state->maxcode_bits); state->use_reset_code = code & 0x80; @@ -261,12 +278,12 @@ init(struct archive_read *a, const void *buff, size_t n) state->prefix[code] = 0; state->suffix[code] = code; } - next_code(a, state); - return (ARCHIVE_OK); + next_code(self); + return (self); fatal: - finish(a); - return (ARCHIVE_FATAL); + compress_source_close(self); + return (NULL); } /* @@ -274,93 +291,59 @@ fatal: * as necessary. */ static ssize_t -read_ahead(struct archive_read *a, const void **p, size_t min) +compress_source_read(struct archive_read_source *self, const void **pblock) { struct private_data *state; - size_t read_avail; + unsigned char *p, *start, *end; int ret; - state = (struct private_data *)a->decompressor->data; - if (!a->client_reader) { - archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, - "No read callback is registered? " - "This is probably an internal programming error."); - return (ARCHIVE_FATAL); - } - - read_avail = state->next_out - state->read_next; - - if (read_avail < min && state->end_of_stream) { - if (state->end_of_stream == ARCHIVE_EOF) - return (0); - else - return (-1); + state = (struct private_data *)self->data; + if (state->end_of_stream) { + *pblock = NULL; + return (0); } - - if (read_avail < min) { - memmove(state->uncompressed_buffer, state->read_next, - read_avail); - state->read_next = (unsigned char *)state->uncompressed_buffer; - state->next_out = state->read_next + read_avail; - state->avail_out - = state->uncompressed_buffer_size - read_avail; - - while (read_avail < state->uncompressed_buffer_size - && !state->end_of_stream) { - if (state->stackp > state->stack) { - *state->next_out++ = *--state->stackp; - state->avail_out--; - read_avail++; - } else { - ret = next_code(a, state); - if (ret == ARCHIVE_EOF) - state->end_of_stream = ret; - else if (ret != ARCHIVE_OK) - return (ret); - } + p = start = (unsigned char *)state->out_block; + end = start + state->out_block_size; + + while (p < end && !state->end_of_stream) { + if (state->stackp > state->stack) { + *p++ = *--state->stackp; + } else { + ret = next_code(self); + if (ret == ARCHIVE_EOF) + state->end_of_stream = ret; + else if (ret != ARCHIVE_OK) + return (ret); } } - *p = state->read_next; - return (read_avail); + *pblock = start; + return (p - start); } /* - * Mark a previously-returned block of data as read. + * Clean up the reader. */ -static ssize_t -read_consume(struct archive_read *a, size_t n) +static int +compress_reader_free(struct archive_reader *self) { - struct private_data *state; - - state = (struct private_data *)a->decompressor->data; - a->archive.file_position += n; - state->read_next += n; - if (state->read_next > state->next_out) - __archive_errx(1, "Request to consume too many " - "bytes from compress decompressor"); - return (n); + self->data = NULL; + return (ARCHIVE_OK); } /* - * Clean up the decompressor. + * Close and release a source. */ static int -finish(struct archive_read *a) +compress_source_close(struct archive_read_source *self) { - struct private_data *state; - int ret = ARCHIVE_OK; - - state = (struct private_data *)a->decompressor->data; + struct private_data *state = (struct private_data *)self->data; - if (state != NULL) { - if (state->uncompressed_buffer != NULL) - free(state->uncompressed_buffer); - free(state); - } - - a->decompressor->data = NULL; - return (ret); + self->upstream->close(self->upstream); + free(state->out_block); + free(state); + free(self); + return (ARCHIVE_OK); } /* @@ -369,14 +352,15 @@ finish(struct archive_read *a) * format error, ARCHIVE_EOF if we hit end of data, ARCHIVE_OK otherwise. */ static int -next_code(struct archive_read *a, struct private_data *state) +next_code(struct archive_read_source *self) { + struct private_data *state = (struct private_data *)self->data; int code, newcode; static int debug_buff[1024]; static unsigned debug_index; - code = newcode = getbits(a, state, state->bits); + code = newcode = getbits(self, state->bits); if (code < 0) return (code); @@ -398,7 +382,7 @@ next_code(struct archive_read *a, struct private_data *state) skip_bytes %= state->bits; state->bits_avail = 0; /* Discard rest of this byte. */ while (skip_bytes-- > 0) { - code = getbits(a, state, 8); + code = getbits(self, 8); if (code < 0) return (code); } @@ -408,12 +392,13 @@ next_code(struct archive_read *a, struct private_data *state) state->section_end_code = (1 << state->bits) - 1; state->free_ent = 257; state->oldcode = -1; - return (next_code(a, state)); + return (next_code(self)); } if (code > state->free_ent) { /* An invalid code is a fatal error. */ - archive_set_error(&a->archive, -1, "Invalid compressed data"); + archive_set_error(&(self->archive->archive), -1, + "Invalid compressed data"); return (ARCHIVE_FATAL); } @@ -457,8 +442,9 @@ next_code(struct archive_read *a, struct private_data *state) * -1 indicates end of available data. */ static int -getbits(struct archive_read *a, struct private_data *state, int n) +getbits(struct archive_read_source *self, int n) { + struct private_data *state = (struct private_data *)self->data; int code, ret; static const int mask[] = { 0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff, @@ -469,14 +455,13 @@ getbits(struct archive_read *a, struct private_data *state, int n) while (state->bits_avail < n) { if (state->avail_in <= 0) { read_buf = state->next_in; - ret = (a->client_reader)(&a->archive, a->client_data, - &read_buf); + ret = (self->upstream->read)(self->upstream, &read_buf); state->next_in = read_buf; if (ret < 0) return (ARCHIVE_FATAL); if (ret == 0) return (ARCHIVE_EOF); - a->archive.raw_position += ret; +/* TODO: Fix this a->archive.raw_position += ret; */ state->avail_in = ret; } state->bit_buffer |= *state->next_in++ << state->bits_avail; diff --git a/lib/libarchive/archive_read_support_compression_gzip.c b/lib/libarchive/archive_read_support_compression_gzip.c index 7ca2feb..1e8f818 100644 --- a/lib/libarchive/archive_read_support_compression_gzip.c +++ b/lib/libarchive/archive_read_support_compression_gzip.c @@ -51,32 +51,54 @@ __FBSDID("$FreeBSD$"); #ifdef HAVE_ZLIB_H struct private_data { z_stream stream; - unsigned char *uncompressed_buffer; - size_t uncompressed_buffer_size; - unsigned char *read_next; + unsigned char *out_block; + size_t out_block_size; int64_t total_out; unsigned long crc; + int header_count; char header_done; + char header_state; + char header_flags; char eof; /* True = found end of compressed data. */ }; -static int finish(struct archive_read *); -static ssize_t read_ahead(struct archive_read *, const void **, size_t); -static ssize_t read_consume(struct archive_read *, size_t); -static int drive_decompressor(struct archive_read *a, struct private_data *); +/* Gzip Source. */ +static ssize_t gzip_source_read(struct archive_read_source *, const void **); +static int gzip_source_close(struct archive_read_source *); #endif -/* These two functions are defined even if we lack the library. See below. */ -static int bid(const void *, size_t); -static int init(struct archive_read *, const void *, size_t); +/* + * Note that we can detect gzip archives even if we can't decompress + * them. (In fact, we like detecting them because we can give better + * 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 *); int archive_read_support_compression_gzip(struct archive *_a) { struct archive_read *a = (struct archive_read *)_a; - if (__archive_read_register_compression(a, bid, init) != NULL) - return (ARCHIVE_OK); - return (ARCHIVE_FATAL); + struct archive_reader *reader = __archive_read_get_reader(a); + + if (reader == NULL) + return (ARCHIVE_FATAL); + + reader->data = NULL; + reader->bid = gzip_reader_bid; + reader->init = gzip_reader_init; + reader->free = gzip_reader_free; + return (ARCHIVE_OK); +} + +static int +gzip_reader_free(struct archive_reader *self){ + (void)self; /* UNUSED */ + return (ARCHIVE_OK); } /* @@ -87,11 +109,13 @@ archive_read_support_compression_gzip(struct archive *_a) * from verifying as much as we would like. */ static int -bid(const void *buff, size_t len) +gzip_reader_bid(struct archive_reader *self, const void *buff, size_t len) { const unsigned char *buffer; int bits_checked; + (void)self; /* UNUSED */ + if (len < 1) return (0); @@ -139,8 +163,9 @@ bid(const void *buff, size_t len) * decompression. We can, however, still detect compressed archives * and emit a useful message. */ -static int -init(struct archive_read *a, const void *buff, size_t n) +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) { (void)a; /* UNUSED */ (void)buff; /* UNUSED */ @@ -148,50 +173,53 @@ init(struct archive_read *a, const void *buff, size_t n) archive_set_error(&a->archive, -1, "This version of libarchive was compiled without gzip support"); - return (ARCHIVE_FATAL); + return (NULL); } - #else /* - * Setup the callbacks. + * Initialize the source object. */ -static int -init(struct archive_read *a, const void *buff, size_t n) +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 const size_t out_block_size = 64 * 1024; + void *out_block; + struct archive_read_source *self; struct private_data *state; - int ret; + + (void)reader; /* UNUSED */ a->archive.compression_code = ARCHIVE_COMPRESSION_GZIP; a->archive.compression_name = "gzip"; - state = (struct private_data *)malloc(sizeof(*state)); - if (state == NULL) { + 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); - return (ARCHIVE_FATAL); + free(out_block); + free(state); + free(self); + return (NULL); } - memset(state, 0, sizeof(*state)); + + 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->skip = NULL; /* not supported */ + self->close = gzip_source_close; state->crc = crc32(0L, NULL, 0); state->header_done = 0; /* We've not yet begun to parse header... */ - state->uncompressed_buffer_size = 64 * 1024; - state->uncompressed_buffer = (unsigned char *)malloc(state->uncompressed_buffer_size); - state->stream.next_out = state->uncompressed_buffer; - state->read_next = state->uncompressed_buffer; - state->stream.avail_out = state->uncompressed_buffer_size; - - if (state->uncompressed_buffer == NULL) { - archive_set_error(&a->archive, ENOMEM, - "Can't allocate %s decompression buffers", - a->archive.compression_name); - free(state); - return (ARCHIVE_FATAL); - } - /* * A bug in zlib.h: stream.next_in should be marked 'const' * but isn't (the library never alters data through the @@ -201,349 +229,262 @@ init(struct archive_read *a, const void *buff, size_t n) state->stream.next_in = (Bytef *)(uintptr_t)(const void *)buff; state->stream.avail_in = n; - a->decompressor->read_ahead = read_ahead; - a->decompressor->consume = read_consume; - a->decompressor->skip = NULL; /* not supported */ - a->decompressor->finish = finish; - - /* - * TODO: Do I need to parse the gzip header before calling - * inflateInit2()? In particular, one of the header bytes - * marks "best compression" or "fastest", which may be - * appropriate for setting the second parameter here. - * However, I think the only penalty for not setting it - * correctly is wasted memory. If this is necessary, it - * should probably go into drive_decompressor() below. - */ - - /* Initialize compression library. */ - ret = inflateInit2(&(state->stream), - -15 /* Don't check for zlib header */); - if (ret == Z_OK) { - a->decompressor->data = state; - 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); - free(state->uncompressed_buffer); - free(state); - - /* Override the error message if we know what really went wrong. */ - switch (ret) { - case Z_STREAM_ERROR: - archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "Internal error initializing compression library: " - "invalid setup parameter"); - break; - case Z_MEM_ERROR: - archive_set_error(&a->archive, ENOMEM, - "Internal error initializing compression library: " - "out of memory"); - break; - case Z_VERSION_ERROR: - archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "Internal error initializing compression library: " - "invalid library version"); - break; - } - - return (ARCHIVE_FATAL); + return (self); } -/* - * Return a block of data from the decompression buffer. Decompress more - * as necessary. - */ -static ssize_t -read_ahead(struct archive_read *a, const void **p, size_t min) +static int +header(struct archive_read_source *self) { struct private_data *state; - size_t read_avail, was_avail; - int ret; - - state = (struct private_data *)a->decompressor->data; - if (!a->client_reader) { - archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, - "No read callback is registered? " - "This is probably an internal programming error."); - return (ARCHIVE_FATAL); - } + int ret, b; - read_avail = state->stream.next_out - state->read_next; + state = (struct private_data *)self->data; - if (read_avail + state->stream.avail_out < min) { - memmove(state->uncompressed_buffer, state->read_next, - read_avail); - state->read_next = state->uncompressed_buffer; - state->stream.next_out = state->read_next + read_avail; - state->stream.avail_out - = state->uncompressed_buffer_size - read_avail; - } + /* + * If still parsing the header, interpret the + * next byte. + */ + b = *(state->stream.next_in++); + state->stream.avail_in--; - while (read_avail < min && /* Haven't satisfied min. */ - read_avail < state->uncompressed_buffer_size) { /* !full */ - was_avail = read_avail; - if ((ret = drive_decompressor(a, state)) < ARCHIVE_OK) - return (ret); - if (ret == ARCHIVE_EOF) - break; /* Break on EOF even if we haven't met min. */ - read_avail = state->stream.next_out - state->read_next; - if (was_avail == read_avail) /* No progress? */ + /* + * Simple state machine to parse the GZip header one byte at + * a time. If you see a way to make this easier to understand, + * please let me know. ;-) + */ + switch (state->header_state) { + case 0: /* First byte of signature. */ + /* We only return EOF for a failure here. */ + if (b != 037) + return (ARCHIVE_EOF); + state->header_state = 1; + break; + case 1: /* Second byte of signature. */ + case 2: /* Compression type must be 8 == deflate. */ + if (b != (0xff & "\037\213\010"[(int)state->header_state])) { + archive_set_error(&self->archive->archive, + ARCHIVE_ERRNO_MISC, + "Invalid GZip header (saw %d at offset %d)", + b, state->header_state); + return (ARCHIVE_FATAL); + } + ++state->header_state; + break; + case 3: /* GZip flags. */ + state->header_flags = b; + state->header_state = 4; + break; + case 4: case 5: case 6: case 7: /* Mod time. */ + case 8: /* Deflate flags. */ + case 9: /* OS. */ + ++state->header_state; + break; + case 10: /* Optional Extra: First byte of Length. */ + if ((state->header_flags & 4)) { + state->header_count = 255 & (int)b; + state->header_state = 11; + break; + } + /* Fall through if no Optional Extra field. */ + case 11: /* Optional Extra: Second byte of Length. */ + if ((state->header_flags & 4)) { + state->header_count + = (0xff00 & ((int)b << 8)) | state->header_count; + state->header_state = 12; + break; + } + /* Fall through if no Optional Extra field. */ + case 12: /* Optional Extra Field: counted length. */ + if ((state->header_flags & 4)) { + --state->header_count; + if (state->header_count == 0) state->header_state = 13; + else state->header_state = 12; + break; + } + /* Fall through if no Optional Extra field. */ + case 13: /* Optional Original Filename. */ + if ((state->header_flags & 8)) { + if (b == 0) state->header_state = 14; + else state->header_state = 13; + break; + } + /* Fall through if no Optional Original Filename. */ + case 14: /* Optional Comment. */ + if ((state->header_flags & 16)) { + if (b == 0) state->header_state = 15; + else state->header_state = 14; + break; + } + /* Fall through if no Optional Comment. */ + case 15: /* Optional Header CRC: First byte. */ + if ((state->header_flags & 2)) { + state->header_state = 16; + break; + } + /* Fall through if no Optional Header CRC. */ + case 16: /* Optional Header CRC: Second byte. */ + if ((state->header_flags & 2)) { + state->header_state = 17; + break; + } + /* Fall through if no Optional Header CRC. */ + case 17: /* First byte of compressed data. */ + state->header_done = 1; /* done with header */ + state->stream.avail_in++; /* Discard first byte. */ + state->stream.next_in--; + + /* Initialize compression library. */ + ret = inflateInit2(&(state->stream), + -15 /* Don't check for zlib header */); + + /* Decipher the error code. */ + switch (ret) { + case Z_OK: + return (ARCHIVE_OK); + case Z_STREAM_ERROR: + archive_set_error(&self->archive->archive, + ARCHIVE_ERRNO_MISC, + "Internal error initializing compression library: " + "invalid setup parameter"); + break; + case Z_MEM_ERROR: + archive_set_error(&self->archive->archive, ENOMEM, + "Internal error initializing compression library: " + "out of memory"); + break; + case Z_VERSION_ERROR: + archive_set_error(&self->archive->archive, + ARCHIVE_ERRNO_MISC, + "Internal error initializing compression library: " + "invalid library version"); break; + default: + archive_set_error(&self->archive->archive, + ARCHIVE_ERRNO_MISC, + "Internal error initializing compression library: " + " Zlib error %d", ret); + break; + } + return (ARCHIVE_FATAL); } - *p = state->read_next; - return (read_avail); + return (ARCHIVE_OK); } -/* - * Mark a previously-returned block of data as read. - */ static ssize_t -read_consume(struct archive_read *a, size_t n) -{ - struct private_data *state; - - state = (struct private_data *)a->decompressor->data; - a->archive.file_position += n; - state->read_next += n; - if (state->read_next > state->stream.next_out) - __archive_errx(1, "Request to consume too many " - "bytes from gzip decompressor"); - return (n); -} - -/* - * Clean up the decompressor. - */ -static int -finish(struct archive_read *a) +gzip_source_read(struct archive_read_source *self, const void **p) { struct private_data *state; + size_t read_avail, decompressed; + const void *read_buf; int ret; - state = (struct private_data *)a->decompressor->data; - ret = ARCHIVE_OK; - switch (inflateEnd(&(state->stream))) { - case Z_OK: - break; - default: - archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "Failed to clean up %s compressor", - a->archive.compression_name); - ret = ARCHIVE_FATAL; - } - - free(state->uncompressed_buffer); - free(state); - - a->decompressor->data = NULL; - return (ret); -} + state = (struct private_data *)self->data; + read_avail = 0; -/* - * Utility function to pull data through decompressor, reading input - * blocks as necessary. - */ -static int -drive_decompressor(struct archive_read *a, struct private_data *state) -{ - ssize_t ret; - size_t decompressed, total_decompressed; - int count, flags, header_state; - unsigned char *output; - unsigned char b; - const void *read_buf; + /* Empty our output buffer. */ + state->stream.next_out = state->out_block; + state->stream.avail_out = state->out_block_size; - if (state->eof) - return (ARCHIVE_EOF); - flags = 0; - count = 0; - header_state = 0; - total_decompressed = 0; - for (;;) { + /* Try to fill the output buffer. */ + while (state->stream.avail_out > 0 && !state->eof) { + /* If the last upstream block is done, get another one. */ if (state->stream.avail_in == 0) { - read_buf = state->stream.next_in; - ret = (a->client_reader)(&a->archive, a->client_data, + ret = (self->upstream->read)(self->upstream, &read_buf); - state->stream.next_in = (unsigned char *)(uintptr_t)read_buf; - if (ret < 0) { - /* - * TODO: Find a better way to handle - * this read failure. - */ - goto fatal; - } - if (ret == 0 && total_decompressed == 0) { - archive_set_error(&a->archive, EIO, - "Premature end of %s compressed data", - a->archive.compression_name); + /* 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); - } - a->archive.raw_position += ret; state->stream.avail_in = ret; + /* There is no more data, return whatever we have. */ + if (ret == 0) { + state->eof = 1; + break; + } } + /* If we're still parsing header bytes, walk through those. */ if (!state->header_done) { - /* - * If still parsing the header, interpret the - * next byte. - */ - b = *(state->stream.next_in++); - state->stream.avail_in--; - - /* - * Yes, this is somewhat crude, but it works, - * GZip format isn't likely to change anytime - * in the near future, and header parsing is - * certainly not a performance issue, so - * there's little point in making this more - * elegant. Of course, if you see an easy way - * to make this more elegant, please let me - * know.. ;-) - */ - switch (header_state) { - case 0: /* First byte of signature. */ - if (b != 037) - goto fatal; - header_state = 1; - break; - case 1: /* Second byte of signature. */ - if (b != 0213) - goto fatal; - header_state = 2; - break; - case 2: /* Compression type must be 8. */ - if (b != 8) - goto fatal; - header_state = 3; - break; - case 3: /* GZip flags. */ - flags = b; - header_state = 4; - break; - case 4: case 5: case 6: case 7: /* Mod time. */ - header_state++; - break; - case 8: /* Deflate flags. */ - header_state = 9; - break; - case 9: /* OS. */ - header_state = 10; - break; - case 10: /* Optional Extra: First byte of Length. */ - if ((flags & 4)) { - count = 255 & (int)b; - header_state = 11; - break; - } - /* - * Fall through if there is no - * Optional Extra field. - */ - case 11: /* Optional Extra: Second byte of Length. */ - if ((flags & 4)) { - count = (0xff00 & ((int)b << 8)) | count; - header_state = 12; - break; - } - /* - * Fall through if there is no - * Optional Extra field. - */ - case 12: /* Optional Extra Field: counted length. */ - if ((flags & 4)) { - --count; - if (count == 0) header_state = 13; - else header_state = 12; - break; - } - /* - * Fall through if there is no - * Optional Extra field. - */ - case 13: /* Optional Original Filename. */ - if ((flags & 8)) { - if (b == 0) header_state = 14; - else header_state = 13; - break; - } - /* - * Fall through if no Optional - * Original Filename. - */ - case 14: /* Optional Comment. */ - if ((flags & 16)) { - if (b == 0) header_state = 15; - else header_state = 14; - break; - } - /* Fall through if no Optional Comment. */ - case 15: /* Optional Header CRC: First byte. */ - if ((flags & 2)) { - header_state = 16; - break; - } - /* Fall through if no Optional Header CRC. */ - case 16: /* Optional Header CRC: Second byte. */ - if ((flags & 2)) { - header_state = 17; + ret = header(self); + if (ret < ARCHIVE_OK) + return (ret); + if (ret == ARCHIVE_EOF) + state->eof = 1; + } else { + /* Decompress as much as we can in one pass. */ + /* XXX Skip trailer XXX */ + ret = inflate(&(state->stream), 0); + switch (ret) { + case Z_STREAM_END: /* Found end of stream. */ + switch (inflateEnd(&(state->stream))) { + case Z_OK: break; + default: + archive_set_error(&self->archive->archive, + ARCHIVE_ERRNO_MISC, + "Failed to clean up gzip decompressor"); + return (ARCHIVE_FATAL); } - /* Fall through if no Optional Header CRC. */ - case 17: /* First byte of compressed data. */ - state->header_done = 1; /* done with header */ - state->stream.avail_in++; - state->stream.next_in--; + /* Restart header parser with the next block. */ + state->header_state = state->header_done = 0; + /* FALL THROUGH */ + case Z_OK: /* Decompressor made some progress. */ + /* If we filled our buffer, update stats and return. */ + break; + default: + /* Return an error. */ + archive_set_error(&self->archive->archive, + ARCHIVE_ERRNO_MISC, + "%s decompression failed", + self->archive->archive.compression_name); + return (ARCHIVE_FATAL); } + } + } - /* - * TODO: Consider moving the inflateInit2 call - * here so it can include the compression type - * from the header? - */ - } else { - output = state->stream.next_out; + /* We've read as much as we can. */ + decompressed = state->stream.next_out - state->out_block; + state->total_out += decompressed; + if (decompressed == 0) + *p = NULL; + else + *p = state->out_block; + return (decompressed); - /* Decompress some data. */ - ret = inflate(&(state->stream), 0); - decompressed = state->stream.next_out - output; +} - /* Accumulate the CRC of the uncompressed data. */ - state->crc = crc32(state->crc, output, decompressed); +/* + * Clean up the decompressor. + */ +static int +gzip_source_close(struct archive_read_source *self) +{ + struct private_data *state; + int ret; - /* Accumulate the total bytes of output. */ - state->total_out += decompressed; - total_decompressed += decompressed; + state = (struct private_data *)self->data; + ret = ARCHIVE_OK; - switch (ret) { - case Z_OK: /* Decompressor made some progress. */ - if (decompressed > 0) - return (ARCHIVE_OK); - break; - case Z_STREAM_END: /* Found end of stream. */ - /* - * TODO: Verify gzip trailer - * (uncompressed length and CRC). - */ - state->eof = 1; - return (ARCHIVE_OK); - default: - /* Any other return value is an error. */ - goto fatal; - } + if (state->header_done) { + switch (inflateEnd(&(state->stream))) { + case Z_OK: + break; + default: + archive_set_error(&(self->archive->archive), + ARCHIVE_ERRNO_MISC, + "Failed to clean up %s compressor", + self->archive->archive.compression_name); + ret = ARCHIVE_FATAL; } } - return (ARCHIVE_OK); - /* Return a fatal error. */ -fatal: - archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "%s decompression failed", a->archive.compression_name); - return (ARCHIVE_FATAL); + free(state->out_block); + free(state); + free(self); + return (ret); } #endif /* HAVE_ZLIB_H */ diff --git a/lib/libarchive/archive_read_support_compression_none.c b/lib/libarchive/archive_read_support_compression_none.c index 2b9d43f..e05614f 100644 --- a/lib/libarchive/archive_read_support_compression_none.c +++ b/lib/libarchive/archive_read_support_compression_none.c @@ -26,345 +26,15 @@ #include "archive_platform.h" __FBSDID("$FreeBSD$"); -#ifdef HAVE_ERRNO_H -#include <errno.h> -#endif -#ifdef HAVE_LIMITS_H -#include <limits.h> -#endif -#ifdef HAVE_STDLIB_H -#include <stdlib.h> -#endif -#ifdef HAVE_STRING_H -#include <string.h> -#endif -#ifdef HAVE_UNISTD_H -#include <unistd.h> -#endif - #include "archive.h" -#include "archive_private.h" -#include "archive_read_private.h" - -struct archive_decompress_none { - 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; -}; /* - * Size of internal buffer used for combining short reads. This is - * also an upper limit on the size of a read request. Recall, - * however, that we can (and will!) return blocks of data larger than - * this. The read semantics are: you ask for a minimum, I give you a - * pointer to my best-effort match and tell you how much data is - * there. It could be less than you asked for, it could be much more. - * For example, a client might use mmap() to "read" the entire file as - * a single block. In that case, I will return that entire block to - * my clients. + * Uncompressed streams are handled implicitly by the read core, + * so this is now a no-op. */ -#define BUFFER_SIZE 65536 - -#define minimum(a, b) (a < b ? a : b) - -static int archive_decompressor_none_bid(const void *, size_t); -static int archive_decompressor_none_finish(struct archive_read *); -static int archive_decompressor_none_init(struct archive_read *, - const void *, size_t); -static ssize_t archive_decompressor_none_read_ahead(struct archive_read *, - const void **, size_t); -static ssize_t archive_decompressor_none_read_consume(struct archive_read *, - size_t); -static off_t archive_decompressor_none_skip(struct archive_read *, off_t); - int -archive_read_support_compression_none(struct archive *_a) +archive_read_support_compression_none(struct archive *a) { - struct archive_read *a = (struct archive_read *)_a; - if (__archive_read_register_compression(a, - archive_decompressor_none_bid, - archive_decompressor_none_init) != NULL) - return (ARCHIVE_OK); - return (ARCHIVE_FATAL); -} - -/* - * Try to detect an "uncompressed" archive. - */ -static int -archive_decompressor_none_bid(const void *buff, size_t len) -{ - (void)buff; - (void)len; - - return (1); /* Default: We'll take it if noone else does. */ -} - -static int -archive_decompressor_none_init(struct archive_read *a, const void *buff, size_t n) -{ - struct archive_decompress_none *state; - - a->archive.compression_code = ARCHIVE_COMPRESSION_NONE; - a->archive.compression_name = "none"; - - state = (struct archive_decompress_none *)malloc(sizeof(*state)); - if (!state) { - archive_set_error(&a->archive, ENOMEM, "Can't allocate input data"); - return (ARCHIVE_FATAL); - } - memset(state, 0, sizeof(*state)); - - state->buffer_size = BUFFER_SIZE; - state->buffer = (char *)malloc(state->buffer_size); - state->next = state->buffer; - if (state->buffer == NULL) { - free(state); - archive_set_error(&a->archive, ENOMEM, "Can't allocate input buffer"); - return (ARCHIVE_FATAL); - } - - /* Save reference to first block of data. */ - state->client_buff = buff; - state->client_total = n; - state->client_next = state->client_buff; - state->client_avail = state->client_total; - - a->decompressor->data = state; - a->decompressor->read_ahead = archive_decompressor_none_read_ahead; - a->decompressor->consume = archive_decompressor_none_read_consume; - a->decompressor->skip = archive_decompressor_none_skip; - a->decompressor->finish = archive_decompressor_none_finish; - - return (ARCHIVE_OK); -} - -/* - * We just pass through pointers to the client buffer if we can. - * If the client buffer is short, then we copy stuff to our internal - * buffer to combine reads. - */ -static ssize_t -archive_decompressor_none_read_ahead(struct archive_read *a, const void **buff, - size_t min) -{ - struct archive_decompress_none *state; - ssize_t bytes_read; - - state = (struct archive_decompress_none *)a->decompressor->data; - if (state->fatal) - return (-1); - - /* - * Don't make special efforts to handle requests larger than - * the copy buffer. - */ - if (min > state->buffer_size) - min = state->buffer_size; - - /* - * Keep pulling more data until we can satisfy the request. - */ - for (;;) { - - /* - * If we can satisfy from the copy buffer, we're done. - */ - if (state->avail >= min) { - *buff = state->next; - return (state->avail); - } - - /* - * We can satisfy directly from client buffer if everything - * currently in the copy buffer is still in the client buffer. - */ - if (state->client_total >= state->client_avail + state->avail - && state->client_avail + state->avail >= min) { - /* "Roll back" to client buffer. */ - state->client_avail += state->avail; - state->client_next -= state->avail; - /* Copy buffer is now empty. */ - state->avail = 0; - state->next = state->buffer; - /* Return data from client buffer. */ - *buff = state->client_next; - return (state->client_avail); - } - - /* Move data forward in copy buffer if necessary. */ - if (state->next > state->buffer && - state->next + min > state->buffer + state->buffer_size) { - if (state->avail > 0) - memmove(state->buffer, state->next, state->avail); - state->next = state->buffer; - } - - /* If we've used up the client data, get more. */ - if (state->client_avail <= 0) { - bytes_read = (a->client_reader)(&a->archive, - a->client_data, &state->client_buff); - if (bytes_read < 0) { /* Read error. */ - state->client_total = state->client_avail = 0; - state->client_next = state->client_buff = NULL; - state->fatal = 1; - return (-1); - } - if (bytes_read == 0) { /* End-of-file. */ - state->client_total = state->client_avail = 0; - state->client_next = state->client_buff = NULL; - state->end_of_file = 1; - /* Return whatever we do have. */ - *buff = state->next; - return (state->avail); - } - a->archive.raw_position += bytes_read; - state->client_total = bytes_read; - state->client_avail = state->client_total; - state->client_next = state->client_buff; - } - else - { - /* We can add client data to copy buffer. */ - /* First estimate: copy to fill rest of buffer. */ - size_t tocopy = (state->buffer + state->buffer_size) - - (state->next + state->avail); - /* Don't copy more than is available. */ - if (tocopy > state->client_avail) - tocopy = state->client_avail; - memcpy(state->next + state->avail, state->client_next, - tocopy); - /* Remove this data from client buffer. */ - state->client_next += tocopy; - state->client_avail -= tocopy; - /* add it to copy buffer. */ - state->avail += tocopy; - } - } -} - -/* - * Mark the appropriate data as used. Note that the request here will - * often be much smaller than the size of the previous read_ahead - * request. - */ -static ssize_t -archive_decompressor_none_read_consume(struct archive_read *a, size_t request) -{ - struct archive_decompress_none *state; - - state = (struct archive_decompress_none *)a->decompressor->data; - if (state->avail > 0) { - /* Read came from copy buffer. */ - state->next += request; - state->avail -= request; - } else { - /* Read came from client buffer. */ - state->client_next += request; - state->client_avail -= request; - } - a->archive.file_position += request; - return (request); -} - -/* - * Skip forward by exactly the requested bytes or else return - * ARCHIVE_FATAL. Note that this differs from the contract for - * read_ahead, which does not guarantee a minimum count. - */ -static off_t -archive_decompressor_none_skip(struct archive_read *a, off_t request) -{ - struct archive_decompress_none *state; - off_t bytes_skipped, total_bytes_skipped = 0; - size_t min; - - state = (struct archive_decompress_none *)a->decompressor->data; - if (state->fatal) - return (-1); - /* - * If there is data in the buffers already, use that first. - */ - if (state->avail > 0) { - min = minimum(request, (off_t)state->avail); - bytes_skipped = archive_decompressor_none_read_consume(a, min); - request -= bytes_skipped; - total_bytes_skipped += bytes_skipped; - } - if (state->client_avail > 0) { - min = minimum(request, (off_t)state->client_avail); - bytes_skipped = archive_decompressor_none_read_consume(a, min); - request -= bytes_skipped; - total_bytes_skipped += bytes_skipped; - } - if (request == 0) - return (total_bytes_skipped); - /* - * If a client_skipper was provided, try that first. - */ -#if ARCHIVE_API_VERSION < 2 - if ((a->client_skipper != NULL) && (request < SSIZE_MAX)) { -#else - if (a->client_skipper != NULL) { -#endif - bytes_skipped = (a->client_skipper)(&a->archive, - a->client_data, request); - if (bytes_skipped < 0) { /* error */ - state->client_total = state->client_avail = 0; - state->client_next = state->client_buff = NULL; - state->fatal = 1; - return (bytes_skipped); - } - total_bytes_skipped += bytes_skipped; - a->archive.file_position += bytes_skipped; - request -= bytes_skipped; - state->client_next = state->client_buff; - a->archive.raw_position += bytes_skipped; - state->client_avail = state->client_total = 0; - } - /* - * Note that client_skipper will usually not satisfy the - * full request (due to low-level blocking concerns), - * so even if client_skipper is provided, we may still - * have to use ordinary reads to finish out the request. - */ - while (request > 0) { - const void* dummy_buffer; - ssize_t bytes_read; - bytes_read = archive_decompressor_none_read_ahead(a, - &dummy_buffer, 1); - 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, - "Truncated input file (need to skip %jd bytes)", - (intmax_t)request); - return (ARCHIVE_FATAL); - } - min = (size_t)(minimum(bytes_read, request)); - bytes_read = archive_decompressor_none_read_consume(a, min); - total_bytes_skipped += bytes_read; - request -= bytes_read; - } - return (total_bytes_skipped); -} - -static int -archive_decompressor_none_finish(struct archive_read *a) -{ - struct archive_decompress_none *state; - - state = (struct archive_decompress_none *)a->decompressor->data; - free(state->buffer); - free(state); - a->decompressor->data = NULL; + (void)a; /* UNUSED */ return (ARCHIVE_OK); } diff --git a/lib/libarchive/archive_read_support_compression_program.c b/lib/libarchive/archive_read_support_compression_program.c index 0bea78d..a9d6c5f 100644 --- a/lib/libarchive/archive_read_support_compression_program.c +++ b/lib/libarchive/archive_read_support_compression_program.c @@ -75,64 +75,94 @@ archive_read_support_compression_program(struct archive *_a, const char *cmd) #include "filter_fork.h" -struct archive_decompress_program { +struct program_reader { + char *cmd; + int bid; +}; + +struct program_source { char *description; pid_t child; int child_stdin, child_stdout; - char *child_out_buf; - char *child_out_buf_next; - size_t child_out_buf_len, child_out_buf_avail; + char *out_buf; + size_t out_buf_len; const char *child_in_buf; size_t child_in_buf_avail; }; -static int archive_decompressor_program_bid(const void *, size_t); -static int archive_decompressor_program_finish(struct archive_read *); -static int archive_decompressor_program_init(struct archive_read *, +static int program_reader_bid(struct archive_reader *, const void *, size_t); -static ssize_t archive_decompressor_program_read_ahead(struct archive_read *, - const void **, size_t); -static ssize_t archive_decompressor_program_read_consume(struct archive_read *, - 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 ssize_t program_source_read(struct archive_read_source *, + const void **); +static int program_source_close(struct archive_read_source *); + int archive_read_support_compression_program(struct archive *_a, const char *cmd) { struct archive_read *a = (struct archive_read *)_a; - struct decompressor_t *decompressor; + struct archive_reader *reader = __archive_read_get_reader(a); + struct program_reader *state; - if (cmd == NULL || *cmd == '\0') - return (ARCHIVE_WARN); + state = (struct program_reader *)calloc(sizeof (*state), 1); - decompressor = __archive_read_register_compression(a, - archive_decompressor_program_bid, - archive_decompressor_program_init); - if (decompressor == NULL) - return (ARCHIVE_WARN); + if (state == NULL) + return (ARCHIVE_FATAL); + if (reader == NULL) + return (ARCHIVE_FATAL); - decompressor->config = strdup(cmd); + 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; + return (ARCHIVE_OK); +} + +static int +program_reader_free(struct archive_reader *self) +{ + free(self->data); return (ARCHIVE_OK); } /* * If the user used us to register, they must really want us to - * handle it, so this module always bids INT_MAX. + * handle it, so we always bid INT_MAX the first time we're called. + * After that, we always return zero, lest we end up instantiating + * an infinite pipeline. */ static int -archive_decompressor_program_bid(const void *buff, size_t len) +program_reader_bid(struct archive_reader *self, const void *buff, size_t len) { + struct program_reader *state = self->data; + int bid = state->bid; + (void)buff; /* UNUSED */ (void)len; /* UNUSED */ - return (INT_MAX); /* Default: We'll take it. */ + state->bid = 0; /* Don't bid again on this pipeline. */ + + return (bid); /* Default: We'll take it if we haven't yet bid. */ } +/* + * Use select() to decide whether the child is ready for read or write. + */ + static ssize_t -child_read(struct archive_read *a, char *buf, size_t buf_len) +child_read(struct archive_read_source *self, char *buf, size_t buf_len) { - struct archive_decompress_program *state = a->decompressor->data; + struct program_source *state = self->data; ssize_t ret, requested; const void *child_buf; @@ -161,8 +191,7 @@ restart_read: if (state->child_in_buf_avail == 0) { child_buf = state->child_in_buf; - ret = (a->client_reader)(&a->archive, - a->client_data,&child_buf); + ret = (self->upstream->read)(self->upstream, &child_buf); state->child_in_buf = (const char *)child_buf; if (ret < 0) { @@ -211,118 +240,103 @@ restart_read: } } -static int -archive_decompressor_program_init(struct archive_read *a, const void *buff, size_t n) +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) { - struct archive_decompress_program *state; - const char *cmd = a->decompressor->config; + struct program_source *state; + struct program_reader *reader_state; + struct archive_read_source *self; + static const size_t out_buf_len = 65536; + char *out_buf; + char *description; const char *prefix = "Program: "; - state = (struct archive_decompress_program *)malloc(sizeof(*state)); - if (!state) { + reader_state = (struct program_reader *)reader->data; + + self = (struct archive_read_source *)malloc(sizeof(*self)); + state = (struct program_source *)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, "Can't allocate input data"); - return (ARCHIVE_FATAL); + free(self); + free(state); + free(out_buf); + free(description); + return (NULL); } a->archive.compression_code = ARCHIVE_COMPRESSION_PROGRAM; - state->description = (char *)malloc(strlen(prefix) + strlen(cmd) + 1); + state->description = description; strcpy(state->description, prefix); - strcat(state->description, cmd); + strcat(state->description, reader_state->cmd); a->archive.compression_name = state->description; - state->child_out_buf_next = state->child_out_buf = malloc(65536); - if (!state->child_out_buf) { - free(state); - archive_set_error(&a->archive, ENOMEM, - "Can't allocate filter buffer"); - return (ARCHIVE_FATAL); - } - state->child_out_buf_len = 65536; - state->child_out_buf_avail = 0; + 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(cmd, + if ((state->child = __archive_create_child(reader_state->cmd, &state->child_stdin, &state->child_stdout)) == -1) { - free(state->child_out_buf); + free(state->out_buf); free(state); archive_set_error(&a->archive, EINVAL, "Can't initialise filter"); - return (ARCHIVE_FATAL); + return (NULL); } - a->decompressor->data = state; - a->decompressor->read_ahead = archive_decompressor_program_read_ahead; - a->decompressor->consume = archive_decompressor_program_read_consume; - a->decompressor->skip = NULL; - a->decompressor->finish = archive_decompressor_program_finish; + self->data = state; + self->read = program_source_read; + self->skip = NULL; + self->close = program_source_close; + self->upstream = upstream; + self->archive = a; /* XXX Check that we can read at least one byte? */ - return (ARCHIVE_OK); + return (self); } static ssize_t -archive_decompressor_program_read_ahead(struct archive_read *a, const void **buff, - size_t min) +program_source_read(struct archive_read_source *self, const void **buff) { - struct archive_decompress_program *state; - ssize_t bytes_read; - - state = (struct archive_decompress_program *)a->decompressor->data; - - if (min > state->child_out_buf_len) - min = state->child_out_buf_len; - - while (state->child_stdout != -1 && min > state->child_out_buf_avail) { - if (state->child_out_buf != state->child_out_buf_next) { - memmove(state->child_out_buf, state->child_out_buf_next, - state->child_out_buf_avail); - state->child_out_buf_next = state->child_out_buf; - } - - bytes_read = child_read(a, - state->child_out_buf + state->child_out_buf_avail, - state->child_out_buf_len - state->child_out_buf_avail); - if (bytes_read == -1) - return (-1); - if (bytes_read == 0) + struct program_source *state; + ssize_t bytes, total; + char *p; + + state = (struct program_source *)self->data; + + total = 0; + p = state->out_buf; + while (state->child_stdout != -1) { + bytes = child_read(self, p, state->out_buf_len - total); + if (bytes < 0) + return (bytes); + if (bytes == 0) break; - state->child_out_buf_avail += bytes_read; - a->archive.raw_position += bytes_read; + total += bytes; +/* TODO: fix this */ /* a->archive.raw_position += bytes_read; */ } - *buff = state->child_out_buf_next; - return (state->child_out_buf_avail); -} - -static ssize_t -archive_decompressor_program_read_consume(struct archive_read *a, size_t request) -{ - struct archive_decompress_program *state; - - state = (struct archive_decompress_program *)a->decompressor->data; - - state->child_out_buf_next += request; - state->child_out_buf_avail -= request; - - a->archive.file_position += request; - return (request); + *buff = state->out_buf; + return (total); } static int -archive_decompressor_program_finish(struct archive_read *a) +program_source_close(struct archive_read_source *self) { - struct archive_decompress_program *state; + struct program_source *state; int status; - state = (struct archive_decompress_program *)a->decompressor->data; - - /* Release our configuration data. */ - free(a->decompressor->config); - a->decompressor->config = NULL; + state = (struct program_source *)self->data; /* Shut down the child. */ if (state->child_stdin != -1) @@ -333,10 +347,10 @@ archive_decompressor_program_finish(struct archive_read *a) continue; /* Release our private data. */ - free(state->child_out_buf); + free(state->out_buf); free(state->description); free(state); - a->decompressor->data = NULL; + free(self); return (ARCHIVE_OK); } diff --git a/lib/libarchive/archive_read_support_format_ar.c b/lib/libarchive/archive_read_support_format_ar.c index 175876e..7f3588c 100644 --- a/lib/libarchive/archive_read_support_format_ar.c +++ b/lib/libarchive/archive_read_support_format_ar.c @@ -135,7 +135,6 @@ static int archive_read_format_ar_bid(struct archive_read *a) { struct ar *ar; - ssize_t bytes_read; const void *h; if (a->archive.archive_format != 0 && @@ -149,8 +148,7 @@ archive_read_format_ar_bid(struct archive_read *a) * Verify the 8-byte file signature. * TODO: Do we need to check more than this? */ - bytes_read = (a->decompressor->read_ahead)(a, &h, 8); - if (bytes_read < 8) + if ((h = __archive_read_ahead(a, 8, NULL)) == NULL) return (-1); if (strncmp((const char*)h, "!<arch>\n", 8) == 0) { return (64); @@ -166,7 +164,7 @@ archive_read_format_ar_read_header(struct archive_read *a, struct ar *ar; uint64_t number; /* Used to hold parsed numbers before validation. */ ssize_t bytes_read; - size_t bsd_name_length, entry_size, s; + size_t bsd_name_length, entry_size; char *p, *st; const void *b; const char *h; @@ -179,18 +177,16 @@ archive_read_format_ar_read_header(struct archive_read *a, * We are now at the beginning of the archive, * so we need first consume the ar global header. */ - (a->decompressor->consume)(a, 8); + __archive_read_consume(a, 8); /* Set a default format code for now. */ a->archive.archive_format = ARCHIVE_FORMAT_AR; } /* Read the header for the next file entry. */ - bytes_read = (a->decompressor->read_ahead)(a, &b, 60); - if (bytes_read < 60) { + if ((b = __archive_read_ahead(a, 60, &bytes_read)) == NULL) /* Broken header. */ return (ARCHIVE_EOF); - } - (a->decompressor->consume)(a, 60); + __archive_read_consume(a, 60); h = (const char *)b; /* Verify the magic signature on the file header. */ @@ -296,16 +292,10 @@ archive_read_format_ar_read_header(struct archive_read *a, } ar->strtab = st; ar->strtab_size = entry_size; - for (s = entry_size; s > 0; s -= bytes_read) { - bytes_read = (a->decompressor->read_ahead)(a, &b, s); - if (bytes_read <= 0) - return (ARCHIVE_FATAL); - if (bytes_read > (ssize_t)s) - bytes_read = s; - memcpy(st, b, bytes_read); - st += bytes_read; - (a->decompressor->consume)(a, bytes_read); - } + if ((b = __archive_read_ahead(a, entry_size, NULL)) == NULL) + return (ARCHIVE_FATAL); + memcpy(st, b, entry_size); + __archive_read_consume(a, entry_size); /* All contents are consumed. */ ar->entry_bytes_remaining = 0; archive_entry_set_size(entry, ar->entry_bytes_remaining); @@ -365,15 +355,12 @@ archive_read_format_ar_read_header(struct archive_read *a, archive_entry_set_size(entry, ar->entry_bytes_remaining); /* Read the long name into memory. */ - bytes_read = (a->decompressor->read_ahead)(a, &b, bsd_name_length); - if (bytes_read <= 0) - return (ARCHIVE_FATAL); - if ((size_t)bytes_read < bsd_name_length) { + if ((b = __archive_read_ahead(a, bsd_name_length, NULL)) == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Truncated input file"); return (ARCHIVE_FATAL); } - (a->decompressor->consume)(a, bsd_name_length); + __archive_read_consume(a, bsd_name_length); /* Store it in the entry. */ p = (char *)malloc(bsd_name_length + 1); @@ -453,7 +440,7 @@ archive_read_format_ar_read_data(struct archive_read *a, ar = (struct ar *)(a->format->data); if (ar->entry_bytes_remaining > 0) { - bytes_read = (a->decompressor->read_ahead)(a, buff, 1); + *buff = __archive_read_ahead(a, 1, &bytes_read); if (bytes_read == 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Truncated ar archive"); @@ -467,16 +454,16 @@ archive_read_format_ar_read_data(struct archive_read *a, *offset = ar->entry_offset; ar->entry_offset += bytes_read; ar->entry_bytes_remaining -= bytes_read; - (a->decompressor->consume)(a, (size_t)bytes_read); + __archive_read_consume(a, (size_t)bytes_read); return (ARCHIVE_OK); } else { while (ar->entry_padding > 0) { - bytes_read = (a->decompressor->read_ahead)(a, buff, 1); + *buff = __archive_read_ahead(a, 1, &bytes_read); if (bytes_read <= 0) return (ARCHIVE_FATAL); if (bytes_read > ar->entry_padding) bytes_read = (ssize_t)ar->entry_padding; - (a->decompressor->consume)(a, (size_t)bytes_read); + __archive_read_consume(a, (size_t)bytes_read); ar->entry_padding -= bytes_read; } *buff = NULL; @@ -491,20 +478,11 @@ archive_read_format_ar_skip(struct archive_read *a) { off_t bytes_skipped; struct ar* ar; - int r = ARCHIVE_OK; - const void *b; /* Dummy variables */ - size_t s; - off_t o; ar = (struct ar *)(a->format->data); - if (a->decompressor->skip == NULL) { - while (r == ARCHIVE_OK) - r = archive_read_format_ar_read_data(a, &b, &s, &o); - return (r); - } - bytes_skipped = (a->decompressor->skip)(a, ar->entry_bytes_remaining + - ar->entry_padding); + bytes_skipped = __archive_read_skip(a, + ar->entry_bytes_remaining + ar->entry_padding); if (bytes_skipped < 0) return (ARCHIVE_FATAL); diff --git a/lib/libarchive/archive_read_support_format_cpio.c b/lib/libarchive/archive_read_support_format_cpio.c index 5cb5fe6..218371a 100644 --- a/lib/libarchive/archive_read_support_format_cpio.c +++ b/lib/libarchive/archive_read_support_format_cpio.c @@ -165,7 +165,6 @@ archive_read_support_format_cpio(struct archive *_a) static int archive_read_format_cpio_bid(struct archive_read *a) { - int bytes_read; const void *h; const unsigned char *p; struct cpio *cpio; @@ -173,11 +172,7 @@ archive_read_format_cpio_bid(struct archive_read *a) cpio = (struct cpio *)(a->format->data); - bytes_read = (a->decompressor->read_ahead)(a, &h, 6); - /* Convert error code into error return. */ - if (bytes_read < 0) - return ((int)bytes_read); - if (bytes_read < 6) + if ((h = __archive_read_ahead(a, 6, NULL)) == NULL) return (-1); p = (const unsigned char *)h; @@ -228,7 +223,6 @@ archive_read_format_cpio_read_header(struct archive_read *a, struct archive_entry *entry) { struct cpio *cpio; - size_t bytes; const void *h; size_t namelength; size_t name_pad; @@ -241,21 +235,20 @@ archive_read_format_cpio_read_header(struct archive_read *a, return (r); /* Read name from buffer. */ - bytes = (a->decompressor->read_ahead)(a, &h, namelength + name_pad); - if (bytes < namelength + name_pad) + h = __archive_read_ahead(a, namelength + name_pad, NULL); + if (h == NULL) return (ARCHIVE_FATAL); - (a->decompressor->consume)(a, namelength + name_pad); + __archive_read_consume(a, namelength + name_pad); archive_strncpy(&cpio->entry_name, (const char *)h, namelength); archive_entry_set_pathname(entry, cpio->entry_name.s); cpio->entry_offset = 0; /* If this is a symlink, read the link contents. */ if (archive_entry_filetype(entry) == AE_IFLNK) { - bytes = (a->decompressor->read_ahead)(a, &h, - cpio->entry_bytes_remaining); - if ((off_t)bytes < cpio->entry_bytes_remaining) + h = __archive_read_ahead(a, cpio->entry_bytes_remaining, NULL); + if (h == NULL) return (ARCHIVE_FATAL); - (a->decompressor->consume)(a, cpio->entry_bytes_remaining); + __archive_read_consume(a, cpio->entry_bytes_remaining); archive_strncpy(&cpio->entry_linkname, (const char *)h, cpio->entry_bytes_remaining); archive_entry_set_symlink(entry, cpio->entry_linkname.s); @@ -284,7 +277,7 @@ archive_read_format_cpio_read_data(struct archive_read *a, cpio = (struct cpio *)(a->format->data); if (cpio->entry_bytes_remaining > 0) { - bytes_read = (a->decompressor->read_ahead)(a, buff, 1); + *buff = __archive_read_ahead(a, 1, &bytes_read); if (bytes_read <= 0) return (ARCHIVE_FATAL); if (bytes_read > cpio->entry_bytes_remaining) @@ -293,16 +286,16 @@ archive_read_format_cpio_read_data(struct archive_read *a, *offset = cpio->entry_offset; cpio->entry_offset += bytes_read; cpio->entry_bytes_remaining -= bytes_read; - (a->decompressor->consume)(a, bytes_read); + __archive_read_consume(a, bytes_read); return (ARCHIVE_OK); } else { while (cpio->entry_padding > 0) { - bytes_read = (a->decompressor->read_ahead)(a, buff, 1); + *buff = __archive_read_ahead(a, 1, &bytes_read); if (bytes_read <= 0) return (ARCHIVE_FATAL); if (bytes_read > cpio->entry_padding) bytes_read = cpio->entry_padding; - (a->decompressor->consume)(a, bytes_read); + __archive_read_consume(a, bytes_read); cpio->entry_padding -= bytes_read; } *buff = NULL; @@ -339,8 +332,8 @@ find_newc_header(struct archive_read *a) size_t skip, bytes, skipped = 0; for (;;) { - bytes = (a->decompressor->read_ahead)(a, &h, 2048); - if (bytes < sizeof(struct cpio_newc_header)) + h = __archive_read_ahead(a, sizeof(struct cpio_newc_header), &bytes); + if (h == NULL) return (ARCHIVE_FATAL); p = h; q = p + bytes; @@ -362,7 +355,7 @@ find_newc_header(struct archive_read *a) if (memcmp("07070", p, 5) == 0 && is_hex(p, sizeof(struct cpio_newc_header))) { skip = p - (const char *)h; - (a->decompressor->consume)(a, skip); + __archive_read_consume(a, skip); skipped += skip; if (skipped > 0) { archive_set_error(&a->archive, @@ -385,7 +378,7 @@ find_newc_header(struct archive_read *a) } } skip = p - (const char *)h; - (a->decompressor->consume)(a, skip); + __archive_read_consume(a, skip); skipped += skip; } } @@ -396,7 +389,6 @@ header_newc(struct archive_read *a, struct cpio *cpio, { const void *h; const struct cpio_newc_header *header; - size_t bytes; int r; r = find_newc_header(a); @@ -404,10 +396,10 @@ header_newc(struct archive_read *a, struct cpio *cpio, return (r); /* Read fixed-size portion of header. */ - bytes = (a->decompressor->read_ahead)(a, &h, sizeof(struct cpio_newc_header)); - if (bytes < sizeof(struct cpio_newc_header)) + h = __archive_read_ahead(a, sizeof(struct cpio_newc_header), NULL); + if (h == NULL) return (ARCHIVE_FATAL); - (a->decompressor->consume)(a, sizeof(struct cpio_newc_header)); + __archive_read_consume(a, sizeof(struct cpio_newc_header)); /* Parse out hex fields. */ header = (const struct cpio_newc_header *)h; @@ -474,8 +466,8 @@ find_odc_header(struct archive_read *a) size_t skip, bytes, skipped = 0; for (;;) { - bytes = (a->decompressor->read_ahead)(a, &h, 512); - if (bytes < sizeof(struct cpio_odc_header)) + h = __archive_read_ahead(a, sizeof(struct cpio_odc_header), &bytes); + if (h == NULL) return (ARCHIVE_FATAL); p = h; q = p + bytes; @@ -495,7 +487,7 @@ find_odc_header(struct archive_read *a) if (memcmp("070707", p, 6) == 0 && is_octal(p, sizeof(struct cpio_odc_header))) { skip = p - (const char *)h; - (a->decompressor->consume)(a, skip); + __archive_read_consume(a, skip); skipped += skip; if (skipped > 0) { archive_set_error(&a->archive, @@ -518,7 +510,7 @@ find_odc_header(struct archive_read *a) } } skip = p - (const char *)h; - (a->decompressor->consume)(a, skip); + __archive_read_consume(a, skip); skipped += skip; } } @@ -530,7 +522,6 @@ header_odc(struct archive_read *a, struct cpio *cpio, const void *h; int r; const struct cpio_odc_header *header; - size_t bytes; a->archive.archive_format = ARCHIVE_FORMAT_CPIO_POSIX; a->archive.archive_format_name = "POSIX octet-oriented cpio"; @@ -541,10 +532,10 @@ header_odc(struct archive_read *a, struct cpio *cpio, return (r); /* Read fixed-size portion of header. */ - bytes = (a->decompressor->read_ahead)(a, &h, sizeof(struct cpio_odc_header)); - if (bytes < sizeof(struct cpio_odc_header)) + h = __archive_read_ahead(a, sizeof(struct cpio_odc_header), NULL); + if (h == NULL) return (ARCHIVE_FATAL); - (a->decompressor->consume)(a, sizeof(struct cpio_odc_header)); + __archive_read_consume(a, sizeof(struct cpio_odc_header)); /* Parse out octal fields. */ header = (const struct cpio_odc_header *)h; @@ -578,16 +569,15 @@ header_bin_le(struct archive_read *a, struct cpio *cpio, { const void *h; const struct cpio_bin_header *header; - size_t bytes; a->archive.archive_format = ARCHIVE_FORMAT_CPIO_BIN_LE; a->archive.archive_format_name = "cpio (little-endian binary)"; /* Read fixed-size portion of header. */ - bytes = (a->decompressor->read_ahead)(a, &h, sizeof(struct cpio_bin_header)); - if (bytes < sizeof(struct cpio_bin_header)) + h = __archive_read_ahead(a, sizeof(struct cpio_bin_header), NULL); + if (h == NULL) return (ARCHIVE_FATAL); - (a->decompressor->consume)(a, sizeof(struct cpio_bin_header)); + __archive_read_consume(a, sizeof(struct cpio_bin_header)); /* Parse out binary fields. */ header = (const struct cpio_bin_header *)h; @@ -615,17 +605,15 @@ header_bin_be(struct archive_read *a, struct cpio *cpio, { const void *h; const struct cpio_bin_header *header; - size_t bytes; a->archive.archive_format = ARCHIVE_FORMAT_CPIO_BIN_BE; a->archive.archive_format_name = "cpio (big-endian binary)"; /* Read fixed-size portion of header. */ - bytes = (a->decompressor->read_ahead)(a, &h, - sizeof(struct cpio_bin_header)); - if (bytes < sizeof(struct cpio_bin_header)) + h = __archive_read_ahead(a, sizeof(struct cpio_bin_header), NULL); + if (h == NULL) return (ARCHIVE_FATAL); - (a->decompressor->consume)(a, sizeof(struct cpio_bin_header)); + __archive_read_consume(a, sizeof(struct cpio_bin_header)); /* Parse out binary fields. */ header = (const struct cpio_bin_header *)h; diff --git a/lib/libarchive/archive_read_support_format_empty.c b/lib/libarchive/archive_read_support_format_empty.c index 976306c..5850320 100644 --- a/lib/libarchive/archive_read_support_format_empty.c +++ b/lib/libarchive/archive_read_support_format_empty.c @@ -57,11 +57,10 @@ archive_read_support_format_empty(struct archive *_a) static int archive_read_format_empty_bid(struct archive_read *a) { - int bytes_read; const void *h; - bytes_read = (a->decompressor->read_ahead)(a, &h, 1); - if (bytes_read > 0) + h = __archive_read_ahead(a, 1, NULL); + if (h != NULL) return (-1); return (1); } diff --git a/lib/libarchive/archive_read_support_format_iso9660.c b/lib/libarchive/archive_read_support_format_iso9660.c index 6e0da0e..0ec55aa 100644 --- a/lib/libarchive/archive_read_support_format_iso9660.c +++ b/lib/libarchive/archive_read_support_format_iso9660.c @@ -288,8 +288,8 @@ archive_read_format_iso9660_bid(struct archive_read *a) * 8 sectors of the volume descriptor table. Of course, * if the I/O layer gives us more, we'll take it. */ - bytes_read = (a->decompressor->read_ahead)(a, &h, 32768 + 8*2048); - if (bytes_read < 32768 + 8*2048) + h = __archive_read_ahead(a, 32768 + 8*2048, &bytes_read); + if (h == NULL) return (-1); p = (const unsigned char *)h; @@ -334,7 +334,6 @@ archive_read_format_iso9660_read_header(struct archive_read *a, { struct iso9660 *iso9660; struct file_info *file; - ssize_t bytes_read; int r; iso9660 = (struct iso9660 *)(a->format->data); @@ -407,20 +406,18 @@ archive_read_format_iso9660_read_header(struct archive_read *a, ssize_t step = iso9660->logical_block_size; if (step > iso9660->entry_bytes_remaining) step = iso9660->entry_bytes_remaining; - bytes_read = (a->decompressor->read_ahead)(a, &block, step); - if (bytes_read < step) { + block = __archive_read_ahead(a, step, NULL); + if (block == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Failed to read full block when scanning ISO9660 directory list"); release_file(iso9660, file); return (ARCHIVE_FATAL); } - if (bytes_read > step) - bytes_read = step; - (a->decompressor->consume)(a, bytes_read); - iso9660->current_position += bytes_read; - iso9660->entry_bytes_remaining -= bytes_read; + __archive_read_consume(a, step); + iso9660->current_position += step; + iso9660->entry_bytes_remaining -= step; for (p = (const unsigned char *)block; - *p != 0 && p < (const unsigned char *)block + bytes_read; + *p != 0 && p < (const unsigned char *)block + step; p += *p) { struct file_info *child; @@ -472,11 +469,11 @@ archive_read_format_iso9660_read_data(struct archive_read *a, return (ARCHIVE_EOF); } - bytes_read = (a->decompressor->read_ahead)(a, buff, 1); + *buff = __archive_read_ahead(a, 1, &bytes_read); if (bytes_read == 0) archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Truncated input file"); - if (bytes_read <= 0) + if (buff == NULL) return (ARCHIVE_FATAL); if (bytes_read > iso9660->entry_bytes_remaining) bytes_read = iso9660->entry_bytes_remaining; @@ -485,7 +482,7 @@ archive_read_format_iso9660_read_data(struct archive_read *a, iso9660->entry_sparse_offset += bytes_read; iso9660->entry_bytes_remaining -= bytes_read; iso9660->current_position += bytes_read; - (a->decompressor->consume)(a, bytes_read); + __archive_read_consume(a, bytes_read); return (ARCHIVE_OK); } @@ -926,7 +923,7 @@ fprintf(stderr, " *** Discarding CE data.\n"); if (iso9660->current_position < offset) { off_t step = offset - iso9660->current_position; off_t bytes_read; - bytes_read = (a->decompressor->skip)(a, step); + bytes_read = __archive_read_skip(a, step); if (bytes_read < 0) return (bytes_read); iso9660->current_position = offset; @@ -940,19 +937,18 @@ fprintf(stderr, " *** Discarding CE data.\n"); if (offset == file->ce_offset) { const void *p; ssize_t size = file->ce_size; - ssize_t bytes_read; const unsigned char *rr_start; file->ce_offset = 0; file->ce_size = 0; - bytes_read = (a->decompressor->read_ahead)(a, &p, size); - if (bytes_read > size) - bytes_read = size; + p = __archive_read_ahead(a, size, NULL); + if (p == NULL) + return (ARCHIVE_FATAL); rr_start = (const unsigned char *)p; parse_rockridge(iso9660, file, rr_start, - rr_start + bytes_read); - (a->decompressor->consume)(a, bytes_read); - iso9660->current_position += bytes_read; + rr_start + size); + __archive_read_consume(a, size); + iso9660->current_position += size; add_entry(iso9660, file); } } diff --git a/lib/libarchive/archive_read_support_format_mtree.c b/lib/libarchive/archive_read_support_format_mtree.c index a94e488..7617d04 100644 --- a/lib/libarchive/archive_read_support_format_mtree.c +++ b/lib/libarchive/archive_read_support_format_mtree.c @@ -187,32 +187,17 @@ cleanup(struct archive_read *a) static int mtree_bid(struct archive_read *a) { - struct mtree *mtree; - ssize_t bytes_read; - const void *h; const char *signature = "#mtree"; const char *p; - int bid; - - mtree = (struct mtree *)(a->format->data); /* Now let's look at the actual header and see if it matches. */ - bytes_read = (a->decompressor->read_ahead)(a, &h, strlen(signature)); - - if (bytes_read <= 0) - return (bytes_read); - - p = h; - bid = 0; - while (bytes_read > 0 && *signature != '\0') { - if (*p != *signature) - return (bid = 0); - bid += 8; - p++; - signature++; - bytes_read--; - } - return (bid); + p = __archive_read_ahead(a, strlen(signature), NULL); + if (p == NULL) + return (-1); + + if (strncmp(p, signature, strlen(signature)) == 0) + return (8 * strlen(signature)); + return (0); } /* @@ -1239,8 +1224,8 @@ readline(struct archive_read *a, struct mtree *mtree, char **start, ssize_t limi /* Accumulate line in a line buffer. */ for (;;) { /* Read some more. */ - bytes_read = (a->decompressor->read_ahead)(a, &t, 1); - if (bytes_read == 0) + t = __archive_read_ahead(a, 1, &bytes_read); + if (t == NULL) return (0); if (bytes_read < 0) return (ARCHIVE_FATAL); @@ -1263,7 +1248,7 @@ readline(struct archive_read *a, struct mtree *mtree, char **start, ssize_t limi return (ARCHIVE_FATAL); } memcpy(mtree->line.s + total_size, t, bytes_read); - (a->decompressor->consume)(a, bytes_read); + __archive_read_consume(a, bytes_read); total_size += bytes_read; /* Null terminate. */ mtree->line.s[total_size] = '\0'; diff --git a/lib/libarchive/archive_read_support_format_tar.c b/lib/libarchive/archive_read_support_format_tar.c index e624a90..ae0c528 100644 --- a/lib/libarchive/archive_read_support_format_tar.c +++ b/lib/libarchive/archive_read_support_format_tar.c @@ -294,21 +294,15 @@ static int archive_read_format_tar_bid(struct archive_read *a) { int bid; - ssize_t bytes_read; const void *h; const struct archive_entry_header_ustar *header; bid = 0; /* Now let's look at the actual header and see if it matches. */ - if (a->decompressor->read_ahead != NULL) - bytes_read = (a->decompressor->read_ahead)(a, &h, 512); - else - bytes_read = 0; /* Empty file. */ - if (bytes_read < 0) - return (ARCHIVE_FATAL); - if (bytes_read < 512) - return (0); + h = __archive_read_ahead(a, 512, NULL); + if (h == NULL) + return (-1); /* If it's an end-of-archive mark, we can handle it. */ if ((*(const char *)h) == 0 @@ -479,7 +473,7 @@ archive_read_format_tar_read_data(struct archive_read *a, /* If we're at end of file, return EOF. */ if (tar->sparse_list == NULL || tar->entry_bytes_remaining == 0) { - if ((a->decompressor->skip)(a, tar->entry_padding) < 0) + if (__archive_read_skip(a, tar->entry_padding) < 0) return (ARCHIVE_FATAL); tar->entry_padding = 0; *buff = NULL; @@ -488,14 +482,14 @@ archive_read_format_tar_read_data(struct archive_read *a, return (ARCHIVE_EOF); } - bytes_read = (a->decompressor->read_ahead)(a, buff, 1); - if (bytes_read == 0) { + *buff = __archive_read_ahead(a, 1, &bytes_read); + if (bytes_read < 0) + return (ARCHIVE_FATAL); + if (*buff == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Truncated tar archive"); return (ARCHIVE_FATAL); } - if (bytes_read < 0) - return (ARCHIVE_FATAL); if (bytes_read > tar->entry_bytes_remaining) bytes_read = tar->entry_bytes_remaining; /* Don't read more than is available in the @@ -507,7 +501,7 @@ archive_read_format_tar_read_data(struct archive_read *a, tar->sparse_list->remaining -= bytes_read; tar->sparse_list->offset += bytes_read; tar->entry_bytes_remaining -= bytes_read; - (a->decompressor->consume)(a, bytes_read); + __archive_read_consume(a, bytes_read); return (ARCHIVE_OK); } @@ -524,8 +518,8 @@ archive_read_format_tar_skip(struct archive_read *a) * length requested or fail, so we can rely upon the entire entry * plus padding being skipped. */ - bytes_skipped = (a->decompressor->skip)(a, tar->entry_bytes_remaining + - tar->entry_padding); + bytes_skipped = __archive_read_skip(a, + tar->entry_bytes_remaining + tar->entry_padding); if (bytes_skipped < 0) return (ARCHIVE_FATAL); @@ -552,31 +546,35 @@ tar_read_header(struct archive_read *a, struct tar *tar, const struct archive_entry_header_ustar *header; /* Read 512-byte header record */ - bytes = (a->decompressor->read_ahead)(a, &h, 512); + h = __archive_read_ahead(a, 512, &bytes); if (bytes < 0) return (bytes); - if (bytes == 0) { - /* - * An archive that just ends without a proper - * end-of-archive marker. Yes, there are tar programs - * that do this; hold our nose and accept it. - */ - return (ARCHIVE_EOF); - } - if (bytes < 512) { + if (bytes < 512) { /* Short read or EOF. */ + /* Try requesting just one byte and see what happens. */ + h = __archive_read_ahead(a, 1, &bytes); + if (bytes == 0) { + /* + * The archive ends at a 512-byte boundary but + * without a proper end-of-archive marker. + * Yes, there are tar writers that do this; + * hold our nose and accept it. + */ + return (ARCHIVE_EOF); + } + /* Archive ends with a partial block; this is bad. */ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated tar archive"); return (ARCHIVE_FATAL); } - (a->decompressor->consume)(a, 512); + __archive_read_consume(a, 512); /* Check for end-of-archive mark. */ if (((*(const char *)h)==0) && archive_block_is_null((const unsigned char *)h)) { /* Try to consume a second all-null record, as well. */ - bytes = (a->decompressor->read_ahead)(a, &h, 512); - if (bytes > 0) - (a->decompressor->consume)(a, bytes); + h = __archive_read_ahead(a, 512, NULL); + if (h != NULL) + __archive_read_consume(a, 512); archive_set_error(&a->archive, 0, NULL); if (a->archive.archive_format_name == NULL) { a->archive.archive_format = ARCHIVE_FORMAT_TAR; @@ -837,10 +835,8 @@ read_body_to_string(struct archive_read *a, struct tar *tar, struct archive_string *as, const void *h) { off_t size, padded_size; - ssize_t bytes_read, bytes_to_copy; const struct archive_entry_header_ustar *header; const void *src; - char *dest; (void)tar; /* UNUSED */ header = (const struct archive_entry_header_ustar *)h; @@ -858,27 +854,14 @@ read_body_to_string(struct archive_read *a, struct tar *tar, return (ARCHIVE_FATAL); } - /* Read the body into the string. */ + /* Read the body into the string. */ padded_size = (size + 511) & ~ 511; - dest = as->s; - while (padded_size > 0) { - bytes_read = (a->decompressor->read_ahead)(a, &src, padded_size); - if (bytes_read == 0) - return (ARCHIVE_EOF); - if (bytes_read < 0) - return (ARCHIVE_FATAL); - if (bytes_read > padded_size) - bytes_read = padded_size; - (a->decompressor->consume)(a, bytes_read); - bytes_to_copy = bytes_read; - if ((off_t)bytes_to_copy > size) - bytes_to_copy = (ssize_t)size; - memcpy(dest, src, bytes_to_copy); - dest += bytes_to_copy; - size -= bytes_to_copy; - padded_size -= bytes_read; - } - *dest = '\0'; + src = __archive_read_ahead(a, padded_size, NULL); + if (src == NULL) + return (ARCHIVE_FATAL); + memcpy(as->s, src, size); + __archive_read_consume(a, padded_size); + as->s[size] = '\0'; return (ARCHIVE_OK); } @@ -1766,7 +1749,7 @@ gnu_sparse_old_read(struct archive_read *a, struct tar *tar, return (ARCHIVE_OK); do { - bytes_read = (a->decompressor->read_ahead)(a, &data, 512); + data = __archive_read_ahead(a, 512, &bytes_read); if (bytes_read < 0) return (ARCHIVE_FATAL); if (bytes_read < 512) { @@ -1775,7 +1758,7 @@ gnu_sparse_old_read(struct archive_read *a, struct tar *tar, "detected while reading sparse file data"); return (ARCHIVE_FATAL); } - (a->decompressor->consume)(a, 512); + __archive_read_consume(a, 512); ext = (const struct extended *)data; gnu_sparse_old_parse(tar, ext->sparse, 21); } while (ext->isextended[0] != 0); @@ -1953,7 +1936,7 @@ gnu_sparse_10_read(struct archive_read *a, struct tar *tar) /* Skip rest of block... */ bytes_read = tar->entry_bytes_remaining - remaining; to_skip = 0x1ff & -bytes_read; - if (to_skip != (a->decompressor->skip)(a, to_skip)) + if (to_skip != __archive_read_skip(a, to_skip)) return (ARCHIVE_FATAL); return (bytes_read + to_skip); } @@ -2108,7 +2091,7 @@ readline(struct archive_read *a, struct tar *tar, const char **start, const char *s; void *p; - bytes_read = (a->decompressor->read_ahead)(a, &t, 1); + t = __archive_read_ahead(a, 1, &bytes_read); if (bytes_read <= 0) return (ARCHIVE_FATAL); s = t; /* Start of line? */ @@ -2122,7 +2105,7 @@ readline(struct archive_read *a, struct tar *tar, const char **start, "Line too long"); return (ARCHIVE_FATAL); } - (a->decompressor->consume)(a, bytes_read); + __archive_read_consume(a, bytes_read); *start = s; return (bytes_read); } @@ -2140,7 +2123,7 @@ readline(struct archive_read *a, struct tar *tar, const char **start, return (ARCHIVE_FATAL); } memcpy(tar->line.s + total_size, t, bytes_read); - (a->decompressor->consume)(a, bytes_read); + __archive_read_consume(a, bytes_read); total_size += bytes_read; /* If we found '\n', clean up and return. */ if (p != NULL) { @@ -2148,7 +2131,7 @@ readline(struct archive_read *a, struct tar *tar, const char **start, return (total_size); } /* Read some more. */ - bytes_read = (a->decompressor->read_ahead)(a, &t, 1); + t = __archive_read_ahead(a, 1, &bytes_read); if (bytes_read <= 0) return (ARCHIVE_FATAL); s = t; /* Start of line? */ diff --git a/lib/libarchive/archive_read_support_format_zip.c b/lib/libarchive/archive_read_support_format_zip.c index 575cd33..a86c533 100644 --- a/lib/libarchive/archive_read_support_format_zip.c +++ b/lib/libarchive/archive_read_support_format_zip.c @@ -36,6 +36,10 @@ __FBSDID("$FreeBSD$"); #include <time.h> #ifdef HAVE_ZLIB_H #include <zlib.h> +#else +/* Hmmm... This is necessary, but means that we can't correctly extract + * even uncompressed entries on platforms that lack zlib. */ +#define crc32(crc, buf, len) (unsigned long)0 #endif #include "archive.h" @@ -167,9 +171,9 @@ archive_read_format_zip_bid(struct archive_read *a) { const char *p; const void *buff; - size_t bytes_avail; + size_t bytes_avail, offset; - if ((p = __archive_read_ahead(a, 4)) == NULL) + if ((p = __archive_read_ahead(a, 4, NULL)) == NULL) return (-1); /* @@ -189,42 +193,37 @@ archive_read_format_zip_bid(struct archive_read *a) /* * Attempt to handle self-extracting archives * by noting a PE header and searching forward - * up to 64k for a 'PK\003\004' marker. + * up to 128k for a 'PK\003\004' marker. */ if (p[0] == 'M' && p[1] == 'Z') { /* - * TODO: Additional checks that this really is a PE - * file before we invoke the 128k lookahead below. - * No point in allocating a bigger lookahead buffer - * if we don't need to. - */ - /* - * TODO: Of course, the compression layer lookahead - * buffers aren't dynamically sized yet; they should be. - */ - bytes_avail = (a->decompressor->read_ahead)(a, &buff, 128*1024); - p = (const char *)buff; - - /* - * TODO: Optimize by jumping forward based on values - * in the PE header. Note that we don't need to be - * exact, but we mustn't skip too far. The search - * below will compensate if we undershoot. Skipping - * will also reduce the chance of false positives - * (which is not really all that high to begin with, - * so maybe skipping isn't really necessary). + * TODO: Optimize by initializing 'offset' to an + * estimate of the likely start of the archive data + * based on values in the PE header. Note that we + * don't need to be exact, but we mustn't skip too + * far. The search below will compensate if we + * undershoot. */ - - while (p < bytes_avail + (const char *)buff) { - if (p[0] == 'P' && p[1] == 'K' /* "PK" signature */ - && p[2] == 3 && p[3] == 4 /* File entry */ - && p[8] == 8 /* compression == deflate */ - && p[9] == 0 /* High byte of compression */ - ) - { - return (30); + offset = 0; + while (offset < 124000) { + /* Get 4k of data beyond where we stopped. */ + buff = __archive_read_ahead(a, offset + 4096, + &bytes_avail); + if (bytes_avail < offset + 1) + break; + p = (const char *)buff + offset; + while (p + 9 < (const char *)buff + bytes_avail) { + if (p[0] == 'P' && p[1] == 'K' /* signature */ + && p[2] == 3 && p[3] == 4 /* File entry */ + && p[8] == 8 /* compression == deflate */ + && p[9] == 0 /* High byte of compression */ + ) + { + return (30); + } + ++p; } - ++p; + offset = p - (const char *)buff; } } @@ -251,7 +250,7 @@ skip_sfx(struct archive_read *a) * reduce the chance of a false positive. */ for (;;) { - bytes = (a->decompressor->read_ahead)(a, &h, 4096); + h = __archive_read_ahead(a, 4, &bytes); if (bytes < 4) return (ARCHIVE_FATAL); p = h; @@ -267,7 +266,7 @@ skip_sfx(struct archive_read *a) /* TODO: Additional verification here. */ if (memcmp("PK\003\004", p, 4) == 0) { skip = p - (const char *)h; - (a->decompressor->consume)(a, skip); + __archive_read_consume(a, skip); return (ARCHIVE_OK); } p += 4; @@ -279,7 +278,7 @@ skip_sfx(struct archive_read *a) } } skip = p - (const char *)h; - (a->decompressor->consume)(a, skip); + __archive_read_consume(a, skip); } } @@ -303,7 +302,7 @@ archive_read_format_zip_read_header(struct archive_read *a, zip->entry_uncompressed_bytes_read = 0; zip->entry_compressed_bytes_read = 0; zip->entry_crc32 = crc32(0, NULL, 0); - if ((h = __archive_read_ahead(a, 4)) == NULL) + if ((h = __archive_read_ahead(a, 4, NULL)) == NULL) return (ARCHIVE_FATAL); signature = (const char *)h; @@ -312,7 +311,7 @@ archive_read_format_zip_read_header(struct archive_read *a, r = skip_sfx(a); if (r < ARCHIVE_WARN) return (r); - if ((h = __archive_read_ahead(a, 4)) == NULL) + if ((h = __archive_read_ahead(a, 4, NULL)) == NULL) return (ARCHIVE_FATAL); signature = (const char *)h; } @@ -329,8 +328,8 @@ archive_read_format_zip_read_header(struct archive_read *a, * skip the PK00; the first real file header should follow. */ if (signature[2] == '0' && signature[3] == '0') { - (a->decompressor->consume)(a, 4); - if ((h = __archive_read_ahead(a, 4)) == NULL) + __archive_read_consume(a, 4); + if ((h = __archive_read_ahead(a, 4, NULL)) == NULL) return (ARCHIVE_FATAL); signature = (const char *)h; if (signature[0] != 'P' || signature[1] != 'K') { @@ -381,7 +380,7 @@ zip_read_file_header(struct archive_read *a, struct archive_entry *entry, const struct zip_file_header *p; const void *h; - if ((p = __archive_read_ahead(a, sizeof *p)) == NULL) { + if ((p = __archive_read_ahead(a, sizeof *p, NULL)) == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated ZIP file header"); return (ARCHIVE_FATAL); @@ -408,11 +407,11 @@ zip_read_file_header(struct archive_read *a, struct archive_entry *entry, zip->uncompressed_size = archive_le32dec(p->uncompressed_size); zip->compressed_size = archive_le32dec(p->compressed_size); - (a->decompressor->consume)(a, sizeof(struct zip_file_header)); + __archive_read_consume(a, sizeof(struct zip_file_header)); /* Read the filename. */ - if ((h = __archive_read_ahead(a, zip->filename_length)) == NULL) { + if ((h = __archive_read_ahead(a, zip->filename_length, NULL)) == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated ZIP file header"); return (ARCHIVE_FATAL); @@ -420,7 +419,7 @@ zip_read_file_header(struct archive_read *a, struct archive_entry *entry, if (archive_string_ensure(&zip->pathname, zip->filename_length) == NULL) __archive_errx(1, "Out of memory"); archive_strncpy(&zip->pathname, h, zip->filename_length); - (a->decompressor->consume)(a, zip->filename_length); + __archive_read_consume(a, zip->filename_length); archive_entry_set_pathname(entry, zip->pathname.s); if (zip->pathname.s[archive_strlen(&zip->pathname) - 1] == '/') @@ -429,13 +428,13 @@ zip_read_file_header(struct archive_read *a, struct archive_entry *entry, zip->mode = AE_IFREG | 0777; /* Read the extra data. */ - if ((h = __archive_read_ahead(a, zip->extra_length)) == NULL) { + if ((h = __archive_read_ahead(a, zip->extra_length, NULL)) == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated ZIP file header"); return (ARCHIVE_FATAL); } process_extra(h, zip); - (a->decompressor->consume)(a, zip->extra_length); + __archive_read_consume(a, zip->extra_length); /* Populate some additional entry fields: */ archive_entry_set_mode(entry, zip->mode); @@ -504,7 +503,7 @@ archive_read_format_zip_read_data(struct archive_read *a, if (zip->flags & ZIP_LENGTH_AT_END) { const char *p; - if ((p = __archive_read_ahead(a, 16)) == NULL) { + if ((p = __archive_read_ahead(a, 16, NULL)) == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated ZIP end-of-file record"); @@ -513,7 +512,7 @@ archive_read_format_zip_read_data(struct archive_read *a, zip->crc32 = archive_le32dec(p + 4); zip->compressed_size = archive_le32dec(p + 8); zip->uncompressed_size = archive_le32dec(p + 12); - (a->decompressor->consume)(a, 16); + __archive_read_consume(a, 16); } /* Check file size, CRC against these values. */ @@ -622,7 +621,7 @@ zip_read_data_none(struct archive_read *a, const void **buff, * available bytes; asking for more than that forces the * decompressor to combine reads by copying data. */ - bytes_avail = (a->decompressor->read_ahead)(a, buff, 1); + *buff = __archive_read_ahead(a, 1, &bytes_avail); if (bytes_avail <= 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated ZIP file data"); @@ -630,7 +629,7 @@ zip_read_data_none(struct archive_read *a, const void **buff, } if (bytes_avail > zip->entry_bytes_remaining) bytes_avail = zip->entry_bytes_remaining; - (a->decompressor->consume)(a, bytes_avail); + __archive_read_consume(a, bytes_avail); *size = bytes_avail; *offset = zip->entry_offset; zip->entry_offset += *size; @@ -688,7 +687,7 @@ zip_read_data_deflate(struct archive_read *a, const void **buff, * available bytes; asking for more than that forces the * decompressor to combine reads by copying data. */ - bytes_avail = (a->decompressor->read_ahead)(a, &compressed_buff, 1); + compressed_buff = __archive_read_ahead(a, 1, &bytes_avail); if (bytes_avail <= 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated ZIP file body"); @@ -727,7 +726,7 @@ zip_read_data_deflate(struct archive_read *a, const void **buff, /* Consume as much as the compressor actually used. */ bytes_avail = zip->stream.total_in; - (a->decompressor->consume)(a, bytes_avail); + __archive_read_consume(a, bytes_avail); zip->entry_bytes_remaining -= bytes_avail; zip->entry_compressed_bytes_read += bytes_avail; @@ -784,7 +783,7 @@ archive_read_format_zip_read_data_skip(struct archive_read *a) * If the length is at the beginning, we can skip the * compressed data much more quickly. */ - bytes_skipped = (a->decompressor->skip)(a, zip->entry_bytes_remaining); + bytes_skipped = __archive_read_skip(a, zip->entry_bytes_remaining); if (bytes_skipped < 0) return (ARCHIVE_FATAL); |