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