1228753Smm/*-
2228753Smm * Copyright (c) 2003-2007 Tim Kientzle
3248616Smm * Copyright (c) 2012 Michihiro NAKAJIMA
4228753Smm * All rights reserved.
5228753Smm *
6228753Smm * Redistribution and use in source and binary forms, with or without
7228753Smm * modification, are permitted provided that the following conditions
8228753Smm * are met:
9228753Smm * 1. Redistributions of source code must retain the above copyright
10228753Smm *    notice, this list of conditions and the following disclaimer.
11228753Smm * 2. Redistributions in binary form must reproduce the above copyright
12228753Smm *    notice, this list of conditions and the following disclaimer in the
13228753Smm *    documentation and/or other materials provided with the distribution.
14228753Smm *
15228753Smm * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
16228753Smm * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17228753Smm * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18228753Smm * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
19228753Smm * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20228753Smm * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21228753Smm * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22228753Smm * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23228753Smm * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24228753Smm * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25228753Smm */
26228753Smm
27228753Smm#include "archive_platform.h"
28228753Smm
29228753Smm__FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_compression_bzip2.c 201091 2009-12-28 02:22:41Z kientzle $");
30228753Smm
31228753Smm#ifdef HAVE_ERRNO_H
32228753Smm#include <errno.h>
33228753Smm#endif
34228753Smm#include <stdio.h>
35228753Smm#ifdef HAVE_STDLIB_H
36228753Smm#include <stdlib.h>
37228753Smm#endif
38228753Smm#ifdef HAVE_STRING_H
39228753Smm#include <string.h>
40228753Smm#endif
41228753Smm#ifdef HAVE_BZLIB_H
42228753Smm#include <bzlib.h>
43228753Smm#endif
44228753Smm
45228753Smm#include "archive.h"
46228753Smm#include "archive_private.h"
47228753Smm#include "archive_write_private.h"
48228753Smm
49231200Smm#if ARCHIVE_VERSION_NUMBER < 4000000
50228753Smmint
51228753Smmarchive_write_set_compression_bzip2(struct archive *a)
52228753Smm{
53231200Smm	__archive_write_filters_free(a);
54231200Smm	return (archive_write_add_filter_bzip2(a));
55231200Smm}
56231200Smm#endif
57231200Smm
58228753Smmstruct private_data {
59231200Smm	int		 compression_level;
60248616Smm#if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR)
61228753Smm	bz_stream	 stream;
62228753Smm	int64_t		 total_in;
63228753Smm	char		*compressed;
64228753Smm	size_t		 compressed_buffer_size;
65248616Smm#else
66248616Smm	struct archive_write_program_data *pdata;
67248616Smm#endif
68228753Smm};
69228753Smm
70231200Smmstatic int archive_compressor_bzip2_close(struct archive_write_filter *);
71231200Smmstatic int archive_compressor_bzip2_free(struct archive_write_filter *);
72231200Smmstatic int archive_compressor_bzip2_open(struct archive_write_filter *);
73231200Smmstatic int archive_compressor_bzip2_options(struct archive_write_filter *,
74228753Smm		    const char *, const char *);
75231200Smmstatic int archive_compressor_bzip2_write(struct archive_write_filter *,
76228753Smm		    const void *, size_t);
77228753Smm
78228753Smm/*
79231200Smm * Add a bzip2 compression filter to this write handle.
80228753Smm */
81228753Smmint
82231200Smmarchive_write_add_filter_bzip2(struct archive *_a)
83228753Smm{
84228753Smm	struct archive_write *a = (struct archive_write *)_a;
85231200Smm	struct archive_write_filter *f = __archive_write_allocate_filter(_a);
86231200Smm	struct private_data *data;
87231200Smm
88231200Smm	archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
89231200Smm	    ARCHIVE_STATE_NEW, "archive_write_add_filter_bzip2");
90231200Smm
91231200Smm	data = calloc(1, sizeof(*data));
92231200Smm	if (data == NULL) {
93228753Smm		archive_set_error(&a->archive, ENOMEM, "Out of memory");
94228753Smm		return (ARCHIVE_FATAL);
95228753Smm	}
96231200Smm	data->compression_level = 9; /* default */
97231200Smm
98231200Smm	f->data = data;
99231200Smm	f->options = &archive_compressor_bzip2_options;
100231200Smm	f->close = &archive_compressor_bzip2_close;
101231200Smm	f->free = &archive_compressor_bzip2_free;
102231200Smm	f->open = &archive_compressor_bzip2_open;
103248616Smm	f->code = ARCHIVE_FILTER_BZIP2;
104231200Smm	f->name = "bzip2";
105248616Smm#if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR)
106228753Smm	return (ARCHIVE_OK);
107248616Smm#else
108248616Smm	data->pdata = __archive_write_program_allocate();
109248616Smm	if (data->pdata == NULL) {
110248616Smm		free(data);
111248616Smm		archive_set_error(&a->archive, ENOMEM, "Out of memory");
112248616Smm		return (ARCHIVE_FATAL);
113248616Smm	}
114248616Smm	data->compression_level = 0;
115248616Smm	archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
116248616Smm	    "Using external bzip2 program");
117248616Smm	return (ARCHIVE_WARN);
118248616Smm#endif
119228753Smm}
120228753Smm
121228753Smm/*
122248616Smm * Set write options.
123248616Smm */
124248616Smmstatic int
125248616Smmarchive_compressor_bzip2_options(struct archive_write_filter *f,
126248616Smm    const char *key, const char *value)
127248616Smm{
128248616Smm	struct private_data *data = (struct private_data *)f->data;
129248616Smm
130248616Smm	if (strcmp(key, "compression-level") == 0) {
131248616Smm		if (value == NULL || !(value[0] >= '0' && value[0] <= '9') ||
132248616Smm		    value[1] != '\0')
133248616Smm			return (ARCHIVE_WARN);
134248616Smm		data->compression_level = value[0] - '0';
135248616Smm		/* Make '0' be a synonym for '1'. */
136248616Smm		/* This way, bzip2 compressor supports the same 0..9
137248616Smm		 * range of levels as gzip. */
138248616Smm		if (data->compression_level < 1)
139248616Smm			data->compression_level = 1;
140248616Smm		return (ARCHIVE_OK);
141248616Smm	}
142248616Smm
143248616Smm	/* Note: The "warn" return is just to inform the options
144248616Smm	 * supervisor that we didn't handle it.  It will generate
145248616Smm	 * a suitable error if no one used this option. */
146248616Smm	return (ARCHIVE_WARN);
147248616Smm}
148248616Smm
149248616Smm#if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR)
150248616Smm/* Don't compile this if we don't have bzlib. */
151248616Smm
152248616Smm/*
153248616Smm * Yuck.  bzlib.h is not const-correct, so I need this one bit
154248616Smm * of ugly hackery to convert a const * pointer to a non-const pointer.
155248616Smm */
156248616Smm#define	SET_NEXT_IN(st,src)					\
157248616Smm	(st)->stream.next_in = (char *)(uintptr_t)(const void *)(src)
158248616Smmstatic int drive_compressor(struct archive_write_filter *,
159248616Smm		    struct private_data *, int finishing);
160248616Smm
161248616Smm/*
162228753Smm * Setup callback.
163228753Smm */
164228753Smmstatic int
165231200Smmarchive_compressor_bzip2_open(struct archive_write_filter *f)
166228753Smm{
167231200Smm	struct private_data *data = (struct private_data *)f->data;
168228753Smm	int ret;
169228753Smm
170231200Smm	ret = __archive_write_open_filter(f->next_filter);
171231200Smm	if (ret != 0)
172231200Smm		return (ret);
173228753Smm
174231200Smm	if (data->compressed == NULL) {
175238856Smm		size_t bs = 65536, bpb;
176238856Smm		if (f->archive->magic == ARCHIVE_WRITE_MAGIC) {
177238856Smm			/* Buffer size should be a multiple number of the of bytes
178238856Smm			 * per block for performance. */
179238856Smm			bpb = archive_write_get_bytes_per_block(f->archive);
180238856Smm			if (bpb > bs)
181238856Smm				bs = bpb;
182238856Smm			else if (bpb != 0)
183238856Smm				bs -= bs % bpb;
184238856Smm		}
185238856Smm		data->compressed_buffer_size = bs;
186231200Smm		data->compressed
187231200Smm		    = (char *)malloc(data->compressed_buffer_size);
188231200Smm		if (data->compressed == NULL) {
189231200Smm			archive_set_error(f->archive, ENOMEM,
190231200Smm			    "Can't allocate data for compression buffer");
191231200Smm			return (ARCHIVE_FATAL);
192231200Smm		}
193228753Smm	}
194228753Smm
195231200Smm	memset(&data->stream, 0, sizeof(data->stream));
196231200Smm	data->stream.next_out = data->compressed;
197231200Smm	data->stream.avail_out = data->compressed_buffer_size;
198231200Smm	f->write = archive_compressor_bzip2_write;
199228753Smm
200228753Smm	/* Initialize compression library */
201231200Smm	ret = BZ2_bzCompressInit(&(data->stream),
202231200Smm	    data->compression_level, 0, 30);
203228753Smm	if (ret == BZ_OK) {
204231200Smm		f->data = data;
205228753Smm		return (ARCHIVE_OK);
206228753Smm	}
207228753Smm
208228753Smm	/* Library setup failed: clean up. */
209231200Smm	archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
210228753Smm	    "Internal error initializing compression library");
211228753Smm
212228753Smm	/* Override the error message if we know what really went wrong. */
213228753Smm	switch (ret) {
214228753Smm	case BZ_PARAM_ERROR:
215231200Smm		archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
216228753Smm		    "Internal error initializing compression library: "
217228753Smm		    "invalid setup parameter");
218228753Smm		break;
219228753Smm	case BZ_MEM_ERROR:
220231200Smm		archive_set_error(f->archive, ENOMEM,
221228753Smm		    "Internal error initializing compression library: "
222228753Smm		    "out of memory");
223228753Smm		break;
224228753Smm	case BZ_CONFIG_ERROR:
225231200Smm		archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
226228753Smm		    "Internal error initializing compression library: "
227228753Smm		    "mis-compiled library");
228228753Smm		break;
229228753Smm	}
230228753Smm
231228753Smm	return (ARCHIVE_FATAL);
232228753Smm
233228753Smm}
234228753Smm
235228753Smm/*
236228753Smm * Write data to the compressed stream.
237228753Smm *
238228753Smm * Returns ARCHIVE_OK if all data written, error otherwise.
239228753Smm */
240228753Smmstatic int
241231200Smmarchive_compressor_bzip2_write(struct archive_write_filter *f,
242231200Smm    const void *buff, size_t length)
243228753Smm{
244231200Smm	struct private_data *data = (struct private_data *)f->data;
245228753Smm
246228753Smm	/* Update statistics */
247231200Smm	data->total_in += length;
248228753Smm
249228753Smm	/* Compress input data to output buffer */
250231200Smm	SET_NEXT_IN(data, buff);
251231200Smm	data->stream.avail_in = length;
252231200Smm	if (drive_compressor(f, data, 0))
253228753Smm		return (ARCHIVE_FATAL);
254228753Smm	return (ARCHIVE_OK);
255228753Smm}
256228753Smm
257228753Smm
258228753Smm/*
259228753Smm * Finish the compression.
260228753Smm */
261228753Smmstatic int
262231200Smmarchive_compressor_bzip2_close(struct archive_write_filter *f)
263228753Smm{
264231200Smm	struct private_data *data = (struct private_data *)f->data;
265231200Smm	int ret, r1;
266228753Smm
267231200Smm	/* Finish compression cycle. */
268231200Smm	ret = drive_compressor(f, data, 1);
269231200Smm	if (ret == ARCHIVE_OK) {
270228753Smm		/* Write the last block */
271231200Smm		ret = __archive_write_filter(f->next_filter,
272231200Smm		    data->compressed,
273231200Smm		    data->compressed_buffer_size - data->stream.avail_out);
274231200Smm	}
275228753Smm
276231200Smm	switch (BZ2_bzCompressEnd(&(data->stream))) {
277231200Smm	case BZ_OK:
278231200Smm		break;
279231200Smm	default:
280231200Smm		archive_set_error(f->archive, ARCHIVE_ERRNO_PROGRAMMER,
281231200Smm		    "Failed to clean up compressor");
282231200Smm		ret = ARCHIVE_FATAL;
283231200Smm	}
284228753Smm
285231200Smm	r1 = __archive_write_close_filter(f->next_filter);
286231200Smm	return (r1 < ret ? r1 : ret);
287231200Smm}
288228753Smm
289231200Smmstatic int
290231200Smmarchive_compressor_bzip2_free(struct archive_write_filter *f)
291231200Smm{
292231200Smm	struct private_data *data = (struct private_data *)f->data;
293231200Smm	free(data->compressed);
294231200Smm	free(data);
295231200Smm	f->data = NULL;
296231200Smm	return (ARCHIVE_OK);
297228753Smm}
298228753Smm
299228753Smm/*
300228753Smm * Utility function to push input data through compressor, writing
301228753Smm * full output blocks as necessary.
302228753Smm *
303228753Smm * Note that this handles both the regular write case (finishing ==
304228753Smm * false) and the end-of-archive case (finishing == true).
305228753Smm */
306228753Smmstatic int
307231200Smmdrive_compressor(struct archive_write_filter *f,
308231200Smm    struct private_data *data, int finishing)
309228753Smm{
310228753Smm	int ret;
311228753Smm
312228753Smm	for (;;) {
313231200Smm		if (data->stream.avail_out == 0) {
314231200Smm			ret = __archive_write_filter(f->next_filter,
315231200Smm			    data->compressed,
316231200Smm			    data->compressed_buffer_size);
317231200Smm			if (ret != ARCHIVE_OK) {
318228753Smm				/* TODO: Handle this write failure */
319228753Smm				return (ARCHIVE_FATAL);
320228753Smm			}
321231200Smm			data->stream.next_out = data->compressed;
322231200Smm			data->stream.avail_out = data->compressed_buffer_size;
323228753Smm		}
324228753Smm
325228753Smm		/* If there's nothing to do, we're done. */
326231200Smm		if (!finishing && data->stream.avail_in == 0)
327228753Smm			return (ARCHIVE_OK);
328228753Smm
329231200Smm		ret = BZ2_bzCompress(&(data->stream),
330228753Smm		    finishing ? BZ_FINISH : BZ_RUN);
331228753Smm
332228753Smm		switch (ret) {
333228753Smm		case BZ_RUN_OK:
334228753Smm			/* In non-finishing case, did compressor
335228753Smm			 * consume everything? */
336231200Smm			if (!finishing && data->stream.avail_in == 0)
337228753Smm				return (ARCHIVE_OK);
338228753Smm			break;
339228753Smm		case BZ_FINISH_OK:  /* Finishing: There's more work to do */
340228753Smm			break;
341228753Smm		case BZ_STREAM_END: /* Finishing: all done */
342228753Smm			/* Only occurs in finishing case */
343228753Smm			return (ARCHIVE_OK);
344228753Smm		default:
345228753Smm			/* Any other return value indicates an error */
346231200Smm			archive_set_error(f->archive,
347228753Smm			    ARCHIVE_ERRNO_PROGRAMMER,
348228753Smm			    "Bzip2 compression failed;"
349228753Smm			    " BZ2_bzCompress() returned %d",
350228753Smm			    ret);
351228753Smm			return (ARCHIVE_FATAL);
352228753Smm		}
353228753Smm	}
354228753Smm}
355228753Smm
356248616Smm#else /* HAVE_BZLIB_H && BZ_CONFIG_ERROR */
357248616Smm
358248616Smmstatic int
359248616Smmarchive_compressor_bzip2_open(struct archive_write_filter *f)
360248616Smm{
361248616Smm	struct private_data *data = (struct private_data *)f->data;
362248616Smm	struct archive_string as;
363248616Smm	int r;
364248616Smm
365248616Smm	archive_string_init(&as);
366248616Smm	archive_strcpy(&as, "bzip2");
367248616Smm
368248616Smm	/* Specify compression level. */
369248616Smm	if (data->compression_level > 0) {
370248616Smm		archive_strcat(&as, " -");
371248616Smm		archive_strappend_char(&as, '0' + data->compression_level);
372248616Smm	}
373248616Smm	f->write = archive_compressor_bzip2_write;
374248616Smm
375248616Smm	r = __archive_write_program_open(f, data->pdata, as.s);
376248616Smm	archive_string_free(&as);
377248616Smm	return (r);
378248616Smm}
379248616Smm
380248616Smmstatic int
381248616Smmarchive_compressor_bzip2_write(struct archive_write_filter *f, const void *buff,
382248616Smm    size_t length)
383248616Smm{
384248616Smm	struct private_data *data = (struct private_data *)f->data;
385248616Smm
386248616Smm	return __archive_write_program_write(f, data->pdata, buff, length);
387248616Smm}
388248616Smm
389248616Smmstatic int
390248616Smmarchive_compressor_bzip2_close(struct archive_write_filter *f)
391248616Smm{
392248616Smm	struct private_data *data = (struct private_data *)f->data;
393248616Smm
394248616Smm	return __archive_write_program_close(f, data->pdata);
395248616Smm}
396248616Smm
397248616Smmstatic int
398248616Smmarchive_compressor_bzip2_free(struct archive_write_filter *f)
399248616Smm{
400248616Smm	struct private_data *data = (struct private_data *)f->data;
401248616Smm
402248616Smm	__archive_write_program_free(data->pdata);
403248616Smm	free(data);
404248616Smm	return (ARCHIVE_OK);
405248616Smm}
406248616Smm
407228753Smm#endif /* HAVE_BZLIB_H && BZ_CONFIG_ERROR */
408