chiark / gitweb /
d6b81330361199d9f3aef903831d20ff1756f0f1
[elogind.git] / src / import / import-compress.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2015 Lennart Poettering
7
8   systemd is free software; you can redistribute it and/or modify it
9   under the terms of the GNU Lesser General Public License as published by
10   the Free Software Foundation; either version 2.1 of the License, or
11   (at your option) any later version.
12
13   systemd is distributed in the hope that it will be useful, but
14   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   You should have received a copy of the GNU Lesser General Public License
19   along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include "util.h"
23 #include "import-compress.h"
24
25 void import_compress_free(ImportCompress *c) {
26         assert(c);
27
28         if (c->type == IMPORT_COMPRESS_XZ)
29                 lzma_end(&c->xz);
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         }
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
95         c->encoding = false;
96
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
110         if (c->encoding)
111                 return -EINVAL;
112
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);
139                         if (lzr != LZMA_OK && lzr != LZMA_STREAM_END)
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);
160                         if (r != Z_OK && r != Z_STREAM_END)
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);
181                         if (r != BZ_OK && r != BZ_STREAM_END)
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
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:
237                 return -EOPNOTSUPP;
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:
368                 return -EOPNOTSUPP;
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);
403                         if (lzr != LZMA_OK && lzr != LZMA_STREAM_END)
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);
424                         if (r != Z_OK && r != Z_STREAM_END)
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);
444                         if (r != BZ_FINISH_OK && r != BZ_STREAM_END)
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:
456                 return -EOPNOTSUPP;
457         }
458
459         return 0;
460 }
461
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);