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