Write-blocking cleanup, largely thanks to Colin Percival (cperciva@).

* If write block size is zero, don't block at all.
     This supports the unusual requirement of applications
     that need "no-delay" writes.
   * Expose _write_finish_entry() to give such applications more
     control over write boundaries.  (Normal applications do not
     need this, as entries are completed automatically.)
   * Correct the type of write callbacks; this is a minor API
     change that does not affect the ABI.
   * Correct the error handling in _write_next_header() around
     completing the previous entry.
   * Correct the documentation for block-size markers:  Remove
     docs for the long-defunct _read_set_block_size(); document
     all of the write block size manipulators.

MFC after: 14 days
This commit is contained in:
Tim Kientzle 2006-11-26 19:00:50 +00:00
parent cc005bb92c
commit dc46be1cbf
10 changed files with 91 additions and 34 deletions

View File

@ -161,7 +161,6 @@ MLINKS+= archive_read.3 archive_read_open_fd.3
MLINKS+= archive_read.3 archive_read_open_file.3
MLINKS+= archive_read.3 archive_read_open_filename.3
MLINKS+= archive_read.3 archive_read_open_memory.3
MLINKS+= archive_read.3 archive_read_set_bytes_per_block.3
MLINKS+= archive_read.3 archive_read_support_compression_all.3
MLINKS+= archive_read.3 archive_read_support_compression_bzip2.3
MLINKS+= archive_read.3 archive_read_support_compression_compress.3
@ -181,6 +180,9 @@ MLINKS+= archive_util.3 archive_format_name.3
MLINKS+= archive_util.3 archive_set_error.3
MLINKS+= archive_write.3 archive_write_data.3
MLINKS+= archive_write.3 archive_write_finish.3
MLINKS+= archive_write.3 archive_write_finish_entry.3
MLINKS+= archive_write.3 archive_write_get_bytes_in_last_block.3
MLINKS+= archive_write.3 archive_write_get_bytes_per_block.3
MLINKS+= archive_write.3 archive_write_header.3
MLINKS+= archive_write.3 archive_write_new.3
MLINKS+= archive_write.3 archive_write_open.3

View File

@ -116,7 +116,7 @@ typedef ssize_t archive_skip_callback(struct archive *, void *_client_data,
size_t request);
/* Returns size actually written, zero on EOF, -1 on error. */
typedef ssize_t archive_write_callback(struct archive *, void *_client_data,
void *_buffer, size_t _length);
const void *_buffer, size_t _length);
typedef int archive_open_callback(struct archive *, void *_client_data);
typedef int archive_close_callback(struct archive *, void *_client_data);
@ -358,6 +358,7 @@ int archive_write_header(struct archive *,
struct archive_entry *);
/* TODO: should be ssize_t, but that might require .so version bump? */
int archive_write_data(struct archive *, const void *, size_t);
int archive_write_finish_entry(struct archive *);
int archive_write_close(struct archive *);
void archive_write_finish(struct archive *);

View File

@ -29,7 +29,6 @@
.Os
.Sh NAME
.Nm archive_read_new ,
.Nm archive_read_set_bytes_per_block ,
.Nm archive_read_support_compression_all ,
.Nm archive_read_support_compression_bzip2 ,
.Nm archive_read_support_compression_compress ,
@ -62,8 +61,6 @@
.Ft struct archive *
.Fn archive_read_new "void"
.Ft int
.Fn archive_read_set_bytes_per_block "struct archive *" "int"
.Ft int
.Fn archive_read_support_compression_all "struct archive *"
.Ft int
.Fn archive_read_support_compression_bzip2 "struct archive *"
@ -129,11 +126,6 @@ order they would be used:
Allocates and initializes a
.Tn struct archive
object suitable for reading from an archive.
.It Fn archive_read_set_bytes_per_block
Sets the block size used for reading the archive data.
This controls the size that will be used when invoking the read
callback function.
The default is 20 records or 10240 bytes for tar formats.
.It Fn archive_read_support_compression_all , Fn archive_read_support_compression_bzip2 , Fn archive_read_support_compression_compress , Fn archive_read_support_compression_gzip , Fn archive_read_support_compression_none
Enables auto-detection code and decompression support for the
specified compression.

View File

