]>
Commit | Line | Data |
---|---|---|
53e1b683 | 1 | /* SPDX-License-Identifier: LGPL-2.1+ */ |
3e2cda69 LP |
2 | /*** |
3 | This file is part of systemd. | |
4 | ||
5 | Copyright 2015 Lennart Poettering | |
6 | ||
7 | systemd is free software; you can redistribute it and/or modify it | |
8 | under the terms of the GNU Lesser General Public License as published by | |
9 | the Free Software Foundation; either version 2.1 of the License, or | |
10 | (at your option) any later version. | |
11 | ||
12 | systemd is distributed in the hope that it will be useful, but | |
13 | WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
15 | Lesser General Public License for more details. | |
16 | ||
17 | You should have received a copy of the GNU Lesser General Public License | |
18 | along with systemd; If not, see <http://www.gnu.org/licenses/>. | |
19 | ***/ | |
20 | ||
3e2cda69 | 21 | #include "import-compress.h" |
8b43440b LP |
22 | #include "string-table.h" |
23 | #include "util.h" | |
3e2cda69 LP |
24 | |
25 | void import_compress_free(ImportCompress *c) { | |
26 | assert(c); | |
27 | ||
28 | if (c->type == IMPORT_COMPRESS_XZ) | |
29 | lzma_end(&c->xz); | |
587fec42 LP |
30 | else if (c->type == IMPORT_COMPRESS_GZIP) { |
31 | if (c->encoding) | |
32 | deflateEnd(&c->gzip); | |
33 | else | |
34 | inflateEnd(&c->gzip); | |
35 | } else if (c->type == IMPORT_COMPRESS_BZIP2) { | |
36 | if (c->encoding) | |
37 | BZ2_bzCompressEnd(&c->bzip2); | |
38 | else | |
39 | BZ2_bzDecompressEnd(&c->bzip2); | |
40 | } | |
3e2cda69 LP |
41 | |
42 | c->type = IMPORT_COMPRESS_UNKNOWN; | |
43 | } | |
44 | ||
45 | int import_uncompress_detect(ImportCompress *c, const void *data, size_t size) { | |
46 | static const uint8_t xz_signature[] = { | |
47 | 0xfd, '7', 'z', 'X', 'Z', 0x00 | |
48 | }; | |
49 | static const uint8_t gzip_signature[] = { | |
50 | 0x1f, 0x8b | |
51 | }; | |
52 | static const uint8_t bzip2_signature[] = { | |
53 | 'B', 'Z', 'h' | |
54 | }; | |
55 | ||
56 | int r; | |
57 | ||
58 | assert(c); | |
59 | ||
60 | if (c->type != IMPORT_COMPRESS_UNKNOWN) | |
61 | return 1; | |
62 | ||
63 | if (size < MAX3(sizeof(xz_signature), | |
64 | sizeof(gzip_signature), | |
65 | sizeof(bzip2_signature))) | |
66 | return 0; | |
67 | ||
68 | assert(data); | |
69 | ||
70 | if (memcmp(data, xz_signature, sizeof(xz_signature)) == 0) { | |
71 | lzma_ret xzr; | |
72 | ||
73 | xzr = lzma_stream_decoder(&c->xz, UINT64_MAX, LZMA_TELL_UNSUPPORTED_CHECK); | |
74 | if (xzr != LZMA_OK) | |
75 | return -EIO; | |
76 | ||
77 | c->type = IMPORT_COMPRESS_XZ; | |
78 | ||
79 | } else if (memcmp(data, gzip_signature, sizeof(gzip_signature)) == 0) { | |
80 | r = inflateInit2(&c->gzip, 15+16); | |
81 | if (r != Z_OK) | |
82 | return -EIO; | |
83 | ||
84 | c->type = IMPORT_COMPRESS_GZIP; | |
85 | ||
86 | } else if (memcmp(data, bzip2_signature, sizeof(bzip2_signature)) == 0) { | |
87 | r = BZ2_bzDecompressInit(&c->bzip2, 0, 0); | |
88 | if (r != BZ_OK) | |
89 | return -EIO; | |
90 | ||
91 | c->type = IMPORT_COMPRESS_BZIP2; | |
92 | } else | |
93 | c->type = IMPORT_COMPRESS_UNCOMPRESSED; | |
94 | ||
587fec42 LP |
95 | c->encoding = false; |
96 | ||
3e2cda69 LP |
97 | return 1; |
98 | } | |
99 | ||
100 | int import_uncompress(ImportCompress *c, const void *data, size_t size, ImportCompressCallback callback, void *userdata) { | |
101 | int r; | |
102 | ||
103 | assert(c); | |
104 | assert(callback); | |
105 | ||
106 | r = import_uncompress_detect(c, data, size); | |
107 | if (r <= 0) | |
108 | return r; | |
109 | ||
587fec42 LP |
110 | if (c->encoding) |
111 | return -EINVAL; | |
112 | ||
3e2cda69 LP |
113 | if (size <= 0) |
114 | return 1; | |
115 | ||
116 | assert(data); | |
117 | ||
118 | switch (c->type) { | |
119 | ||
120 | case IMPORT_COMPRESS_UNCOMPRESSED: | |
121 | r = callback(data, size, userdata); | |
122 | if (r < 0) | |
123 | return r; | |
124 | ||
125 | break; | |
126 | ||
127 | case IMPORT_COMPRESS_XZ: | |
128 | c->xz.next_in = data; | |
129 | c->xz.avail_in = size; | |
130 | ||
131 | while (c->xz.avail_in > 0) { | |
132 | uint8_t buffer[16 * 1024]; | |
133 | lzma_ret lzr; | |
134 | ||
135 | c->xz.next_out = buffer; | |
136 | c->xz.avail_out = sizeof(buffer); | |
137 | ||
138 | lzr = lzma_code(&c->xz, LZMA_RUN); | |
ec2ce0c5 | 139 | if (!IN_SET(lzr, LZMA_OK, LZMA_STREAM_END)) |
3e2cda69 LP |
140 | return -EIO; |
141 | ||
142 | r = callback(buffer, sizeof(buffer) - c->xz.avail_out, userdata); | |
143 | if (r < 0) | |
144 | return r; | |
145 | } | |
146 | ||
147 | break; | |
148 | ||
149 | case IMPORT_COMPRESS_GZIP: | |
150 | c->gzip.next_in = (void*) data; | |
151 | c->gzip.avail_in = size; | |
152 | ||
153 | while (c->gzip.avail_in > 0) { | |
154 | uint8_t buffer[16 * 1024]; | |
155 | ||
156 | c->gzip.next_out = buffer; | |
157 | c->gzip.avail_out = sizeof(buffer); | |
158 | ||
159 | r = inflate(&c->gzip, Z_NO_FLUSH); | |
ec2ce0c5 | 160 | if (!IN_SET(r, Z_OK, Z_STREAM_END)) |
3e2cda69 LP |
161 | return -EIO; |
162 | ||
163 | r = callback(buffer, sizeof(buffer) - c->gzip.avail_out, userdata); | |
164 | if (r < 0) | |
165 | return r; | |
166 | } | |
167 | ||
168 | break; | |
169 | ||
170 | case IMPORT_COMPRESS_BZIP2: | |
171 | c->bzip2.next_in = (void*) data; | |
172 | c->bzip2.avail_in = size; | |
173 | ||
174 | while (c->bzip2.avail_in > 0) { | |
175 | uint8_t buffer[16 * 1024]; | |
176 | ||
177 | c->bzip2.next_out = (char*) buffer; | |
178 | c->bzip2.avail_out = sizeof(buffer); | |
179 | ||
180 | r = BZ2_bzDecompress(&c->bzip2); | |
ec2ce0c5 | 181 | if (!IN_SET(r, BZ_OK, BZ_STREAM_END)) |
3e2cda69 LP |
182 | return -EIO; |
183 | ||
184 | r = callback(buffer, sizeof(buffer) - c->bzip2.avail_out, userdata); | |
185 | if (r < 0) | |
186 | return r; | |
187 | } | |
188 | ||
189 | break; | |
190 | ||
191 | default: | |
192 | assert_not_reached("Unknown compression"); | |
193 | } | |
194 | ||
195 | return 1; | |
196 | } | |
197 | ||
587fec42 LP |
198 | int import_compress_init(ImportCompress *c, ImportCompressType t) { |
199 | int r; | |
200 | ||
201 | assert(c); | |
202 | ||
203 | switch (t) { | |
204 | ||
205 | case IMPORT_COMPRESS_XZ: { | |
206 | lzma_ret xzr; | |
207 | ||
208 | xzr = lzma_easy_encoder(&c->xz, LZMA_PRESET_DEFAULT, LZMA_CHECK_CRC64); | |
209 | if (xzr != LZMA_OK) | |
210 | return -EIO; | |
211 | ||
212 | c->type = IMPORT_COMPRESS_XZ; | |
213 | break; | |
214 | } | |
215 | ||
216 | case IMPORT_COMPRESS_GZIP: | |
217 | r = deflateInit2(&c->gzip, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 15 + 16, 8, Z_DEFAULT_STRATEGY); | |
218 | if (r != Z_OK) | |
219 | return -EIO; | |
220 | ||
221 | c->type = IMPORT_COMPRESS_GZIP; | |
222 | break; | |
223 | ||
224 | case IMPORT_COMPRESS_BZIP2: | |
225 | r = BZ2_bzCompressInit(&c->bzip2, 9, 0, 0); | |
226 | if (r != BZ_OK) | |
227 | return -EIO; | |
228 | ||
229 | c->type = IMPORT_COMPRESS_BZIP2; | |
230 | break; | |
231 | ||
232 | case IMPORT_COMPRESS_UNCOMPRESSED: | |
233 | c->type = IMPORT_COMPRESS_UNCOMPRESSED; | |
234 | break; | |
235 | ||
236 | default: | |
15411c0c | 237 | return -EOPNOTSUPP; |
587fec42 LP |
238 | } |
239 | ||
240 | c->encoding = true; | |
241 | return 0; | |
242 | } | |
243 | ||
244 | static int enlarge_buffer(void **buffer, size_t *buffer_size, size_t *buffer_allocated) { | |
245 | size_t l; | |
246 | void *p; | |
247 | ||
248 | if (*buffer_allocated > *buffer_size) | |
249 | return 0; | |
250 | ||
251 | l = MAX(16*1024U, (*buffer_size * 2)); | |
252 | p = realloc(*buffer, l); | |
253 | if (!p) | |
254 | return -ENOMEM; | |
255 | ||
256 | *buffer = p; | |
257 | *buffer_allocated = l; | |
258 | ||
259 | return 1; | |
260 | } | |
261 | ||
262 | int import_compress(ImportCompress *c, const void *data, size_t size, void **buffer, size_t *buffer_size, size_t *buffer_allocated) { | |
263 | int r; | |
264 | ||
265 | assert(c); | |
266 | assert(buffer); | |
267 | assert(buffer_size); | |
268 | assert(buffer_allocated); | |
269 | ||
270 | if (!c->encoding) | |
271 | return -EINVAL; | |
272 | ||
273 | if (size <= 0) | |
274 | return 0; | |
275 | ||
276 | assert(data); | |
277 | ||
278 | *buffer_size = 0; | |
279 | ||
280 | switch (c->type) { | |
281 | ||
282 | case IMPORT_COMPRESS_XZ: | |
283 | ||
284 | c->xz.next_in = data; | |
285 | c->xz.avail_in = size; | |
286 | ||
287 | while (c->xz.avail_in > 0) { | |
288 | lzma_ret lzr; | |
289 | ||
290 | r = enlarge_buffer(buffer, buffer_size, buffer_allocated); | |
291 | if (r < 0) | |
292 | return r; | |
293 | ||
294 | c->xz.next_out = (uint8_t*) *buffer + *buffer_size; | |
295 | c->xz.avail_out = *buffer_allocated - *buffer_size; | |
296 | ||
297 | lzr = lzma_code(&c->xz, LZMA_RUN); | |
298 | if (lzr != LZMA_OK) | |
299 | return -EIO; | |
300 | ||
301 | *buffer_size += (*buffer_allocated - *buffer_size) - c->xz.avail_out; | |
302 | } | |
303 | ||
304 | break; | |
305 | ||
306 | case IMPORT_COMPRESS_GZIP: | |
307 | ||
308 | c->gzip.next_in = (void*) data; | |
309 | c->gzip.avail_in = size; | |
310 | ||
311 | while (c->gzip.avail_in > 0) { | |
312 | r = enlarge_buffer(buffer, buffer_size, buffer_allocated); | |
313 | if (r < 0) | |
314 | return r; | |
315 | ||
316 | c->gzip.next_out = (uint8_t*) *buffer + *buffer_size; | |
317 | c->gzip.avail_out = *buffer_allocated - *buffer_size; | |
318 | ||
319 | r = deflate(&c->gzip, Z_NO_FLUSH); | |
320 | if (r != Z_OK) | |
321 | return -EIO; | |
322 | ||
323 | *buffer_size += (*buffer_allocated - *buffer_size) - c->gzip.avail_out; | |
324 | } | |
325 | ||
326 | break; | |
327 | ||
328 | case IMPORT_COMPRESS_BZIP2: | |
329 | ||
330 | c->bzip2.next_in = (void*) data; | |
331 | c->bzip2.avail_in = size; | |
332 | ||
333 | while (c->bzip2.avail_in > 0) { | |
334 | r = enlarge_buffer(buffer, buffer_size, buffer_allocated); | |
335 | if (r < 0) | |
336 | return r; | |
337 | ||
338 | c->bzip2.next_out = (void*) ((uint8_t*) *buffer + *buffer_size); | |
339 | c->bzip2.avail_out = *buffer_allocated - *buffer_size; | |
340 | ||
341 | r = BZ2_bzCompress(&c->bzip2, BZ_RUN); | |
342 | if (r != BZ_RUN_OK) | |
343 | return -EIO; | |
344 | ||
345 | *buffer_size += (*buffer_allocated - *buffer_size) - c->bzip2.avail_out; | |
346 | } | |
347 | ||
348 | break; | |
349 | ||
350 | case IMPORT_COMPRESS_UNCOMPRESSED: | |
351 | ||
352 | if (*buffer_allocated < size) { | |
353 | void *p; | |
354 | ||
355 | p = realloc(*buffer, size); | |
356 | if (!p) | |
357 | return -ENOMEM; | |
358 | ||
359 | *buffer = p; | |
360 | *buffer_allocated = size; | |
361 | } | |
362 | ||
363 | memcpy(*buffer, data, size); | |
364 | *buffer_size = size; | |
365 | break; | |
366 | ||
367 | default: | |
15411c0c | 368 | return -EOPNOTSUPP; |
587fec42 LP |
369 | } |
370 | ||
371 | return 0; | |
372 | } | |
373 | ||
374 | int import_compress_finish(ImportCompress *c, void **buffer, size_t *buffer_size, size_t *buffer_allocated) { | |
375 | int r; | |
376 | ||
377 | assert(c); | |
378 | assert(buffer); | |
379 | assert(buffer_size); | |
380 | assert(buffer_allocated); | |
381 | ||
382 | if (!c->encoding) | |
383 | return -EINVAL; | |
384 | ||
385 | *buffer_size = 0; | |
386 | ||
387 | switch (c->type) { | |
388 | ||
389 | case IMPORT_COMPRESS_XZ: { | |
390 | lzma_ret lzr; | |
391 | ||
392 | c->xz.avail_in = 0; | |
393 | ||
394 | do { | |
395 | r = enlarge_buffer(buffer, buffer_size, buffer_allocated); | |
396 | if (r < 0) | |
397 | return r; | |
398 | ||
399 | c->xz.next_out = (uint8_t*) *buffer + *buffer_size; | |
400 | c->xz.avail_out = *buffer_allocated - *buffer_size; | |
401 | ||
402 | lzr = lzma_code(&c->xz, LZMA_FINISH); | |
ec2ce0c5 | 403 | if (!IN_SET(lzr, LZMA_OK, LZMA_STREAM_END)) |
587fec42 LP |
404 | return -EIO; |
405 | ||
406 | *buffer_size += (*buffer_allocated - *buffer_size) - c->xz.avail_out; | |
407 | } while (lzr != LZMA_STREAM_END); | |
408 | ||
409 | break; | |
410 | } | |
411 | ||
412 | case IMPORT_COMPRESS_GZIP: | |
413 | c->gzip.avail_in = 0; | |
414 | ||
415 | do { | |
416 | r = enlarge_buffer(buffer, buffer_size, buffer_allocated); | |
417 | if (r < 0) | |
418 | return r; | |
419 | ||
420 | c->gzip.next_out = (uint8_t*) *buffer + *buffer_size; | |
421 | c->gzip.avail_out = *buffer_allocated - *buffer_size; | |
422 | ||
423 | r = deflate(&c->gzip, Z_FINISH); | |
ec2ce0c5 | 424 | if (!IN_SET(r, Z_OK, Z_STREAM_END)) |
587fec42 LP |
425 | return -EIO; |
426 | ||
427 | *buffer_size += (*buffer_allocated - *buffer_size) - c->gzip.avail_out; | |
428 | } while (r != Z_STREAM_END); | |
429 | ||
430 | break; | |
431 | ||
432 | case IMPORT_COMPRESS_BZIP2: | |
433 | c->bzip2.avail_in = 0; | |
434 | ||
435 | do { | |
436 | r = enlarge_buffer(buffer, buffer_size, buffer_allocated); | |
437 | if (r < 0) | |
438 | return r; | |
439 | ||
440 | c->bzip2.next_out = (void*) ((uint8_t*) *buffer + *buffer_size); | |
441 | c->bzip2.avail_out = *buffer_allocated - *buffer_size; | |
442 | ||
443 | r = BZ2_bzCompress(&c->bzip2, BZ_FINISH); | |
ec2ce0c5 | 444 | if (!IN_SET(r, BZ_FINISH_OK, BZ_STREAM_END)) |
587fec42 LP |
445 | return -EIO; |
446 | ||
447 | *buffer_size += (*buffer_allocated - *buffer_size) - c->bzip2.avail_out; | |
448 | } while (r != BZ_STREAM_END); | |
449 | ||
450 | break; | |
451 | ||
452 | case IMPORT_COMPRESS_UNCOMPRESSED: | |
453 | break; | |
454 | ||
455 | default: | |
15411c0c | 456 | return -EOPNOTSUPP; |
587fec42 LP |
457 | } |
458 | ||
459 | return 0; | |
460 | } | |
461 | ||
3e2cda69 LP |
462 | static const char* const import_compress_type_table[_IMPORT_COMPRESS_TYPE_MAX] = { |
463 | [IMPORT_COMPRESS_UNKNOWN] = "unknown", | |
464 | [IMPORT_COMPRESS_UNCOMPRESSED] = "uncompressed", | |
465 | [IMPORT_COMPRESS_XZ] = "xz", | |
466 | [IMPORT_COMPRESS_GZIP] = "gzip", | |
467 | [IMPORT_COMPRESS_BZIP2] = "bzip2", | |
468 | }; | |
469 | ||
470 | DEFINE_STRING_TABLE_LOOKUP(import_compress_type, ImportCompressType); |