Merge from libarchive.googlecode.com: Numerous fixes to the

write options handling, including documentation.
This commit is contained in:
Tim Kientzle 2009-04-17 00:42:45 +00:00
parent 625683944d
commit 8952729381
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=191166
4 changed files with 357 additions and 229 deletions

View File

@ -43,6 +43,9 @@
.Nm archive_write_set_compression_gzip , .Nm archive_write_set_compression_gzip ,
.Nm archive_write_set_compression_none , .Nm archive_write_set_compression_none ,
.Nm archive_write_set_compression_program , .Nm archive_write_set_compression_program ,
.Nm archive_write_set_compressor_options ,
.Nm archive_write_set_format_options ,
.Nm archive_write_set_options ,
.Nm archive_write_open , .Nm archive_write_open ,
.Nm archive_write_open_fd , .Nm archive_write_open_fd ,
.Nm archive_write_open_FILE , .Nm archive_write_open_FILE ,
@ -73,10 +76,7 @@
.Ft int .Ft int
.Fn archive_write_set_compression_none "struct archive *" .Fn archive_write_set_compression_none "struct archive *"
.Ft int .Ft int
.Fo archive_write_set_compression_program .Fn archive_write_set_compression_program "struct archive *" "const char * cmd"
.Fa "struct archive *"
.Fa "const char * cmd"
.Fc
.Ft int .Ft int
.Fn archive_write_set_format_cpio "struct archive *" .Fn archive_write_set_format_cpio "struct archive *"
.Ft int .Ft int
@ -90,6 +90,12 @@
.Ft int .Ft int
.Fn archive_write_set_format_ustar "struct archive *" .Fn archive_write_set_format_ustar "struct archive *"
.Ft int .Ft int
.Fn archive_write_set_format_options "struct archive *" "const char *"
.Ft int
.Fn archive_write_set_compressor_options "struct archive *" "const char *"
.Ft int
.Fn archive_write_set_options "struct archive *" "const char *"
.Ft int
.Fo archive_write_open .Fo archive_write_open
.Fa "struct archive *" .Fa "struct archive *"
.Fa "void *client_data" .Fa "void *client_data"
@ -210,6 +216,68 @@ Note that the compressed output is always properly blocked.
The archive will be fed into the specified compression program. The archive will be fed into the specified compression program.
The output of that program is blocked and written to the client The output of that program is blocked and written to the client
write callbacks. write callbacks.
.It Xo
.Fn archive_write_set_compressor_options ,
.Fn archive_write_set_format_options ,
.Fn archive_write_set_options
.Xc
Specifies options that will be passed to the currently-enabled
compressor and/or format writer.
The argument is a comma-separated list of individual options.
Individual options have one of the following forms:
.Bl -tag -compact -width indent
.It Ar option=value
The option/value pair will be provided to every module.
Modules that do not accept an option with this name will ignore it.
.It Ar option
The option will be provided to every module with a value of
.Dq 1 .
.It Ar !option
The option will be provided to every module with a NULL value.
.It Ar module:option=value , Ar module:option , Ar module:!option
As above, but the corresponding option and value will be provided
only to modules whose name matches
.Ar module .
.El
The return value will be
.Cm ARCHIVE_OK
if any module accepts the option, or
.Cm ARCHIVE_WARN
if no module accepted the option, or
.Cm ARCHIVE_FATAL
if there was a fatal error while attempting to process the option.
.Pp
The currently supported options are:
.Bl -tag -compact -width indent
.It Compressor gzip
.Bl -tag -compact -width indent
.It Cm compression-level
The value is interpreted as a decimal integer specifying the
gzip compression level.
.El
.It Compressor xz
.Bl -tag -compact -width indent
.It Cm compression-level
The value is interpreted as a decimal integer specifying the
compression level.
.El
.It Format mtree
.Bl -tag -compact -width indent
.It Cm cksum , Cm device , Cm flags , Cm gid , Cm gname , Cm indent , Cm link , Cm md5 , Cm mode , Cm nlink , Cm rmd160 , Cm sha1 , Cm sha256 , Cm sha384 , Cm sha512 , Cm size , Cm time , Cm uid , Cm uname
Enable a particular keyword in the mtree output.
Prefix with an exclamation mark to disable the corresponding keyword.
The default is equivalent to
.Dq device, flags, gid, gname, link, mode, nlink, size, time, type, uid, uname .
.It Cm all
Enables all of the above keywords.
.It Cm use-set
Enables generation of
.Cm /set
lines that specify default values for the following files and/or directories.
.It Cm indent
XXX needs explanation XXX
.El
.El
.It Fn archive_write_open .It Fn archive_write_open
Freeze the settings, open the archive, and prepare for writing entries. Freeze the settings, open the archive, and prepare for writing entries.
This is the most generic form of this function, which accepts This is the most generic form of this function, which accepts

