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