vknetd - Ignore SIGPIPE
[dragonfly.git] / contrib / libarchive / libarchive / archive_write_set_compression_bzip2.c
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_bzip2.c 201091 2009-12-28 02:22:41Z kientzle $");
29
30 #ifdef HAVE_ERRNO_H
31 #include <errno.h>
32 #endif
33 #include <stdio.h>
34 #ifdef HAVE_STDLIB_H
35 #include <stdlib.h>
36 #endif
37 #ifdef HAVE_STRING_H
38 #include <string.h>
39 #endif
40 #ifdef HAVE_BZLIB_H
41 #include <bzlib.h>
42 #endif
43
44 #include "archive.h"
45 #include "archive_private.h"
46 #include "archive_write_private.h"
47
48 #if !defined(HAVE_BZLIB_H) || !defined(BZ_CONFIG_ERROR)
49 int
50 archive_write_set_compression_bzip2(struct archive *a)
51 {
52         archive_set_error(a, ARCHIVE_ERRNO_MISC,
53             "bzip2 compression not supported on this platform");
54         return (ARCHIVE_FATAL);
55 }
56 #else
57 /* Don't compile this if we don't have bzlib. */
58
59 struct private_data {
60         bz_stream        stream;
61         int64_t          total_in;
62         char            *compressed;
63         size_t           compressed_buffer_size;
64 };
65
66 struct private_config {
67         int              compression_level;
68 };
69
70 /*
71  * Yuck.  bzlib.h is not const-correct, so I need this one bit
72  * of ugly hackery to convert a const * pointer to a non-const pointer.
73  */
74 #define SET_NEXT_IN(st,src)                                     \
75         (st)->stream.next_in = (char *)(uintptr_t)(const void *)(src)
76
77 static int      archive_compressor_bzip2_finish(struct archive_write *);
78 static int      archive_compressor_bzip2_init(struct archive_write *);
79 static int      archive_compressor_bzip2_options(struct archive_write *,
80                     const char *, const char *);
81 static int      archive_compressor_bzip2_write(struct archive_write *,
82                     const void *, size_t);
83 static int      drive_compressor(struct archive_write *, struct private_data *,
84                     int finishing);
85
86 /*
87  * Allocate, initialize and return an archive object.
88  */
89 int
90 archive_write_set_compression_bzip2(struct archive *_a)
91 {
92         struct archive_write *a = (struct archive_write *)_a;
93         struct private_config *config;
94         __archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
95             ARCHIVE_STATE_NEW, "archive_write_set_compression_bzip2");
96         config = malloc(sizeof(*config));
97         if (config == NULL) {
98                 archive_set_error(&a->archive, ENOMEM, "Out of memory");
99                 return (ARCHIVE_FATAL);
100         }
101         a->compressor.config = config;
102         a->compressor.finish = archive_compressor_bzip2_finish;
103         config->compression_level = 9; /* default */
104         a->compressor.init = &archive_compressor_bzip2_init;
105         a->compressor.options = &archive_compressor_bzip2_options;
106         a->archive.compression_code = ARCHIVE_COMPRESSION_BZIP2;
107         a->archive.compression_name = "bzip2";
108         return (ARCHIVE_OK);
109 }
110
111 /*
112  * Setup callback.
113  */
114 static int
115 archive_compressor_bzip2_init(struct archive_write *a)
116 {
117         int ret;
118         struct private_data *state;
119         struct private_config *config;
120
121         config = (struct private_config *)a->compressor.config;
122         if (a->client_opener != NULL) {
123                 ret = (a->client_opener)(&a->archive, a->client_data);
124                 if (ret != 0)
125                         return (ret);
126         }
127
128         state = (struct private_data *)malloc(sizeof(*state));
129         if (state == NULL) {
130                 archive_set_error(&a->archive, ENOMEM,
131                     "Can't allocate data for compression");
132                 return (ARCHIVE_FATAL);
133         }
134         memset(state, 0, sizeof(*state));
135
136         state->compressed_buffer_size = a->bytes_per_block;
137         state->compressed = (char *)malloc(state->compressed_buffer_size);
138
139         if (state->compressed == NULL) {
140                 archive_set_error(&a->archive, ENOMEM,
141                     "Can't allocate data for compression buffer");
142                 free(state);
143                 return (ARCHIVE_FATAL);
144         }
145
146         state->stream.next_out = state->compressed;
147         state->stream.avail_out = state->compressed_buffer_size;
148         a->compressor.write = archive_compressor_bzip2_write;
149
150         /* Initialize compression library */
151         ret = BZ2_bzCompressInit(&(state->stream),
152             config->compression_level, 0, 30);
153         if (ret == BZ_OK) {
154                 a->compressor.data = state;
155                 return (ARCHIVE_OK);
156         }
157
158         /* Library setup failed: clean up. */
159         archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
160             "Internal error initializing compression library");
161         free(state->compressed);
162         free(state);
163
164         /* Override the error message if we know what really went wrong. */
165         switch (ret) {
166         case BZ_PARAM_ERROR:
167                 archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
168                     "Internal error initializing compression library: "
169                     "invalid setup parameter");
170                 break;
171         case BZ_MEM_ERROR:
172                 archive_set_error(&a->archive, ENOMEM,
173                     "Internal error initializing compression library: "
174                     "out of memory");
175                 break;
176         case BZ_CONFIG_ERROR:
177                 archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
178                     "Internal error initializing compression library: "
179                     "mis-compiled library");
180                 break;
181         }
182
183         return (ARCHIVE_FATAL);
184
185 }
186
187 /*
188  * Set write options.
189  */
190 static int
191 archive_compressor_bzip2_options(struct archive_write *a, const char *key,
192     const char *value)
193 {
194         struct private_config *config;
195
196         config = (struct private_config *)a->compressor.config;
197         if (strcmp(key, "compression-level") == 0) {
198                 if (value == NULL || !(value[0] >= '0' && value[0] <= '9') ||
199                     value[1] != '\0')
200                         return (ARCHIVE_WARN);
201                 config->compression_level = value[0] - '0';
202                 /* Make '0' be a synonym for '1'. */
203                 /* This way, bzip2 compressor supports the same 0..9
204                  * range of levels as gzip. */
205                 if (config->compression_level < 1)
206                         config->compression_level = 1;
207                 return (ARCHIVE_OK);
208         }
209
210         return (ARCHIVE_WARN);
211 }
212
213 /*
214  * Write data to the compressed stream.
215  *
216  * Returns ARCHIVE_OK if all data written, error otherwise.
217  */
218 static int
219 archive_compressor_bzip2_write(struct archive_write *a, const void *buff,
220     size_t length)
221 {
222         struct private_data *state;
223
224         state = (struct private_data *)a->compressor.data;
225         if (a->client_writer == NULL) {
226                 archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
227                     "No write callback is registered?  "
228                     "This is probably an internal programming error.");
229                 return (ARCHIVE_FATAL);
230         }
231
232         /* Update statistics */
233         state->total_in += length;
234
235         /* Compress input data to output buffer */
236         SET_NEXT_IN(state, buff);
237         state->stream.avail_in = length;
238         if (drive_compressor(a, state, 0))
239                 return (ARCHIVE_FATAL);
240         a->archive.file_position += length;
241         return (ARCHIVE_OK);
242 }
243
244
245 /*
246  * Finish the compression.
247  */
248 static int
249 archive_compressor_bzip2_finish(struct archive_write *a)
250 {
251         ssize_t block_length;
252         int ret;
253         struct private_data *state;
254         ssize_t target_block_length;
255         ssize_t bytes_written;
256         unsigned tocopy;
257
258         ret = ARCHIVE_OK;
259         state = (struct private_data *)a->compressor.data;
260         if (state != NULL) {
261                 if (a->client_writer == NULL) {
262                         archive_set_error(&a->archive,
263                             ARCHIVE_ERRNO_PROGRAMMER,
264                             "No write callback is registered?\n"
265                             "This is probably an internal programming error.");
266                         ret = ARCHIVE_FATAL;
267                         goto cleanup;
268                 }
269
270                 /* By default, always pad the uncompressed data. */
271                 if (a->pad_uncompressed) {
272                         tocopy = a->bytes_per_block -
273                             (state->total_in % a->bytes_per_block);
274                         while (tocopy > 0 && tocopy < (unsigned)a->bytes_per_block) {
275                                 SET_NEXT_IN(state, a->nulls);
276                                 state->stream.avail_in = tocopy < a->null_length ?
277                                     tocopy : a->null_length;
278                                 state->total_in += state->stream.avail_in;
279                                 tocopy -= state->stream.avail_in;
280                                 ret = drive_compressor(a, state, 0);
281                                 if (ret != ARCHIVE_OK)
282                                         goto cleanup;
283                         }
284                 }
285
286                 /* Finish compression cycle. */
287                 if ((ret = drive_compressor(a, state, 1)))
288                         goto cleanup;
289
290                 /* Optionally, pad the final compressed block. */
291                 block_length = state->stream.next_out - state->compressed;
292
293                 /* Tricky calculation to determine size of last block. */
294                 if (a->bytes_in_last_block <= 0)
295                         /* Default or Zero: pad to full block */
296                         target_block_length = a->bytes_per_block;
297                 else
298                         /* Round length to next multiple of bytes_in_last_block. */
299                         target_block_length = a->bytes_in_last_block *
300                             ( (block_length + a->bytes_in_last_block - 1) /
301                                 a->bytes_in_last_block);
302                 if (target_block_length > a->bytes_per_block)
303                         target_block_length = a->bytes_per_block;
304                 if (block_length < target_block_length) {
305                         memset(state->stream.next_out, 0,
306                             target_block_length - block_length);
307                         block_length = target_block_length;
308                 }
309
310                 /* Write the last block */
311                 bytes_written = (a->client_writer)(&a->archive, a->client_data,
312                     state->compressed, block_length);
313
314                 /* TODO: Handle short write of final block. */
315                 if (bytes_written <= 0)
316                         ret = ARCHIVE_FATAL;
317                 else {
318                         a->archive.raw_position += ret;
319                         ret = ARCHIVE_OK;
320                 }
321
322                 /* Cleanup: shut down compressor, release memory, etc. */
323 cleanup:
324                 switch (BZ2_bzCompressEnd(&(state->stream))) {
325                 case BZ_OK:
326                         break;
327                 default:
328                         archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
329                             "Failed to clean up compressor");
330                         ret = ARCHIVE_FATAL;
331                 }
332
333                 free(state->compressed);
334                 free(state);
335         }
336         /* Free configuration data even if we were never fully initialized. */
337         free(a->compressor.config);
338         a->compressor.config = NULL;
339         return (ret);
340 }
341
342 /*
343  * Utility function to push input data through compressor, writing
344  * full output blocks as necessary.
345  *
346  * Note that this handles both the regular write case (finishing ==
347  * false) and the end-of-archive case (finishing == true).
348  */
349 static int
350 drive_compressor(struct archive_write *a, struct private_data *state, int finishing)
351 {
352         ssize_t bytes_written;
353         int ret;
354
355         for (;;) {
356                 if (state->stream.avail_out == 0) {
357                         bytes_written = (a->client_writer)(&a->archive,
358                             a->client_data, state->compressed,
359                             state->compressed_buffer_size);
360                         if (bytes_written <= 0) {
361                                 /* TODO: Handle this write failure */
362                                 return (ARCHIVE_FATAL);
363                         } else if ((size_t)bytes_written < state->compressed_buffer_size) {
364                                 /* Short write: Move remainder to
365                                  * front and keep filling */
366                                 memmove(state->compressed,
367                                     state->compressed + bytes_written,
368                                     state->compressed_buffer_size - bytes_written);
369                         }
370
371                         a->archive.raw_position += bytes_written;
372                         state->stream.next_out = state->compressed +
373                             state->compressed_buffer_size - bytes_written;
374                         state->stream.avail_out = bytes_written;
375                 }
376
377                 /* If there's nothing to do, we're done. */
378                 if (!finishing && state->stream.avail_in == 0)
379                         return (ARCHIVE_OK);
380
381                 ret = BZ2_bzCompress(&(state->stream),
382                     finishing ? BZ_FINISH : BZ_RUN);
383
384                 switch (ret) {
385                 case BZ_RUN_OK:
386                         /* In non-finishing case, did compressor
387                          * consume everything? */
388                         if (!finishing && state->stream.avail_in == 0)
389                                 return (ARCHIVE_OK);
390                         break;
391                 case BZ_FINISH_OK:  /* Finishing: There's more work to do */
392                         break;
393                 case BZ_STREAM_END: /* Finishing: all done */
394                         /* Only occurs in finishing case */
395                         return (ARCHIVE_OK);
396                 default:
397                         /* Any other return value indicates an error */
398                         archive_set_error(&a->archive,
399                             ARCHIVE_ERRNO_PROGRAMMER,
400                             "Bzip2 compression failed;"
401                             " BZ2_bzCompress() returned %d",
402                             ret);
403                         return (ARCHIVE_FATAL);
404                 }
405         }
406 }
407
408 #endif /* HAVE_BZLIB_H && BZ_CONFIG_ERROR */