From d28e6ae9bc1305ca975d3b61babb5d3dc83c0adf Mon Sep 17 00:00:00 2001 From: Michihiro NAKAJIMA Date: Sat, 13 Oct 2012 19:28:37 +0900 Subject: [PATCH] Enable bzip2 compression to use an external bzip2 program when libbz2 is unavailable. --- libarchive/archive_write_add_filter_bzip2.c | 134 ++++++++++++------- libarchive/test/test_empty_write.c | 2 +- libarchive/test/test_filter_count.c | 9 +- libarchive/test/test_read_truncated_filter.c | 2 +- libarchive/test/test_write_filter_bzip2.c | 88 ++++++++---- 5 files changed, 161 insertions(+), 74 deletions(-) diff --git a/libarchive/archive_write_add_filter_bzip2.c b/libarchive/archive_write_add_filter_bzip2.c index 022f7282f..e63c574ca 100644 --- a/libarchive/archive_write_add_filter_bzip2.c +++ b/libarchive/archive_write_add_filter_bzip2.c @@ -1,5 +1,6 @@ /*- * Copyright (c) 2003-2007 Tim Kientzle + * Copyright (c) 2012 Michihiro NAKAJIMA * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -54,32 +55,18 @@ archive_write_set_compression_bzip2(struct archive *a) } #endif -#if !defined(HAVE_BZLIB_H) || !defined(BZ_CONFIG_ERROR) -int -archive_write_add_filter_bzip2(struct archive *a) -{ - archive_set_error(a, ARCHIVE_ERRNO_MISC, - "bzip2 compression not supported on this platform"); - return (ARCHIVE_FATAL); -} -#else -/* Don't compile this if we don't have bzlib. */ - struct private_data { int compression_level; +#if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR) bz_stream stream; int64_t total_in; char *compressed; size_t compressed_buffer_size; +#else + struct archive_write_program_data *pdata; +#endif }; -/* - * Yuck. bzlib.h is not const-correct, so I need this one bit - * of ugly hackery to convert a const * pointer to a non-const pointer. - */ -#define SET_NEXT_IN(st,src) \ - (st)->stream.next_in = (char *)(uintptr_t)(const void *)(src) - static int archive_compressor_bzip2_close(struct archive_write_filter *); static int archive_compressor_bzip2_free(struct archive_write_filter *); static int archive_compressor_bzip2_open(struct archive_write_filter *); @@ -87,6 +74,44 @@ static int archive_compressor_bzip2_options(struct archive_write_filter *, const char *, const char *); static int archive_compressor_bzip2_write(struct archive_write_filter *, const void *, size_t); + +/* + * Set write options. + */ +static int +archive_compressor_bzip2_options(struct archive_write_filter *f, + const char *key, const char *value) +{ + struct private_data *data = (struct private_data *)f->data; + + if (strcmp(key, "compression-level") == 0) { + if (value == NULL || !(value[0] >= '0' && value[0] <= '9') || + value[1] != '\0') + return (ARCHIVE_WARN); + data->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 (data->compression_level < 1) + data->compression_level = 1; + return (ARCHIVE_OK); + } + + /* Note: The "warn" return is just to inform the options + * supervisor that we didn't handle it. It will generate + * a suitable error if no one used this option. */ + return (ARCHIVE_WARN); +} + +#if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR) +/* Don't compile this if we don't have bzlib. */ + +/* + * Yuck. bzlib.h is not const-correct, so I need this one bit + * of ugly hackery to convert a const * pointer to a non-const pointer. + */ +#define SET_NEXT_IN(st,src) \ + (st)->stream.next_in = (char *)(uintptr_t)(const void *)(src) static int drive_compressor(struct archive_write_filter *, struct private_data *, int finishing); @@ -194,34 +219,6 @@ archive_compressor_bzip2_open(struct archive_write_filter *f) } -/* - * Set write options. - */ -static int -archive_compressor_bzip2_options(struct archive_write_filter *f, - const char *key, const char *value) -{ - struct private_data *data = (struct private_data *)f->data; - - if (strcmp(key, "compression-level") == 0) { - if (value == NULL || !(value[0] >= '0' && value[0] <= '9') || - value[1] != '\0') - return (ARCHIVE_WARN); - data->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 (data->compression_level < 1) - data->compression_level = 1; - return (ARCHIVE_OK); - } - - /* Note: The "warn" return is just to inform the options - * supervisor that we didn't handle it. It will generate - * a suitable error if no one used this option. */ - return (ARCHIVE_WARN); -} - /* * Write data to the compressed stream. * @@ -343,4 +340,49 @@ drive_compressor(struct archive_write_filter *f, } } +#else /* HAVE_BZLIB_H && BZ_CONFIG_ERROR */ + +static int +archive_compressor_bzip2_open(struct archive_write_filter *f) +{ + struct private_data *data = (struct private_data *)f->data; + struct archive_string as; + int r; + + archive_string_init(&as); + archive_strcpy(&as, "bzip2"); + + /* Specify compression level. */ + if (data->compression_level > 0) { + archive_strcat(&as, " -"); + archive_strappend_char(&as, '0' + data->compression_level); + } + r = __archive_write_program_set_cmd(data->pdata, as.s); + archive_string_free(&as); + if (r != ARCHIVE_OK) { + archive_set_error(f->archive, ENOMEM, "Can't allocate memory"); + return (ARCHIVE_FATAL); + } + f->write = archive_compressor_bzip2_write; + + return __archive_write_program_open(f, data->pdata); +} + +static int +archive_compressor_bzip2_write(struct archive_write_filter *f, const void *buff, + size_t length) +{ + struct private_data *data = (struct private_data *)f->data; + + return __archive_write_program_write(f, data->pdata, buff, length); +} + +static int +archive_compressor_bzip2_close(struct archive_write_filter *f) +{ + struct private_data *data = (struct private_data *)f->data; + + return __archive_write_program_close(f, data->pdata); +} + #endif /* HAVE_BZLIB_H && BZ_CONFIG_ERROR */ diff --git a/libarchive/test/test_empty_write.c b/libarchive/test/test_empty_write.c index 60ba4168c..c35979bfb 100644 --- a/libarchive/test/test_empty_write.c +++ b/libarchive/test/test_empty_write.c @@ -75,7 +75,7 @@ DEFINE_TEST(test_empty_write) assert((a = archive_write_new()) != NULL); assertA(0 == archive_write_set_format_ustar(a)); r = archive_write_add_filter_bzip2(a); - if (r == ARCHIVE_FATAL) { + if (r != ARCHIVE_OK && !canBzip2()) { skipping("Empty write to bzip2-compressed archive"); } else { assertEqualIntA(a, ARCHIVE_OK, r); diff --git a/libarchive/test/test_filter_count.c b/libarchive/test/test_filter_count.c index e15ab11c6..4885ab89a 100644 --- a/libarchive/test/test_filter_count.c +++ b/libarchive/test/test_filter_count.c @@ -32,7 +32,10 @@ static void read_test(const char *name) { struct archive* a = archive_read_new(); - if(ARCHIVE_OK != archive_read_support_filter_bzip2(a)) { + int r; + + r = archive_read_support_filter_bzip2(a); + if((ARCHIVE_WARN == r && !canBzip2()) || ARCHIVE_WARN > r) { skipping("bzip2 unsupported"); return; } @@ -52,11 +55,13 @@ write_test(void) { char buff[4096]; struct archive* a = archive_write_new(); + int r; assertEqualIntA(a, ARCHIVE_OK, archive_write_set_format_ustar(a)); assertEqualIntA(a, ARCHIVE_OK, archive_write_set_bytes_per_block(a, 10)); - if(ARCHIVE_OK != archive_write_add_filter_bzip2(a)) { + r = archive_write_add_filter_bzip2(a); + if((ARCHIVE_WARN == r && !canBzip2()) || ARCHIVE_WARN > r) { skipping("bzip2 unsupported"); return; } diff --git a/libarchive/test/test_read_truncated_filter.c b/libarchive/test/test_read_truncated_filter.c index 9128e83eb..73254d398 100644 --- a/libarchive/test/test_read_truncated_filter.c +++ b/libarchive/test/test_read_truncated_filter.c @@ -131,7 +131,7 @@ test_truncation(const char *compression, DEFINE_TEST(test_read_truncated_filter) { - test_truncation("bzip2", archive_write_add_filter_bzip2, 0); + test_truncation("bzip2", archive_write_add_filter_bzip2, canBzip2()); test_truncation("compress", archive_write_add_filter_compress, 0); test_truncation("gzip", archive_write_add_filter_gzip, canGzip()); test_truncation("lzip", archive_write_add_filter_lzip, 0); diff --git a/libarchive/test/test_write_filter_bzip2.c b/libarchive/test/test_write_filter_bzip2.c index 96ac0e62d..74150c8fb 100644 --- a/libarchive/test/test_write_filter_bzip2.c +++ b/libarchive/test/test_write_filter_bzip2.c @@ -41,7 +41,7 @@ DEFINE_TEST(test_write_filter_bzip2) size_t buffsize, datasize; char path[16]; size_t used1, used2; - int i, r; + int i, r, use_prog; buffsize = 2000000; assert(NULL != (buff = (char *)malloc(buffsize))); @@ -56,16 +56,19 @@ DEFINE_TEST(test_write_filter_bzip2) assert((a = archive_write_new()) != NULL); assertEqualIntA(a, ARCHIVE_OK, archive_write_set_format_ustar(a)); r = archive_write_add_filter_bzip2(a); - if (r == ARCHIVE_FATAL) { + use_prog = (r == ARCHIVE_WARN && canBzip2()); + if (r != ARCHIVE_OK && !use_prog) { skipping("bzip2 writing not supported on this platform"); assertEqualInt(ARCHIVE_OK, archive_write_free(a)); return; } + assertEqualIntA(a, ARCHIVE_OK, archive_write_set_bytes_per_block(a, 10)); assertEqualInt(ARCHIVE_FILTER_BZIP2, archive_filter_code(a, 0)); assertEqualString("bzip2", archive_filter_name(a, 0)); - assertEqualIntA(a, ARCHIVE_OK, archive_write_open_memory(a, buff, buffsize, &used1)); + assertEqualIntA(a, ARCHIVE_OK, + archive_write_open_memory(a, buff, buffsize, &used1)); assertEqualInt(ARCHIVE_FILTER_BZIP2, archive_filter_code(a, 0)); assertEqualString("bzip2", archive_filter_name(a, 0)); assert((ae = archive_entry_new()) != NULL); @@ -85,7 +88,8 @@ DEFINE_TEST(test_write_filter_bzip2) assert((a = archive_read_new()) != NULL); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a)); - assertEqualIntA(a, ARCHIVE_OK, archive_read_open_memory(a, buff, used1)); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_open_memory(a, buff, used1)); for (i = 0; i < 999; i++) { sprintf(path, "file%03d", i); if (!assertEqualInt(0, archive_read_next_header(a, &ae))) @@ -104,16 +108,22 @@ DEFINE_TEST(test_write_filter_bzip2) assertEqualIntA(a, ARCHIVE_OK, archive_write_set_format_ustar(a)); assertEqualIntA(a, ARCHIVE_OK, archive_write_set_bytes_per_block(a, 10)); - assertEqualIntA(a, ARCHIVE_OK, archive_write_add_filter_bzip2(a)); - assertEqualIntA(a, ARCHIVE_FAILED, - archive_write_set_filter_option(a, NULL, "nonexistent-option", "0")); - assertEqualIntA(a, ARCHIVE_FAILED, - archive_write_set_filter_option(a, NULL, "compression-level", "abc")); - assertEqualIntA(a, ARCHIVE_FAILED, - archive_write_set_filter_option(a, NULL, "compression-level", "99")); + if (use_prog) + assertEqualIntA(a, ARCHIVE_WARN, + archive_write_add_filter_bzip2(a)); + else + assertEqualIntA(a, ARCHIVE_OK, + archive_write_add_filter_bzip2(a)); + assertEqualIntA(a, ARCHIVE_FAILED, archive_write_set_filter_option(a, + NULL, "nonexistent-option", "0")); + assertEqualIntA(a, ARCHIVE_FAILED, archive_write_set_filter_option(a, + NULL, "compression-level", "abc")); + assertEqualIntA(a, ARCHIVE_FAILED, archive_write_set_filter_option(a, + NULL, "compression-level", "99")); + assertEqualIntA(a, ARCHIVE_OK, archive_write_set_filter_option(a, + NULL, "compression-level", "9")); assertEqualIntA(a, ARCHIVE_OK, - archive_write_set_filter_option(a, NULL, "compression-level", "9")); - assertEqualIntA(a, ARCHIVE_OK, archive_write_open_memory(a, buff, buffsize, &used2)); + archive_write_open_memory(a, buff, buffsize, &used2)); for (i = 0; i < 999; i++) { sprintf(path, "file%03d", i); assert((ae = archive_entry_new()) != NULL); @@ -121,7 +131,8 @@ DEFINE_TEST(test_write_filter_bzip2) archive_entry_set_size(ae, datasize); archive_entry_set_filetype(ae, AE_IFREG); assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae)); - assertA(datasize == (size_t)archive_write_data(a, data, datasize)); + assertA(datasize == (size_t)archive_write_data(a, data, + datasize)); archive_entry_free(ae); } assertEqualIntA(a, ARCHIVE_OK, archive_write_close(a)); @@ -138,7 +149,8 @@ DEFINE_TEST(test_write_filter_bzip2) assert((a = archive_read_new()) != NULL); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a)); - assertEqualIntA(a, ARCHIVE_OK, archive_read_open_memory(a, buff, used2)); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_open_memory(a, buff, used2)); for (i = 0; i < 999; i++) { sprintf(path, "file%03d", i); if (!assertEqualInt(0, archive_read_next_header(a, &ae))) @@ -156,10 +168,16 @@ DEFINE_TEST(test_write_filter_bzip2) assertEqualIntA(a, ARCHIVE_OK, archive_write_set_format_ustar(a)); assertEqualIntA(a, ARCHIVE_OK, archive_write_set_bytes_per_block(a, 10)); - assertEqualIntA(a, ARCHIVE_OK, archive_write_add_filter_bzip2(a)); + if (use_prog) + assertEqualIntA(a, ARCHIVE_WARN, + archive_write_add_filter_bzip2(a)); + else + assertEqualIntA(a, ARCHIVE_OK, + archive_write_add_filter_bzip2(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_write_set_filter_option(a, + NULL, "compression-level", "1")); assertEqualIntA(a, ARCHIVE_OK, - archive_write_set_filter_option(a, NULL, "compression-level", "1")); - assertEqualIntA(a, ARCHIVE_OK, archive_write_open_memory(a, buff, buffsize, &used2)); + archive_write_open_memory(a, buff, buffsize, &used2)); for (i = 0; i < 999; i++) { sprintf(path, "file%03d", i); assert((ae = archive_entry_new()) != NULL); @@ -183,7 +201,8 @@ DEFINE_TEST(test_write_filter_bzip2) assert((a = archive_read_new()) != NULL); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a)); - assertEqualIntA(a, ARCHIVE_OK, archive_read_open_memory(a, buff, used2)); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_open_memory(a, buff, used2)); for (i = 0; i < 999; i++) { sprintf(path, "file%03d", i); if (!assertEqualInt(0, archive_read_next_header(a, &ae))) @@ -199,24 +218,45 @@ DEFINE_TEST(test_write_filter_bzip2) * don't crash or leak memory. */ assert((a = archive_write_new()) != NULL); - assertEqualIntA(a, ARCHIVE_OK, archive_write_add_filter_bzip2(a)); + if (use_prog) + assertEqualIntA(a, ARCHIVE_WARN, + archive_write_add_filter_bzip2(a)); + else + assertEqualIntA(a, ARCHIVE_OK, + archive_write_add_filter_bzip2(a)); assertEqualInt(ARCHIVE_OK, archive_write_free(a)); assert((a = archive_write_new()) != NULL); - assertEqualIntA(a, ARCHIVE_OK, archive_write_add_filter_bzip2(a)); + if (use_prog) + assertEqualIntA(a, ARCHIVE_WARN, + archive_write_add_filter_bzip2(a)); + else + assertEqualIntA(a, ARCHIVE_OK, + archive_write_add_filter_bzip2(a)); assertEqualInt(ARCHIVE_OK, archive_write_close(a)); assertEqualInt(ARCHIVE_OK, archive_write_free(a)); assert((a = archive_write_new()) != NULL); assertEqualIntA(a, ARCHIVE_OK, archive_write_set_format_ustar(a)); - assertEqualIntA(a, ARCHIVE_OK, archive_write_add_filter_bzip2(a)); + if (use_prog) + assertEqualIntA(a, ARCHIVE_WARN, + archive_write_add_filter_bzip2(a)); + else + assertEqualIntA(a, ARCHIVE_OK, + archive_write_add_filter_bzip2(a)); assertEqualInt(ARCHIVE_OK, archive_write_close(a)); assertEqualInt(ARCHIVE_OK, archive_write_free(a)); assert((a = archive_write_new()) != NULL); assertEqualIntA(a, ARCHIVE_OK, archive_write_set_format_ustar(a)); - assertEqualIntA(a, ARCHIVE_OK, archive_write_add_filter_bzip2(a)); - assertEqualIntA(a, ARCHIVE_OK, archive_write_open_memory(a, buff, buffsize, &used2)); + if (use_prog) + assertEqualIntA(a, ARCHIVE_WARN, + archive_write_add_filter_bzip2(a)); + else + assertEqualIntA(a, ARCHIVE_OK, + archive_write_add_filter_bzip2(a)); + assertEqualIntA(a, ARCHIVE_OK, + archive_write_open_memory(a, buff, buffsize, &used2)); assertEqualInt(ARCHIVE_OK, archive_write_close(a)); assertEqualInt(ARCHIVE_OK, archive_write_free(a)); -- 2.47.2