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 ret = __archive_write_open_filter(f->next_filter); 171 if (ret != 0) 172 return (ret); 173 174 if (data->compressed == NULL) { 175 size_t bs = 65536, bpb; 176 if (f->archive->magic == ARCHIVE_WRITE_MAGIC) { 177 /* Buffer size should be a multiple number of the of bytes 178 * per block for performance. */ 179 bpb = archive_write_get_bytes_per_block(f->archive); 180 if (bpb > bs) 181 bs = bpb; 182 else if (bpb != 0) 183 bs -= bs % bpb; 184 } 185 data->compressed_buffer_size = bs; 186 data->compressed 187 = (char *)malloc(data->compressed_buffer_size); 188 if (data->compressed == NULL) { 189 archive_set_error(f->archive, ENOMEM, 190 "Can't allocate data for compression buffer"); 191 return (ARCHIVE_FATAL); 192 } 193 } 194 195 memset(&data->stream, 0, sizeof(data->stream)); 196 data->stream.next_out = data->compressed; 197 data->stream.avail_out = data->compressed_buffer_size; 198 f->write = archive_compressor_bzip2_write; 199 200 /* Initialize compression library */ 201 ret = BZ2_bzCompressInit(&(data->stream), 202 data->compression_level, 0, 30); 203 if (ret == BZ_OK) { 204 f->data = data; 205 return (ARCHIVE_OK); 206 } 207 208 /* Library setup failed: clean up. */ 209 archive_set_error(f->archive, ARCHIVE_ERRNO_MISC, 210 "Internal error initializing compression library"); 211 212 /* Override the error message if we know what really went wrong. */ 213 switch (ret) { 214 case BZ_PARAM_ERROR: 215 archive_set_error(f->archive, ARCHIVE_ERRNO_MISC, 216 "Internal error initializing compression library: " 217 "invalid setup parameter"); 218 break; 219 case BZ_MEM_ERROR: 220 archive_set_error(f->archive, ENOMEM, 221 "Internal error initializing compression library: " 222 "out of memory"); 223 break; 224 case BZ_CONFIG_ERROR: 225 archive_set_error(f->archive, ARCHIVE_ERRNO_MISC, 226 "Internal error initializing compression library: " 227 "mis-compiled library"); 228 break; 229 } 230 231 return (ARCHIVE_FATAL); 232 233 } 234 235 /* 236 * Write data to the compressed stream. 237 * 238 * Returns ARCHIVE_OK if all data written, error otherwise. 239 */ 240 static int 241 archive_compressor_bzip2_write(struct archive_write_filter *f, 242 const void *buff, size_t length) 243 { 244 struct private_data *data = (struct private_data *)f->data; 245 246 /* Update statistics */ 247 data->total_in += length; 248 249 /* Compress input data to output buffer */ 250 SET_NEXT_IN(data, buff); 251 data->stream.avail_in = length; 252 if (drive_compressor(f, data, 0)) 253 return (ARCHIVE_FATAL); 254 return (ARCHIVE_OK); 255 } 256 257 258 /* 259 * Finish the compression. 260 */ 261 static int 262 archive_compressor_bzip2_close(struct archive_write_filter *f) 263 { 264 struct private_data *data = (struct private_data *)f->data; 265 int ret, r1; 266 267 /* Finish compression cycle. */ 268 ret = drive_compressor(f, data, 1); 269 if (ret == ARCHIVE_OK) { 270 /* Write the last block */ 271 ret = __archive_write_filter(f->next_filter, 272 data->compressed, 273 data->compressed_buffer_size - data->stream.avail_out); 274 } 275 276 switch (BZ2_bzCompressEnd(&(data->stream))) { 277 case BZ_OK: 278 break; 279 default: 280 archive_set_error(f->archive, ARCHIVE_ERRNO_PROGRAMMER, 281 "Failed to clean up compressor"); 282 ret = ARCHIVE_FATAL; 283 } 284 285 r1 = __archive_write_close_filter(f->next_filter); 286 return (r1 < ret ? r1 : ret); 287 } 288 289 static int 290 archive_compressor_bzip2_free(struct archive_write_filter *f) 291 { 292 struct private_data *data = (struct private_data *)f->data; 293 free(data->compressed); 294 free(data); 295 f->data = NULL; 296 return (ARCHIVE_OK); 297 } 298 299 /* 300 * Utility function to push input data through compressor, writing 301 * full output blocks as necessary. 302 * 303 * Note that this handles both the regular write case (finishing == 304 * false) and the end-of-archive case (finishing == true). 305 */ 306 static int 307 drive_compressor(struct archive_write_filter *f, 308 struct private_data *data, int finishing) 309 { 310 int ret; 311 312 for (;;) { 313 if (data->stream.avail_out == 0) { 314 ret = __archive_write_filter(f->next_filter, 315 data->compressed, 316 data->compressed_buffer_size); 317 if (ret != ARCHIVE_OK) { 318 /* TODO: Handle this write failure */ 319 return (ARCHIVE_FATAL); 320 } 321 data->stream.next_out = data->compressed; 322 data->stream.avail_out = data->compressed_buffer_size; 323 } 324 325 /* If there's nothing to do, we're done. */ 326 if (!finishing && data->stream.avail_in == 0) 327 return (ARCHIVE_OK); 328 329 ret = BZ2_bzCompress(&(data->stream), 330 finishing ? BZ_FINISH : BZ_RUN); 331 332 switch (ret) { 333 case BZ_RUN_OK: 334 /* In non-finishing case, did compressor 335 * consume everything? */ 336 if (!finishing && data->stream.avail_in == 0) 337 return (ARCHIVE_OK); 338 break; 339 case BZ_FINISH_OK: /* Finishing: There's more work to do */ 340 break; 341 case BZ_STREAM_END: /* Finishing: all done */ 342 /* Only occurs in finishing case */ 343 return (ARCHIVE_OK); 344 default: 345 /* Any other return value indicates an error */ 346 archive_set_error(f->archive, 347 ARCHIVE_ERRNO_PROGRAMMER, 348 "Bzip2 compression failed;" 349 " BZ2_bzCompress() returned %d", 350 ret); 351 return (ARCHIVE_FATAL); 352 } 353 } 354 } 355 356 #else /* HAVE_BZLIB_H && BZ_CONFIG_ERROR */ 357 358 static int 359 archive_compressor_bzip2_open(struct archive_write_filter *f) 360 { 361 struct private_data *data = (struct private_data *)f->data; 362 struct archive_string as; 363 int r; 364 365 archive_string_init(&as); 366 archive_strcpy(&as, "bzip2"); 367 368 /* Specify compression level. */ 369 if (data->compression_level > 0) { 370 archive_strcat(&as, " -"); 371 archive_strappend_char(&as, '0' + data->compression_level); 372 } 373 f->write = archive_compressor_bzip2_write; 374 375 r = __archive_write_program_open(f, data->pdata, as.s); 376 archive_string_free(&as); 377 return (r); 378 } 379 380 static int 381 archive_compressor_bzip2_write(struct archive_write_filter *f, const void *buff, 382 size_t length) 383 { 384 struct private_data *data = (struct private_data *)f->data; 385 386 return __archive_write_program_write(f, data->pdata, buff, length); 387 } 388 389 static int 390 archive_compressor_bzip2_close(struct archive_write_filter *f) 391 { 392 struct private_data *data = (struct private_data *)f->data; 393 394 return __archive_write_program_close(f, data->pdata); 395 } 396 397 static int 398 archive_compressor_bzip2_free(struct archive_write_filter *f) 399 { 400 struct private_data *data = (struct private_data *)f->data; 401 402 __archive_write_program_free(data->pdata); 403 free(data); 404 return (ARCHIVE_OK); 405 } 406 407 #endif /* HAVE_BZLIB_H && BZ_CONFIG_ERROR */ 408