summaryrefslogtreecommitdiff
path: root/libarchive/archive_read.c
diff options
context:
space:
mode:
authorAndres Mejia <amejia004@gmail.com>2012-02-24 10:57:20 -0500
committerAndres Mejia <amejia004@gmail.com>2012-03-25 22:47:14 -0400
commit7b543b13d813e529364c7b70ae204ce7c0eddfc3 (patch)
treef6393a3480c0d3cfc8da3eee7f390b0f04b83d25 /libarchive/archive_read.c
parentf079aadc03aae8647db50536ca43e1ca75fc4775 (diff)
downloadlibarchive-7b543b13d813e529364c7b70ae204ce7c0eddfc3.tar.gz
Implement support for reading from multiple data objects as one large stream.
This enables support for reading archives split using the split utility.
Diffstat (limited to 'libarchive/archive_read.c')
-rw-r--r--libarchive/archive_read.c171
1 files changed, 162 insertions, 9 deletions
diff --git a/libarchive/archive_read.c b/libarchive/archive_read.c
index e99906f8..502d7f45 100644
--- a/libarchive/archive_read.c
+++ b/libarchive/archive_read.c
@@ -241,14 +241,64 @@ client_seek_proxy(struct archive_read_filter *self, int64_t offset, int whence)
static int
client_close_proxy(struct archive_read_filter *self)
{
- int r = ARCHIVE_OK;
+ int r = ARCHIVE_OK, r2;
+ unsigned int i;
+
+ if (self->archive->client.closer == NULL)
+ return (r);
+ for (i = 0; i < self->archive->client.nodes; i++)
+ {
+ r2 = (self->archive->client.closer)
+ ((struct archive *)self->archive,
+ self->archive->client.dataset[i].data);
+ if (r > r2)
+ r = r2;
+ }
+ return (r);
+}
- if (self->archive->client.closer != NULL)
- r = (self->archive->client.closer)((struct archive *)self->archive,
+static int
+client_open_proxy(struct archive_read_filter *self)
+{
+ int r = ARCHIVE_OK;
+ if (self->archive->client.opener != NULL)
+ r = (self->archive->client.opener)((struct archive *)self->archive,
self->data);
return (r);
}
+static int
+client_switch_proxy(struct archive_read_filter *self, unsigned int iindex)
+{
+ int r1 = ARCHIVE_OK, r2 = ARCHIVE_OK;
+ void *data2 = NULL;
+
+ /* Don't do anything if already in the specified data node */
+ if (self->archive->client.cursor == iindex)
+ return (ARCHIVE_OK);
+
+ self->archive->client.cursor = iindex;
+ data2 = self->archive->client.dataset[self->archive->client.cursor].data;
+ if (self->archive->client.switcher != NULL)
+ {
+ r1 = r2 = (self->archive->client.switcher)
+ ((struct archive *)self->archive, self->data, data2);
+ self->data = data2;
+ }
+ else
+ {
+ /* Attempt to call close and open instead */
+ if (self->archive->client.closer != NULL)
+ r1 = (self->archive->client.closer)
+ ((struct archive *)self->archive, self->data);
+ self->data = data2;
+ if (self->archive->client.opener != NULL)
+ r2 = (self->archive->client.opener)
+ ((struct archive *)self->archive, self->data);
+ }
+ return (r1 < r2) ? r1 : r2;
+}
+
int
archive_read_set_open_callback(struct archive *_a,
archive_open_callback *client_opener)
@@ -305,21 +355,101 @@ archive_read_set_close_callback(struct archive *_a,
}
int
+archive_read_set_switch_callback(struct archive *_a,
+ archive_switch_callback *client_switcher)
+{
+ struct archive_read *a = (struct archive_read *)_a;
+ archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW,
+ "archive_read_set_switch_callback");
+ a->client.switcher = client_switcher;
+ return ARCHIVE_OK;
+}
+
+int
archive_read_set_callback_data(struct archive *_a, void *client_data)
{
+ return archive_read_set_callback_data2(_a, client_data, 0);
+}
+
+int
+archive_read_set_callback_data2(struct archive *_a, void *client_data,
+ unsigned int iindex)
+{
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;
+ "archive_read_set_callback_data2");
+
+ if (a->client.nodes == 0)
+ {
+ a->client.dataset =
+ (struct archive_read_data_node *)calloc(1, sizeof(*a->client.dataset));
+ if (a->client.dataset == NULL)
+ {
+ archive_set_error(&a->archive, ENOMEM,
+ "No memory.");
+ return ARCHIVE_FATAL;
+ }
+ a->client.nodes = 1;
+ }
+
+ if (iindex > a->client.nodes - 1)
+ {
+ archive_set_error(&a->archive, EINVAL,
+ "Invalid index specified.");
+ return ARCHIVE_FATAL;
+ }
+ a->client.dataset[iindex].data = client_data;
+ return ARCHIVE_OK;
+}
+
+int
+archive_read_add_callback_data(struct archive *_a, void *client_data,
+ unsigned int iindex)
+{
+ struct archive_read *a = (struct archive_read *)_a;
+ unsigned int i;
+ archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW,
+ "archive_read_add_callback_data");
+ if (iindex > a->client.nodes)
+ {
+ archive_set_error(&a->archive, EINVAL,
+ "Invalid index specified.");
+ return ARCHIVE_FATAL;
+ }
+ if ((a->client.dataset =
+ (struct archive_read_data_node *)realloc(a->client.dataset,
+ sizeof(*a->client.dataset) * (++(a->client.nodes)))) == NULL)
+ {
+ archive_set_error(&a->archive, ENOMEM,
+ "No memory.");
+ return ARCHIVE_FATAL;
+ }
+ for (i = a->client.nodes - 1; i > iindex && i > 0; i--)
+ a->client.dataset[i].data = a->client.dataset[i-1].data;
+ a->client.dataset[iindex].data = client_data;
return ARCHIVE_OK;
}
int
+archive_read_append_callback_data(struct archive *_a, void *client_data)
+{
+ struct archive_read *a = (struct archive_read *)_a;
+ return archive_read_add_callback_data(_a, client_data, a->client.nodes);
+}
+
+int
+archive_read_prepend_callback_data(struct archive *_a, void *client_data)
+{
+ return archive_read_add_callback_data(_a, client_data, 0);
+}
+
+int
archive_read_open1(struct archive *_a)
{
struct archive_read *a = (struct archive_read *)_a;
struct archive_read_filter *filter;
int slot, e;
+ unsigned int i;
archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW,
"archive_read_open");
@@ -334,11 +464,14 @@ archive_read_open1(struct archive *_a)
/* Open data source. */
if (a->client.opener != NULL) {
- e =(a->client.opener)(&a->archive, a->client.data);
+ e =(a->client.opener)(&a->archive, a->client.dataset[0].data);
if (e != 0) {
/* If the open failed, call the closer to clean up. */
if (a->client.closer)
- (a->client.closer)(&a->archive, a->client.data);
+ {
+ for (i = 0; i < a->client.nodes; i++)
+ (a->client.closer)(&a->archive, a->client.dataset[i].data);
+ }
return (e);
}
}
@@ -349,11 +482,13 @@ archive_read_open1(struct archive *_a)
filter->bidder = NULL;
filter->upstream = NULL;
filter->archive = a;
- filter->data = a->client.data;
+ filter->data = a->client.dataset[0].data;
+ filter->open = client_open_proxy;
filter->read = client_read_proxy;
filter->skip = client_skip_proxy;
filter->seek = client_seek_proxy;
filter->close = client_close_proxy;
+ filter->sswitch = client_switch_proxy;
filter->name = "none";
filter->code = ARCHIVE_COMPRESSION_NONE;
a->filter = filter;
@@ -374,6 +509,9 @@ archive_read_open1(struct archive *_a)
a->format = &(a->formats[slot]);
a->archive.state = ARCHIVE_STATE_HEADER;
+
+ /* Ensure libarchive starts from the first node in a multivolume set */
+ client_switch_proxy(a->filter, 0);
return (e);
}
@@ -846,6 +984,7 @@ _archive_read_free(struct archive *_a)
archive_entry_free(a->entry);
a->archive.magic = 0;
__archive_clean(&a->archive);
+ free(a->client.dataset);
free(a);
return (r);
}
@@ -1094,7 +1233,15 @@ __archive_read_filter_ahead(struct archive_read_filter *filter,
*avail = ARCHIVE_FATAL;
return (NULL);
}
- if (bytes_read == 0) { /* Premature end-of-file. */
+ if (bytes_read == 0) {
+ /* Check for another client object first */
+ if (filter->archive->client.cursor != filter->archive->client.nodes - 1)
+ {
+ if (client_switch_proxy(filter,
+ filter->archive->client.cursor + 1) == (ARCHIVE_OK))
+ continue;
+ }
+ /* Premature end-of-file. */
filter->client_total = filter->client_avail = 0;
filter->client_next = filter->client_buff = NULL;
filter->end_of_file = 1;
@@ -1274,6 +1421,12 @@ advance_file_pointer(struct archive_read_filter *filter, int64_t request)
}
if (bytes_read == 0) {
+ if (filter->archive->client.cursor != filter->archive->client.nodes - 1)
+ {
+ if (client_switch_proxy(filter,
+ filter->archive->client.cursor + 1) == (ARCHIVE_OK))
+ continue;
+ }
filter->client_buff = NULL;
filter->end_of_file = 1;
return (total_bytes_skipped);