diff options
author | Tim Kientzle <kientzle@FreeBSD.org> | 2006-11-24 02:00:48 +0000 |
---|---|---|
committer | Tim Kientzle <kientzle@FreeBSD.org> | 2006-11-24 02:00:48 +0000 |
commit | 13e2d5bcd5d782ffbb0b00cfd56bc1a0c0d438fa (patch) | |
tree | 1718edba490b96d4478714d3358a3b2dfc19fda7 | |
parent | ac39496f20a3ad81373c2f442303af3d571d560c (diff) |
Notes
-rw-r--r-- | lib/libarchive/archive.h.in | 16 | ||||
-rw-r--r-- | lib/libarchive/archive_read_open_file.c | 177 | ||||
-rw-r--r-- | lib/libarchive/archive_read_open_memory.c | 147 | ||||
-rw-r--r-- | lib/libarchive/archive_write_open_file.c | 98 | ||||
-rw-r--r-- | lib/libarchive/archive_write_open_memory.c | 127 |
5 files changed, 354 insertions, 211 deletions
diff --git a/lib/libarchive/archive.h.in b/lib/libarchive/archive.h.in index 1deced1ad1c67..4567de7099e58 100644 --- a/lib/libarchive/archive.h.in +++ b/lib/libarchive/archive.h.in @@ -37,6 +37,7 @@ #include <sys/types.h> /* Linux requires this for off_t */ @ARCHIVE_H_INCLUDE_INTTYPES_H@ +#include <stdio.h> /* For FILE * */ #include <unistd.h> /* For ssize_t and size_t */ #ifdef __cplusplus @@ -58,6 +59,7 @@ extern "C" { * * 1 - Version tests are available. * 2 - archive_{read,write}_close available separately from _finish. + * 3 - open_memory, open_memory2, open_FILE, open_fd available */ #define ARCHIVE_API_VERSION @ARCHIVE_API_MAJOR@ int archive_api_version(void); @@ -206,9 +208,18 @@ int archive_read_open_filename(struct archive *, /* archive_read_open_file() is a deprecated synonym for ..._open_filename(). */ int archive_read_open_file(struct archive *, const char *_filename, size_t _block_size); +/* Read an archive that's stored in memory. */ +int archive_read_open_memory(struct archive *, + void * buff, size_t size); +/* A more involved version that is only used for internal testing. */ +int archive_read_open_memory2(struct archive *a, void *buff, + size_t size, size_t read_size); /* Read an archive that's already open, using the file descriptor. */ int archive_read_open_fd(struct archive *, int _fd, size_t _block_size); +/* Read an archive that's already open, using a FILE *. */ +/* Note: DO NOT use this with tape drives. */ +int archive_read_open_FILE(struct archive *, FILE *_file); /* Parses and returns next entry header. */ int archive_read_next_header(struct archive *, @@ -333,6 +344,11 @@ int archive_write_open_fd(struct archive *, int _fd); int archive_write_open_filename(struct archive *, const char *_file); /* A deprecated synonym for archive_write_open_filename() */ int archive_write_open_file(struct archive *, const char *_file); +int archive_write_open_FILE(struct archive *, FILE *); +/* _buffSize is the size of the buffer, _used refers to a variable that + * will be updated after each write into the buffer. */ +int archive_write_open_memory(struct archive *, + void *_buffer, size_t _buffSize, size_t *_used); /* * Note that the library will truncate writes beyond the size provided diff --git a/lib/libarchive/archive_read_open_file.c b/lib/libarchive/archive_read_open_file.c index cefc2861b13ef..cb18fc3ad9e13 100644 --- a/lib/libarchive/archive_read_open_file.c +++ b/lib/libarchive/archive_read_open_file.c @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2003-2004 Tim Kientzle + * Copyright (c) 2003-2006 Tim Kientzle * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -48,12 +48,10 @@ __FBSDID("$FreeBSD$"); #include "archive.h" -struct read_file_data { - int fd; +struct read_FILE_data { + FILE *f; size_t block_size; void *buffer; - mode_t st_mode; /* Mode bits for opened file. */ - char filename[1]; /* Must be last! */ }; static int file_close(struct archive *, void *); @@ -62,91 +60,54 @@ static ssize_t file_read(struct archive *, void *, const void **buff); static ssize_t file_skip(struct archive *, void *, size_t request); int -archive_read_open_file(struct archive *a, const char *filename, - size_t block_size) +archive_read_open_FILE(struct archive *a, FILE *f) { - return (archive_read_open_filename(a, filename, block_size)); -} + struct read_FILE_data *mine; -int -archive_read_open_filename(struct archive *a, const char *filename, - size_t block_size) -{ - struct read_file_data *mine; - - if (filename == NULL || filename[0] == '\0') { - mine = (struct read_file_data *)malloc(sizeof(*mine)); - if (mine == NULL) { - archive_set_error(a, ENOMEM, "No memory"); - return (ARCHIVE_FATAL); - } - mine->filename[0] = '\0'; - } else { - mine = (struct read_file_data *)malloc(sizeof(*mine) + strlen(filename)); - if (mine == NULL) { - archive_set_error(a, ENOMEM, "No memory"); - return (ARCHIVE_FATAL); - } - strcpy(mine->filename, filename); + mine = (struct read_FILE_data *)malloc(sizeof(*mine)); + if (mine == NULL) { + archive_set_error(a, ENOMEM, "No memory"); + return (ARCHIVE_FATAL); } - mine->block_size = block_size; - mine->buffer = NULL; - mine->fd = -1; - return (archive_read_open2(a, mine, file_open, file_read, file_skip, file_close)); + mine->block_size = 128 * 1024; + mine->buffer = malloc(mine->block_size); + if (mine->buffer == NULL) { + archive_set_error(a, ENOMEM, "No memory"); + free(mine); + return (ARCHIVE_FATAL); + } + mine->f = f; + return (archive_read_open2(a, mine, file_open, file_read, + file_skip, file_close)); } static int file_open(struct archive *a, void *client_data) { - struct read_file_data *mine = (struct read_file_data *)client_data; + struct read_FILE_data *mine = (struct read_FILE_data *)client_data; struct stat st; - mine->buffer = malloc(mine->block_size); - if (mine->buffer == NULL) { - archive_set_error(a, ENOMEM, "No memory"); - return (ARCHIVE_FATAL); - } - if (mine->filename[0] != '\0') - mine->fd = open(mine->filename, O_RDONLY); - else - mine->fd = 0; /* Fake "open" for stdin. */ - if (mine->fd < 0) { - archive_set_error(a, errno, "Failed to open '%s'", - mine->filename); - return (ARCHIVE_FATAL); - } - if (fstat(mine->fd, &st) == 0) { - /* If we're reading a file from disk, ensure that we don't - overwrite it with an extracted file. */ - if (S_ISREG(st.st_mode)) - archive_read_extract_set_skip_file(a, st.st_dev, st.st_ino); - /* Remember mode so close can decide whether to flush. */ - mine->st_mode = st.st_mode; - } else { - if (mine->filename[0] == '\0') - archive_set_error(a, errno, "Can't stat stdin"); - else - archive_set_error(a, errno, "Can't stat '%s'", - mine->filename); - return (ARCHIVE_FATAL); - } - return (0); + /* + * If we can't fstat() the file, it may just be that + * it's not a file. (FILE * objects can wrap many kinds + * of I/O streams.) + */ + if (fstat(fileno(mine->f), &st) == 0 && S_ISREG(st.st_mode)) + archive_read_extract_set_skip_file(a, st.st_dev, st.st_ino); + + return (ARCHIVE_OK); } static ssize_t file_read(struct archive *a, void *client_data, const void **buff) { - struct read_file_data *mine = (struct read_file_data *)client_data; + struct read_FILE_data *mine = (struct read_FILE_data *)client_data; ssize_t bytes_read; *buff = mine->buffer; - bytes_read = read(mine->fd, mine->buffer, mine->block_size); + bytes_read = fread(mine->buffer, 1, mine->block_size, mine->f); if (bytes_read < 0) { - if (mine->filename[0] == '\0') - archive_set_error(a, errno, "Error reading stdin"); - else - archive_set_error(a, errno, "Error reading '%s'", - mine->filename); + archive_set_error(a, errno, "Error reading file"); } return (bytes_read); } @@ -154,77 +115,33 @@ file_read(struct archive *a, void *client_data, const void **buff) static ssize_t file_skip(struct archive *a, void *client_data, size_t request) { - struct read_file_data *mine = (struct read_file_data *)client_data; - off_t old_offset, new_offset; + struct read_FILE_data *mine = (struct read_FILE_data *)client_data; - /* Reduce request to the next smallest multiple of block_size */ - request = (request / mine->block_size) * mine->block_size; /* - * Hurray for lazy evaluation: if the first lseek fails, the second - * one will not be executed. + * Note: the 'fd' and 'filename' versions round the request + * down to a multiple of the block size to ensure proper + * operation on block-oriented media such as tapes. But stdio + * doesn't work with such media (it doesn't ensure blocking), + * so we don't need to bother. */ - if (((old_offset = lseek(mine->fd, 0, SEEK_CUR)) < 0) || - ((new_offset = lseek(mine->fd, request, SEEK_CUR)) < 0)) +#if HAVE_FSEEKO + if (fseeko(mine->f, request, SEEK_CUR) != 0) +#else + if (fseek(mine->f, request, SEEK_CUR) != 0) +#endif { - if (errno == ESPIPE) - { - /* - * Failure to lseek() can be caused by the file - * descriptor pointing to a pipe, socket or FIFO. - * Return 0 here, so the compression layer will use - * read()s instead to advance the file descriptor. - * It's slower of course, but works as well. - */ - return (0); - } - /* - * There's been an error other than ESPIPE. This is most - * likely caused by a programmer error (too large request) - * or a corrupted archive file. - */ - if (mine->filename[0] == '\0') - /* - * Should never get here, since lseek() on stdin ought - * to return an ESPIPE error. - */ - archive_set_error(a, errno, "Error seeking in stdin"); - else - archive_set_error(a, errno, "Error seeking in '%s'", - mine->filename); - return (-1); + archive_set_error(a, errno, "Error skipping forward"); + return (ARCHIVE_FATAL); } - return (new_offset - old_offset); + return (request); } static int file_close(struct archive *a, void *client_data) { - struct read_file_data *mine = (struct read_file_data *)client_data; + struct read_FILE_data *mine = (struct read_FILE_data *)client_data; (void)a; /* UNUSED */ - - /* - * Sometimes, we should flush the input before closing. - * Regular files: faster to just close without flush. - * Devices: must not flush (user might need to - * read the "next" item on a non-rewind device). - * Pipes and sockets: must flush (otherwise, the - * program feeding the pipe or socket may complain). - * Here, I flush everything except for regular files and - * device nodes. - */ - if (!S_ISREG(mine->st_mode) - && !S_ISCHR(mine->st_mode) - && !S_ISBLK(mine->st_mode)) { - ssize_t bytesRead; - do { - bytesRead = read(mine->fd, mine->buffer, - mine->block_size); - } while (bytesRead > 0); - } - /* If a named file was opened, then it needs to be closed. */ - if (mine->filename[0] != '\0') - close(mine->fd); if (mine->buffer != NULL) free(mine->buffer); free(mine); diff --git a/lib/libarchive/archive_read_open_memory.c b/lib/libarchive/archive_read_open_memory.c new file mode 100644 index 0000000000000..a639d85de6557 --- /dev/null +++ b/lib/libarchive/archive_read_open_memory.c @@ -0,0 +1,147 @@ +/*- + * Copyright (c) 2003-2006 Tim Kientzle + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "archive_platform.h" +__FBSDID("$FreeBSD$"); + +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +#include "archive.h" + +/* + * Glue to read an archive from a block of memory. + * + * This is mostly a huge help in building test harnesses; + * test programs can build archives in memory and read them + * back again without having to mess with files on disk. + */ + +struct read_memory_data { + unsigned char *buffer; + unsigned char *end; + ssize_t read_size; +}; + +static int memory_read_close(struct archive *, void *); +static int memory_read_open(struct archive *, void *); +static ssize_t memory_read_skip(struct archive *, void *, size_t request); +static ssize_t memory_read(struct archive *, void *, const void **buff); + +int +archive_read_open_memory(struct archive *a, void *buff, size_t size) +{ + return archive_read_open_memory2(a, buff, size, size); +} + +/* + * Don't use _open_memory2() in production code; the archive_read_open_memory() + * version is the one you really want. This is just here so that + * test harnesses can exercise block operations inside the library. + */ +int +archive_read_open_memory2(struct archive *a, void *buff, + size_t size, size_t read_size) +{ + struct read_memory_data *mine; + + mine = (struct read_memory_data *)malloc(sizeof(*mine)); + if (mine == NULL) { + archive_set_error(a, ENOMEM, "No memory"); + return (ARCHIVE_FATAL); + } + memset(mine, 0, sizeof(*mine)); + mine->buffer = (unsigned char *)buff; + mine->end = mine->buffer + size; + mine->read_size = read_size; + return (archive_read_open2(a, mine, memory_read_open, + memory_read, memory_read_skip, memory_read_close)); +} + +/* + * There's nothing to open. + */ +static int +memory_read_open(struct archive *a, void *client_data) +{ + (void)a; /* UNUSED */ + (void)client_data; /* UNUSED */ + return (ARCHIVE_OK); +} + +/* + * This is scary simple: Just advance a pointer. Limiting + * to read_size is not technically necessary, but it exercises + * more of the internal logic when used with a small block size + * in a test harness. Production use should not specify a block + * size; then this is much faster. + */ +static ssize_t +memory_read(struct archive *a, void *client_data, const void **buff) +{ + struct read_memory_data *mine = (struct read_memory_data *)client_data; + ssize_t size; + + (void)a; /* UNUSED */ + *buff = mine->buffer; + size = mine->end - mine->buffer; + if (size > mine->read_size) + size = mine->read_size; + mine->buffer += size; + return (size); +} + +/* + * Advancing is just as simple. Again, this is doing more than + * necessary in order to better exercise internal code when used + * as a test harness. + */ +static ssize_t +memory_read_skip(struct archive *a, void *client_data, size_t skip) +{ + struct read_memory_data *mine = (struct read_memory_data *)client_data; + + (void)a; /* UNUSED */ + if (mine->buffer + skip > mine->end) + skip = mine->end - mine->buffer; + /* Round down to block size. */ + skip /= mine->read_size; + skip *= mine->read_size; + mine->buffer += skip; + return (skip); +} + +/* + * Close is just cleaning up our one small bit of data. + */ +static int +memory_read_close(struct archive *a, void *client_data) +{ + struct read_memory_data *mine = (struct read_memory_data *)client_data; + (void)a; /* UNUSED */ + free(mine); + return (ARCHIVE_OK); +} diff --git a/lib/libarchive/archive_write_open_file.c b/lib/libarchive/archive_write_open_file.c index c37caeb2fa31f..0eb1a0320445d 100644 --- a/lib/libarchive/archive_write_open_file.c +++ b/lib/libarchive/archive_write_open_file.c @@ -48,9 +48,8 @@ __FBSDID("$FreeBSD$"); #include "archive.h" -struct write_file_data { - int fd; - char filename[1]; +struct write_FILE_data { + FILE *f; }; static int file_close(struct archive *, void *); @@ -58,32 +57,16 @@ static int file_open(struct archive *, void *); static ssize_t file_write(struct archive *, void *, void *buff, size_t); int -archive_write_open_file(struct archive *a, const char *filename) +archive_write_open_FILE(struct archive *a, FILE *f) { - return (archive_write_open_filename(a, filename)); -} - -int -archive_write_open_filename(struct archive *a, const char *filename) -{ - struct write_file_data *mine; + struct write_FILE_data *mine; - if (filename == NULL || filename[0] == '\0') { - mine = (struct write_file_data *)malloc(sizeof(*mine)); - if (mine == NULL) { - archive_set_error(a, ENOMEM, "No memory"); - return (ARCHIVE_FATAL); - } - mine->filename[0] = '\0'; /* Record that we're using stdout. */ - } else { - mine = (struct write_file_data *)malloc(sizeof(*mine) + strlen(filename)); - if (mine == NULL) { - archive_set_error(a, ENOMEM, "No memory"); - return (ARCHIVE_FATAL); - } - strcpy(mine->filename, filename); + mine = (struct write_FILE_data *)malloc(sizeof(*mine)); + if (mine == NULL) { + archive_set_error(a, ENOMEM, "No memory"); + return (ARCHIVE_FATAL); } - mine->fd = -1; + mine->f = f; return (archive_write_open(a, mine, file_open, file_write, file_close)); } @@ -91,53 +74,8 @@ archive_write_open_filename(struct archive *a, const char *filename) static int file_open(struct archive *a, void *client_data) { - int flags; - struct write_file_data *mine; - struct stat st; - - mine = (struct write_file_data *)client_data; - flags = O_WRONLY | O_CREAT | O_TRUNC; - - /* - * Open the file. - */ - if (mine->filename[0] != '\0') { - mine->fd = open(mine->filename, flags, 0666); - if (mine->fd < 0) { - archive_set_error(a, errno, "Failed to open '%s'", - mine->filename); - return (ARCHIVE_FATAL); - } - } else { - /* - * NULL filename is stdout. - */ - mine->fd = 1; - /* By default, pad archive when writing to stdout. */ - if (archive_write_get_bytes_in_last_block(a) < 0) - archive_write_set_bytes_in_last_block(a, 0); - } - - /* - * Set up default last block handling. - */ - if (archive_write_get_bytes_in_last_block(a) < 0) { - if (S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode) || - S_ISFIFO(st.st_mode)) - /* Pad last block when writing to device or FIFO. */ - archive_write_set_bytes_in_last_block(a, 0); - else - /* Don't pad last block otherwise. */ - archive_write_set_bytes_in_last_block(a, 1); - } - - /* - * If the output file is a regular file, don't add it to - * itself. If it's a device file, it's okay to add the device - * entry to the output archive. - */ - if (S_ISREG(st.st_mode)) - archive_write_set_skip_file(a, st.st_dev, st.st_ino); + (void)a; /* UNUSED */ + (void)client_data; /* UNUSED */ return (ARCHIVE_OK); } @@ -145,12 +83,12 @@ file_open(struct archive *a, void *client_data) static ssize_t file_write(struct archive *a, void *client_data, void *buff, size_t length) { - struct write_file_data *mine; - ssize_t bytesWritten; + struct write_FILE_data *mine; + size_t bytesWritten; - mine = (struct write_file_data *)client_data; - bytesWritten = write(mine->fd, buff, length); - if (bytesWritten <= 0) { + mine = client_data; + bytesWritten = fwrite(buff, 1, length, mine->f); + if (bytesWritten < length) { archive_set_error(a, errno, "Write error"); return (-1); } @@ -160,11 +98,9 @@ file_write(struct archive *a, void *client_data, void *buff, size_t length) static int file_close(struct archive *a, void *client_data) { - struct write_file_data *mine = (struct write_file_data *)client_data; + struct write_FILE_data *mine = client_data; (void)a; /* UNUSED */ - if (mine->filename[0] != '\0') - close(mine->fd); free(mine); return (ARCHIVE_OK); } diff --git a/lib/libarchive/archive_write_open_memory.c b/lib/libarchive/archive_write_open_memory.c new file mode 100644 index 0000000000000..58baebdbed573 --- /dev/null +++ b/lib/libarchive/archive_write_open_memory.c @@ -0,0 +1,127 @@ +/*- + * Copyright (c) 2003-2006 Tim Kientzle + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer + * in this position and unchanged. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "archive_platform.h" +__FBSDID("$FreeBSD$"); + +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +#include "archive.h" + +/* + * This is a little tricky. I used to allow the + * compression handling layer to fork the compressor, + * which means this write function gets invoked in + * a separate process. That would, of course, make it impossible + * to actually use the data stored into memory here. + * Fortunately, none of the compressors fork today and + * I'm reluctant to use that route in the future but, if + * forking compressors ever do reappear, this will have + * to get a lot more complicated. + */ + +struct write_memory_data { + size_t used; + size_t size; + size_t * client_size; + unsigned char * buff; +}; + +static int memory_write_close(struct archive *, void *); +static int memory_write_open(struct archive *, void *); +static ssize_t memory_write(struct archive *, void *, void *buff, size_t); + +/* + * Client provides a pointer to a block of memory to receive + * the data. The 'size' param both tells us the size of the + * client buffer and lets us tell the client the final size. + */ +int +archive_write_open_memory(struct archive *a, void *buff, size_t buffSize, size_t *used) +{ + struct write_memory_data *mine; + + mine = (struct write_memory_data *)malloc(sizeof(*mine)); + if (mine == NULL) { + archive_set_error(a, ENOMEM, "No memory"); + return (ARCHIVE_FATAL); + } + memset(mine, 0, sizeof(*mine)); + mine->buff = buff; + mine->size = buffSize; + mine->client_size = used; + return (archive_write_open(a, mine, + memory_write_open, memory_write, memory_write_close)); +} + +static int +memory_write_open(struct archive *a, void *client_data) +{ + struct write_memory_data *mine; + mine = client_data; + mine->used = 0; + if (mine->client_size != NULL) + *mine->client_size = mine->used; + /* Disable padding if it hasn't been set explicitly. */ + if (-1 == archive_write_get_bytes_in_last_block(a)) + archive_write_set_bytes_in_last_block(a, 1); + return (ARCHIVE_OK); +} + +/* + * Copy the data into the client buffer. + * Note that we update mine->client_size on every write. + * In particular, this means the client can follow exactly + * how much has been written into their buffer at any time. + */ +static ssize_t +memory_write(struct archive *a, void *client_data, void *buff, size_t length) +{ + struct write_memory_data *mine; + mine = client_data; + + if (mine->used + length > mine->size) { + archive_set_error(a, ENOMEM, "Buffer exhausted"); + return (ARCHIVE_FATAL); + } + memcpy(mine->buff + mine->used, buff, length); + mine->used += length; + if (mine->client_size != NULL) + *mine->client_size = mine->used; + return (length); +} + +static int +memory_write_close(struct archive *a, void *client_data) +{ + struct write_memory_data *mine; + (void)a; /* UNUSED */ + mine = client_data; + free(mine); + return (ARCHIVE_OK); +} |