Import libarchive and bsdtar. The default tar (/usr/bin/tar) can be choosen
[dragonfly.git] / contrib / libarchive / archive_write_set_compression_gzip.c
1 /*-
2  * Copyright (c) 2003-2004 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  *    in this position and unchanged.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
16  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18  * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
19  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  */
26
27 #include "archive_platform.h"
28
29 /* Don't compile this if we don't have zlib. */
30 #if HAVE_ZLIB_H
31
32 __FBSDID("$FreeBSD: src/lib/libarchive/archive_write_set_compression_gzip.c,v 1.9 2004/11/06 05:25:53 kientzle Exp $");
33
34 #include <errno.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <time.h>
38 #include <zlib.h>
39
40 #include "archive.h"
41 #include "archive_private.h"
42
43 struct private_data {
44         z_stream         stream;
45         int64_t          total_in;
46         unsigned char   *compressed;
47         size_t           compressed_buffer_size;
48         unsigned long    crc;
49 };
50
51
52 /*
53  * Yuck.  zlib.h is not const-correct, so I need this one bit
54  * of ugly hackery to convert a const * pointer to a non-const pointer.
55  */
56 #define SET_NEXT_IN(st,src)                                     \
57         (st)->stream.next_in = (void *)(uintptr_t)(const void *)(src)
58
59 static int      archive_compressor_gzip_finish(struct archive *);
60 static int      archive_compressor_gzip_init(struct archive *);
61 static int      archive_compressor_gzip_write(struct archive *, const void *,
62                     size_t);
63 static int      drive_compressor(struct archive *, struct private_data *,
64                     int finishing);
65
66
67 /*
68  * Allocate, initialize and return a archive object.
69  */
70 int
71 archive_write_set_compression_gzip(struct archive *a)
72 {
73         archive_check_magic(a, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_NEW);
74         a->compression_init = &archive_compressor_gzip_init;
75         a->compression_code = ARCHIVE_COMPRESSION_GZIP;
76         a->compression_name = "gzip";
77         return (ARCHIVE_OK);
78 }
79
80 /*
81  * Setup callback.
82  */
83 static int
84 archive_compressor_gzip_init(struct archive *a)
85 {
86         int ret;
87         struct private_data *state;
88         time_t t;
89
90         a->compression_code = ARCHIVE_COMPRESSION_GZIP;
91         a->compression_name = "gzip";
92
93         if (a->client_opener != NULL) {
94                 ret = (a->client_opener)(a, a->client_data);
95                 if (ret != ARCHIVE_OK)
96                         return (ret);
97         }
98
99         state = (struct private_data *)malloc(sizeof(*state));
100         if (state == NULL) {
101                 archive_set_error(a, ENOMEM,
102                     "Can't allocate data for compression");
103                 return (ARCHIVE_FATAL);
104         }
105         memset(state, 0, sizeof(*state));
106
107         state->compressed_buffer_size = a->bytes_per_block;
108         state->compressed = malloc(state->compressed_buffer_size);
109         state->crc = crc32(0L, NULL, 0);
110
111         if (state->compressed == NULL) {
112                 archive_set_error(a, ENOMEM,
113                     "Can't allocate data for compression buffer");
114                 free(state);
115                 return (ARCHIVE_FATAL);
116         }
117
118         state->stream.next_out = state->compressed;
119         state->stream.avail_out = state->compressed_buffer_size;
120
121         /* Prime output buffer with a gzip header. */
122         t = time(NULL);
123         state->compressed[0] = 0x1f; /* GZip signature bytes */
124         state->compressed[1] = 0x8b;
125         state->compressed[2] = 0x08; /* "Deflate" compression */
126         state->compressed[3] = 0; /* No options */
127         state->compressed[4] = (t)&0xff;  /* Timestamp */
128         state->compressed[5] = (t>>8)&0xff;
129         state->compressed[6] = (t>>16)&0xff;
130         state->compressed[7] = (t>>24)&0xff;
131         state->compressed[8] = 0; /* No deflate options */
132         state->compressed[9] = 3; /* OS=Unix */
133         state->stream.next_out += 10;
134         state->stream.avail_out -= 10;
135
136         a->compression_write = archive_compressor_gzip_write;
137         a->compression_finish = archive_compressor_gzip_finish;
138
139         /* Initialize compression library. */
140         ret = deflateInit2(&(state->stream),
141             Z_DEFAULT_COMPRESSION,
142             Z_DEFLATED,
143             -15 /* < 0 to suppress zlib header */,
144             8,
145             Z_DEFAULT_STRATEGY);
146
147         if (ret == Z_OK) {
148                 a->compression_data = state;
149                 return (0);
150         }
151
152         /* Library setup failed: clean up. */
153         archive_set_error(a, ARCHIVE_ERRNO_MISC, "Internal error "
154             "initializing compression library");
155         free(state->compressed);
156         free(state);
157
158         /* Override the error message if we know what really went wrong. */
159         switch (ret) {
160         case Z_STREAM_ERROR:
161                 archive_set_error(a, ARCHIVE_ERRNO_MISC,
162                     "Internal error initializing "
163                     "compression library: invalid setup parameter");
164                 break;
165         case Z_MEM_ERROR:
166                 archive_set_error(a, ENOMEM, "Internal error initializing "
167                     "compression library");
168                 break;
169         case Z_VERSION_ERROR:
170                 archive_set_error(a, ARCHIVE_ERRNO_MISC,
171                     "Internal error initializing "
172                     "compression library: invalid library version");
173                 break;
174         }
175
176         return (ARCHIVE_FATAL);
177 }
178
179 /*
180  * Write data to the compressed stream.
181  */
182 static int
183 archive_compressor_gzip_write(struct archive *a, const void *buff,
184     size_t length)
185 {
186         struct private_data *state;
187         int ret;
188
189         state = a->compression_data;
190         if (a->client_writer == NULL) {
191                 archive_set_error(a, ARCHIVE_ERRNO_PROGRAMMER,
192                     "No write callback is registered?  "
193                     "This is probably an internal programming error.");
194                 return (ARCHIVE_FATAL);
195         }
196
197         /* Update statistics */
198         state->crc = crc32(state->crc, buff, length);
199         state->total_in += length;
200
201         /* Compress input data to output buffer */
202         SET_NEXT_IN(state, buff);
203         state->stream.avail_in = length;
204         if ((ret = drive_compressor(a, state, 0)) != ARCHIVE_OK)
205                 return (ret);
206
207         a->file_position += length;
208         return (ARCHIVE_OK);
209 }
210
211
212 /*
213  * Finish the compression...
214  */
215 static int
216 archive_compressor_gzip_finish(struct archive *a)
217 {
218         ssize_t block_length, target_block_length, bytes_written;
219         int ret;
220         struct private_data *state;
221         unsigned tocopy;
222         unsigned char trailer[8];
223
224         state = a->compression_data;
225         ret = 0;
226         if (a->client_writer == NULL) {
227                 archive_set_error(a, ARCHIVE_ERRNO_PROGRAMMER,
228                     "No write callback is registered?  "
229                     "This is probably an internal programming error.");
230                 ret = ARCHIVE_FATAL;
231                 goto cleanup;
232         }
233
234         /* By default, always pad the uncompressed data. */
235         if (a->pad_uncompressed) {
236                 tocopy = a->bytes_per_block -
237                     (state->total_in % a->bytes_per_block);
238                 while (tocopy > 0 && tocopy < (unsigned)a->bytes_per_block) {
239                         SET_NEXT_IN(state, a->nulls);
240                         state->stream.avail_in = tocopy < a->null_length ?
241                             tocopy : a->null_length;
242                         state->crc = crc32(state->crc, a->nulls,
243                             state->stream.avail_in);
244                         state->total_in += state->stream.avail_in;
245                         tocopy -= state->stream.avail_in;
246                         ret = drive_compressor(a, state, 0);
247                         if (ret != ARCHIVE_OK)
248                                 goto cleanup;
249                 }
250         }
251
252         /* Finish compression cycle */
253         if (((ret = drive_compressor(a, state, 1))) != ARCHIVE_OK)
254                 goto cleanup;
255
256         /* Build trailer: 4-byte CRC and 4-byte length. */
257         trailer[0] = (state->crc)&0xff;
258         trailer[1] = (state->crc >> 8)&0xff;
259         trailer[2] = (state->crc >> 16)&0xff;
260         trailer[3] = (state->crc >> 24)&0xff;
261         trailer[4] = (state->total_in)&0xff;
262         trailer[5] = (state->total_in >> 8)&0xff;
263         trailer[6] = (state->total_in >> 16)&0xff;
264         trailer[7] = (state->total_in >> 24)&0xff;
265
266         /* Add trailer to current block. */
267         tocopy = 8;
268         if (tocopy > state->stream.avail_out)
269                 tocopy = state->stream.avail_out;
270         memcpy(state->stream.next_out, trailer, tocopy);
271         state->stream.next_out += tocopy;
272         state->stream.avail_out -= tocopy;
273
274         /* If it overflowed, flush and start a new block. */
275         if (tocopy < 8) {
276                 bytes_written = (a->client_writer)(a, a->client_data,
277                     state->compressed, state->compressed_buffer_size);
278                 if (bytes_written <= 0) {
279                         ret = ARCHIVE_FATAL;
280                         goto cleanup;
281                 }
282                 a->raw_position += bytes_written;
283                 state->stream.next_out = state->compressed;
284                 state->stream.avail_out = state->compressed_buffer_size;
285                 memcpy(state->stream.next_out, trailer + tocopy, 8-tocopy);
286                 state->stream.next_out += 8-tocopy;
287                 state->stream.avail_out -= 8-tocopy;
288         }
289
290         /* Optionally, pad the final compressed block. */
291         block_length = state->stream.next_out - state->compressed;
292
293
294         /* Tricky calculation to determine size of last block. */
295         target_block_length = block_length;
296         if (a->bytes_in_last_block <= 0)
297                 /* Default or Zero: pad to full block */
298                 target_block_length = a->bytes_per_block;
299         else
300                 /* Round length to next multiple of bytes_in_last_block. */
301                 target_block_length = a->bytes_in_last_block *
302                     ( (block_length + a->bytes_in_last_block - 1) /
303                         a->bytes_in_last_block);
304         if (target_block_length > a->bytes_per_block)
305                 target_block_length = a->bytes_per_block;
306         if (block_length < target_block_length) {
307                 memset(state->stream.next_out, 0,
308                     target_block_length - block_length);
309                 block_length = target_block_length;
310         }
311
312         /* Write the last block */
313         bytes_written = (a->client_writer)(a, a->client_data,
314             state->compressed, block_length);
315         if (bytes_written <= 0) {
316                 ret = ARCHIVE_FATAL;
317                 goto cleanup;
318         }
319         a->raw_position += bytes_written;
320
321         /* Cleanup: shut down compressor, release memory, etc. */
322 cleanup:
323         switch (deflateEnd(&(state->stream))) {
324         case Z_OK:
325                 break;
326         default:
327                 archive_set_error(a, ARCHIVE_ERRNO_MISC,
328                     "Failed to clean up compressor");
329                 ret = ARCHIVE_FATAL;
330         }
331         free(state->compressed);
332         free(state);
333
334         /* Close the output */
335         if (a->client_closer != NULL)
336                 (a->client_closer)(a, a->client_data);
337
338         return (ret);
339 }
340
341 /*
342  * Utility function to push input data through compressor,
343  * writing full output blocks as necessary.
344  *
345  * Note that this handles both the regular write case (finishing ==
346  * false) and the end-of-archive case (finishing == true).
347  */
348 static int
349 drive_compressor(struct archive *a, struct private_data *state, int finishing)
350 {
351         ssize_t bytes_written;
352         int ret;
353
354         for (;;) {
355                 if (state->stream.avail_out == 0) {
356                         bytes_written = (a->client_writer)(a, a->client_data,
357                             state->compressed, state->compressed_buffer_size);
358                         if (bytes_written <= 0) {
359                                 /* TODO: Handle this write failure */
360                                 return (ARCHIVE_FATAL);
361                         } else if ((size_t)bytes_written < state->compressed_buffer_size) {
362                                 /* Short write: Move remaining to
363                                  * front of block and keep filling */
364                                 memmove(state->compressed,
365                                     state->compressed + bytes_written,
366                                     state->compressed_buffer_size - bytes_written);
367                         }
368                         a->raw_position += bytes_written;
369                         state->stream.next_out
370                             = state->compressed +
371                             state->compressed_buffer_size - bytes_written;
372                         state->stream.avail_out = bytes_written;
373                 }
374
375                 ret = deflate(&(state->stream),
376                     finishing ? Z_FINISH : Z_NO_FLUSH );
377
378                 switch (ret) {
379                 case Z_OK:
380                         /* In non-finishing case, check if compressor
381                          * consumed everything */
382                         if (!finishing && state->stream.avail_in == 0)
383                                 return (ARCHIVE_OK);
384                         /* In finishing case, this return always means
385                          * there's more work */
386                         break;
387                 case Z_STREAM_END:
388                         /* This return can only occur in finishing case. */
389                         return (ARCHIVE_OK);
390                 default:
391                         /* Any other return value indicates an error. */
392                         archive_set_error(a, ARCHIVE_ERRNO_MISC,
393                             "GZip compression failed");
394                         return (ARCHIVE_FATAL);
395                 }
396         }
397 }
398
399 #endif /* HAVE_ZLIB_H */