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 48int 49archive_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 58struct 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 79static int archive_compressor_gzip_options(struct archive_write_filter *, 80 const char *, const char *); 81static int archive_compressor_gzip_open(struct archive_write_filter *); 82static int archive_compressor_gzip_write(struct archive_write_filter *, 83 const void *, size_t); 84static int archive_compressor_gzip_close(struct archive_write_filter *); 85static int archive_compressor_gzip_free(struct archive_write_filter *); 86#ifdef HAVE_ZLIB_H 87static 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 */ 95int 96archive_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 133static int 134archive_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 */ 151static int 152archive_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 */ 179static int 180archive_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 198 = (unsigned char *)malloc(data->compressed_buffer_size); 199 if (data->compressed == NULL) { 200 archive_set_error(f->archive, ENOMEM, 201 "Can't allocate data for compression buffer"); 202 return (ARCHIVE_FATAL); 203 } 204 } 205 206 data->crc = crc32(0L, NULL, 0); 207 data->stream.next_out = data->compressed; 208 data->stream.avail_out = (uInt)data->compressed_buffer_size; 209 210 /* Prime output buffer with a gzip header. */ 211 data->compressed[0] = 0x1f; /* GZip signature bytes */ 212 data->compressed[1] = 0x8b; 213 data->compressed[2] = 0x08; /* "Deflate" compression */ 214 data->compressed[3] = 0; /* No options */ 215 if (data->timestamp >= 0) { 216 time_t t = time(NULL); 217 data->compressed[4] = (uint8_t)(t)&0xff; /* Timestamp */ 218 data->compressed[5] = (uint8_t)(t>>8)&0xff; 219 data->compressed[6] = (uint8_t)(t>>16)&0xff; 220 data->compressed[7] = (uint8_t)(t>>24)&0xff; 221 } else 222 memset(&data->compressed[4], 0, 4); 223 if (data->compression_level == 9) 224 data->compressed[8] = 2; 225 else if(data->compression_level == 1) 226 data->compressed[8] = 4; 227 else 228 data->compressed[8] = 0; 229 data->compressed[9] = 3; /* OS=Unix */ 230 data->stream.next_out += 10; 231 data->stream.avail_out -= 10; 232 233 f->write = archive_compressor_gzip_write; 234 235 /* Initialize compression library. */ 236 ret = deflateInit2(&(data->stream), 237 data->compression_level, 238 Z_DEFLATED, 239 -15 /* < 0 to suppress zlib header */, 240 8, 241 Z_DEFAULT_STRATEGY); 242 243 if (ret == Z_OK) { 244 f->data = data; 245 return (ARCHIVE_OK); 246 } 247 248 /* Library setup failed: clean up. */ 249 archive_set_error(f->archive, ARCHIVE_ERRNO_MISC, "Internal error " 250 "initializing compression library"); 251 252 /* Override the error message if we know what really went wrong. */ 253 switch (ret) { 254 case Z_STREAM_ERROR: 255 archive_set_error(f->archive, ARCHIVE_ERRNO_MISC, 256 "Internal error initializing " 257 "compression library: invalid setup parameter"); 258 break; 259 case Z_MEM_ERROR: 260 archive_set_error(f->archive, ENOMEM, 261 "Internal error initializing compression library"); 262 break; 263 case Z_VERSION_ERROR: 264 archive_set_error(f->archive, ARCHIVE_ERRNO_MISC, 265 "Internal error initializing " 266 "compression library: invalid library version"); 267 break; 268 } 269 270 return (ARCHIVE_FATAL); 271} 272 273/* 274 * Write data to the compressed stream. 275 */ 276static int 277archive_compressor_gzip_write(struct archive_write_filter *f, const void *buff, 278 size_t length) 279{ 280 struct private_data *data = (struct private_data *)f->data; 281 int ret; 282 283 /* Update statistics */ 284 data->crc = crc32(data->crc, (const Bytef *)buff, (uInt)length); 285 data->total_in += length; 286 287 /* Compress input data to output buffer */ 288 SET_NEXT_IN(data, buff); 289 data->stream.avail_in = (uInt)length; 290 if ((ret = drive_compressor(f, data, 0)) != ARCHIVE_OK) 291 return (ret); 292 293 return (ARCHIVE_OK); 294} 295 296/* 297 * Finish the compression... 298 */ 299static int 300archive_compressor_gzip_close(struct archive_write_filter *f) 301{ 302 unsigned char trailer[8]; 303 struct private_data *data = (struct private_data *)f->data; 304 int ret; 305 306 /* Finish compression cycle */ 307 ret = drive_compressor(f, data, 1); 308 if (ret == ARCHIVE_OK) { 309 /* Write the last compressed data. */ 310 ret = __archive_write_filter(f->next_filter, 311 data->compressed, 312 data->compressed_buffer_size - data->stream.avail_out); 313 } 314 if (ret == ARCHIVE_OK) { 315 /* Build and write out 8-byte trailer. */ 316 trailer[0] = (uint8_t)(data->crc)&0xff; 317 trailer[1] = (uint8_t)(data->crc >> 8)&0xff; 318 trailer[2] = (uint8_t)(data->crc >> 16)&0xff; 319 trailer[3] = (uint8_t)(data->crc >> 24)&0xff; 320 trailer[4] = (uint8_t)(data->total_in)&0xff; 321 trailer[5] = (uint8_t)(data->total_in >> 8)&0xff; 322 trailer[6] = (uint8_t)(data->total_in >> 16)&0xff; 323 trailer[7] = (uint8_t)(data->total_in >> 24)&0xff; 324 ret = __archive_write_filter(f->next_filter, trailer, 8); 325 } 326 327 switch (deflateEnd(&(data->stream))) { 328 case Z_OK: 329 break; 330 default: 331 archive_set_error(f->archive, ARCHIVE_ERRNO_MISC, 332 "Failed to clean up compressor"); 333 ret = ARCHIVE_FATAL; 334 } 335 return ret; 336} 337 338/* 339 * Utility function to push input data through compressor, 340 * writing full output blocks as necessary. 341 * 342 * Note that this handles both the regular write case (finishing == 343 * false) and the end-of-archive case (finishing == true). 344 */ 345static int 346drive_compressor(struct archive_write_filter *f, 347 struct private_data *data, int finishing) 348{ 349 int ret; 350 351 for (;;) { 352 if (data->stream.avail_out == 0) { 353 ret = __archive_write_filter(f->next_filter, 354 data->compressed, 355 data->compressed_buffer_size); 356 if (ret != ARCHIVE_OK) 357 return (ARCHIVE_FATAL); 358 data->stream.next_out = data->compressed; 359 data->stream.avail_out = 360 (uInt)data->compressed_buffer_size; 361 } 362 363 /* If there's nothing to do, we're done. */ 364 if (!finishing && data->stream.avail_in == 0) 365 return (ARCHIVE_OK); 366 367 ret = deflate(&(data->stream), 368 finishing ? Z_FINISH : Z_NO_FLUSH ); 369 370 switch (ret) { 371 case Z_OK: 372 /* In non-finishing case, check if compressor 373 * consumed everything */ 374 if (!finishing && data->stream.avail_in == 0) 375 return (ARCHIVE_OK); 376 /* In finishing case, this return always means 377 * there's more work */ 378 break; 379 case Z_STREAM_END: 380 /* This return can only occur in finishing case. */ 381 return (ARCHIVE_OK); 382 default: 383 /* Any other return value indicates an error. */ 384 archive_set_error(f->archive, ARCHIVE_ERRNO_MISC, 385 "GZip compression failed:" 386 " deflate() call returned status %d", 387 ret); 388 return (ARCHIVE_FATAL); 389 } 390 } 391} 392 393#else /* HAVE_ZLIB_H */ 394 395static int 396archive_compressor_gzip_open(struct archive_write_filter *f) 397{ 398 struct private_data *data = (struct private_data *)f->data; 399 struct archive_string as; 400 int r; 401 402 archive_string_init(&as); 403 archive_strcpy(&as, "gzip"); 404 405 /* Specify compression level. */ 406 if (data->compression_level > 0) { 407 archive_strcat(&as, " -"); 408 archive_strappend_char(&as, '0' + data->compression_level); 409 } 410 if (data->timestamp < 0) 411 /* Do not save timestamp. */ 412 archive_strcat(&as, " -n"); 413 else if (data->timestamp > 0) 414 /* Save timestamp. */ 415 archive_strcat(&as, " -N"); 416 417 f->write = archive_compressor_gzip_write; 418 r = __archive_write_program_open(f, data->pdata, as.s); 419 archive_string_free(&as); 420 return (r); 421} 422 423static int 424archive_compressor_gzip_write(struct archive_write_filter *f, const void *buff, 425 size_t length) 426{ 427 struct private_data *data = (struct private_data *)f->data; 428 429 return __archive_write_program_write(f, data->pdata, buff, length); 430} 431 432static int 433archive_compressor_gzip_close(struct archive_write_filter *f) 434{ 435 struct private_data *data = (struct private_data *)f->data; 436 437 return __archive_write_program_close(f, data->pdata); 438} 439 440#endif /* HAVE_ZLIB_H */ 441