diff options
Diffstat (limited to 'contrib/libarchive/libarchive/archive_read.c')
-rw-r--r-- | contrib/libarchive/libarchive/archive_read.c | 923 |
1 files changed, 508 insertions, 415 deletions
diff --git a/contrib/libarchive/libarchive/archive_read.c b/contrib/libarchive/libarchive/archive_read.c index 7873d45..cd5ac20 100644 --- a/contrib/libarchive/libarchive/archive_read.c +++ b/contrib/libarchive/libarchive/archive_read.c @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2003-2007 Tim Kientzle + * Copyright (c) 2003-2011 Tim Kientzle * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -55,12 +55,24 @@ __FBSDID("$FreeBSD$"); #define minimum(a, b) (a < b ? a : b) -static int build_stream(struct archive_read *); +static int choose_filters(struct archive_read *); static int choose_format(struct archive_read *); -static int cleanup_filters(struct archive_read *); +static void free_filters(struct archive_read *); +static int close_filters(struct archive_read *); static struct archive_vtable *archive_read_vtable(void); +static int64_t _archive_filter_bytes(struct archive *, int); +static int _archive_filter_code(struct archive *, int); +static const char *_archive_filter_name(struct archive *, int); +static int _archive_filter_count(struct archive *); static int _archive_read_close(struct archive *); +static int _archive_read_data_block(struct archive *, + const void **, size_t *, int64_t *); static int _archive_read_free(struct archive *); +static int _archive_read_next_header(struct archive *, + struct archive_entry **); +static int _archive_read_next_header2(struct archive *, + struct archive_entry *); +static int64_t advance_file_pointer(struct archive_read_filter *, int64_t); static struct archive_vtable * archive_read_vtable(void) @@ -69,8 +81,16 @@ archive_read_vtable(void) static int inited = 0; if (!inited) { + av.archive_filter_bytes = _archive_filter_bytes; + av.archive_filter_code = _archive_filter_code; + av.archive_filter_name = _archive_filter_name; + av.archive_filter_count = _archive_filter_count; + av.archive_read_data_block = _archive_read_data_block; + av.archive_read_next_header = _archive_read_next_header; + av.archive_read_next_header2 = _archive_read_next_header2; av.archive_free = _archive_read_free; av.archive_close = _archive_read_close; + inited = 1; } return (&av); } @@ -90,7 +110,7 @@ archive_read_new(void) a->archive.magic = ARCHIVE_READ_MAGIC; a->archive.state = ARCHIVE_STATE_NEW; - a->entry = archive_entry_new(); + a->entry = archive_entry_new2(&a->archive); a->archive.vtable = archive_read_vtable(); return (&a->archive); @@ -100,143 +120,51 @@ archive_read_new(void) * Record the do-not-extract-to file. This belongs in archive_read_extract.c. */ void -archive_read_extract_set_skip_file(struct archive *_a, dev_t d, ino_t i) +archive_read_extract_set_skip_file(struct archive *_a, int64_t d, int64_t i) { struct archive_read *a = (struct archive_read *)_a; - __archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_ANY, - "archive_read_extract_set_skip_file"); + + if (ARCHIVE_OK != __archive_check_magic(_a, ARCHIVE_READ_MAGIC, + ARCHIVE_STATE_ANY, "archive_read_extract_set_skip_file")) + return; + a->skip_file_set = 1; a->skip_file_dev = d; a->skip_file_ino = i; } /* - * Set read options for the format. + * Open the archive */ int -archive_read_set_format_options(struct archive *_a, const char *s) +archive_read_open(struct archive *a, void *client_data, + archive_open_callback *client_opener, archive_read_callback *client_reader, + archive_close_callback *client_closer) { - struct archive_read *a; - struct archive_format_descriptor *format; - char key[64], val[64]; - char *valp; - size_t i; - int len, r; - - __archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, - "archive_read_set_format_options"); - - if (s == NULL || *s == '\0') - return (ARCHIVE_OK); - a = (struct archive_read *)_a; - __archive_check_magic(&a->archive, ARCHIVE_READ_MAGIC, - ARCHIVE_STATE_NEW, "archive_read_set_format_options"); - len = 0; - for (i = 0; i < sizeof(a->formats)/sizeof(a->formats[0]); i++) { - format = &a->formats[i]; - if (format == NULL || format->options == NULL || - format->name == NULL) - /* This format does not support option. */ - continue; - - while ((len = __archive_parse_options(s, format->name, - sizeof(key), key, sizeof(val), val)) > 0) { - valp = val[0] == '\0' ? NULL : val; - a->format = format; - r = format->options(a, key, valp); - a->format = NULL; - if (r == ARCHIVE_FATAL) - return (r); - s += len; - } - } - if (len < 0) { - archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "Illegal format options."); - return (ARCHIVE_WARN); - } - return (ARCHIVE_OK); + /* Old archive_read_open() is just a thin shell around + * archive_read_open1. */ + archive_read_set_open_callback(a, client_opener); + archive_read_set_read_callback(a, client_reader); + archive_read_set_close_callback(a, client_closer); + archive_read_set_callback_data(a, client_data); + return archive_read_open1(a); } -/* - * Set read options for the filter. - */ -int -archive_read_set_filter_options(struct archive *_a, const char *s) -{ - struct archive_read *a; - struct archive_read_filter *filter; - struct archive_read_filter_bidder *bidder; - char key[64], val[64]; - int len, r; - - __archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, - "archive_read_set_filter_options"); - - if (s == NULL || *s == '\0') - return (ARCHIVE_OK); - a = (struct archive_read *)_a; - __archive_check_magic(&a->archive, ARCHIVE_READ_MAGIC, - ARCHIVE_STATE_NEW, "archive_read_set_filter_options"); - len = 0; - for (filter = a->filter; filter != NULL; filter = filter->upstream) { - bidder = filter->bidder; - if (bidder == NULL) - continue; - if (bidder->options == NULL) - /* This bidder does not support option */ - continue; - while ((len = __archive_parse_options(s, filter->name, - sizeof(key), key, sizeof(val), val)) > 0) { - if (val[0] == '\0') - r = bidder->options(bidder, key, NULL); - else - r = bidder->options(bidder, key, val); - if (r == ARCHIVE_FATAL) - return (r); - s += len; - } - } - if (len < 0) { - archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "Illegal format options."); - return (ARCHIVE_WARN); - } - return (ARCHIVE_OK); -} -/* - * Set read options for the format and the filter. - */ int -archive_read_set_options(struct archive *_a, const char *s) -{ - int r; - - __archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, - "archive_read_set_options"); - archive_clear_error(_a); - - r = archive_read_set_format_options(_a, s); - if (r != ARCHIVE_OK) - return (r); - r = archive_read_set_filter_options(_a, s); - if (r != ARCHIVE_OK) - return (r); - return (ARCHIVE_OK); -} - -/* - * Open the archive - */ -int -archive_read_open(struct archive *a, void *client_data, - archive_open_callback *client_opener, archive_read_callback *client_reader, +archive_read_open2(struct archive *a, void *client_data, + archive_open_callback *client_opener, + archive_read_callback *client_reader, + archive_skip_callback *client_skipper, archive_close_callback *client_closer) { - /* Old archive_read_open() is just a thin shell around - * archive_read_open2. */ - return archive_read_open2(a, client_data, client_opener, - client_reader, NULL, client_closer); + /* Old archive_read_open2() is just a thin shell around + * archive_read_open1. */ + archive_read_set_callback_data(a, client_data); + archive_read_set_open_callback(a, client_opener); + archive_read_set_read_callback(a, client_reader); + archive_read_set_skip_callback(a, client_skipper); + archive_read_set_close_callback(a, client_closer); + return archive_read_open1(a); } static ssize_t @@ -245,33 +173,70 @@ client_read_proxy(struct archive_read_filter *self, const void **buff) ssize_t r; r = (self->archive->client.reader)(&self->archive->archive, self->data, buff); - self->archive->archive.raw_position += r; return (r); } static int64_t client_skip_proxy(struct archive_read_filter *self, int64_t request) { - int64_t ask, get, total; - /* Limit our maximum seek request to 1GB on platforms - * with 32-bit off_t (such as Windows). */ - int64_t skip_limit = ((int64_t)1) << (sizeof(off_t) * 8 - 2); - - if (self->archive->client.skipper == NULL) - return (0); - total = 0; - for (;;) { - ask = request; - if (ask > skip_limit) - ask = skip_limit; - get = (self->archive->client.skipper)(&self->archive->archive, - self->data, ask); - if (get == 0) - return (total); - request -= get; - self->archive->archive.raw_position += get; - total += get; + if (request < 0) + __archive_errx(1, "Negative skip requested."); + if (request == 0) + return 0; + + if (self->archive->client.skipper != NULL) { + /* Seek requests over 1GiB are broken down into + * multiple seeks. This avoids overflows when the + * requests get passed through 32-bit arguments. */ + int64_t skip_limit = (int64_t)1 << 30; + int64_t total = 0; + for (;;) { + int64_t get, ask = request; + if (ask > skip_limit) + ask = skip_limit; + get = (self->archive->client.skipper)(&self->archive->archive, + self->data, ask); + if (get == 0) + return (total); + request -= get; + total += get; + } + return total; + } else if (self->archive->client.seeker != NULL + && request > 64 * 1024) { + /* If the client provided a seeker but not a skipper, + * we can use the seeker to skip forward. + * + * Note: This isn't always a good idea. The client + * skipper is allowed to skip by less than requested + * if it needs to maintain block alignment. The + * seeker is not allowed to play such games, so using + * the seeker here may be a performance loss compared + * to just reading and discarding. That's why we + * only do this for skips of over 64k. + */ + int64_t before = self->position; + int64_t after = (self->archive->client.seeker)(&self->archive->archive, + self->data, request, SEEK_CUR); + if (after != before + request) + return ARCHIVE_FATAL; + return after - before; } + return 0; +} + +static int64_t +client_seek_proxy(struct archive_read_filter *self, int64_t offset, int whence) +{ + /* DO NOT use the skipper here! If we transparently handled + * forward seek here by using the skipper, that will break + * other libarchive code that assumes a successful forward + * seek means it can also seek backwards. + */ + if (self->archive->client.seeker == NULL) + return (ARCHIVE_FAILED); + return (self->archive->client.seeker)(&self->archive->archive, + self->data, offset, whence); } static int @@ -282,65 +247,134 @@ client_close_proxy(struct archive_read_filter *self) if (self->archive->client.closer != NULL) r = (self->archive->client.closer)((struct archive *)self->archive, self->data); - self->data = NULL; return (r); } +int +archive_read_set_open_callback(struct archive *_a, + archive_open_callback *client_opener) +{ + struct archive_read *a = (struct archive_read *)_a; + archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, + "archive_read_set_open_callback"); + a->client.opener = client_opener; + return ARCHIVE_OK; +} int -archive_read_open2(struct archive *_a, void *client_data, - archive_open_callback *client_opener, - archive_read_callback *client_reader, - archive_skip_callback *client_skipper, +archive_read_set_read_callback(struct archive *_a, + archive_read_callback *client_reader) +{ + struct archive_read *a = (struct archive_read *)_a; + archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, + "archive_read_set_read_callback"); + a->client.reader = client_reader; + return ARCHIVE_OK; +} + +int +archive_read_set_skip_callback(struct archive *_a, + archive_skip_callback *client_skipper) +{ + struct archive_read *a = (struct archive_read *)_a; + archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, + "archive_read_set_skip_callback"); + a->client.skipper = client_skipper; + return ARCHIVE_OK; +} + +int +archive_read_set_seek_callback(struct archive *_a, + archive_seek_callback *client_seeker) +{ + struct archive_read *a = (struct archive_read *)_a; + archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, + "archive_read_set_seek_callback"); + a->client.seeker = client_seeker; + return ARCHIVE_OK; +} + +int +archive_read_set_close_callback(struct archive *_a, archive_close_callback *client_closer) { struct archive_read *a = (struct archive_read *)_a; + archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, + "archive_read_set_close_callback"); + a->client.closer = client_closer; + return ARCHIVE_OK; +} + +int +archive_read_set_callback_data(struct archive *_a, void *client_data) +{ + struct archive_read *a = (struct archive_read *)_a; + archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, + "archive_read_set_callback_data"); + a->client.data = client_data; + return ARCHIVE_OK; +} + +int +archive_read_open1(struct archive *_a) +{ + struct archive_read *a = (struct archive_read *)_a; struct archive_read_filter *filter; - int e; + int slot, e; - __archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, + archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, "archive_read_open"); archive_clear_error(&a->archive); - if (client_reader == NULL) - __archive_errx(1, + if (a->client.reader == NULL) { + archive_set_error(&a->archive, EINVAL, "No reader function provided to archive_read_open"); + a->archive.state = ARCHIVE_STATE_FATAL; + return (ARCHIVE_FATAL); + } /* Open data source. */ - if (client_opener != NULL) { - e =(client_opener)(&a->archive, client_data); + if (a->client.opener != NULL) { + e =(a->client.opener)(&a->archive, a->client.data); if (e != 0) { /* If the open failed, call the closer to clean up. */ - if (client_closer) - (client_closer)(&a->archive, client_data); + if (a->client.closer) + (a->client.closer)(&a->archive, a->client.data); return (e); } } - /* Save the client functions and mock up the initial source. */ - a->client.reader = client_reader; - a->client.skipper = client_skipper; - a->client.closer = client_closer; - filter = calloc(1, sizeof(*filter)); if (filter == NULL) return (ARCHIVE_FATAL); filter->bidder = NULL; filter->upstream = NULL; filter->archive = a; - filter->data = client_data; + filter->data = a->client.data; filter->read = client_read_proxy; filter->skip = client_skip_proxy; + filter->seek = client_seek_proxy; filter->close = client_close_proxy; filter->name = "none"; filter->code = ARCHIVE_COMPRESSION_NONE; a->filter = filter; /* Build out the input pipeline. */ - e = build_stream(a); - if (e == ARCHIVE_OK) - a->archive.state = ARCHIVE_STATE_HEADER; + e = choose_filters(a); + if (e < ARCHIVE_WARN) { + a->archive.state = ARCHIVE_STATE_FATAL; + return (ARCHIVE_FATAL); + } + + slot = choose_format(a); + if (slot < 0) { + close_filters(a); + a->archive.state = ARCHIVE_STATE_FATAL; + return (ARCHIVE_FATAL); + } + a->format = &(a->formats[slot]); + a->archive.state = ARCHIVE_STATE_HEADER; return (e); } @@ -350,7 +384,7 @@ archive_read_open2(struct archive *_a, void *client_data, * building the pipeline. */ static int -build_stream(struct archive_read *a) +choose_filters(struct archive_read *a) { int number_bidders, i, bid, best_bid; struct archive_read_filter_bidder *bidder, *best_bidder; @@ -377,10 +411,11 @@ build_stream(struct archive_read *a) /* If no bidder, we're done. */ if (best_bidder == NULL) { - /* Verify the final pipelin by asking it for some data. */ + /* Verify the filter by asking it for some data. */ __archive_read_filter_ahead(a->filter, 1, &avail); if (avail < 0) { - cleanup_filters(a); + close_filters(a); + free_filters(a); return (ARCHIVE_FATAL); } a->archive.compression_name = a->filter->name; @@ -398,8 +433,9 @@ build_stream(struct archive_read *a) a->filter = filter; r = (best_bidder->init)(a->filter); if (r != ARCHIVE_OK) { - cleanup_filters(a); - return (r); + close_filters(a); + free_filters(a); + return (ARCHIVE_FATAL); } } } @@ -407,60 +443,49 @@ build_stream(struct archive_read *a) /* * Read header of next entry. */ -int -archive_read_next_header2(struct archive *_a, struct archive_entry *entry) +static int +_archive_read_next_header2(struct archive *_a, struct archive_entry *entry) { struct archive_read *a = (struct archive_read *)_a; - int slot, ret; + int r1 = ARCHIVE_OK, r2; - __archive_check_magic(_a, ARCHIVE_READ_MAGIC, + archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA, "archive_read_next_header"); - ++_a->file_count; archive_entry_clear(entry); archive_clear_error(&a->archive); /* - * If no format has yet been chosen, choose one. - */ - if (a->format == NULL) { - slot = choose_format(a); - if (slot < 0) { - a->archive.state = ARCHIVE_STATE_FATAL; - return (ARCHIVE_FATAL); - } - a->format = &(a->formats[slot]); - } - - /* * If client didn't consume entire data, skip any remainder * (This is especially important for GNU incremental directories.) */ if (a->archive.state == ARCHIVE_STATE_DATA) { - ret = archive_read_data_skip(&a->archive); - if (ret == ARCHIVE_EOF) { - archive_set_error(&a->archive, EIO, "Premature end-of-file."); + r1 = archive_read_data_skip(&a->archive); + if (r1 == ARCHIVE_EOF) + archive_set_error(&a->archive, EIO, + "Premature end-of-file."); + if (r1 == ARCHIVE_EOF || r1 == ARCHIVE_FATAL) { a->archive.state = ARCHIVE_STATE_FATAL; return (ARCHIVE_FATAL); } - if (ret != ARCHIVE_OK) - return (ret); } - /* Record start-of-header. */ - a->header_position = a->archive.file_position; + /* Record start-of-header offset in uncompressed stream. */ + a->header_position = a->filter->position; - ret = (a->format->read_header)(a, entry); + ++_a->file_count; + r2 = (a->format->read_header)(a, entry); /* * EOF and FATAL are persistent at this layer. By * modifying the state, we guarantee that future calls to * read a header or read data will fail. */ - switch (ret) { + switch (r2) { case ARCHIVE_EOF: a->archive.state = ARCHIVE_STATE_EOF; + --_a->file_count;/* Revert a file counter. */ break; case ARCHIVE_OK: a->archive.state = ARCHIVE_STATE_DATA; @@ -477,16 +502,17 @@ archive_read_next_header2(struct archive *_a, struct archive_entry *entry) a->read_data_output_offset = 0; a->read_data_remaining = 0; - return (ret); + /* EOF always wins; otherwise return the worst error. */ + return (r2 < r1 || r2 == ARCHIVE_EOF) ? r2 : r1; } int -archive_read_next_header(struct archive *_a, struct archive_entry **entryp) +_archive_read_next_header(struct archive *_a, struct archive_entry **entryp) { int ret; struct archive_read *a = (struct archive_read *)_a; *entryp = NULL; - ret = archive_read_next_header2(_a, a->entry); + ret = _archive_read_next_header2(_a, a->entry); *entryp = a->entry; return ret; } @@ -507,13 +533,15 @@ choose_format(struct archive_read *a) best_bid = -1; best_bid_slot = -1; - /* Set up a->format and a->pformat_data for convenience of bidders. */ + /* Set up a->format for convenience of bidders. */ a->format = &(a->formats[0]); for (i = 0; i < slots; i++, a->format++) { if (a->format->bid) { - bid = (a->format->bid)(a); + bid = (a->format->bid)(a, best_bid); if (bid == ARCHIVE_FATAL) return (ARCHIVE_FATAL); + if (a->filter->position != 0) + __archive_read_seek(a, 0, SEEK_SET); if ((bid > best_bid) || (best_bid_slot < 0)) { best_bid = bid; best_bid_slot = i; @@ -525,10 +553,11 @@ choose_format(struct archive_read *a) * There were no bidders; this is a serious programmer error * and demands a quick and definitive abort. */ - if (best_bid_slot < 0) - __archive_errx(1, "No formats were registered; you must " - "invoke at least one archive_read_support_format_XXX " - "function in order to successfully read an archive."); + if (best_bid_slot < 0) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "No formats registered"); + return (ARCHIVE_FATAL); + } /* * There were bidders, but no non-zero bids; this means we @@ -551,7 +580,7 @@ int64_t archive_read_header_position(struct archive *_a) { struct archive_read *a = (struct archive_read *)_a; - __archive_check_magic(_a, ARCHIVE_READ_MAGIC, + archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_ANY, "archive_read_header_position"); return (a->header_position); } @@ -583,7 +612,7 @@ archive_read_data(struct archive *_a, void *buff, size_t s) while (s > 0) { if (a->read_data_remaining == 0) { read_buf = a->read_data_block; - r = archive_read_data_block(&a->archive, &read_buf, + r = _archive_read_data_block(&a->archive, &read_buf, &a->read_data_remaining, &a->read_data_offset); a->read_data_block = read_buf; if (r == ARCHIVE_EOF) @@ -604,7 +633,7 @@ archive_read_data(struct archive *_a, void *buff, size_t s) } /* Compute the amount of zero padding needed. */ - if (a->read_data_output_offset + (off_t)s < + if (a->read_data_output_offset + (int64_t)s < a->read_data_offset) { len = s; } else if (a->read_data_output_offset < @@ -639,22 +668,6 @@ archive_read_data(struct archive *_a, void *buff, size_t s) return (bytes_read); } -#if ARCHIVE_API_VERSION < 3 -/* - * Obsolete function provided for compatibility only. Note that the API - * of this function doesn't allow the caller to detect if the remaining - * data from the archive entry is shorter than the buffer provided, or - * even if an error occurred while reading data. - */ -int -archive_read_data_into_buffer(struct archive *a, void *d, ssize_t len) -{ - - archive_read_data(a, d, len); - return (ARCHIVE_OK); -} -#endif - /* * Skip over all remaining data in this entry. */ @@ -665,9 +678,9 @@ archive_read_data_skip(struct archive *_a) int r; const void *buff; size_t size; - off_t offset; + int64_t offset; - __archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_DATA, + archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_DATA, "archive_read_data_skip"); if (a->format->read_data_skip != NULL) @@ -694,12 +707,12 @@ archive_read_data_skip(struct archive *_a) * Returns ARCHIVE_OK if the operation is successful, ARCHIVE_EOF if * the end of entry is encountered. */ -int -archive_read_data_block(struct archive *_a, - const void **buff, size_t *size, off_t *offset) +static int +_archive_read_data_block(struct archive *_a, + const void **buff, size_t *size, int64_t *offset) { struct archive_read *a = (struct archive_read *)_a; - __archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_DATA, + archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_DATA, "archive_read_data_block"); if (a->format->read_data == NULL) { @@ -712,69 +725,79 @@ archive_read_data_block(struct archive *_a, return (a->format->read_data)(a, buff, size, offset); } +static int +close_filters(struct archive_read *a) +{ + struct archive_read_filter *f = a->filter; + int r = ARCHIVE_OK; + /* Close each filter in the pipeline. */ + while (f != NULL) { + struct archive_read_filter *t = f->upstream; + if (!f->closed && f->close != NULL) { + int r1 = (f->close)(f); + f->closed = 1; + if (r1 < r) + r = r1; + } + free(f->buffer); + f->buffer = NULL; + f = t; + } + return r; +} + +static void +free_filters(struct archive_read *a) +{ + while (a->filter != NULL) { + struct archive_read_filter *t = a->filter->upstream; + free(a->filter); + a->filter = t; + } +} + /* - * Close the file and release most resources. - * - * Be careful: client might just call read_new and then read_finish. - * Don't assume we actually read anything or performed any non-trivial - * initialization. + * return the count of # of filters in use + */ +static int +_archive_filter_count(struct archive *_a) +{ + struct archive_read *a = (struct archive_read *)_a; + struct archive_read_filter *p = a->filter; + int count = 0; + while(p) { + count++; + p = p->upstream; + } + return count; +} + +/* + * Close the file and all I/O. */ static int _archive_read_close(struct archive *_a) { struct archive_read *a = (struct archive_read *)_a; int r = ARCHIVE_OK, r1 = ARCHIVE_OK; - size_t i, n; - __archive_check_magic(&a->archive, ARCHIVE_READ_MAGIC, - ARCHIVE_STATE_ANY, "archive_read_close"); + archive_check_magic(&a->archive, ARCHIVE_READ_MAGIC, + ARCHIVE_STATE_ANY | ARCHIVE_STATE_FATAL, "archive_read_close"); + if (a->archive.state == ARCHIVE_STATE_CLOSED) + return (ARCHIVE_OK); archive_clear_error(&a->archive); a->archive.state = ARCHIVE_STATE_CLOSED; - - /* Call cleanup functions registered by optional components. */ - if (a->cleanup_archive_extract != NULL) - r = (a->cleanup_archive_extract)(a); - /* TODO: Clean up the formatters. */ /* Release the filter objects. */ - r1 = cleanup_filters(a); + r1 = close_filters(a); if (r1 < r) r = r1; - /* Release the bidder objects. */ - n = sizeof(a->bidders)/sizeof(a->bidders[0]); - for (i = 0; i < n; i++) { - if (a->bidders[i].free != NULL) { - r1 = (a->bidders[i].free)(&a->bidders[i]); - if (r1 < r) - r = r1; - } - } - return (r); } -static int -cleanup_filters(struct archive_read *a) -{ - int r = ARCHIVE_OK; - /* Clean up the filter pipeline. */ - while (a->filter != NULL) { - struct archive_read_filter *t = a->filter->upstream; - if (a->filter->close != NULL) { - int r1 = (a->filter->close)(a->filter); - if (r1 < r) - r = r1; - } - free(a->filter->buffer); - free(a->filter); - a->filter = t; - } - return r; -} - /* * Release memory and other resources. */ @@ -782,15 +805,22 @@ static int _archive_read_free(struct archive *_a) { struct archive_read *a = (struct archive_read *)_a; - int i; + int i, n; int slots; int r = ARCHIVE_OK; - __archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_ANY, - "archive_read_free"); - if (a->archive.state != ARCHIVE_STATE_CLOSED) + if (_a == NULL) + return (ARCHIVE_OK); + archive_check_magic(_a, ARCHIVE_READ_MAGIC, + ARCHIVE_STATE_ANY | ARCHIVE_STATE_FATAL, "archive_read_free"); + if (a->archive.state != ARCHIVE_STATE_CLOSED + && a->archive.state != ARCHIVE_STATE_FATAL) r = archive_read_close(&a->archive); + /* Call cleanup functions registered by optional components. */ + if (a->cleanup_archive_extract != NULL) + r = (a->cleanup_archive_extract)(a); + /* Cleanup format-specific data. */ slots = sizeof(a->formats) / sizeof(a->formats[0]); for (i = 0; i < slots; i++) { @@ -799,14 +829,71 @@ _archive_read_free(struct archive *_a) (a->formats[i].cleanup)(a); } + /* Free the filters */ + free_filters(a); + + /* Release the bidder objects. */ + n = sizeof(a->bidders)/sizeof(a->bidders[0]); + for (i = 0; i < n; i++) { + if (a->bidders[i].free != NULL) { + int r1 = (a->bidders[i].free)(&a->bidders[i]); + if (r1 < r) + r = r1; + } + } + archive_string_free(&a->archive.error_string); if (a->entry) archive_entry_free(a->entry); a->archive.magic = 0; + __archive_clean(&a->archive); free(a); -#if ARCHIVE_API_VERSION > 1 return (r); -#endif +} + +static struct archive_read_filter * +get_filter(struct archive *_a, int n) +{ + struct archive_read *a = (struct archive_read *)_a; + struct archive_read_filter *f = a->filter; + /* We use n == -1 for 'the last filter', which is always the client proxy. */ + if (n == -1 && f != NULL) { + struct archive_read_filter *last = f; + f = f->upstream; + while (f != NULL) { + last = f; + f = f->upstream; + } + return (last); + } + if (n < 0) + return NULL; + while (n > 0 && f != NULL) { + f = f->upstream; + --n; + } + return (f); +} + +static int +_archive_filter_code(struct archive *_a, int n) +{ + struct archive_read_filter *f = get_filter(_a, n); + return f == NULL ? -1 : f->code; +} + +static const char * +_archive_filter_name(struct archive *_a, int n) +{ + struct archive_read_filter *f = get_filter(_a, n); + return f == NULL ? NULL : f->name; +} + +static int64_t +_archive_filter_bytes(struct archive *_a, int n) +{ + struct archive_read_filter *f = get_filter(_a, n); + return f == NULL ? -1 : f->position; } /* @@ -817,16 +904,16 @@ int __archive_read_register_format(struct archive_read *a, void *format_data, const char *name, - int (*bid)(struct archive_read *), + int (*bid)(struct archive_read *, int), int (*options)(struct archive_read *, const char *, const char *), int (*read_header)(struct archive_read *, struct archive_entry *), - int (*read_data)(struct archive_read *, const void **, size_t *, off_t *), + int (*read_data)(struct archive_read *, const void **, size_t *, int64_t *), int (*read_data_skip)(struct archive_read *), int (*cleanup)(struct archive_read *)) { int i, number_slots; - __archive_check_magic(&a->archive, + archive_check_magic(&a->archive, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, "__archive_read_register_format"); @@ -848,74 +935,68 @@ __archive_read_register_format(struct archive_read *a, } } - __archive_errx(1, "Not enough slots for format registration"); - return (ARCHIVE_FATAL); /* Never actually called. */ + archive_set_error(&a->archive, ENOMEM, + "Not enough slots for format registration"); + return (ARCHIVE_FATAL); } /* * Used internally by decompression routines to register their bid and * initialization functions. */ -struct archive_read_filter_bidder * -__archive_read_get_bidder(struct archive_read *a) +int +__archive_read_get_bidder(struct archive_read *a, + struct archive_read_filter_bidder **bidder) { int i, number_slots; - __archive_check_magic(&a->archive, - ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, - "__archive_read_get_bidder"); - number_slots = sizeof(a->bidders) / sizeof(a->bidders[0]); for (i = 0; i < number_slots; i++) { if (a->bidders[i].bid == NULL) { memset(a->bidders + i, 0, sizeof(a->bidders[0])); - return (a->bidders + i); + *bidder = (a->bidders + i); + return (ARCHIVE_OK); } } - __archive_errx(1, "Not enough slots for compression registration"); - return (NULL); /* Never actually executed. */ + archive_set_error(&a->archive, ENOMEM, + "Not enough slots for filter registration"); + return (ARCHIVE_FATAL); } /* - * 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. + * The next section implements the peek/consume internal I/O + * system used by archive readers. This system allows simple + * read-ahead for consumers while preserving zero-copy operation + * most of the time. + * + * The two key operations: + * * The read-ahead function returns a pointer to a block of data + * that satisfies a minimum request. + * * The consume function advances the file pointer. * * In the ideal case, filters generate blocks of data * and __archive_read_ahead() just returns pointers directly into * those blocks. Then __archive_read_consume() just bumps those * pointers. Only if your request would span blocks does the I/O * 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). + * data. * * 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. If you consume everything that's - * immediately available, the next read_ahead() call will pull - * the next block. + * Consume whatever you actually use. * * "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. + * emit all that's available (up to whatever limit you have), consume + * it all, then repeat until you're done. This effectively means that + * you're passing along the blocks that came from your provider. * * "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. */ /* @@ -924,8 +1005,8 @@ __archive_read_get_bidder(struct archive_read *a) * 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. + * * If request can be met, returns pointer to data. + * * If minimum request cannot be met, returns NULL. * * 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 @@ -935,17 +1016,6 @@ __archive_read_get_bidder(struct archive_read *a) * 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 min, ssize_t *avail) { @@ -1034,7 +1104,6 @@ __archive_read_filter_ahead(struct archive_read_filter *filter, *avail = filter->avail; return (NULL); } - filter->position += bytes_read; filter->client_total = bytes_read; filter->client_avail = filter->client_total; filter->client_next = filter->client_buff; @@ -1114,136 +1183,160 @@ __archive_read_filter_ahead(struct archive_read_filter *filter, } /* - * 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. + * Move the file pointer forward. */ -ssize_t -__archive_read_consume(struct archive_read *a, size_t request) +int64_t +__archive_read_consume(struct archive_read *a, int64_t request) { - ssize_t r; - r = __archive_read_filter_consume(a->filter, request); - a->archive.file_position += r; - return (r); + return (__archive_read_filter_consume(a->filter, request)); } -ssize_t +int64_t __archive_read_filter_consume(struct archive_read_filter * filter, - size_t request) + int64_t request) { - if (filter->avail > 0) { - /* Read came from copy buffer. */ - filter->next += request; - filter->avail -= request; - } else { - /* Read came from client buffer. */ - filter->client_next += request; - filter->client_avail -= request; - } - return (request); -} + int64_t skipped; -/* - * 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) -{ - int64_t skipped = __archive_read_skip_lenient(a, request); + if (request == 0) + return 0; + + skipped = advance_file_pointer(filter, request); if (skipped == request) return (skipped); /* We hit EOF before we satisfied the skip request. */ - if (skipped < 0) // Map error code to 0 for error message below. + if (skipped < 0) /* Map error code to 0 for error message below. */ skipped = 0; - archive_set_error(&a->archive, + archive_set_error(&filter->archive->archive, ARCHIVE_ERRNO_MISC, "Truncated input file (needed %jd bytes, only %jd available)", (intmax_t)request, (intmax_t)skipped); return (ARCHIVE_FATAL); } -int64_t -__archive_read_skip_lenient(struct archive_read *a, int64_t request) -{ - int64_t skipped = __archive_read_filter_skip(a->filter, request); - if (skipped > 0) - a->archive.file_position += skipped; - return (skipped); -} - -int64_t -__archive_read_filter_skip(struct archive_read_filter *filter, int64_t request) +/* + * Advance the file pointer by the amount requested. + * Returns the amount actually advanced, which may be less than the + * request if EOF is encountered first. + * Returns a negative value if there's an I/O error. + */ +static int64_t +advance_file_pointer(struct archive_read_filter *filter, int64_t request) { int64_t bytes_skipped, total_bytes_skipped = 0; + ssize_t bytes_read; size_t min; if (filter->fatal) return (-1); - /* - * If there is data in the buffers already, use that first. - */ + + /* Use up the copy buffer first. */ if (filter->avail > 0) { - min = minimum(request, (off_t)filter->avail); - bytes_skipped = __archive_read_filter_consume(filter, min); - request -= bytes_skipped; - total_bytes_skipped += bytes_skipped; + min = minimum(request, (int64_t)filter->avail); + filter->next += min; + filter->avail -= min; + request -= min; + filter->position += min; + total_bytes_skipped += min; } + + /* Then use up the client buffer. */ if (filter->client_avail > 0) { min = minimum(request, (int64_t)filter->client_avail); - bytes_skipped = __archive_read_filter_consume(filter, min); - request -= bytes_skipped; - total_bytes_skipped += bytes_skipped; + filter->client_next += min; + filter->client_avail -= min; + request -= min; + filter->position += min; + total_bytes_skipped += min; } if (request == 0) return (total_bytes_skipped); - /* - * If a client_skipper was provided, try that first. - */ -#if ARCHIVE_API_VERSION < 2 - if ((filter->skip != NULL) && (request < SSIZE_MAX)) { -#else + + /* If there's an optimized skip function, use it. */ if (filter->skip != NULL) { -#endif bytes_skipped = (filter->skip)(filter, request); if (bytes_skipped < 0) { /* error */ - filter->client_total = filter->client_avail = 0; - filter->client_next = filter->client_buff = NULL; filter->fatal = 1; return (bytes_skipped); } + filter->position += bytes_skipped; total_bytes_skipped += bytes_skipped; request -= bytes_skipped; - filter->client_next = filter->client_buff; - filter->client_avail = filter->client_total = 0; + if (request == 0) + return (total_bytes_skipped); } - /* - * 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) { - ssize_t bytes_read; - (void)__archive_read_filter_ahead(filter, 1, &bytes_read); - if (bytes_read < 0) + + /* Use ordinary reads as necessary to complete the request. */ + for (;;) { + bytes_read = (filter->read)(filter, &filter->client_buff); + if (bytes_read < 0) { + filter->client_buff = NULL; + filter->fatal = 1; return (bytes_read); + } + if (bytes_read == 0) { + filter->client_buff = NULL; + filter->end_of_file = 1; return (total_bytes_skipped); } - min = (size_t)(minimum(bytes_read, request)); - bytes_read = __archive_read_filter_consume(filter, min); + + if (bytes_read >= request) { + filter->client_next = + ((const char *)filter->client_buff) + request; + filter->client_avail = bytes_read - request; + filter->client_total = bytes_read; + total_bytes_skipped += request; + filter->position += request; + return (total_bytes_skipped); + } + + filter->position += bytes_read; total_bytes_skipped += bytes_read; request -= bytes_read; } - return (total_bytes_skipped); +} + +/** + * Returns ARCHIVE_FAILED if seeking isn't supported. + */ +int64_t +__archive_read_seek(struct archive_read *a, int64_t offset, int whence) +{ + return __archive_read_filter_seek(a->filter, offset, whence); +} + +int64_t +__archive_read_filter_seek(struct archive_read_filter *filter, int64_t offset, int whence) +{ + int64_t r; + + if (filter->closed || filter->fatal) + return (ARCHIVE_FATAL); + if (filter->seek == NULL) + return (ARCHIVE_FAILED); + r = filter->seek(filter, offset, whence); + if (r >= 0) { + /* + * Ouch. Clearing the buffer like this hurts, especially + * at bid time. A lot of our efficiency at bid time comes + * from having bidders reuse the data we've already read. + * + * TODO: If the seek request is in data we already + * have, then don't call the seek callback. + * + * TODO: Zip seeks to end-of-file at bid time. If + * other formats also start doing this, we may need to + * find a way for clients to fudge the seek offset to + * a block boundary. + * + * Hmmm... If whence was SEEK_END, we know the file + * size is (r - offset). Can we use that to simplify + * the TODO items above? + */ + filter->avail = filter->client_avail = 0; + filter->next = filter->buffer; + filter->position = r; + filter->end_of_file = 0; + } + return r; } |