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.
This commit is contained in:
parent
aa0aa7a113
commit
e250dd4fad
@ -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:
|
||||
|
@ -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:
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
||||
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);
|
||||
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);
|
||||
}
|
||||
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);
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user