archive_write_add_filter_gzip.c revision 231200
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 49int 50archive_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 58int 59archive_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 68struct 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 84static int archive_compressor_gzip_options(struct archive_write_filter *, 85 const char *, const char *); 86static int archive_compressor_gzip_open(struct archive_write_filter *); 87static int archive_compressor_gzip_write(struct archive_write_filter *, 88 const void *, size_t); 89static int archive_compressor_gzip_close(struct archive_write_filter *); 90static int archive_compressor_gzip_free(struct archive_write_filter *); 91static 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 */ 98int 99archive_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 */ 126static int 127archive_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 data->compressed_buffer_size = 65536; 139 data->compressed 140 = (unsigned char *)malloc(data->compressed_buffer_size); 141 if (data->compressed == NULL) { 142 archive_set_error(f->archive, ENOMEM, 143 "Can't allocate data for compression buffer"); 144 return (ARCHIVE_FATAL); 145 } 146 } 147 148 data->crc = crc32(0L, NULL, 0); 149 data->stream.next_out = data->compressed; 150 data->stream.avail_out = data->compressed_buffer_size; 151 152 /* Prime output buffer with a gzip header. */ 153 t = time(NULL); 154 data->compressed[0] = 0x1f; /* GZip signature bytes */ 155 data->compressed[1] = 0x8b; 156 data->compressed[2] = 0x08; /* "Deflate" compression */ 157 data->compressed[3] = 0; /* No options */ 158 data->compressed[4] = (t)&0xff; /* Timestamp */ 159 data->compressed[5] = (t>>8)&0xff; 160 data->compressed[6] = (t>>16)&0xff; 161 data->compressed[7] = (t>>24)&0xff; 162 data->compressed[8] = 0; /* No deflate options */ 163 data->compressed[9] = 3; /* OS=Unix */ 164 data->stream.next_out += 10; 165 data->stream.avail_out -= 10; 166 167 f->write = archive_compressor_gzip_write; 168 169 /* Initialize compression library. */ 170 ret = deflateInit2(&(data->stream), 171 data->compression_level, 172 Z_DEFLATED, 173 -15 /* < 0 to suppress zlib header */, 174 8, 175 Z_DEFAULT_STRATEGY); 176 177 if (ret == Z_OK) { 178 f->data = data; 179 return (ARCHIVE_OK); 180 } 181 182 /* Library setup failed: clean up. */ 183 archive_set_error(f->archive, ARCHIVE_ERRNO_MISC, "Internal error " 184 "initializing compression library"); 185 186 /* Override the error message if we know what really went wrong. */ 187 switch (ret) { 188 case Z_STREAM_ERROR: 189 archive_set_error(f->archive, ARCHIVE_ERRNO_MISC, 190 "Internal error initializing " 191 "compression library: invalid setup parameter"); 192 break; 193 case Z_MEM_ERROR: 194 archive_set_error(f->archive, ENOMEM, "Internal error initializing " 195 "compression library"); 196 break; 197 case Z_VERSION_ERROR: 198 archive_set_error(f->archive, ARCHIVE_ERRNO_MISC, 199 "Internal error initializing " 200 "compression library: invalid library version"); 201 break; 202 } 203 204 return (ARCHIVE_FATAL); 205} 206 207/* 208 * Set write options. 209 */ 210static int 211archive_compressor_gzip_options(struct archive_write_filter *f, const char *key, 212 const char *value) 213{ 214 struct private_data *data = (struct private_data *)f->data; 215 216 if (strcmp(key, "compression-level") == 0) { 217 if (value == NULL || !(value[0] >= '0' && value[0] <= '9') || 218 value[1] != '\0') 219 return (ARCHIVE_WARN); 220 data->compression_level = value[0] - '0'; 221 return (ARCHIVE_OK); 222 } 223 return (ARCHIVE_WARN); 224} 225 226/* 227 * Write data to the compressed stream. 228 */ 229static int 230archive_compressor_gzip_write(struct archive_write_filter *f, const void *buff, 231 size_t length) 232{ 233 struct private_data *data = (struct private_data *)f->data; 234 int ret; 235 236 /* Update statistics */ 237 data->crc = crc32(data->crc, (const Bytef *)buff, length); 238 data->total_in += length; 239 240 /* Compress input data to output buffer */ 241 SET_NEXT_IN(data, buff); 242 data->stream.avail_in = length; 243 if ((ret = drive_compressor(f, data, 0)) != ARCHIVE_OK) 244 return (ret); 245 246 return (ARCHIVE_OK); 247} 248 249/* 250 * Finish the compression... 251 */ 252static int 253archive_compressor_gzip_close(struct archive_write_filter *f) 254{ 255 unsigned char trailer[8]; 256 struct private_data *data = (struct private_data *)f->data; 257 int ret, r1; 258 259 /* Finish compression cycle */ 260 ret = drive_compressor(f, data, 1); 261 if (ret == ARCHIVE_OK) { 262 /* Write the last compressed data. */ 263 ret = __archive_write_filter(f->next_filter, 264 data->compressed, 265 data->compressed_buffer_size - data->stream.avail_out); 266 } 267 if (ret == ARCHIVE_OK) { 268 /* Build and write out 8-byte trailer. */ 269 trailer[0] = (data->crc)&0xff; 270 trailer[1] = (data->crc >> 8)&0xff; 271 trailer[2] = (data->crc >> 16)&0xff; 272 trailer[3] = (data->crc >> 24)&0xff; 273 trailer[4] = (data->total_in)&0xff; 274 trailer[5] = (data->total_in >> 8)&0xff; 275 trailer[6] = (data->total_in >> 16)&0xff; 276 trailer[7] = (data->total_in >> 24)&0xff; 277 ret = __archive_write_filter(f->next_filter, trailer, 8); 278 } 279 280 switch (deflateEnd(&(data->stream))) { 281 case Z_OK: 282 break; 283 default: 284 archive_set_error(f->archive, ARCHIVE_ERRNO_MISC, 285 "Failed to clean up compressor"); 286 ret = ARCHIVE_FATAL; 287 } 288 r1 = __archive_write_close_filter(f->next_filter); 289 return (r1 < ret ? r1 : ret); 290} 291 292static int 293archive_compressor_gzip_free(struct archive_write_filter *f) 294{ 295 struct private_data *data = (struct private_data *)f->data; 296 free(data->compressed); 297 free(data); 298 f->data = NULL; 299 return (ARCHIVE_OK); 300} 301 302/* 303 * Utility function to push input data through compressor, 304 * writing full output blocks as necessary. 305 * 306 * Note that this handles both the regular write case (finishing == 307 * false) and the end-of-archive case (finishing == true). 308 */ 309static int 310drive_compressor(struct archive_write_filter *f, 311 struct private_data *data, int finishing) 312{ 313 int ret; 314 315 for (;;) { 316 if (data->stream.avail_out == 0) { 317 ret = __archive_write_filter(f->next_filter, 318 data->compressed, 319 data->compressed_buffer_size); 320 if (ret != ARCHIVE_OK) 321 return (ARCHIVE_FATAL); 322 data->stream.next_out = data->compressed; 323 data->stream.avail_out = data->compressed_buffer_size; 324 } 325 326 /* If there's nothing to do, we're done. */ 327 if (!finishing && data->stream.avail_in == 0) 328 return (ARCHIVE_OK); 329 330 ret = deflate(&(data->stream), 331 finishing ? Z_FINISH : Z_NO_FLUSH ); 332 333 switch (ret) { 334 case Z_OK: 335 /* In non-finishing case, check if compressor 336 * consumed everything */ 337 if (!finishing && data->stream.avail_in == 0) 338 return (ARCHIVE_OK); 339 /* In finishing case, this return always means 340 * there's more work */ 341 break; 342 case Z_STREAM_END: 343 /* This return can only occur in finishing case. */ 344 return (ARCHIVE_OK); 345 default: 346 /* Any other return value indicates an error. */ 347 archive_set_error(f->archive, ARCHIVE_ERRNO_MISC, 348 "GZip compression failed:" 349 " deflate() call returned status %d", 350 ret); 351 return (ARCHIVE_FATAL); 352 } 353 } 354} 355 356#endif /* HAVE_ZLIB_H */ 357