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-2023 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_LIBZSTD
39 : : #include <zstd.h>
40 : : #define DPKG_ZSTD_MAX_LEVEL ZSTD_maxCLevel()
41 : : #else
42 : : #define DPKG_ZSTD_MAX_LEVEL 22
43 : : #define ZSTD_CLEVEL_DEFAULT 3
44 : : #endif
45 : : #define DPKG_ZSTD_CLEVEL_DEFAULT ZSTD_CLEVEL_DEFAULT
46 : : #ifdef WITH_LIBBZ2
47 : : #include <bzlib.h>
48 : : #endif
49 : :
50 : : #include <dpkg/i18n.h>
51 : : #include <dpkg/dpkg.h>
52 : : #include <dpkg/error.h>
53 : : #include <dpkg/varbuf.h>
54 : : #include <dpkg/fdio.h>
55 : : #include <dpkg/buffer.h>
56 : : #include <dpkg/meminfo.h>
57 : : #include <dpkg/command.h>
58 : : #include <dpkg/compress.h>
59 : : #if USE_LIBZ_IMPL == USE_LIBZ_IMPL_NONE || \
60 : : !defined(WITH_LIBLZMA) || \
61 : : !defined(WITH_LIBZSTD) || \
62 : : !defined(WITH_LIBBZ2)
63 : : #include <dpkg/subproc.h>
64 : :
65 : : static void
66 : : fd_fd_filter(struct command *cmd, int fd_in, int fd_out, const char *delenv[])
67 : : {
68 : : pid_t pid;
69 : :
70 : : pid = subproc_fork();
71 : : if (pid == 0) {
72 : : int i;
73 : :
74 : : if (fd_in != 0) {
75 : : m_dup2(fd_in, 0);
76 : : close(fd_in);
77 : : }
78 : : if (fd_out != 1) {
79 : : m_dup2(fd_out, 1);
80 : : close(fd_out);
81 : : }
82 : :
83 : : for (i = 0; delenv[i]; i++)
84 : : unsetenv(delenv[i]);
85 : :
86 : : command_exec(cmd);
87 : : }
88 : : subproc_reap(pid, cmd->name, 0);
89 : : }
90 : :
91 : : static void
92 : : command_compress_init(struct command *cmd, const char *name, const char *desc,
93 : : int level)
94 : : {
95 : : static char combuf[6];
96 : :
97 : : command_init(cmd, name, desc);
98 : : command_add_arg(cmd, name);
99 : :
100 : : snprintf(combuf, sizeof(combuf), "-c%d", level);
101 : : command_add_arg(cmd, combuf);
102 : : }
103 : :
104 : : static void
105 : : command_decompress_init(struct command *cmd, const char *name, const char *desc)
106 : : {
107 : : command_init(cmd, name, desc);
108 : : command_add_arg(cmd, name);
109 : : command_add_arg(cmd, "-dc");
110 : : }
111 : : #endif
112 : :
113 : : #if defined(WITH_LIBLZMA) || defined(WITH_LIBZSTD)
114 : : enum dpkg_stream_filter {
115 : : DPKG_STREAM_COMPRESS = 1,
116 : : DPKG_STREAM_DECOMPRESS = 2,
117 : : };
118 : :
119 : : enum dpkg_stream_action {
120 : : DPKG_STREAM_INIT = 0,
121 : : DPKG_STREAM_RUN = 1,
122 : : DPKG_STREAM_FINISH = 2,
123 : : };
124 : :
125 : : enum dpkg_stream_status {
126 : : DPKG_STREAM_OK,
127 : : DPKG_STREAM_END,
128 : : DPKG_STREAM_ERROR,
129 : : };
130 : : #endif
131 : :
132 : : struct compressor {
133 : : const char *name;
134 : : const char *extension;
135 : : int default_level;
136 : : void (*fixup_params)(struct compress_params *params);
137 : : void (*compress)(struct compress_params *params,
138 : : int fd_in, int fd_out, const char *desc);
139 : : void (*decompress)(struct compress_params *params,
140 : : int fd_in, int fd_out, const char *desc);
141 : : };
142 : :
143 : : /*
144 : : * No compressor (pass-through).
145 : : */
146 : :
147 : : static void
148 : 76 : fixup_none_params(struct compress_params *params)
149 : : {
150 : 76 : }
151 : :
152 : : static void
153 : 10 : decompress_none(struct compress_params *params, int fd_in, int fd_out,
154 : : const char *desc)
155 : : {
156 : : struct dpkg_error err;
157 : :
158 [ - + ]: 10 : if (fd_fd_copy(fd_in, fd_out, -1, &err) < 0)
159 : 0 : ohshit(_("%s: pass-through copy error: %s"), desc, err.str);
160 : 10 : }
161 : :
162 : : static void
163 : 14 : compress_none(struct compress_params *params, int fd_in, int fd_out,
164 : : const char *desc)
165 : : {
166 : : struct dpkg_error err;
167 : :
168 [ - + ]: 14 : if (fd_fd_copy(fd_in, fd_out, -1, &err) < 0)
169 : 0 : ohshit(_("%s: pass-through copy error: %s"), desc, err.str);
170 : 14 : }
171 : :
172 : : static const struct compressor compressor_none = {
173 : : .name = "none",
174 : : .extension = "",
175 : : .default_level = 0,
176 : : .fixup_params = fixup_none_params,
177 : : .compress = compress_none,
178 : : .decompress = decompress_none,
179 : : };
180 : :
181 : : /*
182 : : * Gzip compressor.
183 : : */
184 : :
185 : : #define GZIP "gzip"
186 : :
187 : : static void
188 : 2 : fixup_gzip_params(struct compress_params *params)
189 : : {
190 : : /* Normalize compression level. */
191 [ - + ]: 2 : if (params->level == 0)
192 : 0 : params->type = COMPRESSOR_TYPE_NONE;
193 : 2 : }
194 : :
195 : : #if USE_LIBZ_IMPL != USE_LIBZ_IMPL_NONE
196 : : static void
197 : 14 : decompress_gzip(struct compress_params *params, int fd_in, int fd_out,
198 : : const char *desc)
199 : : {
200 : : char *buffer;
201 : 14 : size_t bufsize = DPKG_BUFFER_SIZE;
202 : : int z_errnum;
203 : 14 : gzFile gzfile = gzdopen(fd_in, "r");
204 : :
205 [ - + ]: 14 : if (gzfile == NULL)
206 : 0 : ohshit(_("%s: error binding input to gzip stream"), desc);
207 : :
208 : 14 : buffer = m_malloc(bufsize);
209 : :
210 : 166 : for (;;) {
211 : : int actualread, actualwrite;
212 : :
213 : 180 : actualread = gzread(gzfile, buffer, bufsize);
214 [ - + ]: 180 : if (actualread < 0) {
215 : 0 : const char *errmsg = gzerror(gzfile, &z_errnum);
216 : :
217 [ # # ]: 0 : if (z_errnum == Z_ERRNO)
218 : 0 : errmsg = strerror(errno);
219 : 0 : ohshit(_("%s: internal gzip read error: '%s'"), desc,
220 : : errmsg);
221 : : }
222 [ + + ]: 180 : if (actualread == 0) /* EOF. */
223 : 14 : break;
224 : :
225 : 166 : actualwrite = fd_write(fd_out, buffer, actualread);
226 [ - + ]: 166 : if (actualwrite != actualread)
227 : 0 : ohshite(_("%s: internal gzip write error"), desc);
228 : : }
229 : :
230 : 14 : free(buffer);
231 : :
232 : 14 : z_errnum = gzclose(gzfile);
233 [ - + ]: 14 : if (z_errnum) {
234 : : const char *errmsg;
235 : :
236 [ # # ]: 0 : if (z_errnum == Z_ERRNO)
237 : 0 : errmsg = strerror(errno);
238 : : else
239 : 0 : errmsg = zError(z_errnum);
240 : 0 : ohshit(_("%s: internal gzip read error: %s"), desc, errmsg);
241 : : }
242 : :
243 [ - + ]: 14 : if (close(fd_out))
244 : 0 : ohshite(_("%s: internal gzip write error"), desc);
245 : 14 : }
246 : :
247 : : static void
248 : 4 : compress_gzip(struct compress_params *params, int fd_in, int fd_out,
249 : : const char *desc)
250 : : {
251 : : char *buffer;
252 : : char combuf[6];
253 : 4 : size_t bufsize = DPKG_BUFFER_SIZE;
254 : : int strategy;
255 : : int z_errnum;
256 : : gzFile gzfile;
257 : :
258 [ - + ]: 4 : if (params->strategy == COMPRESSOR_STRATEGY_FILTERED)
259 : 0 : strategy = 'f';
260 [ - + ]: 4 : else if (params->strategy == COMPRESSOR_STRATEGY_HUFFMAN)
261 : 0 : strategy = 'h';
262 [ - + ]: 4 : else if (params->strategy == COMPRESSOR_STRATEGY_RLE)
263 : 0 : strategy = 'R';
264 [ - + ]: 4 : else if (params->strategy == COMPRESSOR_STRATEGY_FIXED)
265 : 0 : strategy = 'F';
266 : : else
267 : 4 : strategy = ' ';
268 : :
269 : 4 : snprintf(combuf, sizeof(combuf), "w%d%c", params->level, strategy);
270 : 4 : gzfile = gzdopen(fd_out, combuf);
271 [ - + ]: 4 : if (gzfile == NULL)
272 : 0 : ohshit(_("%s: error binding output to gzip stream"), desc);
273 : :
274 : 4 : buffer = m_malloc(bufsize);
275 : :
276 : 42 : for (;;) {
277 : : int actualread, actualwrite;
278 : :
279 : 46 : actualread = fd_read(fd_in, buffer, bufsize);
280 [ - + ]: 46 : if (actualread < 0)
281 : 0 : ohshite(_("%s: internal gzip read error"), desc);
282 [ + + ]: 46 : if (actualread == 0) /* EOF. */
283 : 4 : break;
284 : :
285 : 42 : actualwrite = gzwrite(gzfile, buffer, actualread);
286 [ - + ]: 42 : if (actualwrite != actualread) {
287 : 0 : const char *errmsg = gzerror(gzfile, &z_errnum);
288 : :
289 [ # # ]: 0 : if (z_errnum == Z_ERRNO)
290 : 0 : errmsg = strerror(errno);
291 : 0 : ohshit(_("%s: internal gzip write error: '%s'"), desc,
292 : : errmsg);
293 : : }
294 : : }
295 : :
296 : 4 : free(buffer);
297 : :
298 : 4 : z_errnum = gzclose(gzfile);
299 [ - + ]: 4 : if (z_errnum) {
300 : : const char *errmsg;
301 : :
302 [ # # ]: 0 : if (z_errnum == Z_ERRNO)
303 : 0 : errmsg = strerror(errno);
304 : : else
305 : 0 : errmsg = zError(z_errnum);
306 : 0 : ohshit(_("%s: internal gzip write error: %s"), desc, errmsg);
307 : : }
308 : 4 : }
309 : : #else
310 : : static const char *env_gzip[] = { "GZIP", NULL };
311 : :
312 : : static void
313 : : decompress_gzip(struct compress_params *params, int fd_in, int fd_out,
314 : : const char *desc)
315 : : {
316 : : struct command cmd;
317 : :
318 : : command_decompress_init(&cmd, GZIP, desc);
319 : :
320 : : fd_fd_filter(&cmd, fd_in, fd_out, env_gzip);
321 : :
322 : : command_destroy(&cmd);
323 : : }
324 : :
325 : : static void
326 : : compress_gzip(struct compress_params *params, int fd_in, int fd_out,
327 : : const char *desc)
328 : : {
329 : : struct command cmd;
330 : :
331 : : command_compress_init(&cmd, GZIP, desc, params->level);
332 : : command_add_arg(&cmd, "-n");
333 : :
334 : : fd_fd_filter(&cmd, fd_in, fd_out, env_gzip);
335 : :
336 : : command_destroy(&cmd);
337 : : }
338 : : #endif
339 : :
340 : : static const struct compressor compressor_gzip = {
341 : : .name = "gzip",
342 : : .extension = ".gz",
343 : : .default_level = 9,
344 : : .fixup_params = fixup_gzip_params,
345 : : .compress = compress_gzip,
346 : : .decompress = decompress_gzip,
347 : : };
348 : :
349 : : /*
350 : : * Bzip2 compressor.
351 : : */
352 : :
353 : : #define BZIP2 "bzip2"
354 : :
355 : : static void
356 : 0 : fixup_bzip2_params(struct compress_params *params)
357 : : {
358 : : /* Normalize compression level. */
359 [ # # ]: 0 : if (params->level == 0)
360 : 0 : params->level = 1;
361 : 0 : }
362 : :
363 : : #ifdef WITH_LIBBZ2
364 : : static void
365 : 1 : decompress_bzip2(struct compress_params *params, int fd_in, int fd_out,
366 : : const char *desc)
367 : : {
368 : : char *buffer;
369 : 1 : size_t bufsize = DPKG_BUFFER_SIZE;
370 : 1 : BZFILE *bzfile = BZ2_bzdopen(fd_in, "r");
371 : :
372 [ - + ]: 1 : if (bzfile == NULL)
373 : 0 : ohshit(_("%s: error binding input to bzip2 stream"), desc);
374 : :
375 : 1 : buffer = m_malloc(bufsize);
376 : :
377 : 20 : for (;;) {
378 : : int actualread, actualwrite;
379 : :
380 : 21 : actualread = BZ2_bzread(bzfile, buffer, bufsize);
381 [ - + ]: 21 : if (actualread < 0) {
382 : 0 : int bz_errnum = 0;
383 : 0 : const char *errmsg = BZ2_bzerror(bzfile, &bz_errnum);
384 : :
385 [ # # ]: 0 : if (bz_errnum == BZ_IO_ERROR)
386 : 0 : errmsg = strerror(errno);
387 : 0 : ohshit(_("%s: internal bzip2 read error: '%s'"), desc,
388 : : errmsg);
389 : : }
390 [ + + ]: 21 : if (actualread == 0) /* EOF. */
391 : 1 : break;
392 : :
393 : 20 : actualwrite = fd_write(fd_out, buffer, actualread);
394 [ - + ]: 20 : if (actualwrite != actualread)
395 : 0 : ohshite(_("%s: internal bzip2 write error"), desc);
396 : : }
397 : :
398 : 1 : free(buffer);
399 : :
400 : 1 : BZ2_bzclose(bzfile);
401 : :
402 [ - + ]: 1 : if (close(fd_out))
403 : 0 : ohshite(_("%s: internal bzip2 write error"), desc);
404 : 1 : }
405 : :
406 : : static void
407 : 0 : compress_bzip2(struct compress_params *params, int fd_in, int fd_out,
408 : : const char *desc)
409 : : {
410 : : char *buffer;
411 : : char combuf[6];
412 : 0 : size_t bufsize = DPKG_BUFFER_SIZE;
413 : : int bz_errnum;
414 : : BZFILE *bzfile;
415 : :
416 : 0 : snprintf(combuf, sizeof(combuf), "w%d", params->level);
417 : 0 : bzfile = BZ2_bzdopen(fd_out, combuf);
418 [ # # ]: 0 : if (bzfile == NULL)
419 : 0 : ohshit(_("%s: error binding output to bzip2 stream"), desc);
420 : :
421 : 0 : buffer = m_malloc(bufsize);
422 : :
423 : 0 : for (;;) {
424 : : int actualread, actualwrite;
425 : :
426 : 0 : actualread = fd_read(fd_in, buffer, bufsize);
427 [ # # ]: 0 : if (actualread < 0)
428 : 0 : ohshite(_("%s: internal bzip2 read error"), desc);
429 [ # # ]: 0 : if (actualread == 0) /* EOF. */
430 : 0 : break;
431 : :
432 : 0 : actualwrite = BZ2_bzwrite(bzfile, buffer, actualread);
433 [ # # ]: 0 : if (actualwrite != actualread) {
434 : 0 : const char *errmsg = BZ2_bzerror(bzfile, &bz_errnum);
435 : :
436 [ # # ]: 0 : if (bz_errnum == BZ_IO_ERROR)
437 : 0 : errmsg = strerror(errno);
438 : 0 : ohshit(_("%s: internal bzip2 write error: '%s'"), desc,
439 : : errmsg);
440 : : }
441 : : }
442 : :
443 : 0 : free(buffer);
444 : :
445 : 0 : BZ2_bzWriteClose(&bz_errnum, bzfile, 0, NULL, NULL);
446 [ # # ]: 0 : if (bz_errnum != BZ_OK) {
447 : 0 : const char *errmsg = _("unexpected bzip2 error");
448 : :
449 [ # # ]: 0 : if (bz_errnum == BZ_IO_ERROR)
450 : 0 : errmsg = strerror(errno);
451 : 0 : ohshit(_("%s: internal bzip2 write error: '%s'"), desc,
452 : : errmsg);
453 : : }
454 : :
455 : : /* Because BZ2_bzWriteClose has done a fflush on the file handle,
456 : : * doing a close on the file descriptor associated with it should
457 : : * be safe™. */
458 [ # # ]: 0 : if (close(fd_out))
459 : 0 : ohshite(_("%s: internal bzip2 write error"), desc);
460 : 0 : }
461 : : #else
462 : : static const char *env_bzip2[] = { "BZIP", "BZIP2", NULL };
463 : :
464 : : static void
465 : : decompress_bzip2(struct compress_params *params, int fd_in, int fd_out,
466 : : const char *desc)
467 : : {
468 : : struct command cmd;
469 : :
470 : : command_decompress_init(&cmd, BZIP2, desc);
471 : :
472 : : fd_fd_filter(&cmd, fd_in, fd_out, env_bzip2);
473 : :
474 : : command_destroy(&cmd);
475 : : }
476 : :
477 : : static void
478 : : compress_bzip2(struct compress_params *params, int fd_in, int fd_out,
479 : : const char *desc)
480 : : {
481 : : struct command cmd;
482 : :
483 : : command_compress_init(&cmd, BZIP2, desc, params->level);
484 : :
485 : : fd_fd_filter(&cmd, fd_in, fd_out, env_bzip2);
486 : :
487 : : command_destroy(&cmd);
488 : : }
489 : : #endif
490 : :
491 : : static const struct compressor compressor_bzip2 = {
492 : : .name = "bzip2",
493 : : .extension = ".bz2",
494 : : .default_level = 9,
495 : : .fixup_params = fixup_bzip2_params,
496 : : .compress = compress_bzip2,
497 : : .decompress = decompress_bzip2,
498 : : };
499 : :
500 : : /*
501 : : * Xz compressor.
502 : : */
503 : :
504 : : #define XZ "xz"
505 : :
506 : : #ifdef WITH_LIBLZMA
507 : : struct io_lzma {
508 : : const char *desc;
509 : :
510 : : struct compress_params *params;
511 : :
512 : : enum dpkg_stream_filter filter;
513 : : enum dpkg_stream_action action;
514 : : enum dpkg_stream_status status;
515 : :
516 : : void (*init)(struct io_lzma *io, lzma_stream *s);
517 : : void (*code)(struct io_lzma *io, lzma_stream *s);
518 : : void (*done)(struct io_lzma *io, lzma_stream *s);
519 : : };
520 : :
521 : : /* XXX: liblzma does not expose error messages. */
522 : : static const char *
523 : 0 : dpkg_lzma_strerror(struct io_lzma *io, lzma_ret code)
524 : : {
525 : 0 : const char *const impossible = _("internal error (bug)");
526 : :
527 [ # # # # : 0 : switch (code) {
# # # # ]
528 : 0 : case LZMA_MEM_ERROR:
529 : 0 : return strerror(ENOMEM);
530 : 0 : case LZMA_MEMLIMIT_ERROR:
531 [ # # ]: 0 : if (io->action == DPKG_STREAM_RUN)
532 : 0 : return _("memory usage limit reached");
533 : 0 : return impossible;
534 : 0 : case LZMA_OPTIONS_ERROR:
535 [ # # ]: 0 : if (io->filter == DPKG_STREAM_COMPRESS &&
536 [ # # ]: 0 : io->action == DPKG_STREAM_INIT)
537 : 0 : return _("unsupported compression preset");
538 [ # # ]: 0 : if (io->filter == DPKG_STREAM_DECOMPRESS &&
539 [ # # ]: 0 : io->action == DPKG_STREAM_RUN)
540 : 0 : return _("unsupported options in file header");
541 : 0 : return impossible;
542 : 0 : case LZMA_DATA_ERROR:
543 [ # # ]: 0 : if (io->action == DPKG_STREAM_RUN)
544 : 0 : return _("compressed data is corrupt");
545 : 0 : return impossible;
546 : 0 : case LZMA_BUF_ERROR:
547 [ # # ]: 0 : if (io->action == DPKG_STREAM_RUN)
548 : 0 : return _("unexpected end of input");
549 : 0 : return impossible;
550 : 0 : case LZMA_FORMAT_ERROR:
551 [ # # ]: 0 : if (io->filter == DPKG_STREAM_DECOMPRESS &&
552 [ # # ]: 0 : io->action == DPKG_STREAM_RUN)
553 : 0 : return _("file format not recognized");
554 : 0 : return impossible;
555 : 0 : case LZMA_UNSUPPORTED_CHECK:
556 [ # # ]: 0 : if (io->filter == DPKG_STREAM_COMPRESS &&
557 [ # # ]: 0 : io->action == DPKG_STREAM_INIT)
558 : 0 : return _("unsupported type of integrity check");
559 : 0 : return impossible;
560 : 0 : default:
561 : 0 : return impossible;
562 : : }
563 : : }
564 : :
565 : : static void
566 : 24 : filter_lzma(struct io_lzma *io, int fd_in, int fd_out)
567 : : {
568 : : uint8_t *buf_in;
569 : : uint8_t *buf_out;
570 : 24 : size_t buf_size = DPKG_BUFFER_SIZE;
571 : 24 : lzma_stream s = LZMA_STREAM_INIT;
572 : :
573 : 24 : buf_in = m_malloc(buf_size);
574 : 24 : buf_out = m_malloc(buf_size);
575 : :
576 : 24 : s.next_out = buf_out;
577 : 24 : s.avail_out = buf_size;
578 : :
579 : 24 : io->status = DPKG_STREAM_OK;
580 : 24 : io->action = DPKG_STREAM_INIT;
581 : 24 : io->init(io, &s);
582 : 24 : io->action = DPKG_STREAM_RUN;
583 : :
584 : : do {
585 : : ssize_t len;
586 : :
587 [ + + + + ]: 155 : if (s.avail_in == 0 && io->action != DPKG_STREAM_FINISH) {
588 : 75 : len = fd_read(fd_in, buf_in, buf_size);
589 [ - + ]: 75 : if (len < 0)
590 : 0 : ohshite(_("%s: lzma read error"), io->desc);
591 [ + + ]: 75 : if (len == 0)
592 : 16 : io->action = DPKG_STREAM_FINISH;
593 : 75 : s.next_in = buf_in;
594 : 75 : s.avail_in = len;
595 : : }
596 : :
597 : 155 : io->code(io, &s);
598 : :
599 [ + + + + ]: 155 : if (s.avail_out == 0 || io->status == DPKG_STREAM_END) {
600 : 104 : len = fd_write(fd_out, buf_out, s.next_out - buf_out);
601 [ - + ]: 104 : if (len < 0)
602 : 0 : ohshite(_("%s: lzma write error"), io->desc);
603 : 104 : s.next_out = buf_out;
604 : 104 : s.avail_out = buf_size;
605 : : }
606 [ + + ]: 155 : } while (io->status != DPKG_STREAM_END);
607 : :
608 : 24 : io->done(io, &s);
609 : :
610 : 24 : free(buf_in);
611 : 24 : free(buf_out);
612 : :
613 [ - + ]: 24 : if (close(fd_out))
614 : 0 : ohshite(_("%s: lzma close error"), io->desc);
615 : 24 : }
616 : :
617 : : static void DPKG_ATTR_NORET
618 : 0 : filter_lzma_error(struct io_lzma *io, lzma_ret ret)
619 : : {
620 : 0 : ohshit(_("%s: lzma error: %s"), io->desc,
621 : : dpkg_lzma_strerror(io, ret));
622 : : }
623 : :
624 : : #ifdef HAVE_LZMA_MT_ENCODER
625 : : static uint64_t
626 : 23 : filter_xz_get_memlimit(void)
627 : : {
628 : : uint64_t mt_memlimit;
629 : :
630 : : /* Ask the kernel what is currently available for us. If this fails
631 : : * initialize the memory limit to half the physical RAM, or to 128 MiB
632 : : * if we cannot infer the number. */
633 [ - + ]: 23 : if (meminfo_get_available(&mt_memlimit) < 0) {
634 : 0 : mt_memlimit = lzma_physmem() / 2;
635 [ # # ]: 0 : if (mt_memlimit == 0)
636 : 0 : mt_memlimit = 128 * 1024 * 1024;
637 : : }
638 : : /* Clamp the multi-threaded memory limit to half the addressable
639 : : * memory on this architecture. */
640 [ - + ]: 23 : if (mt_memlimit > INTPTR_MAX)
641 : 0 : mt_memlimit = INTPTR_MAX;
642 : :
643 : 23 : return mt_memlimit;
644 : : }
645 : :
646 : : static uint32_t
647 : 23 : filter_xz_get_cputhreads(struct compress_params *params)
648 : : {
649 : : long threads_max;
650 : :
651 : 23 : threads_max = lzma_cputhreads();
652 [ - + ]: 23 : if (threads_max == 0)
653 : 0 : threads_max = 1;
654 : :
655 [ - + ]: 23 : if (params->threads_max >= 0)
656 [ # # ]: 0 : return clamp(params->threads_max, 1, threads_max);
657 : :
658 : 23 : return threads_max;
659 : : }
660 : : #endif
661 : :
662 : : static void
663 : 7 : filter_unxz_init(struct io_lzma *io, lzma_stream *s)
664 : : {
665 : : #ifdef HAVE_LZMA_MT_DECODER
666 : 7 : lzma_mt mt_options = {
667 : : .flags = 0,
668 : : .block_size = 0,
669 : : .timeout = 0,
670 : : .filters = NULL,
671 : : };
672 : : #else
673 : : uint64_t memlimit = UINT64_MAX;
674 : : #endif
675 : : lzma_ret ret;
676 : :
677 : 7 : io->filter = DPKG_STREAM_DECOMPRESS;
678 : :
679 : : #ifdef HAVE_LZMA_MT_DECODER
680 : 7 : mt_options.memlimit_stop = UINT64_MAX;
681 : 7 : mt_options.memlimit_threading = filter_xz_get_memlimit();
682 : 7 : mt_options.threads = filter_xz_get_cputhreads(io->params);
683 : :
684 : 7 : ret = lzma_stream_decoder_mt(s, &mt_options);
685 : : #else
686 : : ret = lzma_stream_decoder(s, memlimit, 0);
687 : : #endif
688 [ - + ]: 7 : if (ret != LZMA_OK)
689 : 0 : filter_lzma_error(io, ret);
690 : 7 : }
691 : :
692 : : static void
693 : 16 : filter_xz_init(struct io_lzma *io, lzma_stream *s)
694 : : {
695 : : uint32_t preset;
696 : 16 : lzma_check check = LZMA_CHECK_CRC64;
697 : : #ifdef HAVE_LZMA_MT_ENCODER
698 : : uint64_t mt_memlimit;
699 : 16 : lzma_mt mt_options = {
700 : : .flags = 0,
701 : : .block_size = 0,
702 : : .timeout = 0,
703 : : .filters = NULL,
704 : : .check = check,
705 : : };
706 : : #endif
707 : : lzma_ret ret;
708 : :
709 : 16 : io->filter = DPKG_STREAM_COMPRESS;
710 : :
711 : 16 : preset = io->params->level;
712 [ - + ]: 16 : if (io->params->strategy == COMPRESSOR_STRATEGY_EXTREME)
713 : 0 : preset |= LZMA_PRESET_EXTREME;
714 : :
715 : : #ifdef HAVE_LZMA_MT_ENCODER
716 : 16 : mt_options.preset = preset;
717 : 16 : mt_memlimit = filter_xz_get_memlimit();
718 : 16 : mt_options.threads = filter_xz_get_cputhreads(io->params);
719 : :
720 : : /* Guess whether we have enough RAM to use the multi-threaded encoder,
721 : : * and decrease them up to single-threaded to reduce memory usage. */
722 [ + - ]: 16 : for (; mt_options.threads > 1; mt_options.threads--) {
723 : : uint64_t mt_memusage;
724 : :
725 : 16 : mt_memusage = lzma_stream_encoder_mt_memusage(&mt_options);
726 [ + - ]: 16 : if (mt_memusage < mt_memlimit)
727 : 16 : break;
728 : : }
729 : :
730 : 16 : ret = lzma_stream_encoder_mt(s, &mt_options);
731 : : #else
732 : : ret = lzma_easy_encoder(s, preset, check);
733 : : #endif
734 : :
735 [ - + ]: 16 : if (ret != LZMA_OK)
736 : 0 : filter_lzma_error(io, ret);
737 : 16 : }
738 : :
739 : : static void
740 : 155 : filter_lzma_code(struct io_lzma *io, lzma_stream *s)
741 : : {
742 : : lzma_ret ret;
743 : : lzma_action action;
744 : :
745 [ + + ]: 155 : if (io->action == DPKG_STREAM_RUN)
746 : 135 : action = LZMA_RUN;
747 [ + - ]: 20 : else if (io->action == DPKG_STREAM_FINISH)
748 : 20 : action = LZMA_FINISH;
749 : : else
750 : 0 : internerr("unknown stream filter action %d\n", io->action);
751 : :
752 : 155 : ret = lzma_code(s, action);
753 : :
754 [ + + ]: 155 : if (ret == LZMA_STREAM_END)
755 : 24 : io->status = DPKG_STREAM_END;
756 [ - + ]: 131 : else if (ret != LZMA_OK)
757 : 0 : filter_lzma_error(io, ret);
758 : 155 : }
759 : :
760 : : static void
761 : 24 : filter_lzma_done(struct io_lzma *io, lzma_stream *s)
762 : : {
763 : 24 : lzma_end(s);
764 : 24 : }
765 : :
766 : : static void
767 : 7 : decompress_xz(struct compress_params *params, int fd_in, int fd_out,
768 : : const char *desc)
769 : : {
770 : : struct io_lzma io;
771 : :
772 : 7 : io.init = filter_unxz_init;
773 : 7 : io.code = filter_lzma_code;
774 : 7 : io.done = filter_lzma_done;
775 : 7 : io.desc = desc;
776 : 7 : io.params = params;
777 : :
778 : 7 : filter_lzma(&io, fd_in, fd_out);
779 : 7 : }
780 : :
781 : : static void
782 : 16 : compress_xz(struct compress_params *params, int fd_in, int fd_out,
783 : : const char *desc)
784 : : {
785 : : struct io_lzma io;
786 : :
787 : 16 : io.init = filter_xz_init;
788 : 16 : io.code = filter_lzma_code;
789 : 16 : io.done = filter_lzma_done;
790 : 16 : io.desc = desc;
791 : 16 : io.params = params;
792 : :
793 : 16 : filter_lzma(&io, fd_in, fd_out);
794 : 16 : }
795 : : #else
796 : : static const char *env_xz[] = { "XZ_DEFAULTS", "XZ_OPT", NULL };
797 : :
798 : : static void
799 : : decompress_xz(struct compress_params *params, int fd_in, int fd_out,
800 : : const char *desc)
801 : : {
802 : : struct command cmd;
803 : : char *threads_opt = NULL;
804 : :
805 : : command_decompress_init(&cmd, XZ, desc);
806 : :
807 : : if (params->threads_max > 0) {
808 : : threads_opt = str_fmt("-T%d", params->threads_max);
809 : : command_add_arg(&cmd, threads_opt);
810 : : }
811 : :
812 : : fd_fd_filter(&cmd, fd_in, fd_out, env_xz);
813 : :
814 : : command_destroy(&cmd);
815 : : free(threads_opt);
816 : : }
817 : :
818 : : static void
819 : : compress_xz(struct compress_params *params, int fd_in, int fd_out,
820 : : const char *desc)
821 : : {
822 : : struct command cmd;
823 : : char *threads_opt = NULL;
824 : :
825 : : command_compress_init(&cmd, XZ, desc, params->level);
826 : :
827 : : if (params->strategy == COMPRESSOR_STRATEGY_EXTREME)
828 : : command_add_arg(&cmd, "-e");
829 : :
830 : : if (params->threads_max > 0) {
831 : : /* Do not generate warnings when adjusting memory usage, nor
832 : : * exit with non-zero due to those not emitted warnings. */
833 : : command_add_arg(&cmd, "--quiet");
834 : : command_add_arg(&cmd, "--no-warn");
835 : :
836 : : /* Do not let xz fallback to single-threaded mode, to avoid
837 : : * non-reproducible output. */
838 : : command_add_arg(&cmd, "--no-adjust");
839 : :
840 : : /* The xz -T1 option selects a single-threaded mode which
841 : : * generates different output than in multi-threaded mode.
842 : : * To avoid the non-reproducible output we pass -T+1
843 : : * (supported with xz >= 5.4.0) to request multi-threaded
844 : : * mode with a single thread. */
845 : : if (params->threads_max == 1)
846 : : threads_opt = m_strdup("-T+1");
847 : : else
848 : : threads_opt = str_fmt("-T%d", params->threads_max);
849 : : command_add_arg(&cmd, threads_opt);
850 : : }
851 : :
852 : : fd_fd_filter(&cmd, fd_in, fd_out, env_xz);
853 : :
854 : : command_destroy(&cmd);
855 : : free(threads_opt);
856 : : }
857 : : #endif
858 : :
859 : : static const struct compressor compressor_xz = {
860 : : .name = "xz",
861 : : .extension = ".xz",
862 : : .default_level = 6,
863 : : .fixup_params = fixup_none_params,
864 : : .compress = compress_xz,
865 : : .decompress = decompress_xz,
866 : : };
867 : :
868 : : /*
869 : : * Lzma compressor.
870 : : */
871 : :
872 : : #ifdef WITH_LIBLZMA
873 : : static void
874 : 1 : filter_unlzma_init(struct io_lzma *io, lzma_stream *s)
875 : : {
876 : 1 : uint64_t memlimit = UINT64_MAX;
877 : : lzma_ret ret;
878 : :
879 : 1 : io->filter = DPKG_STREAM_DECOMPRESS;
880 : :
881 : 1 : ret = lzma_alone_decoder(s, memlimit);
882 [ - + ]: 1 : if (ret != LZMA_OK)
883 : 0 : filter_lzma_error(io, ret);
884 : 1 : }
885 : :
886 : : static void
887 : 0 : filter_lzma_init(struct io_lzma *io, lzma_stream *s)
888 : : {
889 : : uint32_t preset;
890 : : lzma_options_lzma options;
891 : : lzma_ret ret;
892 : :
893 : 0 : io->filter = DPKG_STREAM_COMPRESS;
894 : :
895 : 0 : preset = io->params->level;
896 [ # # ]: 0 : if (io->params->strategy == COMPRESSOR_STRATEGY_EXTREME)
897 : 0 : preset |= LZMA_PRESET_EXTREME;
898 [ # # ]: 0 : if (lzma_lzma_preset(&options, preset))
899 : 0 : filter_lzma_error(io, LZMA_OPTIONS_ERROR);
900 : :
901 : 0 : ret = lzma_alone_encoder(s, &options);
902 [ # # ]: 0 : if (ret != LZMA_OK)
903 : 0 : filter_lzma_error(io, ret);
904 : 0 : }
905 : :
906 : : static void
907 : 1 : decompress_lzma(struct compress_params *params, int fd_in, int fd_out,
908 : : const char *desc)
909 : : {
910 : : struct io_lzma io;
911 : :
912 : 1 : io.init = filter_unlzma_init;
913 : 1 : io.code = filter_lzma_code;
914 : 1 : io.done = filter_lzma_done;
915 : 1 : io.desc = desc;
916 : 1 : io.params = params;
917 : :
918 : 1 : filter_lzma(&io, fd_in, fd_out);
919 : 1 : }
920 : :
921 : : static void
922 : 0 : compress_lzma(struct compress_params *params, int fd_in, int fd_out,
923 : : const char *desc)
924 : : {
925 : : struct io_lzma io;
926 : :
927 : 0 : io.init = filter_lzma_init;
928 : 0 : io.code = filter_lzma_code;
929 : 0 : io.done = filter_lzma_done;
930 : 0 : io.desc = desc;
931 : 0 : io.params = params;
932 : :
933 : 0 : filter_lzma(&io, fd_in, fd_out);
934 : 0 : }
935 : : #else
936 : : static void
937 : : decompress_lzma(struct compress_params *params, int fd_in, int fd_out,
938 : : const char *desc)
939 : : {
940 : : struct command cmd;
941 : :
942 : : command_decompress_init(&cmd, XZ, desc);
943 : : command_add_arg(&cmd, "--format=lzma");
944 : :
945 : : fd_fd_filter(&cmd, fd_in, fd_out, env_xz);
946 : :
947 : : command_destroy(&cmd);
948 : : }
949 : :
950 : : static void
951 : : compress_lzma(struct compress_params *params, int fd_in, int fd_out,
952 : : const char *desc)
953 : : {
954 : : struct command cmd;
955 : :
956 : : command_compress_init(&cmd, XZ, desc, params->level);
957 : : command_add_arg(&cmd, "--format=lzma");
958 : :
959 : : fd_fd_filter(&cmd, fd_in, fd_out, env_xz);
960 : :
961 : : command_destroy(&cmd);
962 : : }
963 : : #endif
964 : :
965 : : static const struct compressor compressor_lzma = {
966 : : .name = "lzma",
967 : : .extension = ".lzma",
968 : : .default_level = 6,
969 : : .fixup_params = fixup_none_params,
970 : : .compress = compress_lzma,
971 : : .decompress = decompress_lzma,
972 : : };
973 : :
974 : : /*
975 : : * ZStandard compressor.
976 : : */
977 : :
978 : : #define ZSTD "zstd"
979 : :
980 : : #ifdef WITH_LIBZSTD
981 : : struct io_zstd_stream {
982 : : enum dpkg_stream_filter filter;
983 : : enum dpkg_stream_action action;
984 : : enum dpkg_stream_status status;
985 : :
986 : : union {
987 : : ZSTD_CCtx *c;
988 : : ZSTD_DCtx *d;
989 : : } ctx;
990 : :
991 : : const uint8_t *next_in;
992 : : size_t avail_in;
993 : : uint8_t *next_out;
994 : : size_t avail_out;
995 : : };
996 : :
997 : : struct io_zstd {
998 : : const char *desc;
999 : :
1000 : : struct compress_params *params;
1001 : :
1002 : : void (*init)(struct io_zstd *io, struct io_zstd_stream *s);
1003 : : void (*code)(struct io_zstd *io, struct io_zstd_stream *s);
1004 : : void (*done)(struct io_zstd *io, struct io_zstd_stream *s);
1005 : : };
1006 : :
1007 : : static void DPKG_ATTR_NORET
1008 : 0 : filter_zstd_error(struct io_zstd *io, size_t ret)
1009 : : {
1010 : 0 : ohshit(_("%s: zstd error: %s"), io->desc, ZSTD_getErrorName(ret));
1011 : : }
1012 : :
1013 : : static uint32_t
1014 : 2 : filter_zstd_get_cputhreads(struct compress_params *params)
1015 : : {
1016 : : ZSTD_bounds workers;
1017 : 2 : long threads_max = 1;
1018 : :
1019 : : /* The shared library has not been built with multi-threading. */
1020 : 2 : workers = ZSTD_cParam_getBounds(ZSTD_c_nbWorkers);
1021 [ - + ]: 2 : if (workers.upperBound == 0)
1022 : 0 : return 1;
1023 : :
1024 : : #ifdef _SC_NPROCESSORS_ONLN
1025 : 2 : threads_max = sysconf(_SC_NPROCESSORS_ONLN);
1026 [ - + ]: 2 : if (threads_max < 0)
1027 : 0 : return 1;
1028 : : #endif
1029 : :
1030 [ - + ]: 2 : if (params->threads_max >= 0)
1031 [ # # ]: 0 : return clamp(params->threads_max, 1, threads_max);
1032 : :
1033 : 2 : return threads_max;
1034 : : }
1035 : :
1036 : : static size_t
1037 : 6 : filter_zstd_get_buf_in_size(struct io_zstd_stream *s)
1038 : : {
1039 [ + + ]: 6 : if (s->filter == DPKG_STREAM_DECOMPRESS)
1040 : 4 : return ZSTD_DStreamInSize();
1041 : : else
1042 : 2 : return ZSTD_CStreamInSize();
1043 : : }
1044 : :
1045 : : static size_t
1046 : 6 : filter_zstd_get_buf_out_size(struct io_zstd_stream *s)
1047 : : {
1048 [ + + ]: 6 : if (s->filter == DPKG_STREAM_DECOMPRESS)
1049 : 4 : return ZSTD_DStreamOutSize();
1050 : : else
1051 : 2 : return ZSTD_CStreamOutSize();
1052 : : }
1053 : :
1054 : : static void
1055 : 4 : filter_unzstd_init(struct io_zstd *io, struct io_zstd_stream *s)
1056 : : {
1057 : 4 : s->filter = DPKG_STREAM_DECOMPRESS;
1058 : 4 : s->action = DPKG_STREAM_RUN;
1059 : 4 : s->status = DPKG_STREAM_OK;
1060 : :
1061 : 4 : s->ctx.d = ZSTD_createDCtx();
1062 [ - + ]: 4 : if (s->ctx.d == NULL)
1063 : 0 : ohshit(_("%s: cannot create zstd decompression context"),
1064 : : io->desc);
1065 : 4 : }
1066 : :
1067 : : static void
1068 : 16 : filter_unzstd_code(struct io_zstd *io, struct io_zstd_stream *s)
1069 : : {
1070 : 16 : ZSTD_inBuffer buf_in = { s->next_in, s->avail_in, 0 };
1071 : 16 : ZSTD_outBuffer buf_out = { s->next_out, s->avail_out, 0 };
1072 : : size_t ret;
1073 : :
1074 : 16 : ret = ZSTD_decompressStream(s->ctx.d, &buf_out, &buf_in);
1075 [ - + ]: 16 : if (ZSTD_isError(ret))
1076 : 0 : filter_zstd_error(io, ret);
1077 : :
1078 : 16 : s->next_in += buf_in.pos;
1079 : 16 : s->avail_in -= buf_in.pos;
1080 : 16 : s->next_out += buf_out.pos;
1081 : 16 : s->avail_out -= buf_out.pos;
1082 : :
1083 [ + + ]: 16 : if (ret == 0)
1084 : 4 : s->status = DPKG_STREAM_END;
1085 : 16 : }
1086 : :
1087 : : static void
1088 : 4 : filter_unzstd_done(struct io_zstd *io, struct io_zstd_stream *s)
1089 : : {
1090 : 4 : ZSTD_freeDCtx(s->ctx.d);
1091 : 4 : }
1092 : :
1093 : : static void
1094 : 2 : filter_zstd_init(struct io_zstd *io, struct io_zstd_stream *s)
1095 : : {
1096 : 2 : int clevel = io->params->level;
1097 : : uint32_t nthreads;
1098 : : size_t ret;
1099 : :
1100 : 2 : s->filter = DPKG_STREAM_COMPRESS;
1101 : 2 : s->action = DPKG_STREAM_RUN;
1102 : 2 : s->status = DPKG_STREAM_OK;
1103 : :
1104 : 2 : s->ctx.c = ZSTD_createCCtx();
1105 [ - + ]: 2 : if (s->ctx.c == NULL)
1106 : 0 : ohshit(_("%s: cannot create zstd compression context"),
1107 : : io->desc);
1108 : :
1109 : 2 : ret = ZSTD_CCtx_setParameter(s->ctx.c, ZSTD_c_compressionLevel, clevel);
1110 [ - + ]: 2 : if (ZSTD_isError(ret))
1111 : 0 : filter_zstd_error(io, ret);
1112 : 2 : ret = ZSTD_CCtx_setParameter(s->ctx.c, ZSTD_c_checksumFlag, 1);
1113 [ - + ]: 2 : if (ZSTD_isError(ret))
1114 : 0 : filter_zstd_error(io, ret);
1115 : :
1116 : 2 : nthreads = filter_zstd_get_cputhreads(io->params);
1117 [ + - ]: 2 : if (nthreads > 1)
1118 : 2 : ZSTD_CCtx_setParameter(s->ctx.c, ZSTD_c_nbWorkers, nthreads);
1119 : 2 : }
1120 : :
1121 : : static void
1122 : 7 : filter_zstd_code(struct io_zstd *io, struct io_zstd_stream *s)
1123 : : {
1124 : 7 : ZSTD_inBuffer buf_in = { s->next_in, s->avail_in, 0 };
1125 : 7 : ZSTD_outBuffer buf_out = { s->next_out, s->avail_out, 0 };
1126 : : ZSTD_EndDirective action;
1127 : : size_t ret;
1128 : :
1129 [ + + ]: 7 : if (s->action == DPKG_STREAM_FINISH)
1130 : 3 : action = ZSTD_e_end;
1131 : : else
1132 : 4 : action = ZSTD_e_continue;
1133 : :
1134 : 7 : ret = ZSTD_compressStream2(s->ctx.c, &buf_out, &buf_in, action);
1135 [ - + ]: 7 : if (ZSTD_isError(ret))
1136 : 0 : filter_zstd_error(io, ret);
1137 : :
1138 : 7 : s->next_in += buf_in.pos;
1139 : 7 : s->avail_in -= buf_in.pos;
1140 : 7 : s->next_out += buf_out.pos;
1141 : 7 : s->avail_out -= buf_out.pos;
1142 : :
1143 [ + + + + ]: 7 : if (s->action == DPKG_STREAM_FINISH && ret == 0)
1144 : 2 : s->status = DPKG_STREAM_END;
1145 : 7 : }
1146 : :
1147 : : static void
1148 : 2 : filter_zstd_done(struct io_zstd *io, struct io_zstd_stream *s)
1149 : : {
1150 : 2 : ZSTD_freeCCtx(s->ctx.c);
1151 : 2 : }
1152 : :
1153 : : static void
1154 : 6 : filter_zstd(struct io_zstd *io, int fd_in, int fd_out)
1155 : : {
1156 : : ssize_t buf_in_size;
1157 : : ssize_t buf_out_size;
1158 : : uint8_t *buf_in;
1159 : : uint8_t *buf_out;
1160 : 6 : struct io_zstd_stream s = {
1161 : : .action = DPKG_STREAM_INIT,
1162 : : };
1163 : :
1164 : 6 : io->init(io, &s);
1165 : :
1166 : 6 : buf_in_size = filter_zstd_get_buf_in_size(&s);
1167 : 6 : buf_in = m_malloc(buf_in_size);
1168 : 6 : buf_out_size = filter_zstd_get_buf_out_size(&s);
1169 : 6 : buf_out = m_malloc(buf_out_size);
1170 : :
1171 : 6 : s.next_out = buf_out;
1172 : 6 : s.avail_out = buf_out_size;
1173 : :
1174 : : do {
1175 : : ssize_t len;
1176 : :
1177 [ + + + + ]: 23 : if (s.avail_in == 0 && s.action != DPKG_STREAM_FINISH) {
1178 : 13 : len = fd_read(fd_in, buf_in, buf_in_size);
1179 [ - + ]: 13 : if (len < 0)
1180 : 0 : ohshite(_("%s: zstd read error"), io->desc);
1181 [ + + ]: 13 : if (len < buf_in_size)
1182 : 6 : s.action = DPKG_STREAM_FINISH;
1183 : 13 : s.next_in = buf_in;
1184 : 13 : s.avail_in = len;
1185 : : }
1186 : :
1187 : 23 : io->code(io, &s);
1188 : :
1189 [ + + + + ]: 23 : if (s.avail_out == 0 || s.status == DPKG_STREAM_END) {
1190 : 19 : len = fd_write(fd_out, buf_out, s.next_out - buf_out);
1191 [ - + ]: 19 : if (len < 0)
1192 : 0 : ohshite(_("%s: zstd write error"), io->desc);
1193 : 19 : s.next_out = buf_out;
1194 : 19 : s.avail_out = buf_out_size;
1195 : : }
1196 [ + + ]: 23 : } while (s.status != DPKG_STREAM_END);
1197 : :
1198 : 6 : io->done(io, &s);
1199 : :
1200 : 6 : free(buf_in);
1201 : 6 : free(buf_out);
1202 : :
1203 [ - + ]: 6 : if (close(fd_out))
1204 : 0 : ohshite(_("%s: zstd close error"), io->desc);
1205 : 6 : }
1206 : :
1207 : : static void
1208 : 4 : decompress_zstd(struct compress_params *params, int fd_in, int fd_out,
1209 : : const char *desc)
1210 : : {
1211 : : struct io_zstd io;
1212 : :
1213 : 4 : io.init = filter_unzstd_init;
1214 : 4 : io.code = filter_unzstd_code;
1215 : 4 : io.done = filter_unzstd_done;
1216 : 4 : io.desc = desc;
1217 : 4 : io.params = params;
1218 : :
1219 : 4 : filter_zstd(&io, fd_in, fd_out);
1220 : 4 : }
1221 : :
1222 : : static void
1223 : 2 : compress_zstd(struct compress_params *params, int fd_in, int fd_out,
1224 : : const char *desc)
1225 : : {
1226 : : struct io_zstd io;
1227 : :
1228 : 2 : io.init = filter_zstd_init;
1229 : 2 : io.code = filter_zstd_code;
1230 : 2 : io.done = filter_zstd_done;
1231 : 2 : io.desc = desc;
1232 : 2 : io.params = params;
1233 : :
1234 : 2 : filter_zstd(&io, fd_in, fd_out);
1235 : 2 : }
1236 : : #else
1237 : : static const char *env_zstd[] = {
1238 : : "ZSTD_CLEVEL",
1239 : : "ZSTD_NBTHREADS",
1240 : : NULL,
1241 : : };
1242 : :
1243 : : static void
1244 : : decompress_zstd(struct compress_params *params, int fd_in, int fd_out,
1245 : : const char *desc)
1246 : : {
1247 : : struct command cmd;
1248 : : char *threads_opt = NULL;
1249 : :
1250 : : command_decompress_init(&cmd, ZSTD, desc);
1251 : : command_add_arg(&cmd, "-q");
1252 : :
1253 : : if (params->threads_max > 0) {
1254 : : threads_opt = str_fmt("-T%d", params->threads_max);
1255 : : command_add_arg(&cmd, threads_opt);
1256 : : }
1257 : :
1258 : : fd_fd_filter(&cmd, fd_in, fd_out, env_zstd);
1259 : :
1260 : : command_destroy(&cmd);
1261 : : free(threads_opt);
1262 : : }
1263 : :
1264 : : static void
1265 : : compress_zstd(struct compress_params *params, int fd_in, int fd_out,
1266 : : const char *desc)
1267 : : {
1268 : : struct command cmd;
1269 : : char *threads_opt = NULL;
1270 : :
1271 : : command_compress_init(&cmd, ZSTD, desc, params->level);
1272 : : command_add_arg(&cmd, "-q");
1273 : :
1274 : : if (params->level > 19)
1275 : : command_add_arg(&cmd, "--ultra");
1276 : :
1277 : : if (params->threads_max > 0) {
1278 : : threads_opt = str_fmt("-T%d", params->threads_max);
1279 : : command_add_arg(&cmd, threads_opt);
1280 : : }
1281 : :
1282 : : fd_fd_filter(&cmd, fd_in, fd_out, env_zstd);
1283 : :
1284 : : command_destroy(&cmd);
1285 : : free(threads_opt);
1286 : : }
1287 : : #endif
1288 : :
1289 : : static const struct compressor compressor_zstd = {
1290 : : .name = "zstd",
1291 : : .extension = ".zst",
1292 : : .default_level = DPKG_ZSTD_CLEVEL_DEFAULT,
1293 : : .fixup_params = fixup_none_params,
1294 : : .compress = compress_zstd,
1295 : : .decompress = decompress_zstd,
1296 : : };
1297 : :
1298 : : /*
1299 : : * Generic compressor filter.
1300 : : */
1301 : :
1302 : : static const struct compressor *compressor_array[] = {
1303 : : [COMPRESSOR_TYPE_NONE] = &compressor_none,
1304 : : [COMPRESSOR_TYPE_GZIP] = &compressor_gzip,
1305 : : [COMPRESSOR_TYPE_XZ] = &compressor_xz,
1306 : : [COMPRESSOR_TYPE_ZSTD] = &compressor_zstd,
1307 : : [COMPRESSOR_TYPE_BZIP2] = &compressor_bzip2,
1308 : : [COMPRESSOR_TYPE_LZMA] = &compressor_lzma,
1309 : : };
1310 : :
1311 : : static const struct compressor *
1312 : 262 : compressor(enum compressor_type type)
1313 : : {
1314 : 262 : const enum compressor_type max_type = array_count(compressor_array);
1315 : :
1316 [ + - - + ]: 262 : if (type < 0 || type >= max_type)
1317 : 0 : internerr("compressor_type %d is out of range", type);
1318 : :
1319 : 262 : return compressor_array[type];
1320 : : }
1321 : :
1322 : : const char *
1323 : 0 : compressor_get_name(enum compressor_type type)
1324 : : {
1325 : 0 : return compressor(type)->name;
1326 : : }
1327 : :
1328 : : const char *
1329 : 33 : compressor_get_extension(enum compressor_type type)
1330 : : {
1331 : 33 : return compressor(type)->extension;
1332 : : }
1333 : :
1334 : : enum compressor_type
1335 : 10 : compressor_find_by_name(const char *name)
1336 : : {
1337 : : size_t i;
1338 : :
1339 [ + - ]: 16 : for (i = 0; i < array_count(compressor_array); i++)
1340 [ + + ]: 16 : if (strcmp(compressor_array[i]->name, name) == 0)
1341 : 10 : return i;
1342 : :
1343 : 0 : return COMPRESSOR_TYPE_UNKNOWN;
1344 : : }
1345 : :
1346 : : enum compressor_type
1347 : 46 : compressor_find_by_extension(const char *extension)
1348 : : {
1349 : : size_t i;
1350 : :
1351 [ + + ]: 109 : for (i = 0; i < array_count(compressor_array); i++)
1352 [ + + ]: 108 : if (strcmp(compressor_array[i]->extension, extension) == 0)
1353 : 45 : return i;
1354 : :
1355 : 1 : return COMPRESSOR_TYPE_UNKNOWN;
1356 : : }
1357 : :
1358 : : enum compressor_strategy
1359 : 0 : compressor_get_strategy(const char *name)
1360 : : {
1361 [ # # ]: 0 : if (strcmp(name, "none") == 0)
1362 : 0 : return COMPRESSOR_STRATEGY_NONE;
1363 [ # # ]: 0 : if (strcmp(name, "filtered") == 0)
1364 : 0 : return COMPRESSOR_STRATEGY_FILTERED;
1365 [ # # ]: 0 : if (strcmp(name, "huffman") == 0)
1366 : 0 : return COMPRESSOR_STRATEGY_HUFFMAN;
1367 [ # # ]: 0 : if (strcmp(name, "rle") == 0)
1368 : 0 : return COMPRESSOR_STRATEGY_RLE;
1369 [ # # ]: 0 : if (strcmp(name, "fixed") == 0)
1370 : 0 : return COMPRESSOR_STRATEGY_FIXED;
1371 [ # # ]: 0 : if (strcmp(name, "extreme") == 0)
1372 : 0 : return COMPRESSOR_STRATEGY_EXTREME;
1373 : :
1374 : 0 : return COMPRESSOR_STRATEGY_UNKNOWN;
1375 : : }
1376 : :
1377 : : static void
1378 : 78 : compressor_fixup_params(struct compress_params *params)
1379 : : {
1380 : 78 : compressor(params->type)->fixup_params(params);
1381 : :
1382 [ + - ]: 78 : if (params->level < 0)
1383 : 78 : params->level = compressor(params->type)->default_level;
1384 : 78 : }
1385 : :
1386 : : bool
1387 : 78 : compressor_check_params(struct compress_params *params, struct dpkg_error *err)
1388 : : {
1389 : 78 : compressor_fixup_params(params);
1390 : :
1391 [ + + + - ]: 79 : if ((params->type == COMPRESSOR_TYPE_ZSTD &&
1392 : 1 : params->level > DPKG_ZSTD_MAX_LEVEL) ||
1393 [ + + ]: 78 : (params->type != COMPRESSOR_TYPE_ZSTD &&
1394 [ - + ]: 77 : params->level > 9)) {
1395 : 0 : dpkg_put_error(err, _("invalid compression level %d"),
1396 : : params->level);
1397 : 0 : return false;
1398 : : }
1399 : :
1400 [ + - ]: 78 : if (params->strategy == COMPRESSOR_STRATEGY_NONE)
1401 : 78 : return true;
1402 : :
1403 [ # # ]: 0 : if (params->type == COMPRESSOR_TYPE_GZIP &&
1404 [ # # ]: 0 : (params->strategy == COMPRESSOR_STRATEGY_FILTERED ||
1405 [ # # ]: 0 : params->strategy == COMPRESSOR_STRATEGY_HUFFMAN ||
1406 [ # # ]: 0 : params->strategy == COMPRESSOR_STRATEGY_RLE ||
1407 [ # # ]: 0 : params->strategy == COMPRESSOR_STRATEGY_FIXED))
1408 : 0 : return true;
1409 : :
1410 [ # # ]: 0 : if (params->type == COMPRESSOR_TYPE_XZ &&
1411 [ # # ]: 0 : params->strategy == COMPRESSOR_STRATEGY_EXTREME)
1412 : 0 : return true;
1413 : :
1414 : 0 : dpkg_put_error(err, _("unknown compression strategy"));
1415 : 0 : return false;
1416 : : }
1417 : :
1418 : : void
1419 : 37 : decompress_filter(struct compress_params *params, int fd_in, int fd_out,
1420 : : const char *desc_fmt, ...)
1421 : : {
1422 : : va_list args;
1423 : 37 : struct varbuf desc = VARBUF_INIT;
1424 : :
1425 : 37 : va_start(args, desc_fmt);
1426 : 37 : varbuf_vprintf(&desc, desc_fmt, args);
1427 : 37 : va_end(args);
1428 : :
1429 : 37 : compressor(params->type)->decompress(params, fd_in, fd_out, desc.buf);
1430 : :
1431 : 37 : varbuf_destroy(&desc);
1432 : 37 : }
1433 : :
1434 : : void
1435 : 36 : compress_filter(struct compress_params *params, int fd_in, int fd_out,
1436 : : const char *desc_fmt, ...)
1437 : : {
1438 : : va_list args;
1439 : 36 : struct varbuf desc = VARBUF_INIT;
1440 : :
1441 : 36 : va_start(args, desc_fmt);
1442 : 36 : varbuf_vprintf(&desc, desc_fmt, args);
1443 : 36 : va_end(args);
1444 : :
1445 : 36 : compressor(params->type)->compress(params, fd_in, fd_out, desc.buf);
1446 : :
1447 : 36 : varbuf_destroy(&desc);
1448 : 36 : }
|