Branch data Line data Source code
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 : : #if USE_LIBZ_IMPL != USE_LIBZ_IMPL_NONE
33 : : #include <compat-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/meminfo.h>
49 : : #include <dpkg/command.h>
50 : : #include <dpkg/compress.h>
51 : : #if USE_LIBZ_IMPL == USE_LIBZ_IMPL_NONE || \
52 : : !defined(WITH_LIBLZMA) || \
53 : : !defined(WITH_LIBBZ2)
54 : : #include <dpkg/subproc.h>
55 : :
56 : : static void
57 : : fd_fd_filter(struct command *cmd, int fd_in, int fd_out, const char *delenv[])
58 : : {
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_exec(cmd);
77 : : }
78 : : subproc_reap(pid, cmd->name, 0);
79 : : }
80 : :
81 : : static void
82 : : command_compress_init(struct command *cmd, const char *name, const char *desc,
83 : : int level)
84 : : {
85 : : static char combuf[6];
86 : :
87 : : command_init(cmd, name, desc);
88 : : command_add_arg(cmd, name);
89 : :
90 : : snprintf(combuf, sizeof(combuf), "-c%d", level);
91 : : command_add_arg(cmd, combuf);
92 : : }
93 : :
94 : : static void
95 : : command_decompress_init(struct command *cmd, const char *name, const char *desc)
96 : : {
97 : : command_init(cmd, name, desc);
98 : : command_add_arg(cmd, name);
99 : : command_add_arg(cmd, "-dc");
100 : : }
101 : : #endif
102 : :
103 : : struct compressor {
104 : : const char *name;
105 : : const char *extension;
106 : : int default_level;
107 : : void (*fixup_params)(struct compress_params *params);
108 : : void (*compress)(struct compress_params *params,
109 : : int fd_in, int fd_out, const char *desc);
110 : : void (*decompress)(struct compress_params *params,
111 : : int fd_in, int fd_out, const char *desc);
112 : : };
113 : :
114 : : /*
115 : : * No compressor (pass-through).
116 : : */
117 : :
118 : : static void
119 : 58 : fixup_none_params(struct compress_params *params)
120 : : {
121 : 58 : }
122 : :
123 : : static void
124 : 7 : decompress_none(struct compress_params *params, int fd_in, int fd_out,
125 : : const char *desc)
126 : : {
127 : : struct dpkg_error err;
128 : :
129 [ - + ]: 7 : if (fd_fd_copy(fd_in, fd_out, -1, &err) < 0)
130 : 0 : ohshit(_("%s: pass-through copy error: %s"), desc, err.str);
131 : 7 : }
132 : :
133 : : static void
134 : 4 : compress_none(struct compress_params *params, int fd_in, int fd_out,
135 : : const char *desc)
136 : : {
137 : : struct dpkg_error err;
138 : :
139 [ - + ]: 4 : if (fd_fd_copy(fd_in, fd_out, -1, &err) < 0)
140 : 0 : ohshit(_("%s: pass-through copy error: %s"), desc, err.str);
141 : 4 : }
142 : :
143 : : static const struct compressor compressor_none = {
144 : : .name = "none",
145 : : .extension = "",
146 : : .default_level = 0,
147 : : .fixup_params = fixup_none_params,
148 : : .compress = compress_none,
149 : : .decompress = decompress_none,
150 : : };
151 : :
152 : : /*
153 : : * Gzip compressor.
154 : : */
155 : :
156 : : #define GZIP "gzip"
157 : :
158 : : static void
159 : 1 : fixup_gzip_params(struct compress_params *params)
160 : : {
161 : : /* Normalize compression level. */
162 [ - + ]: 1 : if (params->level == 0)
163 : 0 : params->type = COMPRESSOR_TYPE_NONE;
164 : 1 : }
165 : :
166 : : #if USE_LIBZ_IMPL != USE_LIBZ_IMPL_NONE
167 : : static void
168 : 11 : decompress_gzip(struct compress_params *params, int fd_in, int fd_out,
169 : : const char *desc)
170 : : {
171 : : char *buffer;
172 : 11 : size_t bufsize = DPKG_BUFFER_SIZE;
173 : : int z_errnum;
174 : 11 : gzFile gzfile = gzdopen(fd_in, "r");
175 : :
176 [ - + ]: 11 : if (gzfile == NULL)
177 : 0 : ohshit(_("%s: error binding input to gzip stream"), desc);
178 : :
179 : 11 : buffer = m_malloc(bufsize);
180 : :
181 : 11 : for (;;) {
182 : : int actualread, actualwrite;
183 : :
184 : 22 : actualread = gzread(gzfile, buffer, bufsize);
185 [ - + ]: 22 : if (actualread < 0) {
186 : 0 : const char *errmsg = gzerror(gzfile, &z_errnum);
187 : :
188 [ # # ]: 0 : if (z_errnum == Z_ERRNO)
189 : 0 : errmsg = strerror(errno);
190 : 0 : ohshit(_("%s: internal gzip read error: '%s'"), desc,
191 : : errmsg);
192 : : }
193 [ + + ]: 22 : if (actualread == 0) /* EOF. */
194 : 11 : break;
195 : :
196 : 11 : actualwrite = fd_write(fd_out, buffer, actualread);
197 [ - + ]: 11 : if (actualwrite != actualread)
198 : 0 : ohshite(_("%s: internal gzip write error"), desc);
199 : : }
200 : :
201 : 11 : free(buffer);
202 : :
203 : 11 : z_errnum = gzclose(gzfile);
204 [ - + ]: 11 : if (z_errnum) {
205 : : const char *errmsg;
206 : :
207 [ # # ]: 0 : if (z_errnum == Z_ERRNO)
208 : 0 : errmsg = strerror(errno);
209 : : else
210 : 0 : errmsg = zError(z_errnum);
211 : 0 : ohshit(_("%s: internal gzip read error: %s"), desc, errmsg);
212 : : }
213 : :
214 [ - + ]: 11 : if (close(fd_out))
215 : 0 : ohshite(_("%s: internal gzip write error"), desc);
216 : 11 : }
217 : :
218 : : static void
219 : 2 : compress_gzip(struct compress_params *params, int fd_in, int fd_out,
220 : : const char *desc)
221 : : {
222 : : char *buffer;
223 : : char combuf[6];
224 : 2 : size_t bufsize = DPKG_BUFFER_SIZE;
225 : : int strategy;
226 : : int z_errnum;
227 : : gzFile gzfile;
228 : :
229 [ - + ]: 2 : if (params->strategy == COMPRESSOR_STRATEGY_FILTERED)
230 : 0 : strategy = 'f';
231 [ - + ]: 2 : else if (params->strategy == COMPRESSOR_STRATEGY_HUFFMAN)
232 : 0 : strategy = 'h';
233 [ - + ]: 2 : else if (params->strategy == COMPRESSOR_STRATEGY_RLE)
234 : 0 : strategy = 'R';
235 [ - + ]: 2 : else if (params->strategy == COMPRESSOR_STRATEGY_FIXED)
236 : 0 : strategy = 'F';
237 : : else
238 : 2 : strategy = ' ';
239 : :
240 : 2 : snprintf(combuf, sizeof(combuf), "w%d%c", params->level, strategy);
241 : 2 : gzfile = gzdopen(fd_out, combuf);
242 [ - + ]: 2 : if (gzfile == NULL)
243 : 0 : ohshit(_("%s: error binding output to gzip stream"), desc);
244 : :
245 : 2 : buffer = m_malloc(bufsize);
246 : :
247 : 2 : for (;;) {
248 : : int actualread, actualwrite;
249 : :
250 : 4 : actualread = fd_read(fd_in, buffer, bufsize);
251 [ - + ]: 4 : if (actualread < 0)
252 : 0 : ohshite(_("%s: internal gzip read error"), desc);
253 [ + + ]: 4 : if (actualread == 0) /* EOF. */
254 : 2 : break;
255 : :
256 : 2 : actualwrite = gzwrite(gzfile, buffer, actualread);
257 [ - + ]: 2 : if (actualwrite != actualread) {
258 : 0 : const char *errmsg = gzerror(gzfile, &z_errnum);
259 : :
260 [ # # ]: 0 : if (z_errnum == Z_ERRNO)
261 : 0 : errmsg = strerror(errno);
262 : 0 : ohshit(_("%s: internal gzip write error: '%s'"), desc,
263 : : errmsg);
264 : : }
265 : : }
266 : :
267 : 2 : free(buffer);
268 : :
269 : 2 : z_errnum = gzclose(gzfile);
270 [ - + ]: 2 : if (z_errnum) {
271 : : const char *errmsg;
272 : :
273 [ # # ]: 0 : if (z_errnum == Z_ERRNO)
274 : 0 : errmsg = strerror(errno);
275 : : else
276 : 0 : errmsg = zError(z_errnum);
277 : 0 : ohshit(_("%s: internal gzip write error: %s"), desc, errmsg);
278 : : }
279 : 2 : }
280 : : #else
281 : : static const char *env_gzip[] = { "GZIP", NULL };
282 : :
283 : : static void
284 : : decompress_gzip(struct compress_params *params, int fd_in, int fd_out,
285 : : const char *desc)
286 : : {
287 : : struct command cmd;
288 : :
289 : : command_decompress_init(&cmd, GZIP, desc);
290 : :
291 : : fd_fd_filter(&cmd, fd_in, fd_out, env_gzip);
292 : :
293 : : command_destroy(&cmd);
294 : : }
295 : :
296 : : static void
297 : : compress_gzip(struct compress_params *params, int fd_in, int fd_out,
298 : : const char *desc)
299 : : {
300 : : struct command cmd;
301 : :
302 : : command_compress_init(&cmd, GZIP, desc, params->level);
303 : : command_add_arg(&cmd, "-n");
304 : :
305 : : fd_fd_filter(&cmd, fd_in, fd_out, env_gzip);
306 : :
307 : : command_destroy(&cmd);
308 : : }
309 : : #endif
310 : :
311 : : static const struct compressor compressor_gzip = {
312 : : .name = "gzip",
313 : : .extension = ".gz",
314 : : .default_level = 9,
315 : : .fixup_params = fixup_gzip_params,
316 : : .compress = compress_gzip,
317 : : .decompress = decompress_gzip,
318 : : };
319 : :
320 : : /*
321 : : * Bzip2 compressor.
322 : : */
323 : :
324 : : #define BZIP2 "bzip2"
325 : :
326 : : static void
327 : 0 : fixup_bzip2_params(struct compress_params *params)
328 : : {
329 : : /* Normalize compression level. */
330 [ # # ]: 0 : if (params->level == 0)
331 : 0 : params->level = 1;
332 : 0 : }
333 : :
334 : : #ifdef WITH_LIBBZ2
335 : : static void
336 : 1 : decompress_bzip2(struct compress_params *params, int fd_in, int fd_out,
337 : : const char *desc)
338 : : {
339 : : char *buffer;
340 : 1 : size_t bufsize = DPKG_BUFFER_SIZE;
341 : 1 : BZFILE *bzfile = BZ2_bzdopen(fd_in, "r");
342 : :
343 [ - + ]: 1 : if (bzfile == NULL)
344 : 0 : ohshit(_("%s: error binding input to bzip2 stream"), desc);
345 : :
346 : 1 : buffer = m_malloc(bufsize);
347 : :
348 : 1 : for (;;) {
349 : : int actualread, actualwrite;
350 : :
351 : 2 : actualread = BZ2_bzread(bzfile, buffer, bufsize);
352 [ - + ]: 2 : if (actualread < 0) {
353 : 0 : int bz_errnum = 0;
354 : 0 : const char *errmsg = BZ2_bzerror(bzfile, &bz_errnum);
355 : :
356 [ # # ]: 0 : if (bz_errnum == BZ_IO_ERROR)
357 : 0 : errmsg = strerror(errno);
358 : 0 : ohshit(_("%s: internal bzip2 read error: '%s'"), desc,
359 : : errmsg);
360 : : }
361 [ + + ]: 2 : if (actualread == 0) /* EOF. */
362 : 1 : break;
363 : :
364 : 1 : actualwrite = fd_write(fd_out, buffer, actualread);
365 [ - + ]: 1 : if (actualwrite != actualread)
366 : 0 : ohshite(_("%s: internal bzip2 write error"), desc);
367 : : }
368 : :
369 : 1 : free(buffer);
370 : :
371 : 1 : BZ2_bzclose(bzfile);
372 : :
373 [ - + ]: 1 : if (close(fd_out))
374 : 0 : ohshite(_("%s: internal bzip2 write error"), desc);
375 : 1 : }
376 : :
377 : : static void
378 : 0 : compress_bzip2(struct compress_params *params, int fd_in, int fd_out,
379 : : const char *desc)
380 : : {
381 : : char *buffer;
382 : : char combuf[6];
383 : 0 : size_t bufsize = DPKG_BUFFER_SIZE;
384 : : int bz_errnum;
385 : : BZFILE *bzfile;
386 : :
387 : 0 : snprintf(combuf, sizeof(combuf), "w%d", params->level);
388 : 0 : bzfile = BZ2_bzdopen(fd_out, combuf);
389 [ # # ]: 0 : if (bzfile == NULL)
390 : 0 : ohshit(_("%s: error binding output to bzip2 stream"), desc);
391 : :
392 : 0 : buffer = m_malloc(bufsize);
393 : :
394 : 0 : for (;;) {
395 : : int actualread, actualwrite;
396 : :
397 : 0 : actualread = fd_read(fd_in, buffer, bufsize);
398 [ # # ]: 0 : if (actualread < 0)
399 : 0 : ohshite(_("%s: internal bzip2 read error"), desc);
400 [ # # ]: 0 : if (actualread == 0) /* EOF. */
401 : 0 : break;
402 : :
403 : 0 : actualwrite = BZ2_bzwrite(bzfile, buffer, actualread);
404 [ # # ]: 0 : if (actualwrite != actualread) {
405 : 0 : const char *errmsg = BZ2_bzerror(bzfile, &bz_errnum);
406 : :
407 [ # # ]: 0 : if (bz_errnum == BZ_IO_ERROR)
408 : 0 : errmsg = strerror(errno);
409 : 0 : ohshit(_("%s: internal bzip2 write error: '%s'"), desc,
410 : : errmsg);
411 : : }
412 : : }
413 : :
414 : 0 : free(buffer);
415 : :
416 : 0 : BZ2_bzWriteClose(&bz_errnum, bzfile, 0, NULL, NULL);
417 [ # # ]: 0 : if (bz_errnum != BZ_OK) {
418 : 0 : const char *errmsg = _("unexpected bzip2 error");
419 : :
420 [ # # ]: 0 : if (bz_errnum == BZ_IO_ERROR)
421 : 0 : errmsg = strerror(errno);
422 : 0 : ohshit(_("%s: internal bzip2 write error: '%s'"), desc,
423 : : errmsg);
424 : : }
425 : :
426 : : /* Because BZ2_bzWriteClose has done a fflush on the file handle,
427 : : * doing a close on the file descriptor associated with it should
428 : : * be safe™. */
429 [ # # ]: 0 : if (close(fd_out))
430 : 0 : ohshite(_("%s: internal bzip2 write error"), desc);
431 : 0 : }
432 : : #else
433 : : static const char *env_bzip2[] = { "BZIP", "BZIP2", NULL };
434 : :
435 : : static void
436 : : decompress_bzip2(struct compress_params *params, int fd_in, int fd_out,
437 : : const char *desc)
438 : : {
439 : : struct command cmd;
440 : :
441 : : command_decompress_init(&cmd, BZIP2, desc);
442 : :
443 : : fd_fd_filter(&cmd, fd_in, fd_out, env_bzip2);
444 : :
445 : : command_destroy(&cmd);
446 : : }
447 : :
448 : : static void
449 : : compress_bzip2(struct compress_params *params, int fd_in, int fd_out,
450 : : const char *desc)
451 : : {
452 : : struct command cmd;
453 : :
454 : : command_compress_init(&cmd, BZIP2, desc, params->level);
455 : :
456 : : fd_fd_filter(&cmd, fd_in, fd_out, env_bzip2);
457 : :
458 : : command_destroy(&cmd);
459 : : }
460 : : #endif
461 : :
462 : : static const struct compressor compressor_bzip2 = {
463 : : .name = "bzip2",
464 : : .extension = ".bz2",
465 : : .default_level = 9,
466 : : .fixup_params = fixup_bzip2_params,
467 : : .compress = compress_bzip2,
468 : : .decompress = decompress_bzip2,
469 : : };
470 : :
471 : : /*
472 : : * Xz compressor.
473 : : */
474 : :
475 : : #define XZ "xz"
476 : :
477 : : #ifdef WITH_LIBLZMA
478 : : enum dpkg_stream_status {
479 : : DPKG_STREAM_INIT = DPKG_BIT(1),
480 : : DPKG_STREAM_RUN = DPKG_BIT(2),
481 : : DPKG_STREAM_COMPRESS = DPKG_BIT(3),
482 : : DPKG_STREAM_DECOMPRESS = DPKG_BIT(4),
483 : : DPKG_STREAM_FILTER = DPKG_STREAM_COMPRESS | DPKG_STREAM_DECOMPRESS,
484 : : };
485 : :
486 : : /* XXX: liblzma does not expose error messages. */
487 : : static const char *
488 : 0 : dpkg_lzma_strerror(lzma_ret code, enum dpkg_stream_status status)
489 : : {
490 : 0 : const char *const impossible = _("internal error (bug)");
491 : :
492 [ # # # # : 0 : switch (code) {
# # # # ]
493 : 0 : case LZMA_MEM_ERROR:
494 : 0 : return strerror(ENOMEM);
495 : 0 : case LZMA_MEMLIMIT_ERROR:
496 [ # # ]: 0 : if (status & DPKG_STREAM_RUN)
497 : 0 : return _("memory usage limit reached");
498 : 0 : return impossible;
499 : 0 : case LZMA_OPTIONS_ERROR:
500 [ # # ]: 0 : if (status == (DPKG_STREAM_INIT | DPKG_STREAM_COMPRESS))
501 : 0 : return _("unsupported compression preset");
502 [ # # ]: 0 : if (status == (DPKG_STREAM_RUN | DPKG_STREAM_DECOMPRESS))
503 : 0 : return _("unsupported options in file header");
504 : 0 : return impossible;
505 : 0 : case LZMA_DATA_ERROR:
506 [ # # ]: 0 : if (status & DPKG_STREAM_RUN)
507 : 0 : return _("compressed data is corrupt");
508 : 0 : return impossible;
509 : 0 : case LZMA_BUF_ERROR:
510 [ # # ]: 0 : if (status & DPKG_STREAM_RUN)
511 : 0 : return _("unexpected end of input");
512 : 0 : return impossible;
513 : 0 : case LZMA_FORMAT_ERROR:
514 [ # # ]: 0 : if (status == (DPKG_STREAM_RUN | DPKG_STREAM_DECOMPRESS))
515 : 0 : return _("file format not recognized");
516 : 0 : return impossible;
517 : 0 : case LZMA_UNSUPPORTED_CHECK:
518 [ # # ]: 0 : if (status == (DPKG_STREAM_INIT | DPKG_STREAM_COMPRESS))
519 : 0 : return _("unsupported type of integrity check");
520 : 0 : return impossible;
521 : 0 : default:
522 : 0 : return impossible;
523 : : }
524 : : }
525 : :
526 : : struct io_lzma {
527 : : const char *desc;
528 : :
529 : : struct compress_params *params;
530 : : enum dpkg_stream_status status;
531 : : lzma_action action;
532 : :
533 : : void (*init)(struct io_lzma *io, lzma_stream *s);
534 : : int (*code)(struct io_lzma *io, lzma_stream *s);
535 : : void (*done)(struct io_lzma *io, lzma_stream *s);
536 : : };
537 : :
538 : : static void
539 : 21 : filter_lzma(struct io_lzma *io, int fd_in, int fd_out)
540 : : {
541 : : uint8_t *buf_in;
542 : : uint8_t *buf_out;
543 : 21 : size_t buf_size = DPKG_BUFFER_SIZE;
544 : 21 : lzma_stream s = LZMA_STREAM_INIT;
545 : : lzma_ret ret;
546 : :
547 : 21 : buf_in = m_malloc(buf_size);
548 : 21 : buf_out = m_malloc(buf_size);
549 : :
550 : 21 : s.next_out = buf_out;
551 : 21 : s.avail_out = buf_size;
552 : :
553 : 21 : io->action = LZMA_RUN;
554 : 21 : io->status = DPKG_STREAM_INIT;
555 : 21 : io->init(io, &s);
556 : 21 : io->status = (io->status & DPKG_STREAM_FILTER) | DPKG_STREAM_RUN;
557 : :
558 : : do {
559 : : ssize_t len;
560 : :
561 [ + - + - ]: 35 : if (s.avail_in == 0 && io->action != LZMA_FINISH) {
562 : 35 : len = fd_read(fd_in, buf_in, buf_size);
563 [ - + ]: 35 : if (len < 0)
564 : 0 : ohshite(_("%s: lzma read error"), io->desc);
565 [ + + ]: 35 : if (len == 0)
566 : 14 : io->action = LZMA_FINISH;
567 : 35 : s.next_in = buf_in;
568 : 35 : s.avail_in = len;
569 : : }
570 : :
571 : 35 : ret = io->code(io, &s);
572 : :
573 [ + - + + ]: 35 : if (s.avail_out == 0 || ret == LZMA_STREAM_END) {
574 : 21 : len = fd_write(fd_out, buf_out, s.next_out - buf_out);
575 [ - + ]: 21 : if (len < 0)
576 : 0 : ohshite(_("%s: lzma write error"), io->desc);
577 : 21 : s.next_out = buf_out;
578 : 21 : s.avail_out = buf_size;
579 : : }
580 [ + + ]: 35 : } while (ret != LZMA_STREAM_END);
581 : :
582 : 21 : io->done(io, &s);
583 : :
584 : 21 : free(buf_in);
585 : 21 : free(buf_out);
586 : :
587 [ - + ]: 21 : if (close(fd_out))
588 : 0 : ohshite(_("%s: lzma close error"), io->desc);
589 : 21 : }
590 : :
591 : : static void DPKG_ATTR_NORET
592 : 0 : filter_lzma_error(struct io_lzma *io, lzma_ret ret)
593 : : {
594 : 0 : ohshit(_("%s: lzma error: %s"), io->desc,
595 : : dpkg_lzma_strerror(ret, io->status));
596 : : }
597 : :
598 : : #ifdef HAVE_LZMA_MT_ENCODER
599 : : static uint64_t
600 : 14 : filter_xz_get_memlimit(void)
601 : : {
602 : : uint64_t mt_memlimit;
603 : :
604 : : /* Ask the kernel what is currently available for us. If this fails
605 : : * initialize the memory limit to half the physical RAM, or to 128 MiB
606 : : * if we cannot infer the number. */
607 [ - + ]: 14 : if (meminfo_get_available(&mt_memlimit) < 0) {
608 : 0 : mt_memlimit = lzma_physmem() / 2;
609 [ # # ]: 0 : if (mt_memlimit == 0)
610 : 0 : mt_memlimit = 128 * 1024 * 1024;
611 : : }
612 : : /* Clamp the multi-threaded memory limit to half the addressable
613 : : * memory on this architecture. */
614 [ - + ]: 14 : if (mt_memlimit > INTPTR_MAX)
615 : 0 : mt_memlimit = INTPTR_MAX;
616 : :
617 : 14 : return mt_memlimit;
618 : : }
619 : :
620 : : static uint32_t
621 : 14 : filter_xz_get_cputhreads(struct compress_params *params)
622 : : {
623 : : long threads_max;
624 : :
625 : 14 : threads_max = lzma_cputhreads();
626 [ - + ]: 14 : if (threads_max == 0)
627 : 0 : threads_max = 1;
628 : :
629 [ - + ]: 14 : if (params->threads_max >= 0)
630 [ # # ]: 0 : return clamp(params->threads_max, 1, threads_max);
631 : :
632 : 14 : return threads_max;
633 : : }
634 : : #endif
635 : :
636 : : static void
637 : 6 : filter_unxz_init(struct io_lzma *io, lzma_stream *s)
638 : : {
639 : 6 : uint64_t memlimit = UINT64_MAX;
640 : : lzma_ret ret;
641 : :
642 : 6 : io->status |= DPKG_STREAM_DECOMPRESS;
643 : :
644 : 6 : ret = lzma_stream_decoder(s, memlimit, 0);
645 [ - + ]: 6 : if (ret != LZMA_OK)
646 : 0 : filter_lzma_error(io, ret);
647 : 6 : }
648 : :
649 : : static void
650 : 14 : filter_xz_init(struct io_lzma *io, lzma_stream *s)
651 : : {
652 : : uint32_t preset;
653 : 14 : lzma_check check = LZMA_CHECK_CRC64;
654 : : #ifdef HAVE_LZMA_MT_ENCODER
655 : : uint64_t mt_memlimit;
656 : 14 : lzma_mt mt_options = {
657 : : .flags = 0,
658 : : .block_size = 0,
659 : : .timeout = 0,
660 : : .filters = NULL,
661 : : .check = check,
662 : : };
663 : : #endif
664 : : lzma_ret ret;
665 : :
666 : 14 : io->status |= DPKG_STREAM_COMPRESS;
667 : :
668 : 14 : preset = io->params->level;
669 [ - + ]: 14 : if (io->params->strategy == COMPRESSOR_STRATEGY_EXTREME)
670 : 0 : preset |= LZMA_PRESET_EXTREME;
671 : :
672 : : #ifdef HAVE_LZMA_MT_ENCODER
673 : 14 : mt_options.preset = preset;
674 : 14 : mt_memlimit = filter_xz_get_memlimit();
675 : 14 : mt_options.threads = filter_xz_get_cputhreads(io->params);
676 : :
677 : : /* Guess whether we have enough RAM to use the multi-threaded encoder,
678 : : * and decrease them up to single-threaded to reduce memory usage. */
679 [ + - ]: 14 : for (; mt_options.threads > 1; mt_options.threads--) {
680 : : uint64_t mt_memusage;
681 : :
682 : 14 : mt_memusage = lzma_stream_encoder_mt_memusage(&mt_options);
683 [ + - ]: 14 : if (mt_memusage < mt_memlimit)
684 : 14 : break;
685 : : }
686 : :
687 : 14 : ret = lzma_stream_encoder_mt(s, &mt_options);
688 : : #else
689 : : ret = lzma_easy_encoder(s, preset, check);
690 : : #endif
691 : :
692 [ - + ]: 14 : if (ret != LZMA_OK)
693 : 0 : filter_lzma_error(io, ret);
694 : 14 : }
695 : :
696 : : static int
697 : 35 : filter_lzma_code(struct io_lzma *io, lzma_stream *s)
698 : : {
699 : : lzma_ret ret;
700 : :
701 : 35 : ret = lzma_code(s, io->action);
702 [ + + - + ]: 35 : if (ret != LZMA_OK && ret != LZMA_STREAM_END)
703 : 0 : filter_lzma_error(io, ret);
704 : :
705 : 35 : return ret;
706 : : }
707 : :
708 : : static void
709 : 21 : filter_lzma_done(struct io_lzma *io, lzma_stream *s)
710 : : {
711 : 21 : lzma_end(s);
712 : 21 : }
713 : :
714 : : static void
715 : 6 : decompress_xz(struct compress_params *params, int fd_in, int fd_out,
716 : : const char *desc)
717 : : {
718 : : struct io_lzma io;
719 : :
720 : 6 : io.init = filter_unxz_init;
721 : 6 : io.code = filter_lzma_code;
722 : 6 : io.done = filter_lzma_done;
723 : 6 : io.desc = desc;
724 : 6 : io.params = params;
725 : :
726 : 6 : filter_lzma(&io, fd_in, fd_out);
727 : 6 : }
728 : :
729 : : static void
730 : 14 : compress_xz(struct compress_params *params, int fd_in, int fd_out,
731 : : const char *desc)
732 : : {
733 : : struct io_lzma io;
734 : :
735 : 14 : io.init = filter_xz_init;
736 : 14 : io.code = filter_lzma_code;
737 : 14 : io.done = filter_lzma_done;
738 : 14 : io.desc = desc;
739 : 14 : io.params = params;
740 : :
741 : 14 : filter_lzma(&io, fd_in, fd_out);
742 : 14 : }
743 : : #else
744 : : static const char *env_xz[] = { "XZ_DEFAULTS", "XZ_OPT", NULL };
745 : :
746 : : static void
747 : : decompress_xz(struct compress_params *params, int fd_in, int fd_out,
748 : : const char *desc)
749 : : {
750 : : struct command cmd;
751 : :
752 : : command_decompress_init(&cmd, XZ, desc);
753 : :
754 : : fd_fd_filter(&cmd, fd_in, fd_out, env_xz);
755 : :
756 : : command_destroy(&cmd);
757 : : }
758 : :
759 : : static void
760 : : compress_xz(struct compress_params *params, int fd_in, int fd_out,
761 : : const char *desc)
762 : : {
763 : : struct command cmd;
764 : : char *threads_opt = NULL;
765 : :
766 : : command_compress_init(&cmd, XZ, desc, params->level);
767 : :
768 : : if (params->strategy == COMPRESSOR_STRATEGY_EXTREME)
769 : : command_add_arg(&cmd, "-e");
770 : :
771 : : if (params->threads_max > 0) {
772 : : threads_opt = str_fmt("-T%d", params->threads_max);
773 : : command_add_arg(&cmd, threads_opt);
774 : : }
775 : :
776 : : fd_fd_filter(&cmd, fd_in, fd_out, env_xz);
777 : :
778 : : command_destroy(&cmd);
779 : : free(threads_opt);
780 : : }
781 : : #endif
782 : :
783 : : static const struct compressor compressor_xz = {
784 : : .name = "xz",
785 : : .extension = ".xz",
786 : : .default_level = 6,
787 : : .fixup_params = fixup_none_params,
788 : : .compress = compress_xz,
789 : : .decompress = decompress_xz,
790 : : };
791 : :
792 : : /*
793 : : * Lzma compressor.
794 : : */
795 : :
796 : : #ifdef WITH_LIBLZMA
797 : : static void
798 : 1 : filter_unlzma_init(struct io_lzma *io, lzma_stream *s)
799 : : {
800 : 1 : uint64_t memlimit = UINT64_MAX;
801 : : lzma_ret ret;
802 : :
803 : 1 : io->status |= DPKG_STREAM_DECOMPRESS;
804 : :
805 : 1 : ret = lzma_alone_decoder(s, memlimit);
806 [ - + ]: 1 : if (ret != LZMA_OK)
807 : 0 : filter_lzma_error(io, ret);
808 : 1 : }
809 : :
810 : : static void
811 : 0 : filter_lzma_init(struct io_lzma *io, lzma_stream *s)
812 : : {
813 : : uint32_t preset;
814 : : lzma_options_lzma options;
815 : : lzma_ret ret;
816 : :
817 : 0 : io->status |= DPKG_STREAM_COMPRESS;
818 : :
819 : 0 : preset = io->params->level;
820 [ # # ]: 0 : if (io->params->strategy == COMPRESSOR_STRATEGY_EXTREME)
821 : 0 : preset |= LZMA_PRESET_EXTREME;
822 [ # # ]: 0 : if (lzma_lzma_preset(&options, preset))
823 : 0 : filter_lzma_error(io, LZMA_OPTIONS_ERROR);
824 : :
825 : 0 : ret = lzma_alone_encoder(s, &options);
826 [ # # ]: 0 : if (ret != LZMA_OK)
827 : 0 : filter_lzma_error(io, ret);
828 : 0 : }
829 : :
830 : : static void
831 : 1 : decompress_lzma(struct compress_params *params, int fd_in, int fd_out,
832 : : const char *desc)
833 : : {
834 : : struct io_lzma io;
835 : :
836 : 1 : io.init = filter_unlzma_init;
837 : 1 : io.code = filter_lzma_code;
838 : 1 : io.done = filter_lzma_done;
839 : 1 : io.desc = desc;
840 : 1 : io.params = params;
841 : :
842 : 1 : filter_lzma(&io, fd_in, fd_out);
843 : 1 : }
844 : :
845 : : static void
846 : 0 : compress_lzma(struct compress_params *params, int fd_in, int fd_out,
847 : : const char *desc)
848 : : {
849 : : struct io_lzma io;
850 : :
851 : 0 : io.init = filter_lzma_init;
852 : 0 : io.code = filter_lzma_code;
853 : 0 : io.done = filter_lzma_done;
854 : 0 : io.desc = desc;
855 : 0 : io.params = params;
856 : :
857 : 0 : filter_lzma(&io, fd_in, fd_out);
858 : 0 : }
859 : : #else
860 : : static void
861 : : decompress_lzma(struct compress_params *params, int fd_in, int fd_out,
862 : : const char *desc)
863 : : {
864 : : struct command cmd;
865 : :
866 : : command_decompress_init(&cmd, XZ, desc);
867 : : command_add_arg(&cmd, "--format=lzma");
868 : :
869 : : fd_fd_filter(&cmd, fd_in, fd_out, env_xz);
870 : :
871 : : command_destroy(&cmd);
872 : : }
873 : :
874 : : static void
875 : : compress_lzma(struct compress_params *params, int fd_in, int fd_out,
876 : : const char *desc)
877 : : {
878 : : struct command cmd;
879 : :
880 : : command_compress_init(&cmd, XZ, desc, params->level);
881 : : command_add_arg(&cmd, "--format=lzma");
882 : :
883 : : fd_fd_filter(&cmd, fd_in, fd_out, env_xz);
884 : :
885 : : command_destroy(&cmd);
886 : : }
887 : : #endif
888 : :
889 : : static const struct compressor compressor_lzma = {
890 : : .name = "lzma",
891 : : .extension = ".lzma",
892 : : .default_level = 6,
893 : : .fixup_params = fixup_none_params,
894 : : .compress = compress_lzma,
895 : : .decompress = decompress_lzma,
896 : : };
897 : :
898 : : /*
899 : : * Generic compressor filter.
900 : : */
901 : :
902 : : static const struct compressor *compressor_array[] = {
903 : : [COMPRESSOR_TYPE_NONE] = &compressor_none,
904 : : [COMPRESSOR_TYPE_GZIP] = &compressor_gzip,
905 : : [COMPRESSOR_TYPE_XZ] = &compressor_xz,
906 : : [COMPRESSOR_TYPE_BZIP2] = &compressor_bzip2,
907 : : [COMPRESSOR_TYPE_LZMA] = &compressor_lzma,
908 : : };
909 : :
910 : : static const struct compressor *
911 : 181 : compressor(enum compressor_type type)
912 : : {
913 : 181 : const enum compressor_type max_type = array_count(compressor_array);
914 : :
915 [ + - - + ]: 181 : if (type < 0 || type >= max_type)
916 : 0 : internerr("compressor_type %d is out of range", type);
917 : :
918 : 181 : return compressor_array[type];
919 : : }
920 : :
921 : : const char *
922 : 0 : compressor_get_name(enum compressor_type type)
923 : : {
924 : 0 : return compressor(type)->name;
925 : : }
926 : :
927 : : const char *
928 : 17 : compressor_get_extension(enum compressor_type type)
929 : : {
930 : 17 : return compressor(type)->extension;
931 : : }
932 : :
933 : : enum compressor_type
934 : 3 : compressor_find_by_name(const char *name)
935 : : {
936 : : size_t i;
937 : :
938 [ + - ]: 4 : for (i = 0; i < array_count(compressor_array); i++)
939 [ + + ]: 4 : if (strcmp(compressor_array[i]->name, name) == 0)
940 : 3 : return i;
941 : :
942 : 0 : return COMPRESSOR_TYPE_UNKNOWN;
943 : : }
944 : :
945 : : enum compressor_type
946 : 30 : compressor_find_by_extension(const char *extension)
947 : : {
948 : : size_t i;
949 : :
950 [ + + ]: 66 : for (i = 0; i < array_count(compressor_array); i++)
951 [ + + ]: 65 : if (strcmp(compressor_array[i]->extension, extension) == 0)
952 : 29 : return i;
953 : :
954 : 1 : return COMPRESSOR_TYPE_UNKNOWN;
955 : : }
956 : :
957 : : enum compressor_strategy
958 : 0 : compressor_get_strategy(const char *name)
959 : : {
960 [ # # ]: 0 : if (strcmp(name, "none") == 0)
961 : 0 : return COMPRESSOR_STRATEGY_NONE;
962 [ # # ]: 0 : if (strcmp(name, "filtered") == 0)
963 : 0 : return COMPRESSOR_STRATEGY_FILTERED;
964 [ # # ]: 0 : if (strcmp(name, "huffman") == 0)
965 : 0 : return COMPRESSOR_STRATEGY_HUFFMAN;
966 [ # # ]: 0 : if (strcmp(name, "rle") == 0)
967 : 0 : return COMPRESSOR_STRATEGY_RLE;
968 [ # # ]: 0 : if (strcmp(name, "fixed") == 0)
969 : 0 : return COMPRESSOR_STRATEGY_FIXED;
970 [ # # ]: 0 : if (strcmp(name, "extreme") == 0)
971 : 0 : return COMPRESSOR_STRATEGY_EXTREME;
972 : :
973 : 0 : return COMPRESSOR_STRATEGY_UNKNOWN;
974 : : }
975 : :
976 : : static void
977 : 59 : compressor_fixup_params(struct compress_params *params)
978 : : {
979 : 59 : compressor(params->type)->fixup_params(params);
980 : :
981 [ + - ]: 59 : if (params->level < 0)
982 : 59 : params->level = compressor(params->type)->default_level;
983 : 59 : }
984 : :
985 : : bool
986 : 59 : compressor_check_params(struct compress_params *params, struct dpkg_error *err)
987 : : {
988 : 59 : compressor_fixup_params(params);
989 : :
990 [ + - ]: 59 : if (params->strategy == COMPRESSOR_STRATEGY_NONE)
991 : 59 : return true;
992 : :
993 [ # # ]: 0 : if (params->type == COMPRESSOR_TYPE_GZIP &&
994 [ # # ]: 0 : (params->strategy == COMPRESSOR_STRATEGY_FILTERED ||
995 [ # # ]: 0 : params->strategy == COMPRESSOR_STRATEGY_HUFFMAN ||
996 [ # # ]: 0 : params->strategy == COMPRESSOR_STRATEGY_RLE ||
997 [ # # ]: 0 : params->strategy == COMPRESSOR_STRATEGY_FIXED))
998 : 0 : return true;
999 : :
1000 [ # # ]: 0 : if (params->type == COMPRESSOR_TYPE_XZ &&
1001 [ # # ]: 0 : params->strategy == COMPRESSOR_STRATEGY_EXTREME)
1002 : 0 : return true;
1003 : :
1004 : 0 : dpkg_put_error(err, _("unknown compression strategy"));
1005 : 0 : return false;
1006 : : }
1007 : :
1008 : : void
1009 : 26 : decompress_filter(struct compress_params *params, int fd_in, int fd_out,
1010 : : const char *desc_fmt, ...)
1011 : : {
1012 : : va_list args;
1013 : 26 : struct varbuf desc = VARBUF_INIT;
1014 : :
1015 : 26 : va_start(args, desc_fmt);
1016 : 26 : varbuf_vprintf(&desc, desc_fmt, args);
1017 : 26 : va_end(args);
1018 : :
1019 : 26 : compressor(params->type)->decompress(params, fd_in, fd_out, desc.buf);
1020 : :
1021 : 26 : varbuf_destroy(&desc);
1022 : 26 : }
1023 : :
1024 : : void
1025 : 20 : compress_filter(struct compress_params *params, int fd_in, int fd_out,
1026 : : const char *desc_fmt, ...)
1027 : : {
1028 : : va_list args;
1029 : 20 : struct varbuf desc = VARBUF_INIT;
1030 : :
1031 : 20 : va_start(args, desc_fmt);
1032 : 20 : varbuf_vprintf(&desc, desc_fmt, args);
1033 : 20 : va_end(args);
1034 : :
1035 : 20 : compressor(params->type)->compress(params, fd_in, fd_out, desc.buf);
1036 : :
1037 : 20 : varbuf_destroy(&desc);
1038 : 20 : }
|