alone_decoder.c revision 292588
1///////////////////////////////////////////////////////////////////////////////
2//
3/// \file       alone_decoder.c
4/// \brief      Decoder for LZMA_Alone files
5//
6//  Author:     Lasse Collin
7//
8//  This file has been put into the public domain.
9//  You can do whatever you want with this file.
10//
11///////////////////////////////////////////////////////////////////////////////
12
13#include "alone_decoder.h"
14#include "lzma_decoder.h"
15#include "lz_decoder.h"
16
17
18struct lzma_coder_s {
19	lzma_next_coder next;
20
21	enum {
22		SEQ_PROPERTIES,
23		SEQ_DICTIONARY_SIZE,
24		SEQ_UNCOMPRESSED_SIZE,
25		SEQ_CODER_INIT,
26		SEQ_CODE,
27	} sequence;
28
29	/// If true, reject files that are unlikely to be .lzma files.
30	/// If false, more non-.lzma files get accepted and will give
31	/// LZMA_DATA_ERROR either immediately or after a few output bytes.
32	bool picky;
33
34	/// Position in the header fields
35	size_t pos;
36
37	/// Uncompressed size decoded from the header
38	lzma_vli uncompressed_size;
39
40	/// Memory usage limit
41	uint64_t memlimit;
42
43	/// Amount of memory actually needed (only an estimate)
44	uint64_t memusage;
45
46	/// Options decoded from the header needed to initialize
47	/// the LZMA decoder
48	lzma_options_lzma options;
49};
50
51
52static lzma_ret
53alone_decode(lzma_coder *coder,
54		const lzma_allocator *allocator lzma_attribute((__unused__)),
55		const uint8_t *restrict in, size_t *restrict in_pos,
56		size_t in_size, uint8_t *restrict out,
57		size_t *restrict out_pos, size_t out_size,
58		lzma_action action)
59{
60	while (*out_pos < out_size
61			&& (coder->sequence == SEQ_CODE || *in_pos < in_size))
62	switch (coder->sequence) {
63	case SEQ_PROPERTIES:
64		if (lzma_lzma_lclppb_decode(&coder->options, in[*in_pos]))
65			return LZMA_FORMAT_ERROR;
66
67		coder->sequence = SEQ_DICTIONARY_SIZE;
68		++*in_pos;
69		break;
70
71	case SEQ_DICTIONARY_SIZE:
72		coder->options.dict_size
73				|= (size_t)(in[*in_pos]) << (coder->pos * 8);
74
75		if (++coder->pos == 4) {
76			if (coder->picky && coder->options.dict_size
77					!= UINT32_MAX) {
78				// A hack to ditch tons of false positives:
79				// We allow only dictionary sizes that are
80				// 2^n or 2^n + 2^(n-1). LZMA_Alone created
81				// only files with 2^n, but accepts any
82				// dictionary size.
83				uint32_t d = coder->options.dict_size - 1;
84				d |= d >> 2;
85				d |= d >> 3;
86				d |= d >> 4;
87				d |= d >> 8;
88				d |= d >> 16;
89				++d;
90
91				if (d != coder->options.dict_size)
92					return LZMA_FORMAT_ERROR;
93			}
94
95			coder->pos = 0;
96			coder->sequence = SEQ_UNCOMPRESSED_SIZE;
97		}
98
99		++*in_pos;
100		break;
101
102	case SEQ_UNCOMPRESSED_SIZE:
103		coder->uncompressed_size
104				|= (lzma_vli)(in[*in_pos]) << (coder->pos * 8);
105		++*in_pos;
106		if (++coder->pos < 8)
107			break;
108
109		// Another hack to ditch false positives: Assume that
110		// if the uncompressed size is known, it must be less
111		// than 256 GiB.
112		if (coder->picky
113				&& coder->uncompressed_size != LZMA_VLI_UNKNOWN
114				&& coder->uncompressed_size
115					>= (LZMA_VLI_C(1) << 38))
116			return LZMA_FORMAT_ERROR;
117
118		// Calculate the memory usage so that it is ready
119		// for SEQ_CODER_INIT.
120		coder->memusage = lzma_lzma_decoder_memusage(&coder->options)
121				+ LZMA_MEMUSAGE_BASE;
122
123		coder->pos = 0;
124		coder->sequence = SEQ_CODER_INIT;
125
126	// Fall through
127
128	case SEQ_CODER_INIT: {
129		if (coder->memusage > coder->memlimit)
130			return LZMA_MEMLIMIT_ERROR;
131
132		lzma_filter_info filters[2] = {
133			{
134				.init = &lzma_lzma_decoder_init,
135				.options = &coder->options,
136			}, {
137				.init = NULL,
138			}
139		};
140
141		const lzma_ret ret = lzma_next_filter_init(&coder->next,
142				allocator, filters);
143		if (ret != LZMA_OK)
144			return ret;
145
146		// Use a hack to set the uncompressed size.
147		lzma_lz_decoder_uncompressed(coder->next.coder,
148				coder->uncompressed_size);
149
150		coder->sequence = SEQ_CODE;
151		break;
152	}
153
154	case SEQ_CODE: {
155		return coder->next.code(coder->next.coder,
156				allocator, in, in_pos, in_size,
157				out, out_pos, out_size, action);
158	}
159
160	default:
161		return LZMA_PROG_ERROR;
162	}
163
164	return LZMA_OK;
165}
166
167
168static void
169alone_decoder_end(lzma_coder *coder, const lzma_allocator *allocator)
170{
171	lzma_next_end(&coder->next, allocator);
172	lzma_free(coder, allocator);
173	return;
174}
175
176
177static lzma_ret
178alone_decoder_memconfig(lzma_coder *coder, uint64_t *memusage,
179		uint64_t *old_memlimit, uint64_t new_memlimit)
180{
181	*memusage = coder->memusage;
182	*old_memlimit = coder->memlimit;
183
184	if (new_memlimit != 0) {
185		if (new_memlimit < coder->memusage)
186			return LZMA_MEMLIMIT_ERROR;
187
188		coder->memlimit = new_memlimit;
189	}
190
191	return LZMA_OK;
192}
193
194
195extern lzma_ret
196lzma_alone_decoder_init(lzma_next_coder *next, const lzma_allocator *allocator,
197		uint64_t memlimit, bool picky)
198{
199	lzma_next_coder_init(&lzma_alone_decoder_init, next, allocator);
200
201	if (memlimit == 0)
202		return LZMA_PROG_ERROR;
203
204	if (next->coder == NULL) {
205		next->coder = lzma_alloc(sizeof(lzma_coder), allocator);
206		if (next->coder == NULL)
207			return LZMA_MEM_ERROR;
208
209		next->code = &alone_decode;
210		next->end = &alone_decoder_end;
211		next->memconfig = &alone_decoder_memconfig;
212		next->coder->next = LZMA_NEXT_CODER_INIT;
213	}
214
215	next->coder->sequence = SEQ_PROPERTIES;
216	next->coder->picky = picky;
217	next->coder->pos = 0;
218	next->coder->options.dict_size = 0;
219	next->coder->options.preset_dict = NULL;
220	next->coder->options.preset_dict_size = 0;
221	next->coder->uncompressed_size = 0;
222	next->coder->memlimit = memlimit;
223	next->coder->memusage = LZMA_MEMUSAGE_BASE;
224
225	return LZMA_OK;
226}
227
228
229extern LZMA_API(lzma_ret)
230lzma_alone_decoder(lzma_stream *strm, uint64_t memlimit)
231{
232	lzma_next_strm_init(lzma_alone_decoder_init, strm, memlimit, false);
233
234	strm->internal->supported_actions[LZMA_RUN] = true;
235	strm->internal->supported_actions[LZMA_FINISH] = true;
236
237	return LZMA_OK;
238}
239