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
fd_fd_filter(int fd_in,int fd_out,const char * desc,const char * delenv[],const char * file,...)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
fixup_none_params(struct compress_params * params)103 fixup_none_params(struct compress_params *params)
104 {
105 }
106 
107 static void
decompress_none(int fd_in,int fd_out,const char * desc)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
compress_none(int fd_in,int fd_out,struct compress_params * params,const char * desc)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
fixup_gzip_params(struct compress_params * params)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
decompress_gzip(int fd_in,int fd_out,const char * desc)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
compress_gzip(int fd_in,int fd_out,struct compress_params * params,const char * desc)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
decompress_gzip(int fd_in,int fd_out,const char * desc)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
compress_gzip(int fd_in,int fd_out,struct compress_params * params,const char * desc)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
fixup_bzip2_params(struct compress_params * params)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
decompress_bzip2(int fd_in,int fd_out,const char * desc)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
compress_bzip2(int fd_in,int fd_out,struct compress_params * params,const char * desc)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
decompress_bzip2(int fd_in,int fd_out,const char * desc)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
compress_bzip2(int fd_in,int fd_out,struct compress_params * params,const char * desc)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 *
dpkg_lzma_strerror(lzma_ret code,enum dpkg_stream_status status)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
filter_lzma(struct io_lzma * io,int fd_in,int fd_out)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
filter_lzma_error(struct io_lzma * io,lzma_ret ret)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
filter_unxz_init(struct io_lzma * io,lzma_stream * s)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
filter_xz_init(struct io_lzma * io,lzma_stream * s)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
filter_lzma_code(struct io_lzma * io,lzma_stream * s)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
filter_lzma_done(struct io_lzma * io,lzma_stream * s)598 filter_lzma_done(struct io_lzma *io, lzma_stream *s)
599 {
600 	lzma_end(s);
601 }
602 
603 static void
decompress_xz(int fd_in,int fd_out,const char * desc)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
compress_xz(int fd_in,int fd_out,struct compress_params * params,const char * desc)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
decompress_xz(int fd_in,int fd_out,const char * desc)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
compress_xz(int fd_in,int fd_out,struct compress_params * params,const char * desc)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
filter_unlzma_init(struct io_lzma * io,lzma_stream * s)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
filter_lzma_init(struct io_lzma * io,lzma_stream * s)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
decompress_lzma(int fd_in,int fd_out,const char * desc)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
compress_lzma(int fd_in,int fd_out,struct compress_params * params,const char * desc)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
decompress_lzma(int fd_in,int fd_out,const char * desc)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
compress_lzma(int fd_in,int fd_out,struct compress_params * params,const char * desc)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 *
compressor(enum compressor_type type)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 *
compressor_get_name(enum compressor_type type)777 compressor_get_name(enum compressor_type type)
778 {
779 	return compressor(type)->name;
780 }
781 
782 const char *
compressor_get_extension(enum compressor_type type)783 compressor_get_extension(enum compressor_type type)
784 {
785 	return compressor(type)->extension;
786 }
787 
788 enum compressor_type
compressor_find_by_name(const char * name)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
compressor_find_by_extension(const char * extension)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
compressor_get_strategy(const char * name)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
compressor_fixup_params(struct compress_params * params)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
compressor_check_params(struct compress_params * params,struct dpkg_error * err)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
decompress_filter(enum compressor_type type,int fd_in,int fd_out,const char * desc_fmt,...)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
compress_filter(struct compress_params * params,int fd_in,int fd_out,const char * desc_fmt,...)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 }
894