Merge from libarchive.googlecode.com: Numerous fixes to the
write options handling, including documentation.
This commit is contained in:
parent
625683944d
commit
8952729381
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=191166
@ -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
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user