]> icculus.org git repositories - icculus/xz.git/blob - src/liblzma/common/stream_decoder.c
Take advantage of return_if_error() macro in more places.
[icculus/xz.git] / src / liblzma / common / stream_decoder.c
1 ///////////////////////////////////////////////////////////////////////////////
2 //
3 /// \file       stream_decoder.c
4 /// \brief      Decodes .lzma Streams
5 //
6 //  Copyright (C) 2007 Lasse Collin
7 //
8 //  This library is free software; you can redistribute it and/or
9 //  modify it under the terms of the GNU Lesser General Public
10 //  License as published by the Free Software Foundation; either
11 //  version 2.1 of the License, or (at your option) any later version.
12 //
13 //  This library is distributed in the hope that it will be useful,
14 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
15 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16 //  Lesser General Public License for more details.
17 //
18 ///////////////////////////////////////////////////////////////////////////////
19
20 #include "stream_common.h"
21 #include "check.h"
22 #include "stream_flags_decoder.h"
23 #include "block_decoder.h"
24 #include "metadata_decoder.h"
25
26
27 struct lzma_coder_s {
28         enum {
29                 SEQ_STREAM_HEADER_CODE,
30                 SEQ_BLOCK_HEADER_INIT,
31                 SEQ_BLOCK_HEADER_CODE,
32                 SEQ_METADATA_CODE,
33                 SEQ_DATA_CODE,
34                 SEQ_STREAM_TAIL_INIT,
35                 SEQ_STREAM_TAIL_CODE,
36         } sequence;
37
38         /// Position in variable-length integers and in some other things.
39         size_t pos;
40
41         /// Block or Metadata decoder. This takes little memory and the same
42         /// data structure can be used to decode every Block Header, so it's
43         /// a good idea to have a separate lzma_next_coder structure for it.
44         lzma_next_coder block_decoder;
45
46         /// Block Header decoder; this is separate
47         lzma_next_coder block_header_decoder;
48
49         lzma_options_block block_options;
50
51         /// Information about the sizes of the Blocks
52         lzma_info *info;
53
54         /// Current Block in *info
55         lzma_info_iter iter;
56
57         /// Number of bytes not yet processed from Data Blocks in the Stream.
58         /// This can be LZMA_VLI_VALUE_UNKNOWN. If it is known, it is
59         /// decremented while decoding and verified to match the reality.
60         lzma_vli total_left;
61
62         /// Like uncompressed_left above but for uncompressed data from
63         /// Data Blocks.
64         lzma_vli uncompressed_left;
65
66         /// Stream Flags from Stream Header
67         lzma_stream_flags header_flags;
68
69         /// Stream Flags from Stream tail
70         lzma_stream_flags tail_flags;
71
72         /// Decoder for Stream Header and Stream tail. This takes very
73         /// little memory and the same data structure can be used for
74         /// both Header and tail, so it's a good idea to have a separate
75         /// lzma_next_coder structure for it.
76         lzma_next_coder flags_decoder;
77
78         /// Temporary destination for the decoded Metadata.
79         lzma_metadata metadata;
80
81         /// Pointer to application-supplied pointer where to store the list
82         /// of Extra Records from the Header Metadata Block.
83         lzma_extra **header_extra;
84
85         /// Same as above but Footer Metadata Block
86         lzma_extra **footer_extra;
87 };
88
89
90 static lzma_ret
91 metadata_init(lzma_coder *coder, lzma_allocator *allocator)
92 {
93         assert(coder->metadata.index == NULL);
94         assert(coder->metadata.extra == NULL);
95
96         // Single-Block Streams don't have Metadata Blocks.
97         if (!coder->header_flags.is_multi)
98                 return LZMA_DATA_ERROR;
99
100         coder->block_options.total_limit = LZMA_VLI_VALUE_UNKNOWN;
101
102         // Limit the Uncompressed Size of a Metadata Block. This is to
103         // prevent security issues where input file would have very huge
104         // Metadata.
105         //
106         // FIXME: Hardcoded constant is ugly. Maybe we should provide
107         // some way to specify this from the application.
108         coder->block_options.uncompressed_limit = LZMA_VLI_C(1) << 23;
109
110         lzma_info_size size_type;
111         bool want_extra;
112
113         // If we haven't decoded any Data Blocks yet, this is Header
114         // Metadata Block.
115         if (lzma_info_index_count_get(coder->info) == 0) {
116                 coder->block_options.has_backward_size = false;
117                 coder->block_options.handle_padding = true;
118                 size_type = LZMA_INFO_HEADER_METADATA;
119                 want_extra = coder->header_extra != NULL;
120         } else {
121                 if (lzma_info_index_finish(coder->info))
122                         return LZMA_DATA_ERROR;
123
124                 coder->block_options.has_backward_size = true;
125                 coder->block_options.handle_padding = false;
126                 size_type = LZMA_INFO_FOOTER_METADATA;
127                 want_extra = coder->footer_extra != NULL;
128         }
129
130         coder->block_options.has_uncompressed_size_in_footer = false;
131         coder->block_options.total_size = lzma_info_size_get(
132                         coder->info, size_type);
133
134         coder->sequence = SEQ_METADATA_CODE;
135
136         return lzma_metadata_decoder_init(&coder->block_decoder, allocator,
137                         &coder->block_options, &coder->metadata, want_extra);
138 }
139
140
141 static lzma_ret
142 data_init(lzma_coder *coder, lzma_allocator *allocator)
143 {
144         return_if_error(lzma_info_iter_next(&coder->iter, allocator));
145
146         return_if_error(lzma_info_iter_set(
147                         &coder->iter, LZMA_VLI_VALUE_UNKNOWN,
148                         coder->block_options.uncompressed_size));
149
150         coder->block_options.total_size = coder->iter.total_size;
151         coder->block_options.uncompressed_size = coder->iter.uncompressed_size;
152         coder->block_options.total_limit = coder->total_left;
153         coder->block_options.uncompressed_limit = coder->uncompressed_left;
154
155         if (coder->header_flags.is_multi) {
156                 coder->block_options.has_uncompressed_size_in_footer = false;
157                 coder->block_options.has_backward_size = false;
158                 coder->block_options.handle_padding = true;
159         } else {
160                 coder->block_options.has_uncompressed_size_in_footer
161                                 = coder->iter.uncompressed_size
162                                         == LZMA_VLI_VALUE_UNKNOWN;
163                 coder->block_options.has_backward_size = true;
164                 coder->block_options.handle_padding = false;
165         }
166
167         coder->sequence = SEQ_DATA_CODE;
168
169         return lzma_block_decoder_init(&coder->block_decoder, allocator,
170                         &coder->block_options);
171 }
172
173
174 static lzma_ret
175 stream_decode(lzma_coder *coder, lzma_allocator *allocator,
176                 const uint8_t *restrict in, size_t *restrict in_pos,
177                 size_t in_size, uint8_t *restrict out,
178                 size_t *restrict out_pos, size_t out_size, lzma_action action)
179 {
180         while (*out_pos < out_size && (*in_pos < in_size
181                         || coder->sequence == SEQ_DATA_CODE))
182         switch (coder->sequence) {
183         case SEQ_STREAM_HEADER_CODE: {
184                 const lzma_ret ret = coder->flags_decoder.code(
185                                 coder->flags_decoder.coder,
186                                 allocator, in, in_pos, in_size,
187                                 NULL, NULL, 0, LZMA_RUN);
188                 if (ret != LZMA_STREAM_END)
189                         return ret;
190
191                 coder->sequence = SEQ_BLOCK_HEADER_INIT;
192
193                 // Detect if the Check type is supported and give appropriate
194                 // warning if it isn't. We don't warn every time a new Block
195                 // is started.
196                 lzma_check tmp;
197                 if (lzma_check_init(&tmp, coder->header_flags.check))
198                         return LZMA_UNSUPPORTED_CHECK;
199
200                 break;
201         }
202
203         case SEQ_BLOCK_HEADER_INIT: {
204                 coder->block_options.check = coder->header_flags.check;
205                 coder->block_options.has_crc32 = coder->header_flags.has_crc32;
206
207                 return_if_error(lzma_block_header_decoder_init(
208                                 &coder->block_header_decoder, allocator,
209                                 &coder->block_options));
210
211                 coder->sequence = SEQ_BLOCK_HEADER_CODE;
212         }
213
214         // Fall through
215
216         case SEQ_BLOCK_HEADER_CODE: {
217                 lzma_ret ret = coder->block_header_decoder.code(
218                                 coder->block_header_decoder.coder,
219                                 allocator, in, in_pos, in_size,
220                                 NULL, NULL, 0, LZMA_RUN);
221
222                 if (ret != LZMA_STREAM_END)
223                         return ret;
224
225                 if (coder->block_options.is_metadata)
226                         ret = metadata_init(coder, allocator);
227                 else
228                         ret = data_init(coder, allocator);
229
230                 if (ret != LZMA_OK)
231                         return ret;
232
233                 break;
234         }
235
236         case SEQ_METADATA_CODE: {
237                 lzma_ret ret = coder->block_decoder.code(
238                                 coder->block_decoder.coder, allocator,
239                                 in, in_pos, in_size, NULL, NULL, 0, LZMA_RUN);
240                 if (ret != LZMA_STREAM_END)
241                         return ret;
242
243                 const bool is_header_metadata = lzma_info_index_count_get(
244                                 coder->info) == 0;
245
246                 if (is_header_metadata) {
247                         if (coder->header_extra != NULL) {
248                                 *coder->header_extra = coder->metadata.extra;
249                                 coder->metadata.extra = NULL;
250                         }
251
252                         if (lzma_info_size_set(coder->info,
253                                         LZMA_INFO_HEADER_METADATA,
254                                         coder->block_options.total_size)
255                                         != LZMA_OK)
256                                 return LZMA_PROG_ERROR;
257
258                         coder->sequence = SEQ_BLOCK_HEADER_INIT;
259                 } else {
260                         if (coder->footer_extra != NULL) {
261                                 *coder->footer_extra = coder->metadata.extra;
262                                 coder->metadata.extra = NULL;
263                         }
264
265                         coder->sequence = SEQ_STREAM_TAIL_INIT;
266                 }
267
268                 assert(coder->metadata.extra == NULL);
269
270                 ret = lzma_info_metadata_set(coder->info, allocator,
271                                 &coder->metadata, is_header_metadata, true);
272                 if (ret != LZMA_OK)
273                         return ret;
274
275                 // Intialize coder->total_size and coder->uncompressed_size
276                 // from Header Metadata.
277                 if (is_header_metadata) {
278                         coder->total_left = lzma_info_size_get(
279                                         coder->info, LZMA_INFO_TOTAL);
280                         coder->uncompressed_left = lzma_info_size_get(
281                                         coder->info, LZMA_INFO_UNCOMPRESSED);
282                 }
283
284                 break;
285         }
286
287         case SEQ_DATA_CODE: {
288                 lzma_ret ret = coder->block_decoder.code(
289                                 coder->block_decoder.coder, allocator,
290                                 in, in_pos, in_size, out, out_pos, out_size,
291                                 action);
292
293                 if (ret != LZMA_STREAM_END)
294                         return ret;
295
296                 ret = lzma_info_iter_set(&coder->iter,
297                                 coder->block_options.total_size,
298                                 coder->block_options.uncompressed_size);
299                 if (ret != LZMA_OK)
300                         return ret;
301
302                 // These won't overflow since lzma_info_iter_set() succeeded.
303                 if (coder->total_left != LZMA_VLI_VALUE_UNKNOWN)
304                         coder->total_left -= coder->block_options.total_size;
305                 if (coder->uncompressed_left != LZMA_VLI_VALUE_UNKNOWN)
306                         coder->uncompressed_left -= coder->block_options
307                                         .uncompressed_size;
308
309                 if (!coder->header_flags.is_multi) {
310                         ret = lzma_info_index_finish(coder->info);
311                         if (ret != LZMA_OK)
312                                 return ret;
313
314                         coder->sequence = SEQ_STREAM_TAIL_INIT;
315                         break;
316                 }
317
318                 coder->sequence = SEQ_BLOCK_HEADER_INIT;
319                 break;
320         }
321
322         case SEQ_STREAM_TAIL_INIT: {
323                 lzma_ret ret = lzma_info_index_finish(coder->info);
324                 if (ret != LZMA_OK)
325                         return ret;
326
327                 ret = lzma_stream_tail_decoder_init(&coder->flags_decoder,
328                                 allocator, &coder->tail_flags);
329                 if (ret != LZMA_OK)
330                         return ret;
331
332                 coder->sequence = SEQ_STREAM_TAIL_CODE;
333         }
334
335         // Fall through
336
337         case SEQ_STREAM_TAIL_CODE: {
338                 const lzma_ret ret = coder->flags_decoder.code(
339                                 coder->flags_decoder.coder, allocator,
340                                 in, in_pos, in_size, NULL, NULL, 0, LZMA_RUN);
341                 if (ret != LZMA_STREAM_END)
342                         return ret;
343
344                 if (!lzma_stream_flags_is_equal(
345                                 coder->header_flags, coder->tail_flags))
346                         return LZMA_DATA_ERROR;
347
348                 return LZMA_STREAM_END;
349         }
350
351         default:
352                 return LZMA_PROG_ERROR;
353         }
354
355         return LZMA_OK;
356 }
357
358
359 static void
360 stream_decoder_end(lzma_coder *coder, lzma_allocator *allocator)
361 {
362         lzma_next_coder_end(&coder->block_decoder, allocator);
363         lzma_next_coder_end(&coder->block_header_decoder, allocator);
364         lzma_next_coder_end(&coder->flags_decoder, allocator);
365         lzma_info_free(coder->info, allocator);
366         lzma_index_free(coder->metadata.index, allocator);
367         lzma_extra_free(coder->metadata.extra, allocator);
368         lzma_free(coder, allocator);
369         return;
370 }
371
372
373 static lzma_ret
374 stream_decoder_init(lzma_next_coder *next, lzma_allocator *allocator,
375                 lzma_extra **header, lzma_extra **footer)
376 {
377         if (next->coder == NULL) {
378                 next->coder = lzma_alloc(sizeof(lzma_coder), allocator);
379                 if (next->coder == NULL)
380                         return LZMA_MEM_ERROR;
381
382                 next->code = &stream_decode;
383                 next->end = &stream_decoder_end;
384
385                 next->coder->block_decoder = LZMA_NEXT_CODER_INIT;
386                 next->coder->block_header_decoder = LZMA_NEXT_CODER_INIT;
387                 next->coder->info = NULL;
388                 next->coder->flags_decoder = LZMA_NEXT_CODER_INIT;
389                 next->coder->metadata.index = NULL;
390                 next->coder->metadata.extra = NULL;
391         } else {
392                 lzma_index_free(next->coder->metadata.index, allocator);
393                 next->coder->metadata.index = NULL;
394
395                 lzma_extra_free(next->coder->metadata.extra, allocator);
396                 next->coder->metadata.extra = NULL;
397         }
398
399         next->coder->info = lzma_info_init(next->coder->info, allocator);
400         if (next->coder->info == NULL)
401                 return LZMA_MEM_ERROR;
402
403         lzma_info_iter_begin(next->coder->info, &next->coder->iter);
404
405         // Initialize Stream Header decoder.
406         return_if_error(lzma_stream_header_decoder_init(
407                                 &next->coder->flags_decoder, allocator,
408                                 &next->coder->header_flags));
409
410         // Reset the *foo_extra pointers to NULL. This way the caller knows
411         // if there were no Extra Records. (We don't support appending
412         // Records to Extra list.)
413         if (header != NULL)
414                 *header = NULL;
415         if (footer != NULL)
416                 *footer = NULL;
417
418         // Reset some variables.
419         next->coder->sequence = SEQ_STREAM_HEADER_CODE;
420         next->coder->pos = 0;
421         next->coder->uncompressed_left = LZMA_VLI_VALUE_UNKNOWN;
422         next->coder->total_left = LZMA_VLI_VALUE_UNKNOWN;
423         next->coder->header_extra = header;
424         next->coder->footer_extra = footer;
425
426         return LZMA_OK;
427 }
428
429
430 extern lzma_ret
431 lzma_stream_decoder_init(lzma_next_coder *next, lzma_allocator *allocator,
432                 lzma_extra **header, lzma_extra **footer)
433 {
434         lzma_next_coder_init(
435                         stream_decoder_init, next, allocator, header, footer);
436 }
437
438
439 extern LZMA_API lzma_ret
440 lzma_stream_decoder(lzma_stream *strm,
441                 lzma_extra **header, lzma_extra **footer)
442 {
443         lzma_next_strm_init(strm, stream_decoder_init, header, footer);
444
445         strm->internal->supported_actions[LZMA_RUN] = true;
446         strm->internal->supported_actions[LZMA_SYNC_FLUSH] = true;
447
448         return LZMA_OK;
449 }