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 #ifdef HAVE_ERRNO_H 29 #include <errno.h> 30 #endif 31 #ifdef HAVE_STDLIB_H 32 #include <stdlib.h> 33 #endif 34 #ifdef HAVE_STRING_H 35 #include <string.h> 36 #endif 37 #include <time.h> 38 #ifdef HAVE_ZLIB_H 39 #include <zlib.h> 40 #endif 41 42 #include "archive.h" 43 #include "archive_private.h" 44 #include "archive_string.h" 45 #include "archive_write_private.h" 46 47 #if ARCHIVE_VERSION_NUMBER < 4000000 48 int 49 archive_write_set_compression_gzip(struct archive *a) 50 { 51 __archive_write_filters_free(a); 52 return (archive_write_add_filter_gzip(a)); 53 } 54 #endif 55 56 /* Don't compile this if we don't have zlib. */ 57 58 struct private_data { 59 int compression_level; 60 int timestamp; 61 #ifdef HAVE_ZLIB_H 62 z_stream stream; 63 int64_t total_in; 64 unsigned char *compressed; 65 size_t compressed_buffer_size; 66 unsigned long crc; 67 #else 68 struct archive_write_program_data *pdata; 69 #endif 70 }; 71 72 /* 73 * Yuck. zlib.h is not const-correct, so I need this one bit 74 * of ugly hackery to convert a const * pointer to a non-const pointer. 75 */ 76 #define SET_NEXT_IN(st,src) \ 77 (st)->stream.next_in = (Bytef *)(uintptr_t)(const void *)(src) 78 79 static int archive_compressor_gzip_options(struct archive_write_filter *, 80 const char *, const char *); 81 static int archive_compressor_gzip_open(struct archive_write_filter *); 82 static int archive_compressor_gzip_write(struct archive_write_filter *, 83 const void *, size_t); 84 static int archive_compressor_gzip_close(struct archive_write_filter *); 85 static int archive_compressor_gzip_free(struct archive_write_filter *); 86 #ifdef HAVE_ZLIB_H 87 static int drive_compressor(struct archive_write_filter *, 88 struct private_data *, int finishing); 89 #endif 90 91 92 /* 93 * Add a gzip compression filter to this write handle. 94 */ 95 int 96 archive_write_add_filter_gzip(struct archive *_a) 97 { 98 struct archive_write *a = (struct archive_write *)_a; 99 struct archive_write_filter *f = __archive_write_allocate_filter(_a); 100 struct private_data *data; 101 archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC, 102 ARCHIVE_STATE_NEW, "archive_write_add_filter_gzip"); 103 104 data = calloc(1, sizeof(*data)); 105 if (data == NULL) { 106 archive_set_error(&a->archive, ENOMEM, "Out of memory"); 107 return (ARCHIVE_FATAL); 108 } 109 f->data = data; 110 f->open = &archive_compressor_gzip_open; 111 f->options = &archive_compressor_gzip_options; 112 f->close = &archive_compressor_gzip_close; 113 f->free = &archive_compressor_gzip_free; 114 f->code = ARCHIVE_FILTER_GZIP; 115 f->name = "gzip"; 116 #ifdef HAVE_ZLIB_H 117 data->compression_level = Z_DEFAULT_COMPRESSION; 118 return (ARCHIVE_OK); 119 #else 120 data->pdata = __archive_write_program_allocate("gzip"); 121 if (data->pdata == NULL) { 122 free(data); 123 archive_set_error(&a->archive, ENOMEM, "Out of memory"); 124 return (ARCHIVE_FATAL); 125 } 126 data->compression_level = 0; 127 archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, 128 "Using external gzip program"); 129 return (ARCHIVE_WARN); 130 #endif 131 } 132 133 static int 134 archive_compressor_gzip_free(struct archive_write_filter *f) 135 { 136 struct private_data *data = (struct private_data *)f->data; 137 138 #ifdef HAVE_ZLIB_H 139 free(data->compressed); 140 #else 141 __archive_write_program_free(data->pdata); 142 #endif 143 free(data); 144 f->data = NULL; 145 return (ARCHIVE_OK); 146 } 147 148 /* 149 * Set write options. 150 */ 151 static int 152 archive_compressor_gzip_options(struct archive_write_filter *f, const char *key, 153 const char *value) 154 { 155 struct private_data *data = (struct private_data *)f->data; 156 157 if (strcmp(key, "compression-level") == 0) { 158 if (value == NULL || !(value[0] >= '0' && value[0] <= '9') || 159 value[1] != '\0') 160 return (ARCHIVE_WARN); 161 data->compression_level = value[0] - '0'; 162 return (ARCHIVE_OK); 163 } 164 if (strcmp(key, "timestamp") == 0) { 165 data->timestamp = (value == NULL)?-1:1; 166 return (ARCHIVE_OK); 167 } 168 169 /* Note: The "warn" return is just to inform the options 170 * supervisor that we didn't handle it. It will generate 171 * a suitable error if no one used this option. */ 172 return (ARCHIVE_WARN); 173 } 174 175 #ifdef HAVE_ZLIB_H 176 /* 177 * Setup callback. 178 */ 179 static int 180 archive_compressor_gzip_open(struct archive_write_filter *f) 181 { 182 struct private_data *data = (struct private_data *)f->data; 183 int ret; 184 185 if (data->compressed == NULL) { 186 size_t bs = 65536, bpb; 187 if (f->archive->magic == ARCHIVE_WRITE_MAGIC) { 188 /* Buffer size should be a multiple number of 189 * the of bytes per block for performance. */ 190 bpb = archive_write_get_bytes_per_block(f->archive); 191 if (bpb > bs) 192 bs = bpb; 193 else if (bpb != 0) 194 bs -= bs % bpb; 195 } 196 data->compressed_buffer_size = bs; 197 data->compressed = malloc(data->compressed_buffer_size); 198 if (data->compressed == NULL) { 199 archive_set_error(f->archive, ENOMEM, 200 "Can't allocate data for compression buffer"); 201 return (ARCHIVE_FATAL); 202 } 203 } 204 205 data->crc = crc32(0L, NULL, 0); 206 data->stream.next_out = data->compressed; 207 data->stream.avail_out = (uInt)data->compressed_buffer_size; 208 209 /* Prime output buffer with a gzip header. */ 210 data->compressed[0] = 0x1f; /* GZip signature bytes */ 211 data->compressed[1] = 0x8b; 212 data->compressed[2] = 0x08; /* "Deflate" compression */ 213 data->compressed[3] = 0; /* No options */ 214 if (data->timestamp >= 0) { 215 time_t t = time(NULL); 216 data->compressed[4] = (uint8_t)(t)&0xff; /* Timestamp */ 217 data->compressed[5] = (uint8_t)(t>>8)&0xff; 218 data->compressed[6] = (uint8_t)(t>>16)&0xff; 219 data->compressed[7] = (uint8_t)(t>>24)&0xff; 220 } else 221 memset(&data->compressed[4], 0, 4); 222 if (data->compression_level == 9) 223 data->compressed[8] = 2; 224 else if(data->compression_level == 1) 225 data->compressed[8] = 4; 226 else 227 data->compressed[8] = 0; 228 data->compressed[9] = 3; /* OS=Unix */ 229 data->stream.next_out += 10; 230 data->stream.avail_out -= 10; 231 232 f->write = archive_compressor_gzip_write; 233 234 /* Initialize compression library. */ 235 ret = deflateInit2(&(data->stream), 236 data->compression_level, 237 Z_DEFLATED, 238 -15 /* < 0 to suppress zlib header */, 239 8, 240 Z_DEFAULT_STRATEGY); 241 242 if (ret == Z_OK) { 243 f->data = data; 244 return (ARCHIVE_OK); 245 } 246 247 /* Library setup failed: clean up. */ 248 archive_set_error(f->archive, ARCHIVE_ERRNO_MISC, "Internal error " 249 "initializing compression library"); 250 251 /* Override the error message if we know what really went wrong. */ 252 switch (ret) { 253 case Z_STREAM_ERROR: 254 archive_set_error(f->archive, ARCHIVE_ERRNO_MISC, 255 "Internal error initializing " 256 "compression library: invalid setup parameter"); 257 break; 258 case Z_MEM_ERROR: 259 archive_set_error(f->archive, ENOMEM, 260 "Internal error initializing compression library"); 261 break; 262 case Z_VERSION_ERROR: 263 archive_set_error(f->archive, ARCHIVE_ERRNO_MISC, 264 "Internal error initializing " 265 "compression library: invalid library version"); 266 break; 267 } 268 269 return (ARCHIVE_FATAL); 270 } 271 272 /* 273 * Write data to the compressed stream. 274 */ 275 static int 276 archive_compressor_gzip_write(struct archive_write_filter *f, const void *buff, 277 size_t length) 278 { 279 struct private_data *data = (struct private_data *)f->data; 280 int ret; 281 282 /* Update statistics */ 283 data->crc = crc32(data->crc, (const Bytef *)buff, (uInt)length); 284 data->total_in += length; 285 286 /* Compress input data to output buffer */ 287 SET_NEXT_IN(data, buff); 288 data->stream.avail_in = (uInt)length; 289 if ((ret = drive_compressor(f, data, 0)) != ARCHIVE_OK) 290 return (ret); 291 292 return (ARCHIVE_OK); 293 } 294 295 /* 296 * Finish the compression... 297 */ 298 static int 299 archive_compressor_gzip_close(struct archive_write_filter *f) 300 { 301 unsigned char trailer[8]; 302 struct private_data *data = (struct private_data *)f->data; 303 int ret; 304 305 /* Finish compression cycle */ 306 ret = drive_compressor(f, data, 1); 307 if (ret == ARCHIVE_OK) { 308 /* Write the last compressed data. */ 309 ret = __archive_write_filter(f->next_filter, 310 data->compressed, 311 data->compressed_buffer_size - data->stream.avail_out); 312 } 313 if (ret == ARCHIVE_OK) { 314 /* Build and write out 8-byte trailer. */ 315 trailer[0] = (uint8_t)(data->crc)&0xff; 316 trailer[1] = (uint8_t)(data->crc >> 8)&0xff; 317 trailer[2] = (uint8_t)(data->crc >> 16)&0xff; 318 trailer[3] = (uint8_t)(data->crc >> 24)&0xff; 319 trailer[4] = (uint8_t)(data->total_in)&0xff; 320 trailer[5] = (uint8_t)(data->total_in >> 8)&0xff; 321 trailer[6] = (uint8_t)(data->total_in >> 16)&0xff; 322 trailer[7] = (uint8_t)(data->total_in >> 24)&0xff; 323 ret = __archive_write_filter(f->next_filter, trailer, 8); 324 } 325 326 switch (deflateEnd(&(data->stream))) { 327 case Z_OK: 328 break; 329 default: 330 archive_set_error(f->archive, ARCHIVE_ERRNO_MISC, 331 "Failed to clean up compressor"); 332 ret = ARCHIVE_FATAL; 333 } 334 return ret; 335 } 336 337 /* 338 * Utility function to push input data through compressor, 339 * writing full output blocks as necessary. 340 * 341 * Note that this handles both the regular write case (finishing == 342 * false) and the end-of-archive case (finishing == true). 343 */ 344 static int 345 drive_compressor(struct archive_write_filter *f, 346 struct private_data *data, int finishing) 347 { 348 int ret; 349 350 for (;;) { 351 if (data->stream.avail_out == 0) { 352 ret = __archive_write_filter(f->next_filter, 353 data->compressed, 354 data->compressed_buffer_size); 355 if (ret != ARCHIVE_OK) 356 return (ARCHIVE_FATAL); 357 data->stream.next_out = data->compressed; 358 data->stream.avail_out = 359 (uInt)data->compressed_buffer_size; 360 } 361 362 /* If there's nothing to do, we're done. */ 363 if (!finishing && data->stream.avail_in == 0) 364 return (ARCHIVE_OK); 365 366 ret = deflate(&(data->stream), 367 finishing ? Z_FINISH : Z_NO_FLUSH ); 368 369 switch (ret) { 370 case Z_OK: 371 /* In non-finishing case, check if compressor 372 * consumed everything */ 373 if (!finishing && data->stream.avail_in == 0) 374 return (ARCHIVE_OK); 375 /* In finishing case, this return always means 376 * there's more work */ 377 break; 378 case Z_STREAM_END: 379 /* This return can only occur in finishing case. */ 380 return (ARCHIVE_OK); 381 default: 382 /* Any other return value indicates an error. */ 383 archive_set_error(f->archive, ARCHIVE_ERRNO_MISC, 384 "GZip compression failed:" 385 " deflate() call returned status %d", 386 ret); 387 return (ARCHIVE_FATAL); 388 } 389 } 390 } 391 392 #else /* HAVE_ZLIB_H */ 393 394 static int 395 archive_compressor_gzip_open(struct archive_write_filter *f) 396 { 397 struct private_data *data = (struct private_data *)f->data; 398 struct archive_string as; 399 int r; 400 401 archive_string_init(&as); 402 archive_strcpy(&as, "gzip"); 403 404 /* Specify compression level. */ 405 if (data->compression_level > 0) { 406 archive_strcat(&as, " -"); 407 archive_strappend_char(&as, '0' + data->compression_level); 408 } 409 if (data->timestamp < 0) 410 /* Do not save timestamp. */ 411 archive_strcat(&as, " -n"); 412 else if (data->timestamp > 0) 413 /* Save timestamp. */ 414 archive_strcat(&as, " -N"); 415 416 f->write = archive_compressor_gzip_write; 417 r = __archive_write_program_open(f, data->pdata, as.s); 418 archive_string_free(&as); 419 return (r); 420 } 421 422 static int 423 archive_compressor_gzip_write(struct archive_write_filter *f, const void *buff, 424 size_t length) 425 { 426 struct private_data *data = (struct private_data *)f->data; 427 428 return __archive_write_program_write(f, data->pdata, buff, length); 429 } 430 431 static int 432 archive_compressor_gzip_close(struct archive_write_filter *f) 433 { 434 struct private_data *data = (struct private_data *)f->data; 435 436 return __archive_write_program_close(f, data->pdata); 437 } 438 439 #endif /* HAVE_ZLIB_H */ 440