View File

@ -132,8 +132,14 @@ archive_write_set_format_options(struct archive *_a, const char *s)
{ {
struct archive_write *a = (struct archive_write *)_a; struct archive_write *a = (struct archive_write *)_a;
char key[64], val[64]; char key[64], val[64];
int len, r; int len, r, ret = ARCHIVE_OK;
__archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
ARCHIVE_STATE_NEW, "archive_write_set_format_options");
archive_clear_error(&a->archive);
if (s == NULL || *s == '\0')
return (ARCHIVE_OK);
if (a->format_options == NULL) if (a->format_options == NULL)
/* This format does not support option. */ /* This format does not support option. */
return (ARCHIVE_OK); return (ARCHIVE_OK);
@ -146,14 +152,19 @@ archive_write_set_format_options(struct archive *_a, const char *s)
r = a->format_options(a, key, val); r = a->format_options(a, key, val);
if (r == ARCHIVE_FATAL) if (r == ARCHIVE_FATAL)
return (r); return (r);
if (r < ARCHIVE_OK) { /* This key was not handled. */
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
"Unsupported option ``%s''", key);
ret = ARCHIVE_WARN;
}
s += len; s += len;
} }
if (len < 0) { if (len < 0) {
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
"Illegal format options."); "Malformed options string.");
return (ARCHIVE_WARN); return (ARCHIVE_WARN);
} }
return (ARCHIVE_OK); return (ret);
} }
/* /*
@ -165,10 +176,20 @@ archive_write_set_compressor_options(struct archive *_a, const char *s)
struct archive_write *a = (struct archive_write *)_a; struct archive_write *a = (struct archive_write *)_a;
char key[64], val[64]; char key[64], val[64];
int len, r; int len, r;
int ret = ARCHIVE_OK;
if (a->compressor.options == NULL) __archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
/* This compressor does not support option. */ ARCHIVE_STATE_NEW, "archive_write_set_compressor_options");
archive_clear_error(&a->archive);
if (s == NULL || *s == '\0')
return (ARCHIVE_OK); return (ARCHIVE_OK);
if (a->compressor.options == NULL) {
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
"Unsupported option ``%s''", s);
/* This compressor does not support option. */
return (ARCHIVE_WARN);
}
while ((len = __archive_parse_options(s, a->archive.compression_name, while ((len = __archive_parse_options(s, a->archive.compression_name,
sizeof(key), key, sizeof(val), val)) > 0) { sizeof(key), key, sizeof(val), val)) > 0) {
@ -178,6 +199,11 @@ archive_write_set_compressor_options(struct archive *_a, const char *s)
r = a->compressor.options(a, key, val); r = a->compressor.options(a, key, val);
if (r == ARCHIVE_FATAL) if (r == ARCHIVE_FATAL)
return (r); return (r);
if (r < ARCHIVE_OK) {
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
"Unsupported option ``%s''", key);
ret = ARCHIVE_WARN;
}
s += len; s += len;
} }
if (len < 0) { if (len < 0) {
@ -185,7 +211,7 @@ archive_write_set_compressor_options(struct archive *_a, const char *s)
"Illegal format options."); "Illegal format options.");
return (ARCHIVE_WARN); return (ARCHIVE_WARN);
} }
return (ARCHIVE_OK); return (ret);
} }
/* /*
@ -194,14 +220,16 @@ archive_write_set_compressor_options(struct archive *_a, const char *s)
int int
archive_write_set_options(struct archive *_a, const char *s) archive_write_set_options(struct archive *_a, const char *s)
{ {
int r; int r1, r2;
r = archive_write_set_format_options(_a, s); r1 = archive_write_set_format_options(_a, s);
if (r != ARCHIVE_OK) if (r1 < ARCHIVE_WARN)
return (r); return (r1);
r = archive_write_set_compressor_options(_a, s); r2 = archive_write_set_compressor_options(_a, s);
if (r != ARCHIVE_OK) if (r2 < ARCHIVE_WARN)
return (r); return (r2);
if (r1 == ARCHIVE_WARN && r2 == ARCHIVE_WARN)
return (ARCHIVE_WARN);
return (ARCHIVE_OK); return (ARCHIVE_OK);
} }

View File

@ -47,9 +47,10 @@ __FBSDID("$FreeBSD$");
#ifndef HAVE_BZLIB_H #ifndef HAVE_BZLIB_H
int int
archive_write_set_compression_bzip2(struct archive *_a) archive_write_set_compression_bzip2(struct archive *a)
{ {
/* Unsupported bzip2 compression, we don't have bzlib */ archive_set_error(a, ARCHIVE_ERRNO_MISC,
"bzip2 compression not supported on this platform");
return (ARCHIVE_FATAL); return (ARCHIVE_FATAL);
} }
#else #else
@ -62,6 +63,9 @@ struct private_data {
size_t compressed_buffer_size; size_t compressed_buffer_size;
}; };
struct private_config {
int compression_level;
};
/* /*
* Yuck. bzlib.h is not const-correct, so I need this one bit * Yuck. bzlib.h is not const-correct, so I need this one bit
@ -72,6 +76,8 @@ struct private_data {
static int archive_compressor_bzip2_finish(struct archive_write *); static int archive_compressor_bzip2_finish(struct archive_write *);
static int archive_compressor_bzip2_init(struct archive_write *); static int archive_compressor_bzip2_init(struct archive_write *);
static int archive_compressor_bzip2_options(struct archive_write *,
const char *, const char *);
static int archive_compressor_bzip2_write(struct archive_write *, static int archive_compressor_bzip2_write(struct archive_write *,
const void *, size_t); const void *, size_t);
static int drive_compressor(struct archive_write *, struct private_data *, static int drive_compressor(struct archive_write *, struct private_data *,
@ -84,9 +90,21 @@ int
archive_write_set_compression_bzip2(struct archive *_a) archive_write_set_compression_bzip2(struct archive *_a)
{ {
struct archive_write *a = (struct archive_write *)_a; struct archive_write *a = (struct archive_write *)_a;
struct private_config *config;
__archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC, __archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
ARCHIVE_STATE_NEW, "archive_write_set_compression_bzip2"); ARCHIVE_STATE_NEW, "archive_write_set_compression_bzip2");
config = malloc(sizeof(*config));
if (config == NULL) {
archive_set_error(&a->archive, ENOMEM, "Out of memory");
return (ARCHIVE_FATAL);
}
a->compressor.config = config;
a->compressor.finish = archive_compressor_bzip2_finish;
config->compression_level = 9; /* default */
a->compressor.init = &archive_compressor_bzip2_init; a->compressor.init = &archive_compressor_bzip2_init;
a->compressor.options = &archive_compressor_bzip2_options;
a->archive.compression_code = ARCHIVE_COMPRESSION_BZIP2;
a->archive.compression_name = "bzip2";
return (ARCHIVE_OK); return (ARCHIVE_OK);
} }
@ -98,10 +116,9 @@ archive_compressor_bzip2_init(struct archive_write *a)
{ {
int ret; int ret;
struct private_data *state; struct private_data *state;
struct private_config *config;
a->archive.compression_code = ARCHIVE_COMPRESSION_BZIP2; config = (struct private_config *)a->compressor.config;
a->archive.compression_name = "bzip2";
if (a->client_opener != NULL) { if (a->client_opener != NULL) {
ret = (a->client_opener)(&a->archive, a->client_data); ret = (a->client_opener)(&a->archive, a->client_data);
if (ret != 0) if (ret != 0)
@ -129,10 +146,10 @@ archive_compressor_bzip2_init(struct archive_write *a)
state->stream.next_out = state->compressed; state->stream.next_out = state->compressed;
state->stream.avail_out = state->compressed_buffer_size; state->stream.avail_out = state->compressed_buffer_size;
a->compressor.write = archive_compressor_bzip2_write; a->compressor.write = archive_compressor_bzip2_write;
a->compressor.finish = archive_compressor_bzip2_finish;
/* Initialize compression library */ /* Initialize compression library */
ret = BZ2_bzCompressInit(&(state->stream), 9, 0, 30); ret = BZ2_bzCompressInit(&(state->stream),
config->compression_level, 0, 30);
if (ret == BZ_OK) { if (ret == BZ_OK) {
a->compressor.data = state; a->compressor.data = state;
return (ARCHIVE_OK); return (ARCHIVE_OK);
@ -167,6 +184,32 @@ archive_compressor_bzip2_init(struct archive_write *a)
} }
/*
* Set write options.
*/
static int
archive_compressor_bzip2_options(struct archive_write *a, const char *key,
const char *value)
{
struct private_config *config;
config = (struct private_config *)a->compressor.config;
if (strcmp(key, "compression-level") == 0) {
if (value == NULL || !(value[0] >= '0' && value[0] <= '9') ||
value[1] != '\0')
return (ARCHIVE_WARN);
config->compression_level = value[0] - '0';
/* Make '0' be a synonym for '1'. */
/* This way, bzip2 compressor supports the same 0..9
* range of levels as gzip. */
if (config->compression_level < 1)
config->compression_level = 1;
return (ARCHIVE_OK);
}
return (ARCHIVE_WARN);
}
/* /*
* Write data to the compressed stream. * Write data to the compressed stream.
* *
@ -212,83 +255,88 @@ archive_compressor_bzip2_finish(struct archive_write *a)
ssize_t bytes_written; ssize_t bytes_written;
unsigned tocopy; unsigned tocopy;
state = (struct private_data *)a->compressor.data;
ret = ARCHIVE_OK; ret = ARCHIVE_OK;
if (a->client_writer == NULL) { state = (struct private_data *)a->compressor.data;
archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, if (state != NULL) {
"No write callback is registered?\n" if (a->client_writer == NULL) {
"This is probably an internal programming error."); archive_set_error(&a->archive,
ret = ARCHIVE_FATAL; ARCHIVE_ERRNO_PROGRAMMER,
goto cleanup; "No write callback is registered?\n"
} "This is probably an internal programming error.");
ret = ARCHIVE_FATAL;
/* By default, always pad the uncompressed data. */ goto cleanup;
if (a->pad_uncompressed) {
tocopy = a->bytes_per_block -
(state->total_in % a->bytes_per_block);
while (tocopy > 0 && tocopy < (unsigned)a->bytes_per_block) {
SET_NEXT_IN(state, a->nulls);
state->stream.avail_in = tocopy < a->null_length ?
tocopy : a->null_length;
state->total_in += state->stream.avail_in;
tocopy -= state->stream.avail_in;
ret = drive_compressor(a, state, 0);
if (ret != ARCHIVE_OK)
goto cleanup;
} }
}
/* Finish compression cycle. */ /* By default, always pad the uncompressed data. */
if ((ret = drive_compressor(a, state, 1))) if (a->pad_uncompressed) {
goto cleanup; tocopy = a->bytes_per_block -
(state->total_in % a->bytes_per_block);
while (tocopy > 0 && tocopy < (unsigned)a->bytes_per_block) {
SET_NEXT_IN(state, a->nulls);
state->stream.avail_in = tocopy < a->null_length ?
tocopy : a->null_length;
state->total_in += state->stream.avail_in;
tocopy -= state->stream.avail_in;
ret = drive_compressor(a, state, 0);
if (ret != ARCHIVE_OK)
goto cleanup;
}
}
/* Optionally, pad the final compressed block. */ /* Finish compression cycle. */
block_length = state->stream.next_out - state->compressed; if ((ret = drive_compressor(a, state, 1)))
goto cleanup;
/* Optionally, pad the final compressed block. */
block_length = state->stream.next_out - state->compressed;
/* Tricky calculation to determine size of last block. */ /* Tricky calculation to determine size of last block. */
target_block_length = block_length; target_block_length = block_length;
if (a->bytes_in_last_block <= 0) if (a->bytes_in_last_block <= 0)
/* Default or Zero: pad to full block */ /* Default or Zero: pad to full block */
target_block_length = a->bytes_per_block; target_block_length = a->bytes_per_block;
else else
/* Round length to next multiple of bytes_in_last_block. */ /* Round length to next multiple of bytes_in_last_block. */
target_block_length = a->bytes_in_last_block * target_block_length = a->bytes_in_last_block *
( (block_length + a->bytes_in_last_block - 1) / ( (block_length + a->bytes_in_last_block - 1) /
a->bytes_in_last_block); a->bytes_in_last_block);
if (target_block_length > a->bytes_per_block) if (target_block_length > a->bytes_per_block)
target_block_length = a->bytes_per_block; target_block_length = a->bytes_per_block;
if (block_length < target_block_length) { if (block_length < target_block_length) {
memset(state->stream.next_out, 0, memset(state->stream.next_out, 0,
target_block_length - block_length); target_block_length - block_length);
block_length = target_block_length; block_length = target_block_length;
} }
/* Write the last block */ /* Write the last block */
bytes_written = (a->client_writer)(&a->archive, a->client_data, bytes_written = (a->client_writer)(&a->archive, a->client_data,
state->compressed, block_length); state->compressed, block_length);
/* TODO: Handle short write of final block. */ /* TODO: Handle short write of final block. */
if (bytes_written <= 0) if (bytes_written <= 0)
ret = ARCHIVE_FATAL; ret = ARCHIVE_FATAL;
else { else {
a->archive.raw_position += ret; a->archive.raw_position += ret;
ret = ARCHIVE_OK; ret = ARCHIVE_OK;
} }
/* Cleanup: shut down compressor, release memory, etc. */ /* Cleanup: shut down compressor, release memory, etc. */
cleanup: cleanup:
switch (BZ2_bzCompressEnd(&(state->stream))) { switch (BZ2_bzCompressEnd(&(state->stream))) {
case BZ_OK: case BZ_OK:
break; break;
default: default:
archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
"Failed to clean up compressor"); "Failed to clean up compressor");
ret = ARCHIVE_FATAL; ret = ARCHIVE_FATAL;
} }
free(state->compressed); free(state->compressed);
free(state); free(state);
}
/* Free configuration data even if we were never fully initialized. */
free(a->compressor.config);
a->compressor.config = NULL;
return (ret); return (ret);
} }

