1/*
2 * Copyright (c) 2005 Rob Braun
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 * 3. Neither the name of Rob Braun nor the names of his contributors
14 *    may be used to endorse or promote products derived from this software
15 *    without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 * POSSIBILITY OF SUCH DAMAGE.
28 */
29/*
30 * 19-Sep-2007
31 * DRI: Anders F Bjorklund <afb@rpm5.org>
32 */
33/*
34 * Portions Copyright 2006, Apple Computer, Inc.
35 * Christopher Ryan <ryanc@apple.com>
36*/
37
38
39#include "config.h"
40#ifndef HAVE_ASPRINTF
41#include "asprintf.h"
42#endif
43#include <stdio.h>
44#include <stdlib.h>
45#include <string.h>
46#include <sys/types.h>
47#include <errno.h>
48#ifdef HAVE_LIBLZMA
49#include <lzma.h>
50#endif
51#include "xar.h"
52#include "filetree.h"
53#include "io.h"
54
55#ifdef HAVE_LIBLZMA
56
57#ifndef UINT32_C
58#define UINT32_C(v)  (v ## U) /* from <stdint.h> normally */
59#endif
60#ifndef LZMA_VERSION
61#define LZMA_VERSION UINT32_C(40420000) /* = 4.42.0alpha6 */
62#endif
63
64struct _lzma_context{
65	uint8_t		lzmacompressed;
66	lzma_stream	lzma;
67	lzma_options_stream options;
68	lzma_allocator allocator;
69#if LZMA_VERSION < 40420010U
70	lzma_memory_limitter *limit;
71#else
72	lzma_memlimit *limit;
73#endif
74};
75
76#define preset_level 7
77#define memory_limit 93*1024*1024 /* 1=1M, 5=24M, 6=39M, 7=93M, 8=185M, 9=369M */
78
79#define LZMA_CONTEXT(x) ((struct _lzma_context *)(*x))
80#endif
81
82int xar_lzma_fromheap_done(xar_t x, xar_file_t f, xar_prop_t p, void **context) {
83#ifdef HAVE_LIBLZMA
84
85	if( !context || !LZMA_CONTEXT(context) )
86		return 0;
87
88	if( LZMA_CONTEXT(context)->lzmacompressed){
89		lzma_end(&LZMA_CONTEXT(context)->lzma);
90	}
91
92	/* free the context */
93	free(LZMA_CONTEXT(context));
94	*context = NULL;
95
96#endif
97	return 0;
98}
99
100int xar_lzma_fromheap_in(xar_t x, xar_file_t f, xar_prop_t p, void **in, size_t *inlen, void **context) {
101	const char *opt;
102	xar_prop_t tmpp;
103#ifdef HAVE_LIBLZMA
104	void *out = NULL;
105	size_t outlen, offset = 0;
106	lzma_ret r;
107
108	/* on first run, we init the context and check the compression type */
109	if( !LZMA_CONTEXT(context) ) {
110		*context = calloc(1,sizeof(struct _lzma_context));
111
112		opt = NULL;
113		tmpp = xar_prop_pget(p, "encoding");
114		if( tmpp )
115			opt = xar_attr_pget(f, tmpp, "style");
116		if( !opt ) return 0;
117		if( strcmp(opt, "application/x-lzma") != 0 ) return 0;
118
119		lzma_init_decoder();
120		LZMA_CONTEXT(context)->lzma = LZMA_STREAM_INIT_VAR;
121		r = lzma_stream_decoder(&LZMA_CONTEXT(context)->lzma, NULL, NULL);
122		if( (r != LZMA_OK) ) {
123			xar_err_new(x);
124			xar_err_set_file(x, f);
125			xar_err_set_string(x, "Error decompressing file");
126			xar_err_callback(x, XAR_SEVERITY_FATAL, XAR_ERR_ARCHIVE_EXTRACTION);
127			return -1;
128		}
129		LZMA_CONTEXT(context)->lzmacompressed = 1;
130	}else if( !LZMA_CONTEXT(context)->lzmacompressed ){
131		/* once the context has been initialized, then we have already
132		   checked the compression type, so we need only check if we
133		   actually are compressed */
134		return 0;
135	}
136
137	outlen = *inlen;
138
139	LZMA_CONTEXT(context)->lzma.next_in = *in;
140	LZMA_CONTEXT(context)->lzma.avail_in = *inlen;
141	LZMA_CONTEXT(context)->lzma.next_out = out;
142	LZMA_CONTEXT(context)->lzma.avail_out = 0;
143
144	while( LZMA_CONTEXT(context)->lzma.avail_in != 0 ) {
145		outlen = outlen * 2;
146		out = realloc(out, outlen);
147		if( out == NULL ) abort();
148
149		LZMA_CONTEXT(context)->lzma.next_out = ((unsigned char *)out) + offset;
150		LZMA_CONTEXT(context)->lzma.avail_out = outlen - offset;
151
152		r = lzma_code(&(LZMA_CONTEXT(context)->lzma), LZMA_RUN);
153		if( (r != LZMA_OK) && (r != LZMA_STREAM_END) ) {
154			xar_err_new(x);
155			xar_err_set_file(x, f);
156			xar_err_set_errno(x, r);
157			xar_err_set_string(x, "Error decompressing file");
158			xar_err_callback(x, XAR_SEVERITY_FATAL, XAR_ERR_ARCHIVE_EXTRACTION);
159			return -1;
160		}
161		offset += outlen - offset - LZMA_CONTEXT(context)->lzma.avail_out;
162		if( (r == LZMA_STREAM_END) && (offset == 0) )
163			break;
164	}
165
166	free(*in);
167	*in = out;
168	*inlen = offset;
169#else
170	opt = NULL;
171	tmpp = xar_prop_pget(p, "encoding");
172	if( tmpp )
173		opt = xar_attr_pget(f, tmpp, "style");
174	if( !opt ) return 0;
175	if( strcmp(opt, "application/x-lzma") != 0 ) return 0;
176	xar_err_new(x);
177	xar_err_set_file(x, f);
178	xar_err_set_errno(x, 0);
179	xar_err_set_string(x, "lzma support not compiled in.");
180	xar_err_callback(x, XAR_SEVERITY_FATAL, XAR_ERR_ARCHIVE_EXTRACTION);
181
182#endif /* HAVE_LIBLZMA */
183	return 0;
184}
185
186int xar_lzma_toheap_done(xar_t x, xar_file_t f, xar_prop_t p, void **context) {
187#ifdef HAVE_LIBLZMA
188	xar_prop_t tmpp;
189
190	if( LZMA_CONTEXT(context)->lzmacompressed){
191		lzma_end(&LZMA_CONTEXT(context)->lzma);
192#if LZMA_VERSION < 40420010U
193		lzma_memory_limitter_end(LZMA_CONTEXT(context)->limit, 1);
194#else
195		lzma_memlimit_end(LZMA_CONTEXT(context)->limit, 1);
196#endif
197
198		tmpp = xar_prop_pset(f, p, "encoding", NULL);
199		if( tmpp )
200			xar_attr_pset(f, tmpp, "style", "application/x-lzma");
201	}
202
203	/* free the context */
204	free(LZMA_CONTEXT(context));
205	*context = NULL;
206
207#endif /* HAVE_LIBLZMA */
208	return 0;
209}
210
211int32_t xar_lzma_toheap_in(xar_t x, xar_file_t f, xar_prop_t p, void **in, size_t *inlen, void **context) {
212	const char *opt;
213#ifdef HAVE_LIBLZMA
214	void *out = NULL;
215	size_t outlen, offset = 0;
216	lzma_ret r;
217
218	/* on first run, we init the context and check the compression type */
219	if( !LZMA_CONTEXT(context) ) {
220		int level = preset_level;
221		*context = calloc(1,sizeof(struct _lzma_context));
222
223		opt = xar_opt_get(x, XAR_OPT_COMPRESSION);
224		if( !opt )
225			return 0;
226
227		if( strcmp(opt, XAR_OPT_VAL_LZMA) != 0 )
228			return 0;
229
230		opt = xar_opt_get(x, XAR_OPT_COMPRESSIONARG);
231		if( opt ) {
232			int tmp;
233			errno = 0;
234			tmp = strtol(opt, NULL, 10);
235			if( errno == 0 ) {
236				if( (level >= 0) && (level <= 9) )
237					level = tmp;
238			}
239		}
240
241		lzma_init_encoder();
242		LZMA_CONTEXT(context)->options.check = LZMA_CHECK_CRC64;
243		LZMA_CONTEXT(context)->options.has_crc32 = 1; /* true */
244		LZMA_CONTEXT(context)->options.alignment = 0;
245#if defined (__ppc__) || defined (powerpc) || defined (__ppc64__)
246		LZMA_CONTEXT(context)->options.filters[0].id = LZMA_FILTER_POWERPC;
247#elif defined (__i386__) || defined (__amd64__) || defined(__x86_64__)
248		LZMA_CONTEXT(context)->options.filters[0].id = LZMA_FILTER_X86;
249#else
250		LZMA_CONTEXT(context)->options.filters[0].id = LZMA_FILTER_COPY;
251#endif
252		LZMA_CONTEXT(context)->options.filters[0].options = NULL;
253		LZMA_CONTEXT(context)->options.filters[1].id = LZMA_FILTER_LZMA;
254		LZMA_CONTEXT(context)->options.filters[1].options = (lzma_options_lzma *)(lzma_preset_lzma + level - 1);
255		/* Terminate the filter options array. */
256		LZMA_CONTEXT(context)->options.filters[2].id = UINT64_MAX;
257		LZMA_CONTEXT(context)->lzma = LZMA_STREAM_INIT_VAR;
258#if LZMA_VERSION < 40420010U
259		LZMA_CONTEXT(context)->limit = lzma_memory_limitter_create(memory_limit);
260		LZMA_CONTEXT(context)->allocator.alloc = (void*) lzma_memory_alloc;
261		LZMA_CONTEXT(context)->allocator.free = (void*) lzma_memory_free;
262#else
263		LZMA_CONTEXT(context)->limit = lzma_memlimit_create(memory_limit);
264		LZMA_CONTEXT(context)->allocator.alloc = (void*) lzma_memlimit_alloc;
265		LZMA_CONTEXT(context)->allocator.free = (void*) lzma_memlimit_free;
266#endif
267		LZMA_CONTEXT(context)->allocator.opaque = LZMA_CONTEXT(context)->limit;
268		LZMA_CONTEXT(context)->lzma.allocator = &LZMA_CONTEXT(context)->allocator;
269		r = lzma_stream_encoder_single(&LZMA_CONTEXT(context)->lzma, &(LZMA_CONTEXT(context)->options));
270		if( (r != LZMA_OK) ) {
271			xar_err_new(x);
272			xar_err_set_file(x, f);
273			xar_err_set_string(x, "Error compressing file");
274			xar_err_callback(x, XAR_SEVERITY_FATAL, XAR_ERR_ARCHIVE_CREATION);
275			return -1;
276		}
277		LZMA_CONTEXT(context)->lzmacompressed = 1;
278		if( *inlen == 0 )
279			return 0;
280	}else if( !LZMA_CONTEXT(context)->lzmacompressed ){
281		/* once the context has been initialized, then we have already
282		checked the compression type, so we need only check if we
283		actually are compressed */
284		return 0;
285	}
286
287	outlen = *inlen/2;
288	if(outlen == 0) outlen = 1024;
289	LZMA_CONTEXT(context)->lzma.next_in = *in;
290	LZMA_CONTEXT(context)->lzma.avail_in = *inlen;
291	LZMA_CONTEXT(context)->lzma.next_out = out;
292	LZMA_CONTEXT(context)->lzma.avail_out = 0;
293
294	if( *inlen != 0 ) {
295		do {
296			outlen *= 2;
297			out = realloc(out, outlen);
298			if( out == NULL ) abort();
299
300			LZMA_CONTEXT(context)->lzma.next_out = ((unsigned char *)out) + offset;
301			LZMA_CONTEXT(context)->lzma.avail_out = outlen - offset;
302
303			r = lzma_code(&LZMA_CONTEXT(context)->lzma, LZMA_RUN);
304			offset = outlen - LZMA_CONTEXT(context)->lzma.avail_out;
305		} while( r == LZMA_OK && LZMA_CONTEXT(context)->lzma.avail_in != 0);
306	} else {
307		do {
308			outlen *= 2;
309			out = realloc(out, outlen);
310			if( out == NULL ) abort();
311
312			LZMA_CONTEXT(context)->lzma.next_out = ((unsigned char *)out) + offset;
313			LZMA_CONTEXT(context)->lzma.avail_out = outlen - offset;
314
315			r = lzma_code(&LZMA_CONTEXT(context)->lzma, LZMA_FINISH);
316			offset = outlen - LZMA_CONTEXT(context)->lzma.avail_out;
317		} while( r == LZMA_OK && r != LZMA_STREAM_END);
318	}
319
320	if( (r != LZMA_OK && r != LZMA_STREAM_END) ) {
321		xar_err_new(x);
322		xar_err_set_file(x, f);
323		xar_err_set_string(x, "Error compressing file");
324		xar_err_set_errno(x, r);
325		xar_err_callback(x, XAR_SEVERITY_FATAL, XAR_ERR_ARCHIVE_CREATION);
326		return -1;
327	}
328
329	free(*in);
330	*in = out;
331	*inlen = offset;
332#else
333	opt = xar_opt_get(x, XAR_OPT_COMPRESSION);
334	if( !opt )
335		return 0;
336	if( strcmp(opt, XAR_OPT_VAL_LZMA) != 0 )
337		return 0;
338	xar_err_new(x);
339	xar_err_set_file(x, f);
340	xar_err_set_errno(x, 0);
341	xar_err_set_string(x, "lzma support not compiled in.");
342	xar_err_callback(x, XAR_SEVERITY_FATAL, XAR_ERR_ARCHIVE_CREATION);
343
344#endif /* HAVE_LIBLZMA */
345	return 0;
346}
347