1228753Smm/*- 2228753Smm * Copyright (c) 2003-2007 Tim Kientzle 3228753Smm * All rights reserved. 4228753Smm * 5228753Smm * Redistribution and use in source and binary forms, with or without 6228753Smm * modification, are permitted provided that the following conditions 7228753Smm * are met: 8228753Smm * 1. Redistributions of source code must retain the above copyright 9228753Smm * notice, this list of conditions and the following disclaimer. 10228753Smm * 2. Redistributions in binary form must reproduce the above copyright 11228753Smm * notice, this list of conditions and the following disclaimer in the 12228753Smm * documentation and/or other materials provided with the distribution. 13228753Smm * 14228753Smm * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR 15228753Smm * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16228753Smm * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17228753Smm * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, 18228753Smm * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 19228753Smm * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20228753Smm * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21228753Smm * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22228753Smm * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23228753Smm * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24228753Smm */ 25228753Smm 26228753Smm#include "archive_platform.h" 27228753Smm 28229592Smm__FBSDID("$FreeBSD$"); 29228753Smm 30228753Smm#ifdef HAVE_ERRNO_H 31228753Smm#include <errno.h> 32228753Smm#endif 33228753Smm#include <stdio.h> 34228753Smm#ifdef HAVE_STDLIB_H 35228753Smm#include <stdlib.h> 36228753Smm#endif 37228753Smm#ifdef HAVE_STRING_H 38228753Smm#include <string.h> 39228753Smm#endif 40228753Smm#ifdef HAVE_BZLIB_H 41228753Smm#include <bzlib.h> 42228753Smm#endif 43228753Smm 44228753Smm#include "archive.h" 45228753Smm#include "archive_private.h" 46228753Smm#include "archive_write_private.h" 47228753Smm 48228753Smm#if !defined(HAVE_BZLIB_H) || !defined(BZ_CONFIG_ERROR) 49228753Smmint 50228753Smmarchive_write_set_compression_bzip2(struct archive *a) 51228753Smm{ 52228753Smm archive_set_error(a, ARCHIVE_ERRNO_MISC, 53228753Smm "bzip2 compression not supported on this platform"); 54228753Smm return (ARCHIVE_FATAL); 55228753Smm} 56228753Smm#else 57228753Smm/* Don't compile this if we don't have bzlib. */ 58228753Smm 59228753Smmstruct private_data { 60228753Smm bz_stream stream; 61228753Smm int64_t total_in; 62228753Smm char *compressed; 63228753Smm size_t compressed_buffer_size; 64228753Smm}; 65228753Smm 66228753Smmstruct private_config { 67228753Smm int compression_level; 68228753Smm}; 69228753Smm 70228753Smm/* 71228753Smm * Yuck. bzlib.h is not const-correct, so I need this one bit 72228753Smm * of ugly hackery to convert a const * pointer to a non-const pointer. 73228753Smm */ 74228753Smm#define SET_NEXT_IN(st,src) \ 75228753Smm (st)->stream.next_in = (char *)(uintptr_t)(const void *)(src) 76228753Smm 77228753Smmstatic int archive_compressor_bzip2_finish(struct archive_write *); 78228753Smmstatic int archive_compressor_bzip2_init(struct archive_write *); 79228753Smmstatic int archive_compressor_bzip2_options(struct archive_write *, 80228753Smm const char *, const char *); 81228753Smmstatic int archive_compressor_bzip2_write(struct archive_write *, 82228753Smm const void *, size_t); 83228753Smmstatic int drive_compressor(struct archive_write *, struct private_data *, 84228753Smm int finishing); 85228753Smm 86228753Smm/* 87228753Smm * Allocate, initialize and return an archive object. 88228753Smm */ 89228753Smmint 90228753Smmarchive_write_set_compression_bzip2(struct archive *_a) 91228753Smm{ 92228753Smm struct archive_write *a = (struct archive_write *)_a; 93228753Smm struct private_config *config; 94228753Smm __archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC, 95228753Smm ARCHIVE_STATE_NEW, "archive_write_set_compression_bzip2"); 96228753Smm config = malloc(sizeof(*config)); 97228753Smm if (config == NULL) { 98228753Smm archive_set_error(&a->archive, ENOMEM, "Out of memory"); 99228753Smm return (ARCHIVE_FATAL); 100228753Smm } 101228753Smm a->compressor.config = config; 102228753Smm a->compressor.finish = archive_compressor_bzip2_finish; 103228753Smm config->compression_level = 9; /* default */ 104228753Smm a->compressor.init = &archive_compressor_bzip2_init; 105228753Smm a->compressor.options = &archive_compressor_bzip2_options; 106228753Smm a->archive.compression_code = ARCHIVE_COMPRESSION_BZIP2; 107228753Smm a->archive.compression_name = "bzip2"; 108228753Smm return (ARCHIVE_OK); 109228753Smm} 110228753Smm 111228753Smm/* 112228753Smm * Setup callback. 113228753Smm */ 114228753Smmstatic int 115228753Smmarchive_compressor_bzip2_init(struct archive_write *a) 116228753Smm{ 117228753Smm int ret; 118228753Smm struct private_data *state; 119228753Smm struct private_config *config; 120228753Smm 121228753Smm config = (struct private_config *)a->compressor.config; 122228753Smm if (a->client_opener != NULL) { 123228753Smm ret = (a->client_opener)(&a->archive, a->client_data); 124228753Smm if (ret != 0) 125228753Smm return (ret); 126228753Smm } 127228753Smm 128228753Smm state = (struct private_data *)malloc(sizeof(*state)); 129228753Smm if (state == NULL) { 130228753Smm archive_set_error(&a->archive, ENOMEM, 131228753Smm "Can't allocate data for compression"); 132228753Smm return (ARCHIVE_FATAL); 133228753Smm } 134228753Smm memset(state, 0, sizeof(*state)); 135228753Smm 136228753Smm state->compressed_buffer_size = a->bytes_per_block; 137228753Smm state->compressed = (char *)malloc(state->compressed_buffer_size); 138228753Smm 139228753Smm if (state->compressed == NULL) { 140228753Smm archive_set_error(&a->archive, ENOMEM, 141228753Smm "Can't allocate data for compression buffer"); 142228753Smm free(state); 143228753Smm return (ARCHIVE_FATAL); 144228753Smm } 145228753Smm 146228753Smm state->stream.next_out = state->compressed; 147228753Smm state->stream.avail_out = state->compressed_buffer_size; 148228753Smm a->compressor.write = archive_compressor_bzip2_write; 149228753Smm 150228753Smm /* Initialize compression library */ 151228753Smm ret = BZ2_bzCompressInit(&(state->stream), 152228753Smm config->compression_level, 0, 30); 153228753Smm if (ret == BZ_OK) { 154228753Smm a->compressor.data = state; 155228753Smm return (ARCHIVE_OK); 156228753Smm } 157228753Smm 158228753Smm /* Library setup failed: clean up. */ 159228753Smm archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, 160228753Smm "Internal error initializing compression library"); 161228753Smm free(state->compressed); 162228753Smm free(state); 163228753Smm 164228753Smm /* Override the error message if we know what really went wrong. */ 165228753Smm switch (ret) { 166228753Smm case BZ_PARAM_ERROR: 167228753Smm archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, 168228753Smm "Internal error initializing compression library: " 169228753Smm "invalid setup parameter"); 170228753Smm break; 171228753Smm case BZ_MEM_ERROR: 172228753Smm archive_set_error(&a->archive, ENOMEM, 173228753Smm "Internal error initializing compression library: " 174228753Smm "out of memory"); 175228753Smm break; 176228753Smm case BZ_CONFIG_ERROR: 177228753Smm archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, 178228753Smm "Internal error initializing compression library: " 179228753Smm "mis-compiled library"); 180228753Smm break; 181228753Smm } 182228753Smm 183228753Smm return (ARCHIVE_FATAL); 184228753Smm 185228753Smm} 186228753Smm 187228753Smm/* 188228753Smm * Set write options. 189228753Smm */ 190228753Smmstatic int 191228753Smmarchive_compressor_bzip2_options(struct archive_write *a, const char *key, 192228753Smm const char *value) 193228753Smm{ 194228753Smm struct private_config *config; 195228753Smm 196228753Smm config = (struct private_config *)a->compressor.config; 197228753Smm if (strcmp(key, "compression-level") == 0) { 198228753Smm if (value == NULL || !(value[0] >= '0' && value[0] <= '9') || 199228753Smm value[1] != '\0') 200228753Smm return (ARCHIVE_WARN); 201228753Smm config->compression_level = value[0] - '0'; 202228753Smm /* Make '0' be a synonym for '1'. */ 203228753Smm /* This way, bzip2 compressor supports the same 0..9 204228753Smm * range of levels as gzip. */ 205228753Smm if (config->compression_level < 1) 206228753Smm config->compression_level = 1; 207228753Smm return (ARCHIVE_OK); 208228753Smm } 209228753Smm 210228753Smm return (ARCHIVE_WARN); 211228753Smm} 212228753Smm 213228753Smm/* 214228753Smm * Write data to the compressed stream. 215228753Smm * 216228753Smm * Returns ARCHIVE_OK if all data written, error otherwise. 217228753Smm */ 218228753Smmstatic int 219228753Smmarchive_compressor_bzip2_write(struct archive_write *a, const void *buff, 220228753Smm size_t length) 221228753Smm{ 222228753Smm struct private_data *state; 223228753Smm 224228753Smm state = (struct private_data *)a->compressor.data; 225228753Smm if (a->client_writer == NULL) { 226228753Smm archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, 227228753Smm "No write callback is registered? " 228228753Smm "This is probably an internal programming error."); 229228753Smm return (ARCHIVE_FATAL); 230228753Smm } 231228753Smm 232228753Smm /* Update statistics */ 233228753Smm state->total_in += length; 234228753Smm 235228753Smm /* Compress input data to output buffer */ 236228753Smm SET_NEXT_IN(state, buff); 237228753Smm state->stream.avail_in = length; 238228753Smm if (drive_compressor(a, state, 0)) 239228753Smm return (ARCHIVE_FATAL); 240228753Smm a->archive.file_position += length; 241228753Smm return (ARCHIVE_OK); 242228753Smm} 243228753Smm 244228753Smm 245228753Smm/* 246228753Smm * Finish the compression. 247228753Smm */ 248228753Smmstatic int 249228753Smmarchive_compressor_bzip2_finish(struct archive_write *a) 250228753Smm{ 251228753Smm ssize_t block_length; 252228753Smm int ret; 253228753Smm struct private_data *state; 254228753Smm ssize_t target_block_length; 255228753Smm ssize_t bytes_written; 256228753Smm unsigned tocopy; 257228753Smm 258228753Smm ret = ARCHIVE_OK; 259228753Smm state = (struct private_data *)a->compressor.data; 260228753Smm if (state != NULL) { 261228753Smm if (a->client_writer == NULL) { 262228753Smm archive_set_error(&a->archive, 263228753Smm ARCHIVE_ERRNO_PROGRAMMER, 264228753Smm "No write callback is registered?\n" 265228753Smm "This is probably an internal programming error."); 266228753Smm ret = ARCHIVE_FATAL; 267228753Smm goto cleanup; 268228753Smm } 269228753Smm 270228753Smm /* By default, always pad the uncompressed data. */ 271228753Smm if (a->pad_uncompressed) { 272228753Smm tocopy = a->bytes_per_block - 273228753Smm (state->total_in % a->bytes_per_block); 274228753Smm while (tocopy > 0 && tocopy < (unsigned)a->bytes_per_block) { 275228753Smm SET_NEXT_IN(state, a->nulls); 276228753Smm state->stream.avail_in = tocopy < a->null_length ? 277228753Smm tocopy : a->null_length; 278228753Smm state->total_in += state->stream.avail_in; 279228753Smm tocopy -= state->stream.avail_in; 280228753Smm ret = drive_compressor(a, state, 0); 281228753Smm if (ret != ARCHIVE_OK) 282228753Smm goto cleanup; 283228753Smm } 284228753Smm } 285228753Smm 286228753Smm /* Finish compression cycle. */ 287228753Smm if ((ret = drive_compressor(a, state, 1))) 288228753Smm goto cleanup; 289228753Smm 290228753Smm /* Optionally, pad the final compressed block. */ 291228753Smm block_length = state->stream.next_out - state->compressed; 292228753Smm 293228753Smm /* Tricky calculation to determine size of last block. */ 294228753Smm if (a->bytes_in_last_block <= 0) 295228753Smm /* Default or Zero: pad to full block */ 296228753Smm target_block_length = a->bytes_per_block; 297228753Smm else 298228753Smm /* Round length to next multiple of bytes_in_last_block. */ 299228753Smm target_block_length = a->bytes_in_last_block * 300228753Smm ( (block_length + a->bytes_in_last_block - 1) / 301228753Smm a->bytes_in_last_block); 302228753Smm if (target_block_length > a->bytes_per_block) 303228753Smm target_block_length = a->bytes_per_block; 304228753Smm if (block_length < target_block_length) { 305228753Smm memset(state->stream.next_out, 0, 306228753Smm target_block_length - block_length); 307228753Smm block_length = target_block_length; 308228753Smm } 309228753Smm 310228753Smm /* Write the last block */ 311228753Smm bytes_written = (a->client_writer)(&a->archive, a->client_data, 312228753Smm state->compressed, block_length); 313228753Smm 314228753Smm /* TODO: Handle short write of final block. */ 315228753Smm if (bytes_written <= 0) 316228753Smm ret = ARCHIVE_FATAL; 317228753Smm else { 318228753Smm a->archive.raw_position += ret; 319228753Smm ret = ARCHIVE_OK; 320228753Smm } 321228753Smm 322228753Smm /* Cleanup: shut down compressor, release memory, etc. */ 323228753Smmcleanup: 324228753Smm switch (BZ2_bzCompressEnd(&(state->stream))) { 325228753Smm case BZ_OK: 326228753Smm break; 327228753Smm default: 328228753Smm archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, 329228753Smm "Failed to clean up compressor"); 330228753Smm ret = ARCHIVE_FATAL; 331228753Smm } 332228753Smm 333228753Smm free(state->compressed); 334228753Smm free(state); 335228753Smm } 336228753Smm /* Free configuration data even if we were never fully initialized. */ 337228753Smm free(a->compressor.config); 338228753Smm a->compressor.config = NULL; 339228753Smm return (ret); 340228753Smm} 341228753Smm 342228753Smm/* 343228753Smm * Utility function to push input data through compressor, writing 344228753Smm * full output blocks as necessary. 345228753Smm * 346228753Smm * Note that this handles both the regular write case (finishing == 347228753Smm * false) and the end-of-archive case (finishing == true). 348228753Smm */ 349228753Smmstatic int 350228753Smmdrive_compressor(struct archive_write *a, struct private_data *state, int finishing) 351228753Smm{ 352228753Smm ssize_t bytes_written; 353228753Smm int ret; 354228753Smm 355228753Smm for (;;) { 356228753Smm if (state->stream.avail_out == 0) { 357228753Smm bytes_written = (a->client_writer)(&a->archive, 358228753Smm a->client_data, state->compressed, 359228753Smm state->compressed_buffer_size); 360228753Smm if (bytes_written <= 0) { 361228753Smm /* TODO: Handle this write failure */ 362228753Smm return (ARCHIVE_FATAL); 363228753Smm } else if ((size_t)bytes_written < state->compressed_buffer_size) { 364228753Smm /* Short write: Move remainder to 365228753Smm * front and keep filling */ 366228753Smm memmove(state->compressed, 367228753Smm state->compressed + bytes_written, 368228753Smm state->compressed_buffer_size - bytes_written); 369228753Smm } 370228753Smm 371228753Smm a->archive.raw_position += bytes_written; 372228753Smm state->stream.next_out = state->compressed + 373228753Smm state->compressed_buffer_size - bytes_written; 374228753Smm state->stream.avail_out = bytes_written; 375228753Smm } 376228753Smm 377228753Smm /* If there's nothing to do, we're done. */ 378228753Smm if (!finishing && state->stream.avail_in == 0) 379228753Smm return (ARCHIVE_OK); 380228753Smm 381228753Smm ret = BZ2_bzCompress(&(state->stream), 382228753Smm finishing ? BZ_FINISH : BZ_RUN); 383228753Smm 384228753Smm switch (ret) { 385228753Smm case BZ_RUN_OK: 386228753Smm /* In non-finishing case, did compressor 387228753Smm * consume everything? */ 388228753Smm if (!finishing && state->stream.avail_in == 0) 389228753Smm return (ARCHIVE_OK); 390228753Smm break; 391228753Smm case BZ_FINISH_OK: /* Finishing: There's more work to do */ 392228753Smm break; 393228753Smm case BZ_STREAM_END: /* Finishing: all done */ 394228753Smm /* Only occurs in finishing case */ 395228753Smm return (ARCHIVE_OK); 396228753Smm default: 397228753Smm /* Any other return value indicates an error */ 398228753Smm archive_set_error(&a->archive, 399228753Smm ARCHIVE_ERRNO_PROGRAMMER, 400228753Smm "Bzip2 compression failed;" 401228753Smm " BZ2_bzCompress() returned %d", 402228753Smm ret); 403228753Smm return (ARCHIVE_FATAL); 404228753Smm } 405228753Smm } 406228753Smm} 407228753Smm 408228753Smm#endif /* HAVE_BZLIB_H && BZ_CONFIG_ERROR */ 409