2 * Copyright (c) 2003-2007 Tim Kientzle
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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.
26 #include "archive_platform.h"
28 __FBSDID("$FreeBSD: src/lib/libarchive/archive_write_set_compression_gzip.c,v 1.16 2008/02/21 03:21:50 kientzle Exp $");
45 #include "archive_private.h"
46 #include "archive_write_private.h"
50 archive_write_set_compression_gzip(struct archive *a)
52 archive_set_error(a, ARCHIVE_ERRNO_MISC,
53 "gzip compression not supported on this platform");
54 return (ARCHIVE_FATAL);
57 /* Don't compile this if we don't have zlib. */
62 unsigned char *compressed;
63 size_t compressed_buffer_size;
67 struct private_config {
68 int compression_level;
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.
76 #define SET_NEXT_IN(st,src) \
77 (st)->stream.next_in = (Bytef *)(uintptr_t)(const void *)(src)
79 static int archive_compressor_gzip_finish(struct archive_write *);
80 static int archive_compressor_gzip_init(struct archive_write *);
81 static int archive_compressor_gzip_options(struct archive_write *,
82 const char *, const char *);
83 static int archive_compressor_gzip_write(struct archive_write *,
84 const void *, size_t);
85 static int drive_compressor(struct archive_write *, struct private_data *,
90 * Allocate, initialize and return a archive object.
93 archive_write_set_compression_gzip(struct archive *_a)
95 struct archive_write *a = (struct archive_write *)_a;
96 struct private_config *config;
97 __archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
98 ARCHIVE_STATE_NEW, "archive_write_set_compression_gzip");
99 config = malloc(sizeof(*config));
100 if (config == NULL) {
101 archive_set_error(&a->archive, ENOMEM, "Out of memory");
102 return (ARCHIVE_FATAL);
104 a->compressor.config = config;
105 a->compressor.finish = &archive_compressor_gzip_finish;
106 config->compression_level = Z_DEFAULT_COMPRESSION;
107 a->compressor.init = &archive_compressor_gzip_init;
108 a->compressor.options = &archive_compressor_gzip_options;
109 a->archive.compression_code = ARCHIVE_COMPRESSION_GZIP;
110 a->archive.compression_name = "gzip";
118 archive_compressor_gzip_init(struct archive_write *a)
121 struct private_data *state;
122 struct private_config *config;
125 config = (struct private_config *)a->compressor.config;
127 if (a->client_opener != NULL) {
128 ret = (a->client_opener)(&a->archive, a->client_data);
129 if (ret != ARCHIVE_OK)
134 * The next check is a temporary workaround until the gzip
135 * code can be overhauled some. The code should not require
136 * that compressed_buffer_size == bytes_per_block. Removing
137 * this assumption will allow us to compress larger chunks at
138 * a time, which should improve overall performance
139 * marginally. As a minor side-effect, such a cleanup would
140 * allow us to support truly arbitrary block sizes.
142 if (a->bytes_per_block < 10) {
143 archive_set_error(&a->archive, EINVAL,
144 "GZip compressor requires a minimum 10 byte block size");
145 return (ARCHIVE_FATAL);
148 state = (struct private_data *)malloc(sizeof(*state));
150 archive_set_error(&a->archive, ENOMEM,
151 "Can't allocate data for compression");
152 return (ARCHIVE_FATAL);
154 memset(state, 0, sizeof(*state));
157 * See comment above. We should set compressed_buffer_size to
158 * max(bytes_per_block, 65536), but the code can't handle that yet.
160 state->compressed_buffer_size = a->bytes_per_block;
161 state->compressed = (unsigned char *)malloc(state->compressed_buffer_size);
162 state->crc = crc32(0L, NULL, 0);
164 if (state->compressed == NULL) {
165 archive_set_error(&a->archive, ENOMEM,
166 "Can't allocate data for compression buffer");
168 return (ARCHIVE_FATAL);
171 state->stream.next_out = state->compressed;
172 state->stream.avail_out = state->compressed_buffer_size;
174 /* Prime output buffer with a gzip header. */
176 state->compressed[0] = 0x1f; /* GZip signature bytes */
177 state->compressed[1] = 0x8b;
178 state->compressed[2] = 0x08; /* "Deflate" compression */
179 state->compressed[3] = 0; /* No options */
180 state->compressed[4] = (t)&0xff; /* Timestamp */
181 state->compressed[5] = (t>>8)&0xff;
182 state->compressed[6] = (t>>16)&0xff;
183 state->compressed[7] = (t>>24)&0xff;
184 state->compressed[8] = 0; /* No deflate options */
185 state->compressed[9] = 3; /* OS=Unix */
186 state->stream.next_out += 10;
187 state->stream.avail_out -= 10;
189 a->compressor.write = archive_compressor_gzip_write;
191 /* Initialize compression library. */
192 ret = deflateInit2(&(state->stream),
193 config->compression_level,
195 -15 /* < 0 to suppress zlib header */,
200 a->compressor.data = state;
204 /* Library setup failed: clean up. */
205 archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Internal error "
206 "initializing compression library");
207 free(state->compressed);
210 /* Override the error message if we know what really went wrong. */
213 archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
214 "Internal error initializing "
215 "compression library: invalid setup parameter");
218 archive_set_error(&a->archive, ENOMEM, "Internal error initializing "
219 "compression library");
221 case Z_VERSION_ERROR:
222 archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
223 "Internal error initializing "
224 "compression library: invalid library version");
228 return (ARCHIVE_FATAL);
235 archive_compressor_gzip_options(struct archive_write *a, const char *key,
238 struct private_config *config;
240 config = (struct private_config *)a->compressor.config;
241 if (strcmp(key, "compression-level") == 0) {
242 if (value == NULL || !(value[0] >= '0' && value[0] <= '9') ||
244 return (ARCHIVE_WARN);
245 config->compression_level = value[0] - '0';
249 return (ARCHIVE_WARN);
253 * Write data to the compressed stream.
256 archive_compressor_gzip_write(struct archive_write *a, const void *buff,
259 struct private_data *state;
262 state = (struct private_data *)a->compressor.data;
263 if (a->client_writer == NULL) {
264 archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
265 "No write callback is registered? "
266 "This is probably an internal programming error.");
267 return (ARCHIVE_FATAL);
270 /* Update statistics */
271 state->crc = crc32(state->crc, (const Bytef *)buff, length);
272 state->total_in += length;
274 /* Compress input data to output buffer */
275 SET_NEXT_IN(state, buff);
276 state->stream.avail_in = length;
277 if ((ret = drive_compressor(a, state, 0)) != ARCHIVE_OK)
280 a->archive.file_position += length;
285 * Finish the compression...
288 archive_compressor_gzip_finish(struct archive_write *a)
290 ssize_t block_length, target_block_length, bytes_written;
292 struct private_data *state;
294 unsigned char trailer[8];
296 state = (struct private_data *)a->compressor.data;
299 if (a->client_writer == NULL) {
300 archive_set_error(&a->archive,
301 ARCHIVE_ERRNO_PROGRAMMER,
302 "No write callback is registered? "
303 "This is probably an internal programming error.");
308 /* By default, always pad the uncompressed data. */
309 if (a->pad_uncompressed) {
310 tocopy = a->bytes_per_block -
311 (state->total_in % a->bytes_per_block);
312 while (tocopy > 0 && tocopy < (unsigned)a->bytes_per_block) {
313 SET_NEXT_IN(state, a->nulls);
314 state->stream.avail_in = tocopy < a->null_length ?
315 tocopy : a->null_length;
316 state->crc = crc32(state->crc, a->nulls,
317 state->stream.avail_in);
318 state->total_in += state->stream.avail_in;
319 tocopy -= state->stream.avail_in;
320 ret = drive_compressor(a, state, 0);
321 if (ret != ARCHIVE_OK)
326 /* Finish compression cycle */
327 if (((ret = drive_compressor(a, state, 1))) != ARCHIVE_OK)
330 /* Build trailer: 4-byte CRC and 4-byte length. */
331 trailer[0] = (state->crc)&0xff;
332 trailer[1] = (state->crc >> 8)&0xff;
333 trailer[2] = (state->crc >> 16)&0xff;
334 trailer[3] = (state->crc >> 24)&0xff;
335 trailer[4] = (state->total_in)&0xff;
336 trailer[5] = (state->total_in >> 8)&0xff;
337 trailer[6] = (state->total_in >> 16)&0xff;
338 trailer[7] = (state->total_in >> 24)&0xff;
340 /* Add trailer to current block. */
342 if (tocopy > state->stream.avail_out)
343 tocopy = state->stream.avail_out;
344 memcpy(state->stream.next_out, trailer, tocopy);
345 state->stream.next_out += tocopy;
346 state->stream.avail_out -= tocopy;
348 /* If it overflowed, flush and start a new block. */
350 bytes_written = (a->client_writer)(&a->archive, a->client_data,
351 state->compressed, state->compressed_buffer_size);
352 if (bytes_written <= 0) {
356 a->archive.raw_position += bytes_written;
357 state->stream.next_out = state->compressed;
358 state->stream.avail_out = state->compressed_buffer_size;
359 memcpy(state->stream.next_out, trailer + tocopy, 8-tocopy);
360 state->stream.next_out += 8-tocopy;
361 state->stream.avail_out -= 8-tocopy;
364 /* Optionally, pad the final compressed block. */
365 block_length = state->stream.next_out - state->compressed;
367 /* Tricky calculation to determine size of last block. */
368 target_block_length = block_length;
369 if (a->bytes_in_last_block <= 0)
370 /* Default or Zero: pad to full block */
371 target_block_length = a->bytes_per_block;
373 /* Round length to next multiple of bytes_in_last_block. */
374 target_block_length = a->bytes_in_last_block *
375 ( (block_length + a->bytes_in_last_block - 1) /
376 a->bytes_in_last_block);
377 if (target_block_length > a->bytes_per_block)
378 target_block_length = a->bytes_per_block;
379 if (block_length < target_block_length) {
380 memset(state->stream.next_out, 0,
381 target_block_length - block_length);
382 block_length = target_block_length;
385 /* Write the last block */
386 bytes_written = (a->client_writer)(&a->archive, a->client_data,
387 state->compressed, block_length);
388 if (bytes_written <= 0) {
392 a->archive.raw_position += bytes_written;
394 /* Cleanup: shut down compressor, release memory, etc. */
396 switch (deflateEnd(&(state->stream))) {
400 archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
401 "Failed to clean up compressor");
404 free(state->compressed);
407 /* Clean up config area even if we never initialized. */
408 free(a->compressor.config);
409 a->compressor.config = NULL;
414 * Utility function to push input data through compressor,
415 * writing full output blocks as necessary.
417 * Note that this handles both the regular write case (finishing ==
418 * false) and the end-of-archive case (finishing == true).
421 drive_compressor(struct archive_write *a, struct private_data *state, int finishing)
423 ssize_t bytes_written;
427 if (state->stream.avail_out == 0) {
428 bytes_written = (a->client_writer)(&a->archive,
429 a->client_data, state->compressed,
430 state->compressed_buffer_size);
431 if (bytes_written <= 0) {
432 /* TODO: Handle this write failure */
433 return (ARCHIVE_FATAL);
434 } else if ((size_t)bytes_written < state->compressed_buffer_size) {
435 /* Short write: Move remaining to
436 * front of block and keep filling */
437 memmove(state->compressed,
438 state->compressed + bytes_written,
439 state->compressed_buffer_size - bytes_written);
441 a->archive.raw_position += bytes_written;
442 state->stream.next_out
443 = state->compressed +
444 state->compressed_buffer_size - bytes_written;
445 state->stream.avail_out = bytes_written;
448 /* If there's nothing to do, we're done. */
449 if (!finishing && state->stream.avail_in == 0)
452 ret = deflate(&(state->stream),
453 finishing ? Z_FINISH : Z_NO_FLUSH );
457 /* In non-finishing case, check if compressor
458 * consumed everything */
459 if (!finishing && state->stream.avail_in == 0)
461 /* In finishing case, this return always means
462 * there's more work */
465 /* This return can only occur in finishing case. */
468 /* Any other return value indicates an error. */
469 archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
470 "GZip compression failed:"
471 " deflate() call returned status %d",
473 return (ARCHIVE_FATAL);
478 #endif /* HAVE_ZLIB_H */