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
18typedef struct {
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} lzma_alone_coder;
50
51
52static lzma_ret
53alone_decode(void *coder_ptr, const lzma_allocator *allocator,
54		const uint8_t *restrict in, size_t *restrict in_pos,
55		size_t in_size, uint8_t *restrict out,
56		size_t *restrict out_pos, size_t out_size,
57		lzma_action action)
58{
59	lzma_alone_coder *coder = coder_ptr;
60
61	while (*out_pos < out_size
62			&& (coder->sequence == SEQ_CODE || *in_pos < in_size))
63	switch (coder->sequence) {
64	case SEQ_PROPERTIES:
65		if (lzma_lzma_lclppb_decode(&coder->options, in[*in_pos]))
66			return LZMA_FORMAT_ERROR;
67
68		coder->sequence = SEQ_DICTIONARY_SIZE;
69		++*in_pos;
70		break;
71
72	case SEQ_DICTIONARY_SIZE:
73		coder->options.dict_size
74				|= (size_t)(in[*in_pos]) << (coder->pos * 8);
75
76		if (++coder->pos == 4) {
77			if (coder->picky && coder->options.dict_size
78					!= UINT32_MAX) {
79				// A hack to ditch tons of false positives:
80				// We allow only dictionary sizes that are
81				// 2^n or 2^n + 2^(n-1). LZMA_Alone created
82				// only files with 2^n, but accepts any
83				// dictionary size.
84				uint32_t d = coder->options.dict_size - 1;
85				d |= d >> 2;
86				d |= d >> 3;
87				d |= d >> 4;
88				d |= d >> 8;
89				d |= d >> 16;
90				++d;
91
92				if (d != coder->options.dict_size)
93					return LZMA_FORMAT_ERROR;
94			}
95
96			coder->pos = 0;
97			coder->sequence = SEQ_UNCOMPRESSED_SIZE;
98		}
99
100		++*in_pos;
101		break;
102
103	case SEQ_UNCOMPRESSED_SIZE:
104		coder->uncompressed_size
105				|= (lzma_vli)(in[*in_pos]) << (coder->pos * 8);
106		++*in_pos;
107		if (++coder->pos < 8)
108			break;
109
110		// Another hack to ditch false positives: Assume that
111		// if the uncompressed size is known, it must be less
112		// than 256 GiB.
113		if (coder->picky
114				&& coder->uncompressed_size != LZMA_VLI_UNKNOWN
115				&& coder->uncompressed_size
116					>= (LZMA_VLI_C(1) << 38))
117			return LZMA_FORMAT_ERROR;
118
119		// Calculate the memory usage so that it is ready
120		// for SEQ_CODER_INIT.
121		coder->memusage = lzma_lzma_decoder_memusage(&coder->options)
122				+ LZMA_MEMUSAGE_BASE;
123
124		coder->pos = 0;
125		coder->sequence = SEQ_CODER_INIT;
126
127	// Fall through
128
129	case SEQ_CODER_INIT: {
130		if (coder->memusage > coder->memlimit)
131			return LZMA_MEMLIMIT_ERROR;
132
133		lzma_filter_info filters[2] = {
134			{
135				.init = &lzma_lzma_decoder_init,
136				.options = &coder->options,
137			}, {
138				.init = NULL,
139			}
140		};
141
142		const lzma_ret ret = lzma_next_filter_init(&coder->next,
143				allocator, filters);
144		if (ret != LZMA_OK)
145			return ret;
146
147		// Use a hack to set the uncompressed size.
148		lzma_lz_decoder_uncompressed(coder->next.coder,
149				coder->uncompressed_size);
150
151		coder->sequence = SEQ_CODE;
152		break;
153	}
154
155	case SEQ_CODE: {
156		return coder->next.code(coder->next.coder,
157				allocator, in, in_pos, in_size,
158				out, out_pos, out_size, action);
159	}
160
161	default:
162		return LZMA_PROG_ERROR;
163	}
164
165	return LZMA_OK;
166}
167
168
169static void
170alone_decoder_end(void *coder_ptr, const lzma_allocator *allocator)
171{
172	lzma_alone_coder *coder = coder_ptr;
173	lzma_next_end(&coder->next, allocator);
174	lzma_free(coder, allocator);
175	return;
176}
177
178
179static lzma_ret
180alone_decoder_memconfig(void *coder_ptr, uint64_t *memusage,
181		uint64_t *old_memlimit, uint64_t new_memlimit)
182{
183	lzma_alone_coder *coder = coder_ptr;
184
185	*memusage = coder->memusage;
186	*old_memlimit = coder->memlimit;
187
188	if (new_memlimit != 0) {
189		if (new_memlimit < coder->memusage)
190			return LZMA_MEMLIMIT_ERROR;
191
192		coder->memlimit = new_memlimit;
193	}
194
195	return LZMA_OK;
196}
197
198
199extern lzma_ret
200lzma_alone_decoder_init(lzma_next_coder *next, const lzma_allocator *allocator,
201		uint64_t memlimit, bool picky)
202{
203	lzma_next_coder_init(&lzma_alone_decoder_init, next, allocator);
204
205	lzma_alone_coder *coder = next->coder;
206
207	if (coder == NULL) {
208		coder = lzma_alloc(sizeof(lzma_alone_coder), allocator);
209		if (coder == NULL)
210			return LZMA_MEM_ERROR;
211
212		next->coder = coder;
213		next->code = &alone_decode;
214		next->end = &alone_decoder_end;
215		next->memconfig = &alone_decoder_memconfig;
216		coder->next = LZMA_NEXT_CODER_INIT;
217	}
218
219	coder->sequence = SEQ_PROPERTIES;
220	coder->picky = picky;
221	coder->pos = 0;
222	coder->options.dict_size = 0;
223	coder->options.preset_dict = NULL;
224	coder->options.preset_dict_size = 0;
225	coder->uncompressed_size = 0;
226	coder->memlimit = my_max(1, memlimit);
227	coder->memusage = LZMA_MEMUSAGE_BASE;
228
229	return LZMA_OK;
230}
231
232
233extern LZMA_API(lzma_ret)
234lzma_alone_decoder(lzma_stream *strm, uint64_t memlimit)
235{
236	lzma_next_strm_init(lzma_alone_decoder_init, strm, memlimit, false);
237
238	strm->internal->supported_actions[LZMA_RUN] = true;
239	strm->internal->supported_actions[LZMA_FINISH] = true;
240
241	return LZMA_OK;
242}
243