mirror of
https://github.com/opnsense/src.git
synced 2026-04-22 14:49:36 -04:00
New hooks for reading/writing archives to/from a FILE * or
an in-memory buffer. PR: bin/86742
This commit is contained in:
parent
ac39496f20
commit
13e2d5bcd5
5 changed files with 354 additions and 211 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
147
lib/libarchive/archive_read_open_memory.c
Normal file
147
lib/libarchive/archive_read_open_memory.c
Normal file
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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));
|
||||
}
|
||||
struct write_FILE_data *mine;
|
||||
|
||||
int
|
||||
archive_write_open_filename(struct archive *a, const char *filename)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
|
|
|||
127
lib/libarchive/archive_write_open_memory.c
Normal file
127
lib/libarchive/archive_write_open_memory.c
Normal file
|
|
@ -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);
|
||||
}
|
||||
Loading…
Reference in a new issue