View File

@ -47,9 +47,10 @@ __FBSDID("$FreeBSD$");
#ifndef HAVE_ZLIB_H #ifndef HAVE_ZLIB_H
int int
archive_write_set_compression_gzip(struct archive *_a) archive_write_set_compression_gzip(struct archive *a)
{ {
/* Unsupported gzip compression, we don't have zlib */ archive_set_error(a, ARCHIVE_ERRNO_MISC,
"gzip compression not supported on this platform");
return (ARCHIVE_FATAL); return (ARCHIVE_FATAL);
} }
#else #else
@ -61,7 +62,9 @@ struct private_data {
unsigned char *compressed; unsigned char *compressed;
size_t compressed_buffer_size; size_t compressed_buffer_size;
unsigned long crc; unsigned long crc;
/* Options */ };
struct private_config {
int compression_level; int compression_level;
}; };
@ -90,9 +93,19 @@ int
archive_write_set_compression_gzip(struct archive *_a) archive_write_set_compression_gzip(struct archive *_a)
{ {
struct archive_write *a = (struct archive_write *)_a; struct archive_write *a = (struct archive_write *)_a;
struct private_config *config;
__archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC, __archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
ARCHIVE_STATE_NEW, "archive_write_set_compression_gzip"); ARCHIVE_STATE_NEW, "archive_write_set_compression_gzip");
config = malloc(sizeof(*config));
if (config == NULL) {
archive_set_error(&a->archive, ENOMEM, "Out of memory");
return (ARCHIVE_FATAL);
}
a->compressor.config = config;
a->compressor.finish = &archive_compressor_gzip_finish;
config->compression_level = Z_DEFAULT_COMPRESSION;
a->compressor.init = &archive_compressor_gzip_init; a->compressor.init = &archive_compressor_gzip_init;
a->compressor.options = &archive_compressor_gzip_options;
a->archive.compression_code = ARCHIVE_COMPRESSION_GZIP; a->archive.compression_code = ARCHIVE_COMPRESSION_GZIP;
a->archive.compression_name = "gzip"; a->archive.compression_name = "gzip";
return (ARCHIVE_OK); return (ARCHIVE_OK);
@ -106,10 +119,10 @@ archive_compressor_gzip_init(struct archive_write *a)
{ {
int ret; int ret;
struct private_data *state; struct private_data *state;
struct private_config *config;
time_t t; time_t t;
a->archive.compression_code = ARCHIVE_COMPRESSION_GZIP; config = (struct private_config *)a->compressor.config;
a->archive.compression_name = "gzip";
if (a->client_opener != NULL) { if (a->client_opener != NULL) {
ret = (a->client_opener)(&a->archive, a->client_data); ret = (a->client_opener)(&a->archive, a->client_data);
@ -147,7 +160,6 @@ archive_compressor_gzip_init(struct archive_write *a)
state->compressed_buffer_size = a->bytes_per_block; state->compressed_buffer_size = a->bytes_per_block;
state->compressed = (unsigned char *)malloc(state->compressed_buffer_size); state->compressed = (unsigned char *)malloc(state->compressed_buffer_size);
state->crc = crc32(0L, NULL, 0); state->crc = crc32(0L, NULL, 0);
state->compression_level = Z_DEFAULT_COMPRESSION;
if (state->compressed == NULL) { if (state->compressed == NULL) {
archive_set_error(&a->archive, ENOMEM, archive_set_error(&a->archive, ENOMEM,
@ -174,13 +186,11 @@ archive_compressor_gzip_init(struct archive_write *a)
state->stream.next_out += 10; state->stream.next_out += 10;
state->stream.avail_out -= 10; state->stream.avail_out -= 10;
a->compressor.options = archive_compressor_gzip_options;
a->compressor.write = archive_compressor_gzip_write; a->compressor.write = archive_compressor_gzip_write;
a->compressor.finish = archive_compressor_gzip_finish;
/* Initialize compression library. */ /* Initialize compression library. */
ret = deflateInit2(&(state->stream), ret = deflateInit2(&(state->stream),
state->compression_level, config->compression_level,
Z_DEFLATED, Z_DEFLATED,
-15 /* < 0 to suppress zlib header */, -15 /* < 0 to suppress zlib header */,
8, 8,
@ -225,45 +235,15 @@ static int
archive_compressor_gzip_options(struct archive_write *a, const char *key, archive_compressor_gzip_options(struct archive_write *a, const char *key,
const char *value) const char *value)
{ {
struct private_data *state; struct private_config *config;
int ret;
state = (struct private_data *)a->compressor.data; config = (struct private_config *)a->compressor.config;
if (strcmp(key, "compression-level") == 0) { if (strcmp(key, "compression-level") == 0) {
int level;
if (value == NULL || !(value[0] >= '0' && value[0] <= '9') || if (value == NULL || !(value[0] >= '0' && value[0] <= '9') ||
value[1] != '\0') value[1] != '\0')
return (ARCHIVE_WARN); return (ARCHIVE_WARN);
level = value[0] - '0'; config->compression_level = value[0] - '0';
if (level == state->compression_level) return (ARCHIVE_OK);
return (ARCHIVE_OK);
ret = deflateParams(&(state->stream), level,
Z_DEFAULT_STRATEGY);
if (ret == Z_OK) {
state->compression_level = level;
return (ARCHIVE_OK);
}
switch (ret) {
case Z_STREAM_ERROR:
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
"Internal error updating params "
"compression library: state was inconsistent "
"or parameter was invalid");
break;
case Z_BUF_ERROR:
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
"Internal error updating params "
"compression library: out buffer was zero");
break;
default:
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
"Internal error updatng params "
"compression library");
break;
}
return (ARCHIVE_FATAL);
} }
return (ARCHIVE_WARN); return (ARCHIVE_WARN);
@ -301,7 +281,6 @@ archive_compressor_gzip_write(struct archive_write *a, const void *buff,
return (ARCHIVE_OK); return (ARCHIVE_OK);
} }
/* /*
* Finish the compression... * Finish the compression...
*/ */
@ -316,113 +295,118 @@ archive_compressor_gzip_finish(struct archive_write *a)
state = (struct private_data *)a->compressor.data; state = (struct private_data *)a->compressor.data;
ret = 0; ret = 0;
if (a->client_writer == NULL) { if (state != NULL) {
archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, if (a->client_writer == NULL) {
"No write callback is registered? " archive_set_error(&a->archive,
"This is probably an internal programming error."); ARCHIVE_ERRNO_PROGRAMMER,
ret = ARCHIVE_FATAL; "No write callback is registered? "
goto cleanup; "This is probably an internal programming error.");
} ret = ARCHIVE_FATAL;
goto cleanup;
/* By default, always pad the uncompressed data. */
if (a->pad_uncompressed) {
tocopy = a->bytes_per_block -
(state->total_in % a->bytes_per_block);
while (tocopy > 0 && tocopy < (unsigned)a->bytes_per_block) {
SET_NEXT_IN(state, a->nulls);
state->stream.avail_in = tocopy < a->null_length ?
tocopy : a->null_length;
state->crc = crc32(state->crc, a->nulls,
state->stream.avail_in);
state->total_in += state->stream.avail_in;
tocopy -= state->stream.avail_in;
ret = drive_compressor(a, state, 0);
if (ret != ARCHIVE_OK)
goto cleanup;
} }
}
/* Finish compression cycle */ /* By default, always pad the uncompressed data. */
if (((ret = drive_compressor(a, state, 1))) != ARCHIVE_OK) if (a->pad_uncompressed) {
goto cleanup; tocopy = a->bytes_per_block -
(state->total_in % a->bytes_per_block);
while (tocopy > 0 && tocopy < (unsigned)a->bytes_per_block) {
SET_NEXT_IN(state, a->nulls);
state->stream.avail_in = tocopy < a->null_length ?
tocopy : a->null_length;
state->crc = crc32(state->crc, a->nulls,
state->stream.avail_in);
state->total_in += state->stream.avail_in;
tocopy -= state->stream.avail_in;
ret = drive_compressor(a, state, 0);
if (ret != ARCHIVE_OK)
goto cleanup;
}
}
/* Build trailer: 4-byte CRC and 4-byte length. */ /* Finish compression cycle */
trailer[0] = (state->crc)&0xff; if (((ret = drive_compressor(a, state, 1))) != ARCHIVE_OK)
trailer[1] = (state->crc >> 8)&0xff; goto cleanup;
trailer[2] = (state->crc >> 16)&0xff;
trailer[3] = (state->crc >> 24)&0xff;
trailer[4] = (state->total_in)&0xff;
trailer[5] = (state->total_in >> 8)&0xff;
trailer[6] = (state->total_in >> 16)&0xff;
trailer[7] = (state->total_in >> 24)&0xff;
/* Add trailer to current block. */ /* Build trailer: 4-byte CRC and 4-byte length. */
tocopy = 8; trailer[0] = (state->crc)&0xff;
if (tocopy > state->stream.avail_out) trailer[1] = (state->crc >> 8)&0xff;
tocopy = state->stream.avail_out; trailer[2] = (state->crc >> 16)&0xff;
memcpy(state->stream.next_out, trailer, tocopy); trailer[3] = (state->crc >> 24)&0xff;
state->stream.next_out += tocopy; trailer[4] = (state->total_in)&0xff;
state->stream.avail_out -= tocopy; trailer[5] = (state->total_in >> 8)&0xff;
trailer[6] = (state->total_in >> 16)&0xff;
trailer[7] = (state->total_in >> 24)&0xff;
/* If it overflowed, flush and start a new block. */ /* Add trailer to current block. */
if (tocopy < 8) { tocopy = 8;
if (tocopy > state->stream.avail_out)
tocopy = state->stream.avail_out;
memcpy(state->stream.next_out, trailer, tocopy);
state->stream.next_out += tocopy;
state->stream.avail_out -= tocopy;
/* If it overflowed, flush and start a new block. */
if (tocopy < 8) {
bytes_written = (a->client_writer)(&a->archive, a->client_data,
state->compressed, state->compressed_buffer_size);
if (bytes_written <= 0) {
ret = ARCHIVE_FATAL;
goto cleanup;
}
a->archive.raw_position += bytes_written;
state->stream.next_out = state->compressed;
state->stream.avail_out = state->compressed_buffer_size;
memcpy(state->stream.next_out, trailer + tocopy, 8-tocopy);
state->stream.next_out += 8-tocopy;
state->stream.avail_out -= 8-tocopy;
}
/* Optionally, pad the final compressed block. */
block_length = state->stream.next_out - state->compressed;
/* Tricky calculation to determine size of last block. */
target_block_length = block_length;
if (a->bytes_in_last_block <= 0)
/* Default or Zero: pad to full block */
target_block_length = a->bytes_per_block;
else
/* Round length to next multiple of bytes_in_last_block. */
target_block_length = a->bytes_in_last_block *
( (block_length + a->bytes_in_last_block - 1) /
a->bytes_in_last_block);
if (target_block_length > a->bytes_per_block)
target_block_length = a->bytes_per_block;
if (block_length < target_block_length) {
memset(state->stream.next_out, 0,
target_block_length - block_length);
block_length = target_block_length;
}
/* Write the last block */
bytes_written = (a->client_writer)(&a->archive, a->client_data, bytes_written = (a->client_writer)(&a->archive, a->client_data,
state->compressed, state->compressed_buffer_size); state->compressed, block_length);
if (bytes_written <= 0) { if (bytes_written <= 0) {
ret = ARCHIVE_FATAL; ret = ARCHIVE_FATAL;
goto cleanup; goto cleanup;
} }
a->archive.raw_position += bytes_written; a->archive.raw_position += bytes_written;
state->stream.next_out = state->compressed;
state->stream.avail_out = state->compressed_buffer_size; /* Cleanup: shut down compressor, release memory, etc. */
memcpy(state->stream.next_out, trailer + tocopy, 8-tocopy); cleanup:
state->stream.next_out += 8-tocopy; switch (deflateEnd(&(state->stream))) {
state->stream.avail_out -= 8-tocopy; case Z_OK:
break;
default:
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
"Failed to clean up compressor");
ret = ARCHIVE_FATAL;
}
free(state->compressed);
free(state);
} }
/* Clean up config area even if we never initialized. */
/* Optionally, pad the final compressed block. */ free(a->compressor.config);
block_length = state->stream.next_out - state->compressed; a->compressor.config = NULL;
/* Tricky calculation to determine size of last block. */
target_block_length = block_length;
if (a->bytes_in_last_block <= 0)
/* Default or Zero: pad to full block */
target_block_length = a->bytes_per_block;
else
/* Round length to next multiple of bytes_in_last_block. */
target_block_length = a->bytes_in_last_block *
( (block_length + a->bytes_in_last_block - 1) /
a->bytes_in_last_block);
if (target_block_length > a->bytes_per_block)
target_block_length = a->bytes_per_block;
if (block_length < target_block_length) {
memset(state->stream.next_out, 0,
target_block_length - block_length);
block_length = target_block_length;
}
/* Write the last block */
bytes_written = (a->client_writer)(&a->archive, a->client_data,
state->compressed, block_length);
if (bytes_written <= 0) {
ret = ARCHIVE_FATAL;
goto cleanup;
}
a->archive.raw_position += bytes_written;
/* Cleanup: shut down compressor, release memory, etc. */
cleanup:
switch (deflateEnd(&(state->stream))) {
case Z_OK:
break;
default:
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
"Failed to clean up compressor");
ret = ARCHIVE_FATAL;
}
free(state->compressed);
free(state);
return (ret); return (ret);
} }