summaryrefslogtreecommitdiffstats
path: root/lib/libarchive/archive_read.c
diff options
context:
space:
mode:
authorkientzle <kientzle@FreeBSD.org>2004-06-02 08:14:43 +0000
committerkientzle <kientzle@FreeBSD.org>2004-06-02 08:14:43 +0000
commitd5f7a83e1b2d795a41b6ee882323609891e65aeb (patch)
treeff12dd5c58d3d34186f2f2fd2f6c027221d2ebd8 /lib/libarchive/archive_read.c
parent80d36400edd04b536fd6cab0dae30c9e678e6598 (diff)
downloadFreeBSD-src-d5f7a83e1b2d795a41b6ee882323609891e65aeb.zip
FreeBSD-src-d5f7a83e1b2d795a41b6ee882323609891e65aeb.tar.gz
Refactor read_data:
* New read_data_block is both sparse-file aware and uses zero-copy semantics * Push read_data_block down into specific formats (opens door to various encoded entry bodies, such as zip or gtar -S) * Reimplement read_data, read_data_skip, read_data_into_fd in terms of new read_data_block. * Update documentation It's unfortunate that I couldn't just call the new interface archive_read_data, but didn't want to upset the API that much.
Diffstat (limited to 'lib/libarchive/archive_read.c')
-rw-r--r--lib/libarchive/archive_read.c134
1 files changed, 84 insertions, 50 deletions
diff --git a/lib/libarchive/archive_read.c b/lib/libarchive/archive_read.c
index 7af242a..0e05c4b 100644
--- a/lib/libarchive/archive_read.c
+++ b/lib/libarchive/archive_read.c
@@ -319,42 +319,61 @@ archive_read_header_position(struct archive *a)
}
/*
- * Read data from an archive entry.
+ * Read data from an archive entry, using a read(2)-style interface.
+ * This is a convenience routine that just calls
+ * archive_read_data_block and copies the results into the client
+ * buffer, filling any gaps with zero bytes. Clients using this
+ * API can be completely ignorant of sparse-file issues; sparse files
+ * will simply be padded with nulls.
*/
ssize_t
archive_read_data(struct archive *a, void *buff, size_t s)
{
- const void *data;
- ssize_t bytes_read;
+ off_t remaining;
+ char *dest;
+ size_t bytes_read;
+ size_t len;
+ int r;
+
+ bytes_read = 0;
+ dest = buff;
+
+ while (s > 0) {
+ if (a->read_data_remaining <= 0) {
+ r = archive_read_data_block(a,
+ (const void **)&a->read_data_block,
+ &a->read_data_remaining,
+ &a->read_data_offset);
+ if (r == ARCHIVE_EOF)
+ return (bytes_read);
+ if (r != ARCHIVE_OK)
+ return (r);
+ }
- archive_check_magic(a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_DATA);
- /*
- * off_t is generally at least as wide as size_t, so widen for
- * comparison and narrow for the assignment. Otherwise, on
- * platforms with 32-bit size_t and 64-bit off_t, we won't be
- * able to correctly read archives with entries larger than
- * 4gig.
- */
- if ((off_t)s > a->entry_bytes_remaining)
- s = (size_t)a->entry_bytes_remaining;
- if (s > 0) {
- bytes_read = (a->compression_read_ahead)(a, &data, 1);
- if (bytes_read < 0) {
- a->state = ARCHIVE_STATE_FATAL;
- return (bytes_read);
+ if (a->read_data_offset < a->read_data_output_offset) {
+ remaining =
+ a->read_data_output_offset - a->read_data_offset;
+ if (remaining > (off_t)s)
+ remaining = (off_t)s;
+ len = (size_t)remaining;
+ memset(dest, 0, len);
+ a->read_data_output_offset += len;
+ s -= len;
+ bytes_read += len;
+ } else {
+ len = a->read_data_remaining;
+ if (len > s)
+ len = s;
+ memcpy(dest, a->read_data_block, len);
+ s -= len;
+ a->read_data_remaining -= len;
+ a->read_data_output_offset += len;
+ a->read_data_offset += len;
+ dest += len;
+ bytes_read += len;
}
- if ((size_t)bytes_read > s)
- bytes_read = s;
- } else
- bytes_read = 0;
-
- if (bytes_read > 0) {
- memcpy(buff, data, bytes_read);
- (a->compression_read_consume)(a, bytes_read);
}
-
- a->entry_bytes_remaining -= bytes_read;
- return (bytes_read);
+ return (ARCHIVE_OK);
}
/*
@@ -363,33 +382,46 @@ archive_read_data(struct archive *a, void *buff, size_t s)
int
archive_read_data_skip(struct archive *a)
{
+ int r;
const void *buff;
- ssize_t bytes_read, to_skip;
+ ssize_t size;
+ off_t offset;
archive_check_magic(a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_DATA);
- to_skip = a->entry_bytes_remaining + a->entry_padding;
- a->entry_bytes_remaining = 0;
+ while ((r = archive_read_data_block(a, &buff, &size, &offset)) ==
+ ARCHIVE_OK)
+ ;
+
+ if (r == ARCHIVE_EOF)
+ r = ARCHIVE_OK;
- for (; to_skip > 0; to_skip -= bytes_read) {
- /* TODO: Optimize skip in compression layer. */
- bytes_read = (a->compression_read_ahead)(a, &buff, to_skip);
- if (bytes_read < 0) {
- a->entry_padding = to_skip;
- return (ARCHIVE_FATAL);
- }
- if (bytes_read == 0) {
- archive_set_error(a, EIO,
- "Premature end of archive entry");
- return (ARCHIVE_FATAL);
- }
- if (bytes_read > to_skip)
- bytes_read = to_skip;
- (a->compression_read_consume)(a, bytes_read);
- }
- a->entry_padding = 0;
a->state = ARCHIVE_STATE_HEADER;
- return (ARCHIVE_OK);
+ return (r);
+}
+
+/*
+ * Read the next block of entry data from the archive.
+ * This is a zero-copy interface; the client receives a pointer,
+ * size, and file offset of the next available block of data.
+ *
+ * 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)
+{
+ archive_check_magic(a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_DATA);
+
+ if (a->format->read_data == NULL) {
+ archive_set_error(a, ARCHIVE_ERRNO_PROGRAMMER,
+ "Internal error: "
+ "No format_read_data_block function registered");
+ return (ARCHIVE_FATAL);
+ }
+
+ return (a->format->read_data)(a, buff, size, offset);
}
/*
@@ -445,6 +477,7 @@ __archive_read_register_format(struct archive *a,
void *format_data,
int (*bid)(struct archive *),
int (*read_header)(struct archive *, struct archive_entry *),
+ int (*read_data)(struct archive *, const void **, size_t *, off_t *),
int (*cleanup)(struct archive *))
{
int i, number_slots;
@@ -459,6 +492,7 @@ __archive_read_register_format(struct archive *a,
if (a->formats[i].bid == NULL) {
a->formats[i].bid = bid;
a->formats[i].read_header = read_header;
+ a->formats[i].read_data = read_data;
a->formats[i].cleanup = cleanup;
a->formats[i].format_data = format_data;
return (ARCHIVE_OK);
OpenPOWER on IntegriCloud