diff options
author | kientzle <kientzle@FreeBSD.org> | 2004-06-02 08:14:43 +0000 |
---|---|---|
committer | kientzle <kientzle@FreeBSD.org> | 2004-06-02 08:14:43 +0000 |
commit | d5f7a83e1b2d795a41b6ee882323609891e65aeb (patch) | |
tree | ff12dd5c58d3d34186f2f2fd2f6c027221d2ebd8 | |
parent | 80d36400edd04b536fd6cab0dae30c9e678e6598 (diff) | |
download | FreeBSD-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.
-rw-r--r-- | lib/libarchive/archive.h | 8 | ||||
-rw-r--r-- | lib/libarchive/archive.h.in | 8 | ||||
-rw-r--r-- | lib/libarchive/archive_private.h | 18 | ||||
-rw-r--r-- | lib/libarchive/archive_read.3 | 27 | ||||
-rw-r--r-- | lib/libarchive/archive_read.c | 134 | ||||
-rw-r--r-- | lib/libarchive/archive_read_data_into_fd.c | 55 | ||||
-rw-r--r-- | lib/libarchive/archive_read_support_format_cpio.c | 100 | ||||
-rw-r--r-- | lib/libarchive/archive_read_support_format_tar.c | 81 |
8 files changed, 300 insertions, 131 deletions
diff --git a/lib/libarchive/archive.h b/lib/libarchive/archive.h index 4fc70cf..fed02c0 100644 --- a/lib/libarchive/archive.h +++ b/lib/libarchive/archive.h @@ -166,6 +166,14 @@ int64_t archive_read_header_position(struct archive *); /* Read data from the body of an entry. Similar to read(2). */ ssize_t archive_read_data(struct archive *, void *, size_t); +/* + * A zero-copy version of archive_read_data that also exposes the file offset + * of each returned block. Note that the client has no way to specify + * the desired size of the block. The API does gaurantee that offsets will + * be strictly increasing and that returned blocks will not overlap. + */ +int archive_read_data_block(struct archive *a, + const void **buff, size_t *size, off_t *offset); /*- * Some convenience functions that are built on archive_read_data: diff --git a/lib/libarchive/archive.h.in b/lib/libarchive/archive.h.in index 4fc70cf..fed02c0 100644 --- a/lib/libarchive/archive.h.in +++ b/lib/libarchive/archive.h.in @@ -166,6 +166,14 @@ int64_t archive_read_header_position(struct archive *); /* Read data from the body of an entry. Similar to read(2). */ ssize_t archive_read_data(struct archive *, void *, size_t); +/* + * A zero-copy version of archive_read_data that also exposes the file offset + * of each returned block. Note that the client has no way to specify + * the desired size of the block. The API does gaurantee that offsets will + * be strictly increasing and that returned blocks will not overlap. + */ +int archive_read_data_block(struct archive *a, + const void **buff, size_t *size, off_t *offset); /*- * Some convenience functions that are built on archive_read_data: diff --git a/lib/libarchive/archive_private.h b/lib/libarchive/archive_private.h index cd5fc59..ddf50b1 100644 --- a/lib/libarchive/archive_private.h +++ b/lib/libarchive/archive_private.h @@ -54,12 +54,13 @@ struct archive { size_t null_length; /* - * Used to limit reads of entry data. Eventually, each reader - * will be able to register it's own read_data routine and these - * will move into the per-format data for the formats that use them. + * Used by archive_read_data() to track blocks and copy + * data to client buffers, filling gaps with zero bytes. */ - off_t entry_bytes_remaining; - off_t entry_padding; /* Skip this much after entry data. */ + const char *read_data_block; + off_t read_data_offset; + off_t read_data_output_offset; + size_t read_data_remaining; uid_t user_uid; /* UID of current user. */ @@ -151,6 +152,7 @@ struct archive { struct archive_format_descriptor { 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 *); void *format_data; /* Format-specific data for readers. */ } formats[4]; @@ -168,9 +170,8 @@ struct archive { void *format_data; /* Used by writers. */ /* - * Pointers to format-specific functions. On read, these are - * initialized in the bid process. On write, they're initialized by - * archive_write_set_format_XXX() calls. + * Pointers to format-specific functions for writing. They're + * initialized by archive_write_set_format_XXX() calls. */ int (*format_init)(struct archive *); /* Only used on write. */ int (*format_finish)(struct archive *); @@ -220,6 +221,7 @@ int __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 __archive_read_register_compression(struct archive *a, diff --git a/lib/libarchive/archive_read.3 b/lib/libarchive/archive_read.3 index fef88ea..79174f4 100644 --- a/lib/libarchive/archive_read.3 +++ b/lib/libarchive/archive_read.3 @@ -43,9 +43,10 @@ .Nm archive_read_open_file , .Nm archive_read_next_header , .Nm archive_read_data , +.Nm archive_read_data_block , .Nm archive_read_data_skip , .Nm archive_read_data_into_buffer , -.Nm archive_read_data_into_file , +.Nm archive_read_data_into_fd , .Nm archive_read_extract , .Nm archive_read_extract_set_progress_callback , .Nm archive_read_finish @@ -83,11 +84,13 @@ .Ft ssize_t .Fn archive_read_data "struct archive *" "void *buff" "size_t len" .Ft int +.Fn archive_read_data_block "struct archive *" "const void **buff" "size_t *len" "off_t *offset" +.Ft int .Fn archive_read_data_skip "struct archive *" .Ft int .Fn archive_read_data_into_buffer "struct archive *" "void *" .Ft int -.Fn archive_read_data_into_file "struct archive *" "int fd" +.Fn archive_read_data_into_fd "struct archive *" "int fd" .Ft int .Fn archive_read_extract "struct archive *" "int flags" .Ft void @@ -165,18 +168,30 @@ a .Tn struct archive_entry . .It Fn archive_read_data Read data associated with the header just read. +Internally, this is a convenience function that uses +.Fn archive_read_data_block . +.It Fn archive_read_data_block +Return the next available block of data for this entry. +Unlike +.Fn archive_read_data , +the +.Fn archive_read_data_block +function avoids copying data and allows you to correctly handle +sparse files, as supported by some archive formats. +The library gaurantees that offsets will increase and that blocks +will not overlap. .It Fn archive_read_data_skip A convenience function that repeatedly calls -.Fn archive_read_data +.Fn archive_read_data_block to skip all of the data for this archive entry. .It Fn archive_read_data_into_buffer A convenience function that repeatedly calls -.Fn archive_read_data +.Fn archive_read_data_block to copy the entire entry into the client-supplied buffer. Note that the client is responsible for sizing the buffer appropriately. -.It Fn archive_read_data_into_file +.It Fn archive_read_data_into_fd A convenience function that repeatedly calls -.Fn archive_read_data +.Fn archive_read_data_block to copy the entire entry to the provided file descriptor. .It Fn archive_read_extract A convenience function that recreates the specified object on 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); diff --git a/lib/libarchive/archive_read_data_into_fd.c b/lib/libarchive/archive_read_data_into_fd.c index 9bdad4d..85ba98d 100644 --- a/lib/libarchive/archive_read_data_into_fd.c +++ b/lib/libarchive/archive_read_data_into_fd.c @@ -33,36 +33,49 @@ __FBSDID("$FreeBSD$"); #include "archive.h" #include "archive_private.h" +/* Maximum amount of data to write at one time. */ +#define MAX_WRITE (1024 * 1024) + /* - * This implementation minimizes copying of data. + * This implementation minimizes copying of data and is sparse-file aware. */ ssize_t archive_read_data_into_fd(struct archive *a, int fd) { - ssize_t bytes_read, bytes_written, total_written; + int r; const void *buff; + ssize_t size, bytes_to_write; + ssize_t bytes_written, total_written; + off_t offset; + off_t output_offset; + + archive_check_magic(a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_DATA); total_written = 0; - while (a->entry_bytes_remaining > 0) { - /* Remember: '1' here is minimum, not maximum. */ - /* Read-ahead function will return as much as is convenient. */ - bytes_read = (a->compression_read_ahead)(a, &buff, 1); - if (bytes_read < 0) - return (-1); - if (bytes_read > a->entry_bytes_remaining) - bytes_read = (ssize_t)a->entry_bytes_remaining; - /* Don't copy more than 1 megabyte at a time. */ - if (bytes_read > (1024*1024)) - bytes_read = 1024*1024; + output_offset = 0; - bytes_written = write(fd, buff, bytes_read); - if (bytes_written < 0) - return (-1); - (a->compression_read_consume)(a, bytes_written); - total_written += bytes_written; - a->entry_bytes_remaining -= bytes_written; - if (a->extract_progress != NULL) - (*a->extract_progress)(a->extract_progress_user_data); + while ((r = archive_read_data_block(a, &buff, &size, &offset)) == + ARCHIVE_OK) { + if (offset > output_offset) { + lseek(fd, offset - output_offset, SEEK_CUR); + output_offset = offset; + } + while (size > 0) { + bytes_to_write = size; + if (bytes_to_write > MAX_WRITE) + bytes_to_write = MAX_WRITE; + bytes_written = write(fd, buff, bytes_to_write); + if (bytes_written < 0) + return (-1); + output_offset += bytes_written; + total_written += bytes_written; + size -= bytes_written; + if (a->extract_progress != NULL) + (*a->extract_progress)(a->extract_progress_user_data); + } } + + if (r != ARCHIVE_EOF) + return (-1); return (total_written); } diff --git a/lib/libarchive/archive_read_support_format_cpio.c b/lib/libarchive/archive_read_support_format_cpio.c index 830eafb..03af38f 100644 --- a/lib/libarchive/archive_read_support_format_cpio.c +++ b/lib/libarchive/archive_read_support_format_cpio.c @@ -97,27 +97,32 @@ struct links_entry { #define CPIO_MAGIC 0x13141516 struct cpio { int magic; - int (*read_header)(struct archive *, struct stat *, - size_t *, size_t *); + int (*read_header)(struct archive *, struct cpio *, + struct stat *, size_t *, size_t *); struct links_entry *links_head; struct archive_string entry_name; struct archive_string entry_linkname; + off_t entry_bytes_remaining; + off_t entry_offset; + off_t entry_padding; }; static int64_t atol16(const char *, unsigned); static int64_t atol8(const char *, unsigned); static int archive_read_format_cpio_bid(struct archive *); static int archive_read_format_cpio_cleanup(struct archive *); +static int archive_read_format_cpio_read_data(struct archive *, + const void **, size_t *, off_t *); static int archive_read_format_cpio_read_header(struct archive *, struct archive_entry *); static int be4(const unsigned char *); -static int header_bin_be(struct archive *, struct stat *, +static int header_bin_be(struct archive *, struct cpio *, struct stat *, size_t *, size_t *); -static int header_bin_le(struct archive *, struct stat *, +static int header_bin_le(struct archive *, struct cpio *, struct stat *, size_t *, size_t *); -static int header_newc(struct archive *, struct stat *, +static int header_newc(struct archive *, struct cpio *, struct stat *, size_t *, size_t *); -static int header_odc(struct archive *, struct stat *, +static int header_odc(struct archive *, struct cpio *, struct stat *, size_t *, size_t *); static int le4(const unsigned char *); static void record_hardlink(struct cpio *cpio, struct archive_entry *entry, @@ -137,6 +142,7 @@ archive_read_support_format_cpio(struct archive *a) cpio, archive_read_format_cpio_bid, archive_read_format_cpio_read_header, + archive_read_format_cpio_read_data, archive_read_format_cpio_cleanup); if (r != ARCHIVE_OK) @@ -216,7 +222,7 @@ archive_read_format_cpio_read_header(struct archive *a, memset(&st, 0, sizeof(st)); cpio = *(a->pformat_data); - r = (cpio->read_header(a, &st, &namelength, &name_pad)); + r = (cpio->read_header(a, cpio, &st, &namelength, &name_pad)); if (r != ARCHIVE_OK) return (r); @@ -231,18 +237,19 @@ archive_read_format_cpio_read_header(struct archive *a, (a->compression_read_consume)(a, namelength + name_pad); archive_strncpy(&cpio->entry_name, 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 (S_ISLNK(st.st_mode)) { bytes = (a->compression_read_ahead)(a, &h, - a->entry_bytes_remaining); - if ((off_t)bytes < a->entry_bytes_remaining) + cpio->entry_bytes_remaining); + if ((off_t)bytes < cpio->entry_bytes_remaining) return (ARCHIVE_FATAL); - (a->compression_read_consume)(a, a->entry_bytes_remaining); + (a->compression_read_consume)(a, cpio->entry_bytes_remaining); archive_strncpy(&cpio->entry_linkname, h, - a->entry_bytes_remaining); + cpio->entry_bytes_remaining); archive_entry_set_symlink(entry, cpio->entry_linkname.s); - a->entry_bytes_remaining = 0; + cpio->entry_bytes_remaining = 0; } /* Compare name to "TRAILER!!!" to test for end-of-archive. */ @@ -259,7 +266,44 @@ archive_read_format_cpio_read_header(struct archive *a, } static int -header_newc(struct archive *a, struct stat *st, +archive_read_format_cpio_read_data(struct archive *a, + const void **buff, size_t *size, off_t *offset) +{ + ssize_t bytes_read; + struct cpio *cpio; + + cpio = *(a->pformat_data); + if (cpio->entry_bytes_remaining > 0) { + bytes_read = (a->compression_read_ahead)(a, buff, 1); + if (bytes_read <= 0) + return (ARCHIVE_FATAL); + if (bytes_read > cpio->entry_bytes_remaining) + bytes_read = cpio->entry_bytes_remaining; + *size = bytes_read; + *offset = cpio->entry_offset; + cpio->entry_offset += bytes_read; + cpio->entry_bytes_remaining -= bytes_read; + (a->compression_read_consume)(a, bytes_read); + return (ARCHIVE_OK); + } else { + while (cpio->entry_padding > 0) { + bytes_read = (a->compression_read_ahead)(a, buff, 1); + if (bytes_read <= 0) + return (ARCHIVE_FATAL); + if (bytes_read > cpio->entry_padding) + bytes_read = cpio->entry_padding; + (a->compression_read_consume)(a, bytes_read); + cpio->entry_padding -= bytes_read; + } + *buff = NULL; + *size = 0; + *offset = cpio->entry_offset; + return (ARCHIVE_EOF); + } +} + +static int +header_newc(struct archive *a, struct cpio *cpio, struct stat *st, size_t *namelength, size_t *name_pad) { const void *h; @@ -293,16 +337,16 @@ header_newc(struct archive *a, struct stat *st, * size. struct stat.st_size may only be 32 bits, so * assigning there first could lose information. */ - a->entry_bytes_remaining = + cpio->entry_bytes_remaining = atol16(header->c_filesize, sizeof(header->c_filesize)); - st->st_size = a->entry_bytes_remaining; + st->st_size = cpio->entry_bytes_remaining; /* Pad file contents to a multiple of 4. */ - a->entry_padding = 3 & -a->entry_bytes_remaining; + cpio->entry_padding = 3 & -cpio->entry_bytes_remaining; return (ARCHIVE_OK); } static int -header_odc(struct archive *a, struct stat *st, +header_odc(struct archive *a, struct cpio *cpio, struct stat *st, size_t *namelength, size_t *name_pad) { const void *h; @@ -338,15 +382,15 @@ header_odc(struct archive *a, struct stat *st, * size. struct stat.st_size may only be 32 bits, so * assigning there first could lose information. */ - a->entry_bytes_remaining = + cpio->entry_bytes_remaining = atol8(header->c_filesize, sizeof(header->c_filesize)); - st->st_size = a->entry_bytes_remaining; - a->entry_padding = 0; + st->st_size = cpio->entry_bytes_remaining; + cpio->entry_padding = 0; return (ARCHIVE_OK); } static int -header_bin_le(struct archive *a, struct stat *st, +header_bin_le(struct archive *a, struct cpio *cpio, struct stat *st, size_t *namelength, size_t *name_pad) { const void *h; @@ -376,14 +420,14 @@ header_bin_le(struct archive *a, struct stat *st, *namelength = header->c_namesize[0] + header->c_namesize[1] * 256; *name_pad = *namelength & 1; /* Pad to even. */ - a->entry_bytes_remaining = le4(header->c_filesize); - st->st_size = a->entry_bytes_remaining; - a->entry_padding = a->entry_bytes_remaining & 1; /* Pad to even. */ + cpio->entry_bytes_remaining = le4(header->c_filesize); + st->st_size = cpio->entry_bytes_remaining; + cpio->entry_padding = cpio->entry_bytes_remaining & 1; /* Pad to even. */ return (ARCHIVE_OK); } static int -header_bin_be(struct archive *a, struct stat *st, +header_bin_be(struct archive *a, struct cpio *cpio, struct stat *st, size_t *namelength, size_t *name_pad) { const void *h; @@ -413,9 +457,9 @@ header_bin_be(struct archive *a, struct stat *st, *namelength = header->c_namesize[0] * 256 + header->c_namesize[1]; *name_pad = *namelength & 1; /* Pad to even. */ - a->entry_bytes_remaining = be4(header->c_filesize); - st->st_size = a->entry_bytes_remaining; - a->entry_padding = a->entry_bytes_remaining & 1; /* Pad to even. */ + cpio->entry_bytes_remaining = be4(header->c_filesize); + st->st_size = cpio->entry_bytes_remaining; + cpio->entry_padding = cpio->entry_bytes_remaining & 1; /* Pad to even. */ return (ARCHIVE_OK); } diff --git a/lib/libarchive/archive_read_support_format_tar.c b/lib/libarchive/archive_read_support_format_tar.c index ebe0a81..db7c28b 100644 --- a/lib/libarchive/archive_read_support_format_tar.c +++ b/lib/libarchive/archive_read_support_format_tar.c @@ -76,6 +76,9 @@ struct tar { wchar_t *pax_entry; size_t pax_entry_length; int header_recursion_depth; + off_t entry_bytes_remaining; + off_t entry_offset; + off_t entry_padding; }; static size_t UTF8_mbrtowc(wchar_t *pwc, const char *s, size_t n); @@ -102,6 +105,8 @@ static int header_gnutar(struct archive *, struct tar *, struct archive_entry *, struct stat *, const void *h); static int archive_read_format_tar_bid(struct archive *); static int archive_read_format_tar_cleanup(struct archive *); +static int archive_read_format_tar_read_data(struct archive *a, + const void **buff, size_t *size, off_t *offset); static int archive_read_format_tar_read_header(struct archive *, struct archive_entry *); static int checksum(struct archive *, const void *); @@ -110,8 +115,8 @@ static int pax_attribute(struct archive_entry *, struct stat *, static int pax_header(struct archive *, struct tar *, struct archive_entry *, struct stat *, char *attr); static void pax_time(const wchar_t *, int64_t *sec, long *nanos); -static int read_body_to_string(struct archive *, struct archive_string *, - const void *h); +static int read_body_to_string(struct archive *, struct tar *, + struct archive_string *, const void *h); static int64_t tar_atol(const char *, unsigned); static int64_t tar_atol10(const wchar_t *, unsigned); static int64_t tar_atol256(const char *, unsigned); @@ -139,6 +144,7 @@ archive_read_support_format_tar(struct archive *a) r = __archive_read_register_format(a, tar, archive_read_format_tar_bid, archive_read_format_tar_read_header, + archive_read_format_tar_read_data, archive_read_format_tar_cleanup); if (r != ARCHIVE_OK) @@ -261,10 +267,48 @@ archive_read_format_tar_read_header(struct archive *a, memset(&st, 0, sizeof(st)); tar = *(a->pformat_data); + tar->entry_offset = 0; return (tar_read_header(a, tar, entry, &st)); } +static int +archive_read_format_tar_read_data(struct archive *a, + const void **buff, size_t *size, off_t *offset) +{ + ssize_t bytes_read; + struct tar *tar; + + tar = *(a->pformat_data); + if (tar->entry_bytes_remaining > 0) { + bytes_read = (a->compression_read_ahead)(a, buff, 1); + if (bytes_read <= 0) + return (ARCHIVE_FATAL); + if (bytes_read > tar->entry_bytes_remaining) + bytes_read = tar->entry_bytes_remaining; + *size = bytes_read; + *offset = tar->entry_offset; + tar->entry_offset += bytes_read; + tar->entry_bytes_remaining -= bytes_read; + (a->compression_read_consume)(a, bytes_read); + return (ARCHIVE_OK); + } else { + while (tar->entry_padding > 0) { + bytes_read = (a->compression_read_ahead)(a, buff, 1); + if (bytes_read <= 0) + return (ARCHIVE_FATAL); + if (bytes_read > tar->entry_padding) + bytes_read = tar->entry_padding; + (a->compression_read_consume)(a, bytes_read); + tar->entry_padding -= bytes_read; + } + *buff = NULL; + *size = 0; + *offset = tar->entry_offset; + return (ARCHIVE_EOF); + } +} + /* * This function recursively interprets all of the headers associated * with a single entry. @@ -436,7 +480,7 @@ header_Solaris_ACL(struct archive *a, struct tar *tar, char *p; wchar_t *wp; - err = read_body_to_string(a, &(tar->acl_text), h); + err = read_body_to_string(a, tar, &(tar->acl_text), h); err2 = tar_read_header(a, tar, entry, st); err = err_combine(err, err2); @@ -470,7 +514,7 @@ header_longlink(struct archive *a, struct tar *tar, { int err, err2; - err = read_body_to_string(a, &(tar->longlink), h); + err = read_body_to_string(a, tar, &(tar->longlink), h); err2 = tar_read_header(a, tar, entry, st); if (err == ARCHIVE_OK && err2 == ARCHIVE_OK) { /* Set symlink if symlink already set, else hardlink. */ @@ -488,7 +532,7 @@ header_longname(struct archive *a, struct tar *tar, { int err, err2; - err = read_body_to_string(a, &(tar->longname), h); + err = read_body_to_string(a, tar, &(tar->longname), h); /* Read and parse "real" header, then override name. */ err2 = tar_read_header(a, tar, entry, st); if (err == ARCHIVE_OK && err2 == ARCHIVE_OK) @@ -514,7 +558,8 @@ header_volume(struct archive *a, struct tar *tar, * Read body of an archive entry into an archive_string object. */ static int -read_body_to_string(struct archive *a, struct archive_string *as, const void *h) +read_body_to_string(struct archive *a, struct tar *tar, + struct archive_string *as, const void *h) { const struct archive_entry_header_ustar *header; off_t size; @@ -529,8 +574,8 @@ read_body_to_string(struct archive *a, struct archive_string *as, const void *h) a->state = ARCHIVE_STATE_DATA; /* Read the body into the string. */ - a->entry_bytes_remaining = size; - a->entry_padding = 0x1ff & -size; + tar->entry_bytes_remaining = size; + tar->entry_padding = 0x1ff & -size; archive_string_ensure(as, size+1); err = archive_read_data_into_buffer(a, as->s, size); as->s[size] = 0; /* Null terminate name! */ @@ -707,8 +752,8 @@ header_old_tar(struct archive *a, struct tar *tar, struct archive_entry *entry, st->st_mode |= S_IFDIR; } - a->entry_bytes_remaining = st->st_size; - a->entry_padding = 0x1ff & (-a->entry_bytes_remaining); + tar->entry_bytes_remaining = st->st_size; + tar->entry_padding = 0x1ff & (-tar->entry_bytes_remaining); return (0); } @@ -721,7 +766,7 @@ header_pax_global(struct archive *a, struct tar *tar, { int err, err2; - err = read_body_to_string(a, &(tar->pax_global), h); + err = read_body_to_string(a, tar, &(tar->pax_global), h); err2 = tar_read_header(a, tar, entry, st); return (err_combine(err, err2)); } @@ -732,7 +777,7 @@ header_pax_extensions(struct archive *a, struct tar *tar, { int err, err2; - read_body_to_string(a, &(tar->pax_header), h); + read_body_to_string(a, tar, &(tar->pax_header), h); /* Parse the next header. */ err = tar_read_header(a, tar, entry, st); @@ -749,8 +794,8 @@ header_pax_extensions(struct archive *a, struct tar *tar, */ err2 = pax_header(a, tar, entry, st, tar->pax_header.s); err = err_combine(err, err2); - a->entry_bytes_remaining = st->st_size; - a->entry_padding = 0x1ff & (-a->entry_bytes_remaining); + tar->entry_bytes_remaining = st->st_size; + tar->entry_padding = 0x1ff & (-tar->entry_bytes_remaining); return (err); } @@ -799,8 +844,8 @@ header_ustar(struct archive *a, struct tar *tar, struct archive_entry *entry, tar_atol(header->devminor, sizeof(header->devminor))); } - a->entry_bytes_remaining = st->st_size; - a->entry_padding = 0x1ff & (-a->entry_bytes_remaining); + tar->entry_bytes_remaining = st->st_size; + tar->entry_padding = 0x1ff & (-tar->entry_bytes_remaining); return (0); } @@ -1144,8 +1189,8 @@ header_gnutar(struct archive *a, struct tar *tar, struct archive_entry *entry, /* XXX TODO: Recognize and skip extra GNU header blocks. */ - a->entry_bytes_remaining = st->st_size; - a->entry_padding = 0x1ff & (-a->entry_bytes_remaining); + tar->entry_bytes_remaining = st->st_size; + tar->entry_padding = 0x1ff & (-tar->entry_bytes_remaining); return (0); } |