]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/import/import-compress.c
importd: add API for exporting container/VM images
[thirdparty/systemd.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 -ENOTSUP;
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 -ENOTSUP;
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 -ENOTSUP;
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);