/src/xz/src/liblzma/common/auto_decoder.c
Line | Count | Source (jump to first uncovered line) |
1 | | // SPDX-License-Identifier: 0BSD |
2 | | |
3 | | /////////////////////////////////////////////////////////////////////////////// |
4 | | // |
5 | | /// \file auto_decoder.c |
6 | | /// \brief Autodetect between .xz, .lzma (LZMA_Alone), and .lz (lzip) |
7 | | // |
8 | | // Author: Lasse Collin |
9 | | // |
10 | | /////////////////////////////////////////////////////////////////////////////// |
11 | | |
12 | | #include "stream_decoder.h" |
13 | | #include "alone_decoder.h" |
14 | | #ifdef HAVE_LZIP_DECODER |
15 | | # include "lzip_decoder.h" |
16 | | #endif |
17 | | |
18 | | |
19 | | typedef struct { |
20 | | /// .xz Stream decoder, LZMA_Alone decoder, or lzip decoder |
21 | | lzma_next_coder next; |
22 | | |
23 | | uint64_t memlimit; |
24 | | uint32_t flags; |
25 | | |
26 | | enum { |
27 | | SEQ_INIT, |
28 | | SEQ_CODE, |
29 | | SEQ_FINISH, |
30 | | } sequence; |
31 | | } lzma_auto_coder; |
32 | | |
33 | | |
34 | | static lzma_ret |
35 | | auto_decode(void *coder_ptr, const lzma_allocator *allocator, |
36 | | const uint8_t *restrict in, size_t *restrict in_pos, |
37 | | size_t in_size, uint8_t *restrict out, |
38 | | size_t *restrict out_pos, size_t out_size, lzma_action action) |
39 | 0 | { |
40 | 0 | lzma_auto_coder *coder = coder_ptr; |
41 | |
|
42 | 0 | switch (coder->sequence) { |
43 | 0 | case SEQ_INIT: |
44 | 0 | if (*in_pos >= in_size) |
45 | 0 | return LZMA_OK; |
46 | | |
47 | | // Update the sequence now, because we want to continue from |
48 | | // SEQ_CODE even if we return some LZMA_*_CHECK. |
49 | 0 | coder->sequence = SEQ_CODE; |
50 | | |
51 | | // Detect the file format. .xz files start with 0xFD which |
52 | | // cannot be the first byte of .lzma (LZMA_Alone) format. |
53 | | // The .lz format starts with 0x4C which could be the |
54 | | // first byte of a .lzma file but luckily it would mean |
55 | | // lc/lp/pb being 4/3/1 which liblzma doesn't support because |
56 | | // lc + lp > 4. So using just 0x4C to detect .lz is OK here. |
57 | 0 | if (in[*in_pos] == 0xFD) { |
58 | 0 | return_if_error(lzma_stream_decoder_init( |
59 | 0 | &coder->next, allocator, |
60 | 0 | coder->memlimit, coder->flags)); |
61 | | #ifdef HAVE_LZIP_DECODER |
62 | | } else if (in[*in_pos] == 0x4C) { |
63 | | return_if_error(lzma_lzip_decoder_init( |
64 | | &coder->next, allocator, |
65 | | coder->memlimit, coder->flags)); |
66 | | #endif |
67 | 0 | } else { |
68 | 0 | return_if_error(lzma_alone_decoder_init(&coder->next, |
69 | 0 | allocator, coder->memlimit, true)); |
70 | | |
71 | | // If the application wants to know about missing |
72 | | // integrity check or about the check in general, we |
73 | | // need to handle it here, because LZMA_Alone decoder |
74 | | // doesn't accept any flags. |
75 | 0 | if (coder->flags & LZMA_TELL_NO_CHECK) |
76 | 0 | return LZMA_NO_CHECK; |
77 | | |
78 | 0 | if (coder->flags & LZMA_TELL_ANY_CHECK) |
79 | 0 | return LZMA_GET_CHECK; |
80 | 0 | } |
81 | | |
82 | 0 | FALLTHROUGH; |
83 | |
|
84 | 0 | case SEQ_CODE: { |
85 | 0 | const lzma_ret ret = coder->next.code( |
86 | 0 | coder->next.coder, allocator, |
87 | 0 | in, in_pos, in_size, |
88 | 0 | out, out_pos, out_size, action); |
89 | 0 | if (ret != LZMA_STREAM_END |
90 | 0 | || (coder->flags & LZMA_CONCATENATED) == 0) |
91 | 0 | return ret; |
92 | | |
93 | 0 | coder->sequence = SEQ_FINISH; |
94 | 0 | FALLTHROUGH; |
95 | 0 | } |
96 | | |
97 | 0 | case SEQ_FINISH: |
98 | | // When LZMA_CONCATENATED was used and we were decoding |
99 | | // a LZMA_Alone file, we need to check that there is no |
100 | | // trailing garbage and wait for LZMA_FINISH. |
101 | 0 | if (*in_pos < in_size) |
102 | 0 | return LZMA_DATA_ERROR; |
103 | | |
104 | 0 | return action == LZMA_FINISH ? LZMA_STREAM_END : LZMA_OK; |
105 | | |
106 | 0 | default: |
107 | 0 | assert(0); |
108 | 0 | return LZMA_PROG_ERROR; |
109 | 0 | } |
110 | 0 | } |
111 | | |
112 | | |
113 | | static void |
114 | | auto_decoder_end(void *coder_ptr, const lzma_allocator *allocator) |
115 | 0 | { |
116 | 0 | lzma_auto_coder *coder = coder_ptr; |
117 | 0 | lzma_next_end(&coder->next, allocator); |
118 | 0 | lzma_free(coder, allocator); |
119 | 0 | return; |
120 | 0 | } |
121 | | |
122 | | |
123 | | static lzma_check |
124 | | auto_decoder_get_check(const void *coder_ptr) |
125 | 0 | { |
126 | 0 | const lzma_auto_coder *coder = coder_ptr; |
127 | | |
128 | | // It is LZMA_Alone if get_check is NULL. |
129 | 0 | return coder->next.get_check == NULL ? LZMA_CHECK_NONE |
130 | 0 | : coder->next.get_check(coder->next.coder); |
131 | 0 | } |
132 | | |
133 | | |
134 | | static lzma_ret |
135 | | auto_decoder_memconfig(void *coder_ptr, uint64_t *memusage, |
136 | | uint64_t *old_memlimit, uint64_t new_memlimit) |
137 | 0 | { |
138 | 0 | lzma_auto_coder *coder = coder_ptr; |
139 | |
|
140 | 0 | lzma_ret ret; |
141 | |
|
142 | 0 | if (coder->next.memconfig != NULL) { |
143 | 0 | ret = coder->next.memconfig(coder->next.coder, |
144 | 0 | memusage, old_memlimit, new_memlimit); |
145 | 0 | assert(*old_memlimit == coder->memlimit); |
146 | 0 | } else { |
147 | | // No coder is configured yet. Use the base value as |
148 | | // the current memory usage. |
149 | 0 | *memusage = LZMA_MEMUSAGE_BASE; |
150 | 0 | *old_memlimit = coder->memlimit; |
151 | |
|
152 | 0 | ret = LZMA_OK; |
153 | 0 | if (new_memlimit != 0 && new_memlimit < *memusage) |
154 | 0 | ret = LZMA_MEMLIMIT_ERROR; |
155 | 0 | } |
156 | |
|
157 | 0 | if (ret == LZMA_OK && new_memlimit != 0) |
158 | 0 | coder->memlimit = new_memlimit; |
159 | |
|
160 | 0 | return ret; |
161 | 0 | } |
162 | | |
163 | | |
164 | | static lzma_ret |
165 | | auto_decoder_init(lzma_next_coder *next, const lzma_allocator *allocator, |
166 | | uint64_t memlimit, uint32_t flags) |
167 | 0 | { |
168 | 0 | lzma_next_coder_init(&auto_decoder_init, next, allocator); |
169 | |
|
170 | 0 | if (flags & ~LZMA_SUPPORTED_FLAGS) |
171 | 0 | return LZMA_OPTIONS_ERROR; |
172 | | |
173 | 0 | lzma_auto_coder *coder = next->coder; |
174 | 0 | if (coder == NULL) { |
175 | 0 | coder = lzma_alloc(sizeof(lzma_auto_coder), allocator); |
176 | 0 | if (coder == NULL) |
177 | 0 | return LZMA_MEM_ERROR; |
178 | | |
179 | 0 | next->coder = coder; |
180 | 0 | next->code = &auto_decode; |
181 | 0 | next->end = &auto_decoder_end; |
182 | 0 | next->get_check = &auto_decoder_get_check; |
183 | 0 | next->memconfig = &auto_decoder_memconfig; |
184 | 0 | coder->next = LZMA_NEXT_CODER_INIT; |
185 | 0 | } |
186 | | |
187 | 0 | coder->memlimit = my_max(1, memlimit); |
188 | 0 | coder->flags = flags; |
189 | 0 | coder->sequence = SEQ_INIT; |
190 | |
|
191 | 0 | return LZMA_OK; |
192 | 0 | } |
193 | | |
194 | | |
195 | | extern LZMA_API(lzma_ret) |
196 | | lzma_auto_decoder(lzma_stream *strm, uint64_t memlimit, uint32_t flags) |
197 | 0 | { |
198 | 0 | lzma_next_strm_init(auto_decoder_init, strm, memlimit, flags); |
199 | | |
200 | 0 | strm->internal->supported_actions[LZMA_RUN] = true; |
201 | 0 | strm->internal->supported_actions[LZMA_FINISH] = true; |
202 | |
|
203 | 0 | return LZMA_OK; |
204 | 0 | } |