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