Commit | Line | Data |
---|---|---|
1479465f GJ |
1 | /* |
2 | * libdpkg - Debian packaging suite library routines | |
3 | * compress.c - compression support functions | |
4 | * | |
5 | * Copyright © 2000 Wichert Akkerman <wakkerma@debian.org> | |
6 | * Copyright © 2004 Scott James Remnant <scott@netsplit.com> | |
7 | * Copyright © 2006-2015 Guillem Jover <guillem@debian.org> | |
8 | * | |
9 | * This is free software; you can redistribute it and/or modify | |
10 | * it under the terms of the GNU General Public License as published by | |
11 | * the Free Software Foundation; either version 2 of the License, or | |
12 | * (at your option) any later version. | |
13 | * | |
14 | * This is distributed in the hope that it will be useful, | |
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
17 | * GNU General Public License for more details. | |
18 | * | |
19 | * You should have received a copy of the GNU General Public License | |
20 | * along with this program. If not, see <https://www.gnu.org/licenses/>. | |
21 | */ | |
22 | ||
23 | #include <config.h> | |
24 | #include <compat.h> | |
25 | ||
26 | #include <errno.h> | |
27 | #include <string.h> | |
28 | #include <unistd.h> | |
29 | #include <stdbool.h> | |
30 | #include <stdlib.h> | |
31 | ||
32 | #ifdef WITH_LIBZ | |
33 | #include <zlib.h> | |
34 | #endif | |
35 | #ifdef WITH_LIBLZMA | |
36 | #include <lzma.h> | |
37 | #endif | |
38 | #ifdef WITH_LIBBZ2 | |
39 | #include <bzlib.h> | |
40 | #endif | |
41 | ||
42 | #include <dpkg/i18n.h> | |
43 | #include <dpkg/dpkg.h> | |
44 | #include <dpkg/error.h> | |
45 | #include <dpkg/varbuf.h> | |
46 | #include <dpkg/fdio.h> | |
47 | #include <dpkg/buffer.h> | |
48 | #include <dpkg/command.h> | |
49 | #include <dpkg/compress.h> | |
50 | #if !defined(WITH_LIBZ) || !defined(WITH_LIBLZMA) || !defined(WITH_LIBBZ2) | |
51 | #include <dpkg/subproc.h> | |
52 | ||
53 | static void DPKG_ATTR_SENTINEL | |
54 | fd_fd_filter(int fd_in, int fd_out, const char *desc, const char *delenv[], | |
55 | const char *file, ...) | |
56 | { | |
57 | va_list args; | |
58 | struct command cmd; | |
59 | pid_t pid; | |
60 | int i; | |
61 | ||
62 | pid = subproc_fork(); | |
63 | if (pid == 0) { | |
64 | if (fd_in != 0) { | |
65 | m_dup2(fd_in, 0); | |
66 | close(fd_in); | |
67 | } | |
68 | if (fd_out != 1) { | |
69 | m_dup2(fd_out, 1); | |
70 | close(fd_out); | |
71 | } | |
72 | ||
73 | for (i = 0; delenv[i]; i++) | |
74 | unsetenv(delenv[i]); | |
75 | ||
76 | command_init(&cmd, file, desc); | |
77 | command_add_arg(&cmd, file); | |
78 | va_start(args, file); | |
79 | command_add_argv(&cmd, args); | |
80 | va_end(args); | |
81 | ||
82 | command_exec(&cmd); | |
83 | } | |
84 | subproc_reap(pid, desc, 0); | |
85 | } | |
86 | #endif | |
87 | ||
88 | struct compressor { | |
89 | const char *name; | |
90 | const char *extension; | |
91 | int default_level; | |
92 | void (*fixup_params)(struct compress_params *params); | |
93 | void (*compress)(int fd_in, int fd_out, struct compress_params *params, | |
94 | const char *desc); | |
95 | void (*decompress)(int fd_in, int fd_out, const char *desc); | |
96 | }; | |
97 | ||
98 | /* | |
99 | * No compressor (pass-through). | |
100 | */ | |
101 | ||
102 | static void | |
103 | fixup_none_params(struct compress_params *params) | |
104 | { | |
105 | } | |
106 | ||
107 | static void | |
108 | decompress_none(int fd_in, int fd_out, const char *desc) | |
109 | { | |
110 | struct dpkg_error err; | |
111 | ||
112 | if (fd_fd_copy(fd_in, fd_out, -1, &err) < 0) | |
113 | ohshit(_("%s: pass-through copy error: %s"), desc, err.str); | |
114 | } | |
115 | ||
116 | static void | |
117 | compress_none(int fd_in, int fd_out, struct compress_params *params, const char *desc) | |
118 | { | |
119 | struct dpkg_error err; | |
120 | ||
121 | if (fd_fd_copy(fd_in, fd_out, -1, &err) < 0) | |
122 | ohshit(_("%s: pass-through copy error: %s"), desc, err.str); | |
123 | } | |
124 | ||
125 | static const struct compressor compressor_none = { | |
126 | .name = "none", | |
127 | .extension = "", | |
128 | .default_level = 0, | |
129 | .fixup_params = fixup_none_params, | |
130 | .compress = compress_none, | |
131 | .decompress = decompress_none, | |
132 | }; | |
133 | ||
134 | /* | |
135 | * Gzip compressor. | |
136 | */ | |
137 | ||
138 | #define GZIP "gzip" | |
139 | ||
140 | static void | |
141 | fixup_gzip_params(struct compress_params *params) | |
142 | { | |
143 | /* Normalize compression level. */ | |
144 | if (params->level == 0) | |
145 | params->type = COMPRESSOR_TYPE_NONE; | |
146 | } | |
147 | ||
148 | #ifdef WITH_LIBZ | |
149 | static void | |
150 | decompress_gzip(int fd_in, int fd_out, const char *desc) | |
151 | { | |
152 | char buffer[DPKG_BUFFER_SIZE]; | |
153 | gzFile gzfile = gzdopen(fd_in, "r"); | |
154 | ||
155 | if (gzfile == NULL) | |
156 | ohshit(_("%s: error binding input to gzip stream"), desc); | |
157 | ||
158 | for (;;) { | |
159 | int actualread, actualwrite; | |
160 | ||
161 | actualread = gzread(gzfile, buffer, sizeof(buffer)); | |
162 | if (actualread < 0) { | |
163 | int z_errnum = 0; | |
164 | const char *errmsg = gzerror(gzfile, &z_errnum); | |
165 | ||
166 | if (z_errnum == Z_ERRNO) | |
167 | errmsg = strerror(errno); | |
168 | ohshit(_("%s: internal gzip read error: '%s'"), desc, | |
169 | errmsg); | |
170 | } | |
171 | if (actualread == 0) /* EOF. */ | |
172 | break; | |
173 | ||
174 | actualwrite = fd_write(fd_out, buffer, actualread); | |
175 | if (actualwrite != actualread) | |
176 | ohshite(_("%s: internal gzip write error"), desc); | |
177 | } | |
178 | ||
179 | if (close(fd_out)) | |
180 | ohshite(_("%s: internal gzip write error"), desc); | |
181 | } | |
182 | ||
183 | static void | |
184 | compress_gzip(int fd_in, int fd_out, struct compress_params *params, const char *desc) | |
185 | { | |
186 | char buffer[DPKG_BUFFER_SIZE]; | |
187 | char combuf[6]; | |
188 | int strategy; | |
189 | int z_errnum; | |
190 | gzFile gzfile; | |
191 | ||
192 | if (params->strategy == COMPRESSOR_STRATEGY_FILTERED) | |
193 | strategy = 'f'; | |
194 | else if (params->strategy == COMPRESSOR_STRATEGY_HUFFMAN) | |
195 | strategy = 'h'; | |
196 | else if (params->strategy == COMPRESSOR_STRATEGY_RLE) | |
197 | strategy = 'R'; | |
198 | else if (params->strategy == COMPRESSOR_STRATEGY_FIXED) | |
199 | strategy = 'F'; | |
200 | else | |
201 | strategy = ' '; | |
202 | ||
203 | snprintf(combuf, sizeof(combuf), "w%d%c", params->level, strategy); | |
204 | gzfile = gzdopen(fd_out, combuf); | |
205 | if (gzfile == NULL) | |
206 | ohshit(_("%s: error binding output to gzip stream"), desc); | |
207 | ||
208 | for (;;) { | |
209 | int actualread, actualwrite; | |
210 | ||
211 | actualread = fd_read(fd_in, buffer, sizeof(buffer)); | |
212 | if (actualread < 0) | |
213 | ohshite(_("%s: internal gzip read error"), desc); | |
214 | if (actualread == 0) /* EOF. */ | |
215 | break; | |
216 | ||
217 | actualwrite = gzwrite(gzfile, buffer, actualread); | |
218 | if (actualwrite != actualread) { | |
219 | const char *errmsg = gzerror(gzfile, &z_errnum); | |
220 | ||
221 | if (z_errnum == Z_ERRNO) | |
222 | errmsg = strerror(errno); | |
223 | ohshit(_("%s: internal gzip write error: '%s'"), desc, | |
224 | errmsg); | |
225 | } | |
226 | } | |
227 | ||
228 | z_errnum = gzclose(gzfile); | |
229 | if (z_errnum) { | |
230 | const char *errmsg; | |
231 | ||
232 | if (z_errnum == Z_ERRNO) | |
233 | errmsg = strerror(errno); | |
234 | else | |
235 | errmsg = zError(z_errnum); | |
236 | ohshit(_("%s: internal gzip write error: %s"), desc, errmsg); | |
237 | } | |
238 | } | |
239 | #else | |
240 | static const char *env_gzip[] = { "GZIP", NULL }; | |
241 | ||
242 | static void | |
243 | decompress_gzip(int fd_in, int fd_out, const char *desc) | |
244 | { | |
245 | fd_fd_filter(fd_in, fd_out, desc, env_gzip, GZIP, "-dc", NULL); | |
246 | } | |
247 | ||
248 | static void | |
249 | compress_gzip(int fd_in, int fd_out, struct compress_params *params, const char *desc) | |
250 | { | |
251 | char combuf[6]; | |
252 | ||
253 | snprintf(combuf, sizeof(combuf), "-c%d", params->level); | |
254 | fd_fd_filter(fd_in, fd_out, desc, env_gzip, GZIP, "-n", combuf, NULL); | |
255 | } | |
256 | #endif | |
257 | ||
258 | static const struct compressor compressor_gzip = { | |
259 | .name = "gzip", | |
260 | .extension = ".gz", | |
261 | .default_level = 9, | |
262 | .fixup_params = fixup_gzip_params, | |
263 | .compress = compress_gzip, | |
264 | .decompress = decompress_gzip, | |
265 | }; | |
266 | ||
267 | /* | |
268 | * Bzip2 compressor. | |
269 | */ | |
270 | ||
271 | #define BZIP2 "bzip2" | |
272 | ||
273 | static void | |
274 | fixup_bzip2_params(struct compress_params *params) | |
275 | { | |
276 | /* Normalize compression level. */ | |
277 | if (params->level == 0) | |
278 | params->level = 1; | |
279 | } | |
280 | ||
281 | #ifdef WITH_LIBBZ2 | |
282 | static void | |
283 | decompress_bzip2(int fd_in, int fd_out, const char *desc) | |
284 | { | |
285 | char buffer[DPKG_BUFFER_SIZE]; | |
286 | BZFILE *bzfile = BZ2_bzdopen(fd_in, "r"); | |
287 | ||
288 | if (bzfile == NULL) | |
289 | ohshit(_("%s: error binding input to bzip2 stream"), desc); | |
290 | ||
291 | for (;;) { | |
292 | int actualread, actualwrite; | |
293 | ||
294 | actualread = BZ2_bzread(bzfile, buffer, sizeof(buffer)); | |
295 | if (actualread < 0) { | |
296 | int bz_errnum = 0; | |
297 | const char *errmsg = BZ2_bzerror(bzfile, &bz_errnum); | |
298 | ||
299 | if (bz_errnum == BZ_IO_ERROR) | |
300 | errmsg = strerror(errno); | |
301 | ohshit(_("%s: internal bzip2 read error: '%s'"), desc, | |
302 | errmsg); | |
303 | } | |
304 | if (actualread == 0) /* EOF. */ | |
305 | break; | |
306 | ||
307 | actualwrite = fd_write(fd_out, buffer, actualread); | |
308 | if (actualwrite != actualread) | |
309 | ohshite(_("%s: internal bzip2 write error"), desc); | |
310 | } | |
311 | ||
312 | if (close(fd_out)) | |
313 | ohshite(_("%s: internal bzip2 write error"), desc); | |
314 | } | |
315 | ||
316 | static void | |
317 | compress_bzip2(int fd_in, int fd_out, struct compress_params *params, const char *desc) | |
318 | { | |
319 | char buffer[DPKG_BUFFER_SIZE]; | |
320 | char combuf[6]; | |
321 | int bz_errnum; | |
322 | BZFILE *bzfile; | |
323 | ||
324 | snprintf(combuf, sizeof(combuf), "w%d", params->level); | |
325 | bzfile = BZ2_bzdopen(fd_out, combuf); | |
326 | if (bzfile == NULL) | |
327 | ohshit(_("%s: error binding output to bzip2 stream"), desc); | |
328 | ||
329 | for (;;) { | |
330 | int actualread, actualwrite; | |
331 | ||
332 | actualread = fd_read(fd_in, buffer, sizeof(buffer)); | |
333 | if (actualread < 0) | |
334 | ohshite(_("%s: internal bzip2 read error"), desc); | |
335 | if (actualread == 0) /* EOF. */ | |
336 | break; | |
337 | ||
338 | actualwrite = BZ2_bzwrite(bzfile, buffer, actualread); | |
339 | if (actualwrite != actualread) { | |
340 | const char *errmsg = BZ2_bzerror(bzfile, &bz_errnum); | |
341 | ||
342 | if (bz_errnum == BZ_IO_ERROR) | |
343 | errmsg = strerror(errno); | |
344 | ohshit(_("%s: internal bzip2 write error: '%s'"), desc, | |
345 | errmsg); | |
346 | } | |
347 | } | |
348 | ||
349 | BZ2_bzWriteClose(&bz_errnum, bzfile, 0, NULL, NULL); | |
350 | if (bz_errnum != BZ_OK) { | |
351 | const char *errmsg = _("unexpected bzip2 error"); | |
352 | ||
353 | if (bz_errnum == BZ_IO_ERROR) | |
354 | errmsg = strerror(errno); | |
355 | ohshit(_("%s: internal bzip2 write error: '%s'"), desc, | |
356 | errmsg); | |
357 | } | |
358 | ||
359 | /* Because BZ2_bzWriteClose has done a fflush on the file handle, | |
360 | * doing a close on the file descriptor associated with it should | |
361 | * be safe™. */ | |
362 | if (close(fd_out)) | |
363 | ohshite(_("%s: internal bzip2 write error"), desc); | |
364 | } | |
365 | #else | |
366 | static const char *env_bzip2[] = { "BZIP", "BZIP2", NULL }; | |
367 | ||
368 | static void | |
369 | decompress_bzip2(int fd_in, int fd_out, const char *desc) | |
370 | { | |
371 | fd_fd_filter(fd_in, fd_out, desc, env_bzip2, BZIP2, "-dc", NULL); | |
372 | } | |
373 | ||
374 | static void | |
375 | compress_bzip2(int fd_in, int fd_out, struct compress_params *params, const char *desc) | |
376 | { | |
377 | char combuf[6]; | |
378 | ||
379 | snprintf(combuf, sizeof(combuf), "-c%d", params->level); | |
380 | fd_fd_filter(fd_in, fd_out, desc, env_bzip2, BZIP2, combuf, NULL); | |
381 | } | |
382 | #endif | |
383 | ||
384 | static const struct compressor compressor_bzip2 = { | |
385 | .name = "bzip2", | |
386 | .extension = ".bz2", | |
387 | .default_level = 9, | |
388 | .fixup_params = fixup_bzip2_params, | |
389 | .compress = compress_bzip2, | |
390 | .decompress = decompress_bzip2, | |
391 | }; | |
392 | ||
393 | /* | |
394 | * Xz compressor. | |
395 | */ | |
396 | ||
397 | #define XZ "xz" | |
398 | ||
399 | #ifdef WITH_LIBLZMA | |
400 | enum dpkg_stream_status { | |
401 | DPKG_STREAM_INIT = DPKG_BIT(1), | |
402 | DPKG_STREAM_RUN = DPKG_BIT(2), | |
403 | DPKG_STREAM_COMPRESS = DPKG_BIT(3), | |
404 | DPKG_STREAM_DECOMPRESS = DPKG_BIT(4), | |
405 | DPKG_STREAM_FILTER = DPKG_STREAM_COMPRESS | DPKG_STREAM_DECOMPRESS, | |
406 | }; | |
407 | ||
408 | /* XXX: liblzma does not expose error messages. */ | |
409 | static const char * | |
410 | dpkg_lzma_strerror(lzma_ret code, enum dpkg_stream_status status) | |
411 | { | |
412 | const char *const impossible = _("internal error (bug)"); | |
413 | ||
414 | switch (code) { | |
415 | case LZMA_MEM_ERROR: | |
416 | return strerror(ENOMEM); | |
417 | case LZMA_MEMLIMIT_ERROR: | |
418 | if (status & DPKG_STREAM_RUN) | |
419 | return _("memory usage limit reached"); | |
420 | return impossible; | |
421 | case LZMA_OPTIONS_ERROR: | |
422 | if (status == (DPKG_STREAM_INIT | DPKG_STREAM_COMPRESS)) | |
423 | return _("unsupported compression preset"); | |
424 | if (status == (DPKG_STREAM_RUN | DPKG_STREAM_DECOMPRESS)) | |
425 | return _("unsupported options in file header"); | |
426 | return impossible; | |
427 | case LZMA_DATA_ERROR: | |
428 | if (status & DPKG_STREAM_RUN) | |
429 | return _("compressed data is corrupt"); | |
430 | return impossible; | |
431 | case LZMA_BUF_ERROR: | |
432 | if (status & DPKG_STREAM_RUN) | |
433 | return _("unexpected end of input"); | |
434 | return impossible; | |
435 | case LZMA_FORMAT_ERROR: | |
436 | if (status == (DPKG_STREAM_RUN | DPKG_STREAM_DECOMPRESS)) | |
437 | return _("file format not recognized"); | |
438 | return impossible; | |
439 | case LZMA_UNSUPPORTED_CHECK: | |
440 | if (status == (DPKG_STREAM_INIT | DPKG_STREAM_COMPRESS)) | |
441 | return _("unsupported type of integrity check"); | |
442 | return impossible; | |
443 | default: | |
444 | return impossible; | |
445 | } | |
446 | } | |
447 | ||
448 | struct io_lzma { | |
449 | const char *desc; | |
450 | ||
451 | struct compress_params *params; | |
452 | enum dpkg_stream_status status; | |
453 | lzma_action action; | |
454 | ||
455 | void (*init)(struct io_lzma *io, lzma_stream *s); | |
456 | int (*code)(struct io_lzma *io, lzma_stream *s); | |
457 | void (*done)(struct io_lzma *io, lzma_stream *s); | |
458 | }; | |
459 | ||
460 | static void | |
461 | filter_lzma(struct io_lzma *io, int fd_in, int fd_out) | |
462 | { | |
463 | uint8_t buf_in[DPKG_BUFFER_SIZE]; | |
464 | uint8_t buf_out[DPKG_BUFFER_SIZE]; | |
465 | lzma_stream s = LZMA_STREAM_INIT; | |
466 | lzma_ret ret; | |
467 | ||
468 | s.next_out = buf_out; | |
469 | s.avail_out = sizeof(buf_out); | |
470 | ||
471 | io->action = LZMA_RUN; | |
472 | io->status = DPKG_STREAM_INIT; | |
473 | io->init(io, &s); | |
474 | io->status = (io->status & DPKG_STREAM_FILTER) | DPKG_STREAM_RUN; | |
475 | ||
476 | do { | |
477 | ssize_t len; | |
478 | ||
479 | if (s.avail_in == 0 && io->action != LZMA_FINISH) { | |
480 | len = fd_read(fd_in, buf_in, sizeof(buf_in)); | |
481 | if (len < 0) | |
482 | ohshite(_("%s: lzma read error"), io->desc); | |
483 | if (len == 0) | |
484 | io->action = LZMA_FINISH; | |
485 | s.next_in = buf_in; | |
486 | s.avail_in = len; | |
487 | } | |
488 | ||
489 | ret = io->code(io, &s); | |
490 | ||
491 | if (s.avail_out == 0 || ret == LZMA_STREAM_END) { | |
492 | len = fd_write(fd_out, buf_out, s.next_out - buf_out); | |
493 | if (len < 0) | |
494 | ohshite(_("%s: lzma write error"), io->desc); | |
495 | s.next_out = buf_out; | |
496 | s.avail_out = sizeof(buf_out); | |
497 | } | |
498 | } while (ret != LZMA_STREAM_END); | |
499 | ||
500 | io->done(io, &s); | |
501 | ||
502 | if (close(fd_out)) | |
503 | ohshite(_("%s: lzma close error"), io->desc); | |
504 | } | |
505 | ||
506 | static void DPKG_ATTR_NORET | |
507 | filter_lzma_error(struct io_lzma *io, lzma_ret ret) | |
508 | { | |
509 | ohshit(_("%s: lzma error: %s"), io->desc, | |
510 | dpkg_lzma_strerror(ret, io->status)); | |
511 | } | |
512 | ||
513 | static void | |
514 | filter_unxz_init(struct io_lzma *io, lzma_stream *s) | |
515 | { | |
516 | uint64_t memlimit = UINT64_MAX; | |
517 | lzma_ret ret; | |
518 | ||
519 | io->status |= DPKG_STREAM_DECOMPRESS; | |
520 | ||
521 | ret = lzma_stream_decoder(s, memlimit, 0); | |
522 | if (ret != LZMA_OK) | |
523 | filter_lzma_error(io, ret); | |
524 | } | |
525 | ||
526 | static void | |
527 | filter_xz_init(struct io_lzma *io, lzma_stream *s) | |
528 | { | |
529 | uint32_t preset; | |
530 | lzma_check check = LZMA_CHECK_CRC64; | |
531 | #ifdef HAVE_LZMA_MT | |
532 | uint64_t mt_memlimit; | |
533 | lzma_mt mt_options = { | |
534 | .flags = 0, | |
535 | .block_size = 0, | |
536 | .timeout = 0, | |
537 | .filters = NULL, | |
538 | .check = check, | |
539 | }; | |
540 | #endif | |
541 | lzma_ret ret; | |
542 | ||
543 | io->status |= DPKG_STREAM_COMPRESS; | |
544 | ||
545 | preset = io->params->level; | |
546 | if (io->params->strategy == COMPRESSOR_STRATEGY_EXTREME) | |
547 | preset |= LZMA_PRESET_EXTREME; | |
548 | ||
549 | #ifdef HAVE_LZMA_MT | |
550 | mt_options.preset = preset; | |
551 | ||
552 | /* Initialize the multi-threaded memory limit to half the physical | |
553 | * RAM, or to 128 MiB if we cannot infer the number. */ | |
554 | mt_memlimit = lzma_physmem() / 2; | |
555 | if (mt_memlimit == 0) | |
556 | mt_memlimit = 128 * 1024 * 1024; | |
557 | /* Clamp the multi-threaded memory limit to half the addressable | |
558 | * memory on this architecture. */ | |
559 | if (mt_memlimit > INTPTR_MAX) | |
560 | mt_memlimit = INTPTR_MAX; | |
561 | ||
562 | mt_options.threads = lzma_cputhreads(); | |
563 | if (mt_options.threads == 0) | |
564 | mt_options.threads = 1; | |
565 | ||
566 | /* Guess whether we have enough RAM to use the multi-threaded encoder, | |
567 | * and decrease them up to single-threaded to reduce memory usage. */ | |
568 | for (; mt_options.threads > 1; mt_options.threads--) { | |
569 | uint64_t mt_memusage; | |
570 | ||
571 | mt_memusage = lzma_stream_encoder_mt_memusage(&mt_options); | |
572 | if (mt_memusage < mt_memlimit) | |
573 | break; | |
574 | } | |
575 | ||
576 | ret = lzma_stream_encoder_mt(s, &mt_options); | |
577 | #else | |
578 | ret = lzma_easy_encoder(s, preset, check); | |
579 | #endif | |
580 | ||
581 | if (ret != LZMA_OK) | |
582 | filter_lzma_error(io, ret); | |
583 | } | |
584 | ||
585 | static int | |
586 | filter_lzma_code(struct io_lzma *io, lzma_stream *s) | |
587 | { | |
588 | lzma_ret ret; | |
589 | ||
590 | ret = lzma_code(s, io->action); | |
591 | if (ret != LZMA_OK && ret != LZMA_STREAM_END) | |
592 | filter_lzma_error(io, ret); | |
593 | ||
594 | return ret; | |
595 | } | |
596 | ||
597 | static void | |
598 | filter_lzma_done(struct io_lzma *io, lzma_stream *s) | |
599 | { | |
600 | lzma_end(s); | |
601 | } | |
602 | ||
603 | static void | |
604 | decompress_xz(int fd_in, int fd_out, const char *desc) | |
605 | { | |
606 | struct io_lzma io; | |
607 | ||
608 | io.init = filter_unxz_init; | |
609 | io.code = filter_lzma_code; | |
610 | io.done = filter_lzma_done; | |
611 | io.desc = desc; | |
612 | ||
613 | filter_lzma(&io, fd_in, fd_out); | |
614 | } | |
615 | ||
616 | static void | |
617 | compress_xz(int fd_in, int fd_out, struct compress_params *params, const char *desc) | |
618 | { | |
619 | struct io_lzma io; | |
620 | ||
621 | io.init = filter_xz_init; | |
622 | io.code = filter_lzma_code; | |
623 | io.done = filter_lzma_done; | |
624 | io.desc = desc; | |
625 | io.params = params; | |
626 | ||
627 | filter_lzma(&io, fd_in, fd_out); | |
628 | } | |
629 | #else | |
630 | static const char *env_xz[] = { "XZ_DEFAULTS", "XZ_OPT", NULL }; | |
631 | ||
632 | static void | |
633 | decompress_xz(int fd_in, int fd_out, const char *desc) | |
634 | { | |
635 | fd_fd_filter(fd_in, fd_out, desc, env_xz, XZ, "-dc", NULL); | |
636 | } | |
637 | ||
638 | static void | |
639 | compress_xz(int fd_in, int fd_out, struct compress_params *params, const char *desc) | |
640 | { | |
641 | char combuf[6]; | |
642 | const char *strategy; | |
643 | ||
644 | if (params->strategy == COMPRESSOR_STRATEGY_EXTREME) | |
645 | strategy = "-e"; | |
646 | else | |
647 | strategy = NULL; | |
648 | ||
649 | snprintf(combuf, sizeof(combuf), "-c%d", params->level); | |
650 | fd_fd_filter(fd_in, fd_out, desc, env_xz, XZ, combuf, strategy, NULL); | |
651 | } | |
652 | #endif | |
653 | ||
654 | static const struct compressor compressor_xz = { | |
655 | .name = "xz", | |
656 | .extension = ".xz", | |
657 | .default_level = 6, | |
658 | .fixup_params = fixup_none_params, | |
659 | .compress = compress_xz, | |
660 | .decompress = decompress_xz, | |
661 | }; | |
662 | ||
663 | /* | |
664 | * Lzma compressor. | |
665 | */ | |
666 | ||
667 | #ifdef WITH_LIBLZMA | |
668 | static void | |
669 | filter_unlzma_init(struct io_lzma *io, lzma_stream *s) | |
670 | { | |
671 | uint64_t memlimit = UINT64_MAX; | |
672 | lzma_ret ret; | |
673 | ||
674 | io->status |= DPKG_STREAM_DECOMPRESS; | |
675 | ||
676 | ret = lzma_alone_decoder(s, memlimit); | |
677 | if (ret != LZMA_OK) | |
678 | filter_lzma_error(io, ret); | |
679 | } | |
680 | ||
681 | static void | |
682 | filter_lzma_init(struct io_lzma *io, lzma_stream *s) | |
683 | { | |
684 | uint32_t preset; | |
685 | lzma_options_lzma options; | |
686 | lzma_ret ret; | |
687 | ||
688 | io->status |= DPKG_STREAM_COMPRESS; | |
689 | ||
690 | preset = io->params->level; | |
691 | if (io->params->strategy == COMPRESSOR_STRATEGY_EXTREME) | |
692 | preset |= LZMA_PRESET_EXTREME; | |
693 | if (lzma_lzma_preset(&options, preset)) | |
694 | filter_lzma_error(io, LZMA_OPTIONS_ERROR); | |
695 | ||
696 | ret = lzma_alone_encoder(s, &options); | |
697 | if (ret != LZMA_OK) | |
698 | filter_lzma_error(io, ret); | |
699 | } | |
700 | ||
701 | static void | |
702 | decompress_lzma(int fd_in, int fd_out, const char *desc) | |
703 | { | |
704 | struct io_lzma io; | |
705 | ||
706 | io.init = filter_unlzma_init; | |
707 | io.code = filter_lzma_code; | |
708 | io.done = filter_lzma_done; | |
709 | io.desc = desc; | |
710 | ||
711 | filter_lzma(&io, fd_in, fd_out); | |
712 | } | |
713 | ||
714 | static void | |
715 | compress_lzma(int fd_in, int fd_out, struct compress_params *params, const char *desc) | |
716 | { | |
717 | struct io_lzma io; | |
718 | ||
719 | io.init = filter_lzma_init; | |
720 | io.code = filter_lzma_code; | |
721 | io.done = filter_lzma_done; | |
722 | io.desc = desc; | |
723 | io.params = params; | |
724 | ||
725 | filter_lzma(&io, fd_in, fd_out); | |
726 | } | |
727 | #else | |
728 | static void | |
729 | decompress_lzma(int fd_in, int fd_out, const char *desc) | |
730 | { | |
731 | fd_fd_filter(fd_in, fd_out, desc, env_xz, XZ, "-dc", "--format=lzma", NULL); | |
732 | } | |
733 | ||
734 | static void | |
735 | compress_lzma(int fd_in, int fd_out, struct compress_params *params, const char *desc) | |
736 | { | |
737 | char combuf[6]; | |
738 | ||
739 | snprintf(combuf, sizeof(combuf), "-c%d", params->level); | |
740 | fd_fd_filter(fd_in, fd_out, desc, env_xz, XZ, combuf, "--format=lzma", NULL); | |
741 | } | |
742 | #endif | |
743 | ||
744 | static const struct compressor compressor_lzma = { | |
745 | .name = "lzma", | |
746 | .extension = ".lzma", | |
747 | .default_level = 6, | |
748 | .fixup_params = fixup_none_params, | |
749 | .compress = compress_lzma, | |
750 | .decompress = decompress_lzma, | |
751 | }; | |
752 | ||
753 | /* | |
754 | * Generic compressor filter. | |
755 | */ | |
756 | ||
757 | static const struct compressor *compressor_array[] = { | |
758 | [COMPRESSOR_TYPE_NONE] = &compressor_none, | |
759 | [COMPRESSOR_TYPE_GZIP] = &compressor_gzip, | |
760 | [COMPRESSOR_TYPE_XZ] = &compressor_xz, | |
761 | [COMPRESSOR_TYPE_BZIP2] = &compressor_bzip2, | |
762 | [COMPRESSOR_TYPE_LZMA] = &compressor_lzma, | |
763 | }; | |
764 | ||
765 | static const struct compressor * | |
766 | compressor(enum compressor_type type) | |
767 | { | |
768 | const enum compressor_type max_type = array_count(compressor_array); | |
769 | ||
770 | if (type < 0 || type >= max_type) | |
771 | internerr("compressor_type %d is out of range", type); | |
772 | ||
773 | return compressor_array[type]; | |
774 | } | |
775 | ||
776 | const char * | |
777 | compressor_get_name(enum compressor_type type) | |
778 | { | |
779 | return compressor(type)->name; | |
780 | } | |
781 | ||
782 | const char * | |
783 | compressor_get_extension(enum compressor_type type) | |
784 | { | |
785 | return compressor(type)->extension; | |
786 | } | |
787 | ||
788 | enum compressor_type | |
789 | compressor_find_by_name(const char *name) | |
790 | { | |
791 | size_t i; | |
792 | ||
793 | for (i = 0; i < array_count(compressor_array); i++) | |
794 | if (strcmp(compressor_array[i]->name, name) == 0) | |
795 | return i; | |
796 | ||
797 | return COMPRESSOR_TYPE_UNKNOWN; | |
798 | } | |
799 | ||
800 | enum compressor_type | |
801 | compressor_find_by_extension(const char *extension) | |
802 | { | |
803 | size_t i; | |
804 | ||
805 | for (i = 0; i < array_count(compressor_array); i++) | |
806 | if (strcmp(compressor_array[i]->extension, extension) == 0) | |
807 | return i; | |
808 | ||
809 | return COMPRESSOR_TYPE_UNKNOWN; | |
810 | } | |
811 | ||
812 | enum compressor_strategy | |
813 | compressor_get_strategy(const char *name) | |
814 | { | |
815 | if (strcmp(name, "none") == 0) | |
816 | return COMPRESSOR_STRATEGY_NONE; | |
817 | if (strcmp(name, "filtered") == 0) | |
818 | return COMPRESSOR_STRATEGY_FILTERED; | |
819 | if (strcmp(name, "huffman") == 0) | |
820 | return COMPRESSOR_STRATEGY_HUFFMAN; | |
821 | if (strcmp(name, "rle") == 0) | |
822 | return COMPRESSOR_STRATEGY_RLE; | |
823 | if (strcmp(name, "fixed") == 0) | |
824 | return COMPRESSOR_STRATEGY_FIXED; | |
825 | if (strcmp(name, "extreme") == 0) | |
826 | return COMPRESSOR_STRATEGY_EXTREME; | |
827 | ||
828 | return COMPRESSOR_STRATEGY_UNKNOWN; | |
829 | } | |
830 | ||
831 | static void | |
832 | compressor_fixup_params(struct compress_params *params) | |
833 | { | |
834 | compressor(params->type)->fixup_params(params); | |
835 | ||
836 | if (params->level < 0) | |
837 | params->level = compressor(params->type)->default_level; | |
838 | } | |
839 | ||
840 | bool | |
841 | compressor_check_params(struct compress_params *params, struct dpkg_error *err) | |
842 | { | |
843 | compressor_fixup_params(params); | |
844 | ||
845 | if (params->strategy == COMPRESSOR_STRATEGY_NONE) | |
846 | return true; | |
847 | ||
848 | if (params->type == COMPRESSOR_TYPE_GZIP && | |
849 | (params->strategy == COMPRESSOR_STRATEGY_FILTERED || | |
850 | params->strategy == COMPRESSOR_STRATEGY_HUFFMAN || | |
851 | params->strategy == COMPRESSOR_STRATEGY_RLE || | |
852 | params->strategy == COMPRESSOR_STRATEGY_FIXED)) | |
853 | return true; | |
854 | ||
855 | if (params->type == COMPRESSOR_TYPE_XZ && | |
856 | params->strategy == COMPRESSOR_STRATEGY_EXTREME) | |
857 | return true; | |
858 | ||
859 | dpkg_put_error(err, _("unknown compression strategy")); | |
860 | return false; | |
861 | } | |
862 | ||
863 | void | |
864 | decompress_filter(enum compressor_type type, int fd_in, int fd_out, | |
865 | const char *desc_fmt, ...) | |
866 | { | |
867 | va_list args; | |
868 | struct varbuf desc = VARBUF_INIT; | |
869 | ||
870 | va_start(args, desc_fmt); | |
871 | varbuf_vprintf(&desc, desc_fmt, args); | |
872 | va_end(args); | |
873 | ||
874 | compressor(type)->decompress(fd_in, fd_out, desc.buf); | |
875 | ||
876 | varbuf_destroy(&desc); | |
877 | } | |
878 | ||
879 | void | |
880 | compress_filter(struct compress_params *params, int fd_in, int fd_out, | |
881 | const char *desc_fmt, ...) | |
882 | { | |
883 | va_list args; | |
884 | struct varbuf desc = VARBUF_INIT; | |
885 | ||
886 | va_start(args, desc_fmt); | |
887 | varbuf_vprintf(&desc, desc_fmt, args); | |
888 | va_end(args); | |
889 | ||
890 | compressor(params->type)->compress(fd_in, fd_out, params, desc.buf); | |
891 | ||
892 | varbuf_destroy(&desc); | |
893 | } |