@ -35,6 +35,7 @@
.Nm archive_write_set_format_shar ,
.Nm archive_write_set_format_shar_binary ,
.Nm archive_write_set_format_ustar ,
.Nm archive_write_get_bytes_per_block ,
.Nm archive_write_set_bytes_per_block ,
.Nm archive_write_set_bytes_in_last_block ,
.Nm archive_write_set_compressor_gzip ,
@ -46,6 +47,7 @@
.Nm archive_write_open_memory ,
.Nm archive_write_header ,
.Nm archive_write_data ,
.Nm archive_write_finish_entry ,
.Nm archive_write_close ,
.Nm archive_write_finish
.Nd functions for creating archives
@ -54,6 +56,8 @@
.Ft struct archive *
.Fn archive_write_new "void"
.Ft int
.Fn archive_write_get_bytes_per_block "struct archive *"
.Ft int
.Fn archive_write_set_bytes_per_block "struct archive *" "int bytes_per_block"
.Ft int
.Fn archive_write_set_bytes_in_last_block "struct archive *" "int"
@ -88,6 +92,8 @@
.Ft int
.Fn archive_write_data "struct archive *" "const void *" "size_t"
.Ft int
.Fn archive_write_finish_entry "struct archive *"
.Ft int
.Fn archive_write_close "struct archive *"
.Ft void
.Fn archive_write_finish "struct archive *"
@ -114,6 +120,12 @@ written will be padded to the full block size.
If it is zero, the last block will not be padded.
If it is non-zero, padding will be added both before and after compression.
The default is to use a block size of 10240 bytes and to pad the last block.
Note that a block size of zero will suppress internal blocking
and cause writes to be sent directly to the write callback as they occur.
.It Fn archive_write_get_bytes_per_block
Retrieve the block size to be used for writing.
A value of -1 here indicates that the library should use default values.
A value of zero indicates that internal blocking is suppressed.
.It Fn archive_write_set_bytes_in_last_block
Sets the block size used for writing the last block.
If this value is zero, the last block will be padded to the same size
@ -129,6 +141,9 @@ will set this based on the file type).
Unlike the other
.Dq set
functions, this function can be called after the archive is opened.
.It Fn archive_write_get_bytes_in_last_block
Retrieve the currently-set value for last block size.
A value of -1 here indicates that the library should use default values.
.It Fn archive_write_set_format_cpio , Fn archive_write_set_format_pax , Fn archive_write_set_format_pax_restricted , Fn archive_write_set_format_shar , Fn archive_write_set_format_shar_binary , Fn archive_write_set_format_ustar
Sets the format that will be used for the archive.
The library can write
@ -230,6 +245,15 @@ objects.
.It Fn archive_write_data
Write data corresponding to the header just written.
Returns number of bytes written or -1 on error.
.It Fn archive_write_finish_entry
Close out the entry just written.
In particular, this writes out the final padding required by some formats.
Ordinarily, clients never need to call this, as it
is called automatically by
.Fn archive_write_next_header
and
.Fn archive_write_close
as needed.
.It Fn archive_write_close
Complete the archive and invoke the close callback.
.It Fn archive_write_finish

View File

@ -232,22 +232,22 @@ archive_write_finish(struct archive *a)
free(a);
}
/*
* Write the appropriate header.
*/
int
archive_write_header(struct archive *a, struct archive_entry *entry)
{
int ret;
int ret, r2;
__archive_check_magic(a, ARCHIVE_WRITE_MAGIC,
ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA, "archive_write_header");
ARCHIVE_STATE_DATA | ARCHIVE_STATE_HEADER, "archive_write_header");
archive_string_empty(&a->error_string);
/* Finish last entry. */
if (a->state & ARCHIVE_STATE_DATA)
((a->format_finish_entry)(a));
/* In particular, "retry" and "fatal" get returned immediately. */
ret = archive_write_finish_entry(a);
if (ret < ARCHIVE_OK && ret != ARCHIVE_WARN)
return (ret);
if (a->skip_file_dev != 0 &&
archive_entry_dev(entry) == a->skip_file_dev &&
@ -258,12 +258,28 @@ archive_write_header(struct archive *a, struct archive_entry *entry)
}
/* Format and write header. */
ret = ((a->format_write_header)(a, entry));
r2 = ((a->format_write_header)(a, entry));
if (r2 < ret)
ret = r2;
a->state = ARCHIVE_STATE_DATA;
return (ret);
}
int
archive_write_finish_entry(struct archive * a)
{
int ret = ARCHIVE_OK;
__archive_check_magic(a, ARCHIVE_WRITE_MAGIC,
ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA,
"archive_write_finish_entry");
if (a->state & ARCHIVE_STATE_DATA)
ret = (a->format_finish_entry)(a);
a->state = ARCHIVE_STATE_HEADER;
return (ret);
}
/*
* Note that the compressor is responsible for blocking.
*/
@ -271,7 +287,8 @@ archive_write_header(struct archive *a, struct archive_entry *entry)
int
archive_write_data(struct archive *a, const void *buff, size_t s)
{
__archive_check_magic(a, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_DATA, "archive_write_data");
__archive_check_magic(a, ARCHIVE_WRITE_MAGIC,
ARCHIVE_STATE_DATA, "archive_write_data");
archive_string_empty(&a->error_string);
return ((a->format_write_data)(a, buff, s));
}

