1/*- 2 * Copyright (c) 2003-2007 Tim Kientzle 3 * Copyright (c) 2012 Michihiro NAKAJIMA 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR 16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, 19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27#include "archive_platform.h" 28 29#ifdef HAVE_ERRNO_H 30#include <errno.h> 31#endif 32#include <stdio.h> 33#ifdef HAVE_STDLIB_H 34#include <stdlib.h> 35#endif 36#ifdef HAVE_STRING_H 37#include <string.h> 38#endif 39#ifdef HAVE_BZLIB_H 40#include <bzlib.h> 41#endif 42 43#include "archive.h" 44#include "archive_private.h" 45#include "archive_write_private.h" 46 47#if ARCHIVE_VERSION_NUMBER < 4000000 48int 49archive_write_set_compression_bzip2(struct archive *a) 50{ 51 __archive_write_filters_free(a); 52 return (archive_write_add_filter_bzip2(a)); 53} 54#endif 55 56struct private_data { 57 int compression_level; 58#if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR) 59 bz_stream stream; 60 int64_t total_in; 61 char *compressed; 62 size_t compressed_buffer_size; 63#else 64 struct archive_write_program_data *pdata; 65#endif 66}; 67 68static int archive_compressor_bzip2_close(struct archive_write_filter *); 69static int archive_compressor_bzip2_free(struct archive_write_filter *); 70static int archive_compressor_bzip2_open(struct archive_write_filter *); 71static int archive_compressor_bzip2_options(struct archive_write_filter *, 72 const char *, const char *); 73static int archive_compressor_bzip2_write(struct archive_write_filter *, 74 const void *, size_t); 75 76/* 77 * Add a bzip2 compression filter to this write handle. 78 */ 79int 80archive_write_add_filter_bzip2(struct archive *_a) 81{ 82 struct archive_write *a = (struct archive_write *)_a; 83 struct archive_write_filter *f = __archive_write_allocate_filter(_a); 84 struct private_data *data; 85 86 archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC, 87 ARCHIVE_STATE_NEW, "archive_write_add_filter_bzip2"); 88 89 data = calloc(1, sizeof(*data)); 90 if (data == NULL) { 91 archive_set_error(&a->archive, ENOMEM, "Out of memory"); 92 return (ARCHIVE_FATAL); 93 } 94 data->compression_level = 9; /* default */ 95 96 f->data = data; 97 f->options = &archive_compressor_bzip2_options; 98 f->close = &archive_compressor_bzip2_close; 99 f->free = &archive_compressor_bzip2_free; 100 f->open = &archive_compressor_bzip2_open; 101 f->code = ARCHIVE_FILTER_BZIP2; 102 f->name = "bzip2"; 103#if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR) 104 return (ARCHIVE_OK); 105#else 106 data->pdata = __archive_write_program_allocate("bzip2"); 107 if (data->pdata == NULL) { 108 free(data); 109 archive_set_error(&a->archive, ENOMEM, "Out of memory"); 110 return (ARCHIVE_FATAL); 111 } 112 data->compression_level = 0; 113 archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, 114 "Using external bzip2 program"); 115 return (ARCHIVE_WARN); 116#endif 117} 118 119/* 120 * Set write options. 121 */ 122static int 123archive_compressor_bzip2_options(struct archive_write_filter *f, 124 const char *key, const char *value) 125{ 126 struct private_data *data = (struct private_data *)f->data; 127 128 if (strcmp(key, "compression-level") == 0) { 129 if (value == NULL || !(value[0] >= '0' && value[0] <= '9') || 130 value[1] != '\0') 131 return (ARCHIVE_WARN); 132 data->compression_level = value[0] - '0'; 133 /* Make '0' be a synonym for '1'. */ 134 /* This way, bzip2 compressor supports the same 0..9 135 * range of levels as gzip. */ 136 if (data->compression_level < 1) 137 data->compression_level = 1; 138 return (ARCHIVE_OK); 139 } 140 141 /* Note: The "warn" return is just to inform the options 142 * supervisor that we didn't handle it. It will generate 143 * a suitable error if no one used this option. */ 144 return (ARCHIVE_WARN); 145} 146 147#if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR) 148/* Don't compile this if we don't have bzlib. */ 149 150/* 151 * Yuck. bzlib.h is not const-correct, so I need this one bit 152 * of ugly hackery to convert a const * pointer to a non-const pointer. 153 */ 154#define SET_NEXT_IN(st,src) \ 155 (st)->stream.next_in = (char *)(uintptr_t)(const void *)(src) 156static int drive_compressor(struct archive_write_filter *, 157 struct private_data *, int finishing); 158 159/* 160 * Setup callback. 161 */ 162static int 163archive_compressor_bzip2_open(struct archive_write_filter *f) 164{ 165 struct private_data *data = (struct private_data *)f->data; 166 int ret; 167 168 if (data->compressed == NULL) { 169 size_t bs = 65536, bpb; 170 if (f->archive->magic == ARCHIVE_WRITE_MAGIC) { 171 /* Buffer size should be a multiple number of the of bytes 172 * per block for performance. */ 173 bpb = archive_write_get_bytes_per_block(f->archive); 174 if (bpb > bs) 175 bs = bpb; 176 else if (bpb != 0) 177 bs -= bs % bpb; 178 } 179 data->compressed_buffer_size = bs; 180 data->compressed 181 = (char *)malloc(data->compressed_buffer_size); 182 if (data->compressed == NULL) { 183 archive_set_error(f->archive, ENOMEM, 184 "Can't allocate data for compression buffer"); 185 return (ARCHIVE_FATAL); 186 } 187 } 188 189 memset(&data->stream, 0, sizeof(data->stream)); 190 data->stream.next_out = data->compressed; 191 data->stream.avail_out = (uint32_t)data->compressed_buffer_size; 192 f->write = archive_compressor_bzip2_write; 193 194 /* Initialize compression library */ 195 ret = BZ2_bzCompressInit(&(data->stream), 196 data->compression_level, 0, 30); 197 if (ret == BZ_OK) { 198 f->data = data; 199 return (ARCHIVE_OK); 200 } 201 202 /* Library setup failed: clean up. */ 203 archive_set_error(f->archive, ARCHIVE_ERRNO_MISC, 204 "Internal error initializing compression library"); 205 206 /* Override the error message if we know what really went wrong. */ 207 switch (ret) { 208 case BZ_PARAM_ERROR: 209 archive_set_error(f->archive, ARCHIVE_ERRNO_MISC, 210 "Internal error initializing compression library: " 211 "invalid setup parameter"); 212 break; 213 case BZ_MEM_ERROR: 214 archive_set_error(f->archive, ENOMEM, 215 "Internal error initializing compression library: " 216 "out of memory"); 217 break; 218 case BZ_CONFIG_ERROR: 219 archive_set_error(f->archive, ARCHIVE_ERRNO_MISC, 220 "Internal error initializing compression library: " 221 "mis-compiled library"); 222 break; 223 } 224 225 return (ARCHIVE_FATAL); 226 227} 228 229/* 230 * Write data to the compressed stream. 231 * 232 * Returns ARCHIVE_OK if all data written, error otherwise. 233 */ 234static int 235archive_compressor_bzip2_write(struct archive_write_filter *f, 236 const void *buff, size_t length) 237{ 238 struct private_data *data = (struct private_data *)f->data; 239 240 /* Update statistics */ 241 data->total_in += length; 242 243 /* Compress input data to output buffer */ 244 SET_NEXT_IN(data, buff); 245 data->stream.avail_in = (uint32_t)length; 246 if (drive_compressor(f, data, 0)) 247 return (ARCHIVE_FATAL); 248 return (ARCHIVE_OK); 249} 250 251 252/* 253 * Finish the compression. 254 */ 255static int 256archive_compressor_bzip2_close(struct archive_write_filter *f) 257{ 258 struct private_data *data = (struct private_data *)f->data; 259 int ret; 260 261 /* Finish compression cycle. */ 262 ret = drive_compressor(f, data, 1); 263 if (ret == ARCHIVE_OK) { 264 /* Write the last block */ 265 ret = __archive_write_filter(f->next_filter, 266 data->compressed, 267 data->compressed_buffer_size - data->stream.avail_out); 268 } 269 270 switch (BZ2_bzCompressEnd(&(data->stream))) { 271 case BZ_OK: 272 break; 273 default: 274 archive_set_error(f->archive, ARCHIVE_ERRNO_PROGRAMMER, 275 "Failed to clean up compressor"); 276 ret = ARCHIVE_FATAL; 277 } 278 return ret; 279} 280 281static int 282archive_compressor_bzip2_free(struct archive_write_filter *f) 283{ 284 struct private_data *data = (struct private_data *)f->data; 285 free(data->compressed); 286 free(data); 287 f->data = NULL; 288 return (ARCHIVE_OK); 289} 290 291/* 292 * Utility function to push input data through compressor, writing 293 * full output blocks as necessary. 294 * 295 * Note that this handles both the regular write case (finishing == 296 * false) and the end-of-archive case (finishing == true). 297 */ 298static int 299drive_compressor(struct archive_write_filter *f, 300 struct private_data *data, int finishing) 301{ 302 int ret; 303 304 for (;;) { 305 if (data->stream.avail_out == 0) { 306 ret = __archive_write_filter(f->next_filter, 307 data->compressed, 308 data->compressed_buffer_size); 309 if (ret != ARCHIVE_OK) { 310 /* TODO: Handle this write failure */ 311 return (ARCHIVE_FATAL); 312 } 313 data->stream.next_out = data->compressed; 314 data->stream.avail_out = (uint32_t)data->compressed_buffer_size; 315 } 316 317 /* If there's nothing to do, we're done. */ 318 if (!finishing && data->stream.avail_in == 0) 319 return (ARCHIVE_OK); 320 321 ret = BZ2_bzCompress(&(data->stream), 322 finishing ? BZ_FINISH : BZ_RUN); 323 324 switch (ret) { 325 case BZ_RUN_OK: 326 /* In non-finishing case, did compressor 327 * consume everything? */ 328 if (!finishing && data->stream.avail_in == 0) 329 return (ARCHIVE_OK); 330 break; 331 case BZ_FINISH_OK: /* Finishing: There's more work to do */ 332 break; 333 case BZ_STREAM_END: /* Finishing: all done */ 334 /* Only occurs in finishing case */ 335 return (ARCHIVE_OK); 336 default: 337 /* Any other return value indicates an error */ 338 archive_set_error(f->archive, 339 ARCHIVE_ERRNO_PROGRAMMER, 340 "Bzip2 compression failed;" 341 " BZ2_bzCompress() returned %d", 342 ret); 343 return (ARCHIVE_FATAL); 344 } 345 } 346} 347 348#else /* HAVE_BZLIB_H && BZ_CONFIG_ERROR */ 349 350static int 351archive_compressor_bzip2_open(struct archive_write_filter *f) 352{ 353 struct private_data *data = (struct private_data *)f->data; 354 struct archive_string as; 355 int r; 356 357 archive_string_init(&as); 358 archive_strcpy(&as, "bzip2"); 359 360 /* Specify compression level. */ 361 if (data->compression_level > 0) { 362 archive_strcat(&as, " -"); 363 archive_strappend_char(&as, '0' + data->compression_level); 364 } 365 f->write = archive_compressor_bzip2_write; 366 367 r = __archive_write_program_open(f, data->pdata, as.s); 368 archive_string_free(&as); 369 return (r); 370} 371 372static int 373archive_compressor_bzip2_write(struct archive_write_filter *f, const void *buff, 374 size_t length) 375{ 376 struct private_data *data = (struct private_data *)f->data; 377 378 return __archive_write_program_write(f, data->pdata, buff, length); 379} 380 381static int 382archive_compressor_bzip2_close(struct archive_write_filter *f) 383{ 384 struct private_data *data = (struct private_data *)f->data; 385 386 return __archive_write_program_close(f, data->pdata); 387} 388 389static int 390archive_compressor_bzip2_free(struct archive_write_filter *f) 391{ 392 struct private_data *data = (struct private_data *)f->data; 393 394 __archive_write_program_free(data->pdata); 395 free(data); 396 return (ARCHIVE_OK); 397} 398 399#endif /* HAVE_BZLIB_H && BZ_CONFIG_ERROR */ 400