~ubuntu-branches/ubuntu/trusty/libarchive/trusty

« back to all changes in this revision

Viewing changes to archive_write_set_compression_bzip2.c

  • Committer: Bazaar Package Importer
  • Author(s): John Goerzen
  • Date: 2005-10-18 11:02:06 UTC
  • Revision ID: james.westby@ubuntu.com-20051018110206-akz0ys1qxoojy73o
Tags: upstream-1.02.036
ImportĀ upstreamĀ versionĀ 1.02.036

Show diffs side-by-side

added added

removed removed

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