View File

@ -52,7 +52,7 @@ struct write_fd_data {
static int file_close(struct archive *, void *);
static int file_open(struct archive *, void *);
static ssize_t file_write(struct archive *, void *, void *buff, size_t);
static ssize_t file_write(struct archive *, void *, const void *buff, size_t);
int
archive_write_open_fd(struct archive *a, int fd)
@ -108,7 +108,7 @@ file_open(struct archive *a, void *client_data)
}
static ssize_t
file_write(struct archive *a, void *client_data, void *buff, size_t length)
file_write(struct archive *a, void *client_data, const void *buff, size_t length)
{
struct write_fd_data *mine;
ssize_t bytesWritten;

View File

@ -54,7 +54,7 @@ struct write_FILE_data {
static int file_close(struct archive *, void *);
static int file_open(struct archive *, void *);
static ssize_t file_write(struct archive *, void *, void *buff, size_t);
static ssize_t file_write(struct archive *, void *, const void *buff, size_t);
int
archive_write_open_FILE(struct archive *a, FILE *f)
@ -81,7 +81,7 @@ file_open(struct archive *a, void *client_data)
}
static ssize_t
file_write(struct archive *a, void *client_data, void *buff, size_t length)
file_write(struct archive *a, void *client_data, const void *buff, size_t length)
{
struct write_FILE_data *mine;
size_t bytesWritten;

View File

@ -55,7 +55,7 @@ struct write_file_data {
static int file_close(struct archive *, void *);
static int file_open(struct archive *, void *);
static ssize_t file_write(struct archive *, void *, void *buff, size_t);
static ssize_t file_write(struct archive *, void *, const void *buff, size_t);
int
archive_write_open_file(struct archive *a, const char *filename)
@ -143,7 +143,7 @@ file_open(struct archive *a, void *client_data)
}
static ssize_t
file_write(struct archive *a, void *client_data, void *buff, size_t length)
file_write(struct archive *a, void *client_data, const void *buff, size_t length)
{
struct write_file_data *mine;
ssize_t bytesWritten;

View File

@ -54,7 +54,7 @@ struct write_memory_data {
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);
static ssize_t memory_write(struct archive *, void *, const void *buff, size_t);
/*
* Client provides a pointer to a block of memory to receive
@ -100,7 +100,7 @@ memory_write_open(struct archive *a, void *client_data)
* 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)
memory_write(struct archive *a, void *client_data, const void *buff, size_t length)
{
struct write_memory_data *mine;
mine = client_data;

View File

@ -89,13 +89,14 @@ archive_compressor_none_init(struct archive *a)
memset(state, 0, sizeof(*state));
state->buffer_size = a->bytes_per_block;
state->buffer = (char *)malloc(state->buffer_size);
if (state->buffer == NULL) {
archive_set_error(a, ENOMEM,
"Can't allocate output buffer");
free(state);
return (ARCHIVE_FATAL);
if (state->buffer_size != 0) {
state->buffer = (char *)malloc(state->buffer_size);
if (state->buffer == NULL) {
archive_set_error(a, ENOMEM,
"Can't allocate output buffer");
free(state);
return (ARCHIVE_FATAL);
}
}
state->next = state->buffer;
@ -129,7 +130,27 @@ archive_compressor_none_write(struct archive *a, const void *vbuff,
}
remaining = length;
while (remaining > 0) {
/*
* If there is no buffer for blocking, just pass the data
* straight through to the client write callback. In
* particular, this supports "no write delay" operation for
* special applications. Just set the block size to zero.
*/
if (state->buffer_size == 0) {
while (remaining > 0) {
bytes_written = (a->client_writer)(a, a->client_data,
buff, remaining);
if (bytes_written <= 0)
return (ARCHIVE_FATAL);
remaining -= bytes_written;
buff += bytes_written;
}
a->file_position += length;
return (ARCHIVE_OK);
}
while ((remaining > 0) || (state->avail == 0)) {
/*
* If we have a full output block, write it and reset the
* output buffer.