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