1228753Smm/*- 2228753Smm * Copyright (c) 2003-2007 Tim Kientzle 3248616Smm * Copyright (c) 2012 Michihiro NAKAJIMA 4228753Smm * All rights reserved. 5228753Smm * 6228753Smm * Redistribution and use in source and binary forms, with or without 7228753Smm * modification, are permitted provided that the following conditions 8228753Smm * are met: 9228753Smm * 1. Redistributions of source code must retain the above copyright 10228753Smm * notice, this list of conditions and the following disclaimer. 11228753Smm * 2. Redistributions in binary form must reproduce the above copyright 12228753Smm * notice, this list of conditions and the following disclaimer in the 13228753Smm * documentation and/or other materials provided with the distribution. 14228753Smm * 15228753Smm * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR 16228753Smm * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17228753Smm * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18228753Smm * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, 19228753Smm * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20228753Smm * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21228753Smm * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22228753Smm * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23228753Smm * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24228753Smm * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25228753Smm */ 26228753Smm 27228753Smm#include "archive_platform.h" 28228753Smm 29228753Smm__FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_compression_bzip2.c 201091 2009-12-28 02:22:41Z kientzle $"); 30228753Smm 31228753Smm#ifdef HAVE_ERRNO_H 32228753Smm#include <errno.h> 33228753Smm#endif 34228753Smm#include <stdio.h> 35228753Smm#ifdef HAVE_STDLIB_H 36228753Smm#include <stdlib.h> 37228753Smm#endif 38228753Smm#ifdef HAVE_STRING_H 39228753Smm#include <string.h> 40228753Smm#endif 41228753Smm#ifdef HAVE_BZLIB_H 42228753Smm#include <bzlib.h> 43228753Smm#endif 44228753Smm 45228753Smm#include "archive.h" 46228753Smm#include "archive_private.h" 47228753Smm#include "archive_write_private.h" 48228753Smm 49231200Smm#if ARCHIVE_VERSION_NUMBER < 4000000 50228753Smmint 51228753Smmarchive_write_set_compression_bzip2(struct archive *a) 52228753Smm{ 53231200Smm __archive_write_filters_free(a); 54231200Smm return (archive_write_add_filter_bzip2(a)); 55231200Smm} 56231200Smm#endif 57231200Smm 58228753Smmstruct private_data { 59231200Smm int compression_level; 60248616Smm#if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR) 61228753Smm bz_stream stream; 62228753Smm int64_t total_in; 63228753Smm char *compressed; 64228753Smm size_t compressed_buffer_size; 65248616Smm#else 66248616Smm struct archive_write_program_data *pdata; 67248616Smm#endif 68228753Smm}; 69228753Smm 70231200Smmstatic int archive_compressor_bzip2_close(struct archive_write_filter *); 71231200Smmstatic int archive_compressor_bzip2_free(struct archive_write_filter *); 72231200Smmstatic int archive_compressor_bzip2_open(struct archive_write_filter *); 73231200Smmstatic int archive_compressor_bzip2_options(struct archive_write_filter *, 74228753Smm const char *, const char *); 75231200Smmstatic int archive_compressor_bzip2_write(struct archive_write_filter *, 76228753Smm const void *, size_t); 77228753Smm 78228753Smm/* 79231200Smm * Add a bzip2 compression filter to this write handle. 80228753Smm */ 81228753Smmint 82231200Smmarchive_write_add_filter_bzip2(struct archive *_a) 83228753Smm{ 84228753Smm struct archive_write *a = (struct archive_write *)_a; 85231200Smm struct archive_write_filter *f = __archive_write_allocate_filter(_a); 86231200Smm struct private_data *data; 87231200Smm 88231200Smm archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC, 89231200Smm ARCHIVE_STATE_NEW, "archive_write_add_filter_bzip2"); 90231200Smm 91231200Smm data = calloc(1, sizeof(*data)); 92231200Smm if (data == NULL) { 93228753Smm archive_set_error(&a->archive, ENOMEM, "Out of memory"); 94228753Smm return (ARCHIVE_FATAL); 95228753Smm } 96231200Smm data->compression_level = 9; /* default */ 97231200Smm 98231200Smm f->data = data; 99231200Smm f->options = &archive_compressor_bzip2_options; 100231200Smm f->close = &archive_compressor_bzip2_close; 101231200Smm f->free = &archive_compressor_bzip2_free; 102231200Smm f->open = &archive_compressor_bzip2_open; 103248616Smm f->code = ARCHIVE_FILTER_BZIP2; 104231200Smm f->name = "bzip2"; 105248616Smm#if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR) 106228753Smm return (ARCHIVE_OK); 107248616Smm#else 108248616Smm data->pdata = __archive_write_program_allocate(); 109248616Smm if (data->pdata == NULL) { 110248616Smm free(data); 111248616Smm archive_set_error(&a->archive, ENOMEM, "Out of memory"); 112248616Smm return (ARCHIVE_FATAL); 113248616Smm } 114248616Smm data->compression_level = 0; 115248616Smm archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, 116248616Smm "Using external bzip2 program"); 117248616Smm return (ARCHIVE_WARN); 118248616Smm#endif 119228753Smm} 120228753Smm 121228753Smm/* 122248616Smm * Set write options. 123248616Smm */ 124248616Smmstatic int 125248616Smmarchive_compressor_bzip2_options(struct archive_write_filter *f, 126248616Smm const char *key, const char *value) 127248616Smm{ 128248616Smm struct private_data *data = (struct private_data *)f->data; 129248616Smm 130248616Smm if (strcmp(key, "compression-level") == 0) { 131248616Smm if (value == NULL || !(value[0] >= '0' && value[0] <= '9') || 132248616Smm value[1] != '\0') 133248616Smm return (ARCHIVE_WARN); 134248616Smm data->compression_level = value[0] - '0'; 135248616Smm /* Make '0' be a synonym for '1'. */ 136248616Smm /* This way, bzip2 compressor supports the same 0..9 137248616Smm * range of levels as gzip. */ 138248616Smm if (data->compression_level < 1) 139248616Smm data->compression_level = 1; 140248616Smm return (ARCHIVE_OK); 141248616Smm } 142248616Smm 143248616Smm /* Note: The "warn" return is just to inform the options 144248616Smm * supervisor that we didn't handle it. It will generate 145248616Smm * a suitable error if no one used this option. */ 146248616Smm return (ARCHIVE_WARN); 147248616Smm} 148248616Smm 149248616Smm#if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR) 150248616Smm/* Don't compile this if we don't have bzlib. */ 151248616Smm 152248616Smm/* 153248616Smm * Yuck. bzlib.h is not const-correct, so I need this one bit 154248616Smm * of ugly hackery to convert a const * pointer to a non-const pointer. 155248616Smm */ 156248616Smm#define SET_NEXT_IN(st,src) \ 157248616Smm (st)->stream.next_in = (char *)(uintptr_t)(const void *)(src) 158248616Smmstatic int drive_compressor(struct archive_write_filter *, 159248616Smm struct private_data *, int finishing); 160248616Smm 161248616Smm/* 162228753Smm * Setup callback. 163228753Smm */ 164228753Smmstatic int 165231200Smmarchive_compressor_bzip2_open(struct archive_write_filter *f) 166228753Smm{ 167231200Smm struct private_data *data = (struct private_data *)f->data; 168228753Smm int ret; 169228753Smm 170231200Smm ret = __archive_write_open_filter(f->next_filter); 171231200Smm if (ret != 0) 172231200Smm return (ret); 173228753Smm 174231200Smm if (data->compressed == NULL) { 175238856Smm size_t bs = 65536, bpb; 176238856Smm if (f->archive->magic == ARCHIVE_WRITE_MAGIC) { 177238856Smm /* Buffer size should be a multiple number of the of bytes 178238856Smm * per block for performance. */ 179238856Smm bpb = archive_write_get_bytes_per_block(f->archive); 180238856Smm if (bpb > bs) 181238856Smm bs = bpb; 182238856Smm else if (bpb != 0) 183238856Smm bs -= bs % bpb; 184238856Smm } 185238856Smm data->compressed_buffer_size = bs; 186231200Smm data->compressed 187231200Smm = (char *)malloc(data->compressed_buffer_size); 188231200Smm if (data->compressed == NULL) { 189231200Smm archive_set_error(f->archive, ENOMEM, 190231200Smm "Can't allocate data for compression buffer"); 191231200Smm return (ARCHIVE_FATAL); 192231200Smm } 193228753Smm } 194228753Smm 195231200Smm memset(&data->stream, 0, sizeof(data->stream)); 196231200Smm data->stream.next_out = data->compressed; 197231200Smm data->stream.avail_out = data->compressed_buffer_size; 198231200Smm f->write = archive_compressor_bzip2_write; 199228753Smm 200228753Smm /* Initialize compression library */ 201231200Smm ret = BZ2_bzCompressInit(&(data->stream), 202231200Smm data->compression_level, 0, 30); 203228753Smm if (ret == BZ_OK) { 204231200Smm f->data = data; 205228753Smm return (ARCHIVE_OK); 206228753Smm } 207228753Smm 208228753Smm /* Library setup failed: clean up. */ 209231200Smm archive_set_error(f->archive, ARCHIVE_ERRNO_MISC, 210228753Smm "Internal error initializing compression library"); 211228753Smm 212228753Smm /* Override the error message if we know what really went wrong. */ 213228753Smm switch (ret) { 214228753Smm case BZ_PARAM_ERROR: 215231200Smm archive_set_error(f->archive, ARCHIVE_ERRNO_MISC, 216228753Smm "Internal error initializing compression library: " 217228753Smm "invalid setup parameter"); 218228753Smm break; 219228753Smm case BZ_MEM_ERROR: 220231200Smm archive_set_error(f->archive, ENOMEM, 221228753Smm "Internal error initializing compression library: " 222228753Smm "out of memory"); 223228753Smm break; 224228753Smm case BZ_CONFIG_ERROR: 225231200Smm archive_set_error(f->archive, ARCHIVE_ERRNO_MISC, 226228753Smm "Internal error initializing compression library: " 227228753Smm "mis-compiled library"); 228228753Smm break; 229228753Smm } 230228753Smm 231228753Smm return (ARCHIVE_FATAL); 232228753Smm 233228753Smm} 234228753Smm 235228753Smm/* 236228753Smm * Write data to the compressed stream. 237228753Smm * 238228753Smm * Returns ARCHIVE_OK if all data written, error otherwise. 239228753Smm */ 240228753Smmstatic int 241231200Smmarchive_compressor_bzip2_write(struct archive_write_filter *f, 242231200Smm const void *buff, size_t length) 243228753Smm{ 244231200Smm struct private_data *data = (struct private_data *)f->data; 245228753Smm 246228753Smm /* Update statistics */ 247231200Smm data->total_in += length; 248228753Smm 249228753Smm /* Compress input data to output buffer */ 250231200Smm SET_NEXT_IN(data, buff); 251231200Smm data->stream.avail_in = length; 252231200Smm if (drive_compressor(f, data, 0)) 253228753Smm return (ARCHIVE_FATAL); 254228753Smm return (ARCHIVE_OK); 255228753Smm} 256228753Smm 257228753Smm 258228753Smm/* 259228753Smm * Finish the compression. 260228753Smm */ 261228753Smmstatic int 262231200Smmarchive_compressor_bzip2_close(struct archive_write_filter *f) 263228753Smm{ 264231200Smm struct private_data *data = (struct private_data *)f->data; 265231200Smm int ret, r1; 266228753Smm 267231200Smm /* Finish compression cycle. */ 268231200Smm ret = drive_compressor(f, data, 1); 269231200Smm if (ret == ARCHIVE_OK) { 270228753Smm /* Write the last block */ 271231200Smm ret = __archive_write_filter(f->next_filter, 272231200Smm data->compressed, 273231200Smm data->compressed_buffer_size - data->stream.avail_out); 274231200Smm } 275228753Smm 276231200Smm switch (BZ2_bzCompressEnd(&(data->stream))) { 277231200Smm case BZ_OK: 278231200Smm break; 279231200Smm default: 280231200Smm archive_set_error(f->archive, ARCHIVE_ERRNO_PROGRAMMER, 281231200Smm "Failed to clean up compressor"); 282231200Smm ret = ARCHIVE_FATAL; 283231200Smm } 284228753Smm 285231200Smm r1 = __archive_write_close_filter(f->next_filter); 286231200Smm return (r1 < ret ? r1 : ret); 287231200Smm} 288228753Smm 289231200Smmstatic int 290231200Smmarchive_compressor_bzip2_free(struct archive_write_filter *f) 291231200Smm{ 292231200Smm struct private_data *data = (struct private_data *)f->data; 293231200Smm free(data->compressed); 294231200Smm free(data); 295231200Smm f->data = NULL; 296231200Smm return (ARCHIVE_OK); 297228753Smm} 298228753Smm 299228753Smm/* 300228753Smm * Utility function to push input data through compressor, writing 301228753Smm * full output blocks as necessary. 302228753Smm * 303228753Smm * Note that this handles both the regular write case (finishing == 304228753Smm * false) and the end-of-archive case (finishing == true). 305228753Smm */ 306228753Smmstatic int 307231200Smmdrive_compressor(struct archive_write_filter *f, 308231200Smm struct private_data *data, int finishing) 309228753Smm{ 310228753Smm int ret; 311228753Smm 312228753Smm for (;;) { 313231200Smm if (data->stream.avail_out == 0) { 314231200Smm ret = __archive_write_filter(f->next_filter, 315231200Smm data->compressed, 316231200Smm data->compressed_buffer_size); 317231200Smm if (ret != ARCHIVE_OK) { 318228753Smm /* TODO: Handle this write failure */ 319228753Smm return (ARCHIVE_FATAL); 320228753Smm } 321231200Smm data->stream.next_out = data->compressed; 322231200Smm data->stream.avail_out = data->compressed_buffer_size; 323228753Smm } 324228753Smm 325228753Smm /* If there's nothing to do, we're done. */ 326231200Smm if (!finishing && data->stream.avail_in == 0) 327228753Smm return (ARCHIVE_OK); 328228753Smm 329231200Smm ret = BZ2_bzCompress(&(data->stream), 330228753Smm finishing ? BZ_FINISH : BZ_RUN); 331228753Smm 332228753Smm switch (ret) { 333228753Smm case BZ_RUN_OK: 334228753Smm /* In non-finishing case, did compressor 335228753Smm * consume everything? */ 336231200Smm if (!finishing && data->stream.avail_in == 0) 337228753Smm return (ARCHIVE_OK); 338228753Smm break; 339228753Smm case BZ_FINISH_OK: /* Finishing: There's more work to do */ 340228753Smm break; 341228753Smm case BZ_STREAM_END: /* Finishing: all done */ 342228753Smm /* Only occurs in finishing case */ 343228753Smm return (ARCHIVE_OK); 344228753Smm default: 345228753Smm /* Any other return value indicates an error */ 346231200Smm archive_set_error(f->archive, 347228753Smm ARCHIVE_ERRNO_PROGRAMMER, 348228753Smm "Bzip2 compression failed;" 349228753Smm " BZ2_bzCompress() returned %d", 350228753Smm ret); 351228753Smm return (ARCHIVE_FATAL); 352228753Smm } 353228753Smm } 354228753Smm} 355228753Smm 356248616Smm#else /* HAVE_BZLIB_H && BZ_CONFIG_ERROR */ 357248616Smm 358248616Smmstatic int 359248616Smmarchive_compressor_bzip2_open(struct archive_write_filter *f) 360248616Smm{ 361248616Smm struct private_data *data = (struct private_data *)f->data; 362248616Smm struct archive_string as; 363248616Smm int r; 364248616Smm 365248616Smm archive_string_init(&as); 366248616Smm archive_strcpy(&as, "bzip2"); 367248616Smm 368248616Smm /* Specify compression level. */ 369248616Smm if (data->compression_level > 0) { 370248616Smm archive_strcat(&as, " -"); 371248616Smm archive_strappend_char(&as, '0' + data->compression_level); 372248616Smm } 373248616Smm f->write = archive_compressor_bzip2_write; 374248616Smm 375248616Smm r = __archive_write_program_open(f, data->pdata, as.s); 376248616Smm archive_string_free(&as); 377248616Smm return (r); 378248616Smm} 379248616Smm 380248616Smmstatic int 381248616Smmarchive_compressor_bzip2_write(struct archive_write_filter *f, const void *buff, 382248616Smm size_t length) 383248616Smm{ 384248616Smm struct private_data *data = (struct private_data *)f->data; 385248616Smm 386248616Smm return __archive_write_program_write(f, data->pdata, buff, length); 387248616Smm} 388248616Smm 389248616Smmstatic int 390248616Smmarchive_compressor_bzip2_close(struct archive_write_filter *f) 391248616Smm{ 392248616Smm struct private_data *data = (struct private_data *)f->data; 393248616Smm 394248616Smm return __archive_write_program_close(f, data->pdata); 395248616Smm} 396248616Smm 397248616Smmstatic int 398248616Smmarchive_compressor_bzip2_free(struct archive_write_filter *f) 399248616Smm{ 400248616Smm struct private_data *data = (struct private_data *)f->data; 401248616Smm 402248616Smm __archive_write_program_free(data->pdata); 403248616Smm free(data); 404248616Smm return (ARCHIVE_OK); 405248616Smm} 406248616Smm 407228753Smm#endif /* HAVE_BZLIB_H && BZ_CONFIG_ERROR */ 408