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