xref: /freebsd/contrib/xz/src/xzdec/xzdec.c (revision 26743408e9ff53ac0e041407c359ed3c17c15596)
13b35e7eeSXin LI // SPDX-License-Identifier: 0BSD
23b35e7eeSXin LI 
381ad8388SMartin Matuska ///////////////////////////////////////////////////////////////////////////////
481ad8388SMartin Matuska //
581ad8388SMartin Matuska /// \file       xzdec.c
681ad8388SMartin Matuska /// \brief      Simple single-threaded tool to uncompress .xz or .lzma files
781ad8388SMartin Matuska //
881ad8388SMartin Matuska //  Author:     Lasse Collin
981ad8388SMartin Matuska //
1081ad8388SMartin Matuska ///////////////////////////////////////////////////////////////////////////////
1181ad8388SMartin Matuska 
1281ad8388SMartin Matuska #include "sysdefs.h"
1381ad8388SMartin Matuska #include "lzma.h"
1481ad8388SMartin Matuska 
1581ad8388SMartin Matuska #include <stdarg.h>
1681ad8388SMartin Matuska #include <errno.h>
1781ad8388SMartin Matuska #include <stdio.h>
183b35e7eeSXin LI 
193b35e7eeSXin LI #ifndef _MSC_VER
2081ad8388SMartin Matuska #	include <unistd.h>
213b35e7eeSXin LI #endif
223b35e7eeSXin LI 
233b35e7eeSXin LI #ifdef HAVE_CAP_RIGHTS_LIMIT
243b35e7eeSXin LI #	include <sys/capsicum.h>
253b35e7eeSXin LI #endif
263b35e7eeSXin LI 
273b35e7eeSXin LI #ifdef HAVE_LINUX_LANDLOCK
283b35e7eeSXin LI #	include <linux/landlock.h>
293b35e7eeSXin LI #	include <sys/prctl.h>
303b35e7eeSXin LI #	include <sys/syscall.h>
313b35e7eeSXin LI #	ifdef LANDLOCK_ACCESS_NET_BIND_TCP
323b35e7eeSXin LI #		define LANDLOCK_ABI_MAX 4
333b35e7eeSXin LI #	else
343b35e7eeSXin LI #		define LANDLOCK_ABI_MAX 3
353b35e7eeSXin LI #	endif
363b35e7eeSXin LI #endif
373b35e7eeSXin LI 
383b35e7eeSXin LI #if defined(HAVE_CAP_RIGHTS_LIMIT) || defined(HAVE_PLEDGE) \
393b35e7eeSXin LI 		|| defined(HAVE_LINUX_LANDLOCK)
403b35e7eeSXin LI #	define ENABLE_SANDBOX 1
413b35e7eeSXin LI #endif
4281ad8388SMartin Matuska 
4381ad8388SMartin Matuska #include "getopt.h"
4481ad8388SMartin Matuska #include "tuklib_progname.h"
4581ad8388SMartin Matuska #include "tuklib_exit.h"
4681ad8388SMartin Matuska 
4781ad8388SMartin Matuska #ifdef TUKLIB_DOSLIKE
4881ad8388SMartin Matuska #	include <fcntl.h>
4981ad8388SMartin Matuska #	include <io.h>
503b35e7eeSXin LI #	ifdef _MSC_VER
513b35e7eeSXin LI #		define fileno _fileno
523b35e7eeSXin LI #		define setmode _setmode
533b35e7eeSXin LI #	endif
5481ad8388SMartin Matuska #endif
5581ad8388SMartin Matuska 
5681ad8388SMartin Matuska 
5781ad8388SMartin Matuska #ifdef LZMADEC
5881ad8388SMartin Matuska #	define TOOL_FORMAT "lzma"
5981ad8388SMartin Matuska #else
6081ad8388SMartin Matuska #	define TOOL_FORMAT "xz"
6181ad8388SMartin Matuska #endif
6281ad8388SMartin Matuska 
6381ad8388SMartin Matuska 
6481ad8388SMartin Matuska /// Error messages are suppressed if this is zero, which is the case when
6581ad8388SMartin Matuska /// --quiet has been given at least twice.
66a8675d92SXin LI static int display_errors = 2;
6781ad8388SMartin Matuska 
6881ad8388SMartin Matuska 
69ca6a6373SXin LI lzma_attribute((__format__(__printf__, 1, 2)))
70ca6a6373SXin LI static void
7181ad8388SMartin Matuska my_errorf(const char *fmt, ...)
7281ad8388SMartin Matuska {
7381ad8388SMartin Matuska 	va_list ap;
7481ad8388SMartin Matuska 	va_start(ap, fmt);
7581ad8388SMartin Matuska 
7681ad8388SMartin Matuska 	if (display_errors) {
7781ad8388SMartin Matuska 		fprintf(stderr, "%s: ", progname);
7881ad8388SMartin Matuska 		vfprintf(stderr, fmt, ap);
7981ad8388SMartin Matuska 		fprintf(stderr, "\n");
8081ad8388SMartin Matuska 	}
8181ad8388SMartin Matuska 
8281ad8388SMartin Matuska 	va_end(ap);
8381ad8388SMartin Matuska 	return;
8481ad8388SMartin Matuska }
8581ad8388SMartin Matuska 
8681ad8388SMartin Matuska 
87ca6a6373SXin LI tuklib_attr_noreturn
88ca6a6373SXin LI static void
8981ad8388SMartin Matuska help(void)
9081ad8388SMartin Matuska {
9181ad8388SMartin Matuska 	printf(
9281ad8388SMartin Matuska "Usage: %s [OPTION]... [FILE]...\n"
9342b10a37SXin LI "Decompress files in the ." TOOL_FORMAT " format to standard output.\n"
9481ad8388SMartin Matuska "\n"
9542b10a37SXin LI "  -d, --decompress   (ignored, only decompression is supported)\n"
9642b10a37SXin LI "  -k, --keep         (ignored, files are never deleted)\n"
9742b10a37SXin LI "  -c, --stdout       (ignored, output is always written to standard output)\n"
9881ad8388SMartin Matuska "  -q, --quiet        specify *twice* to suppress errors\n"
9942b10a37SXin LI "  -Q, --no-warn      (ignored, the exit status 2 is never used)\n"
10081ad8388SMartin Matuska "  -h, --help         display this help and exit\n"
10181ad8388SMartin Matuska "  -V, --version      display the version number and exit\n"
10281ad8388SMartin Matuska "\n"
10381ad8388SMartin Matuska "With no FILE, or when FILE is -, read standard input.\n"
10481ad8388SMartin Matuska "\n"
10581ad8388SMartin Matuska "Report bugs to <" PACKAGE_BUGREPORT "> (in English or Finnish).\n"
106e0f0e66dSMartin Matuska PACKAGE_NAME " home page: <" PACKAGE_URL ">\n", progname);
107e0f0e66dSMartin Matuska 
10881ad8388SMartin Matuska 	tuklib_exit(EXIT_SUCCESS, EXIT_FAILURE, display_errors);
10981ad8388SMartin Matuska }
11081ad8388SMartin Matuska 
11181ad8388SMartin Matuska 
112ca6a6373SXin LI tuklib_attr_noreturn
113ca6a6373SXin LI static void
11481ad8388SMartin Matuska version(void)
11581ad8388SMartin Matuska {
11681ad8388SMartin Matuska 	printf(TOOL_FORMAT "dec (" PACKAGE_NAME ") " LZMA_VERSION_STRING "\n"
11781ad8388SMartin Matuska 			"liblzma %s\n", lzma_version_string());
11881ad8388SMartin Matuska 
11981ad8388SMartin Matuska 	tuklib_exit(EXIT_SUCCESS, EXIT_FAILURE, display_errors);
12081ad8388SMartin Matuska }
12181ad8388SMartin Matuska 
12281ad8388SMartin Matuska 
12381ad8388SMartin Matuska /// Parses command line options.
12481ad8388SMartin Matuska static void
12581ad8388SMartin Matuska parse_options(int argc, char **argv)
12681ad8388SMartin Matuska {
127*26743408SXin LI 	static const char short_opts[] = "cdkhqQV";
12881ad8388SMartin Matuska 	static const struct option long_opts[] = {
12981ad8388SMartin Matuska 		{ "stdout",       no_argument,         NULL, 'c' },
13081ad8388SMartin Matuska 		{ "to-stdout",    no_argument,         NULL, 'c' },
13181ad8388SMartin Matuska 		{ "decompress",   no_argument,         NULL, 'd' },
13281ad8388SMartin Matuska 		{ "uncompress",   no_argument,         NULL, 'd' },
13381ad8388SMartin Matuska 		{ "keep",         no_argument,         NULL, 'k' },
13481ad8388SMartin Matuska 		{ "quiet",        no_argument,         NULL, 'q' },
13581ad8388SMartin Matuska 		{ "no-warn",      no_argument,         NULL, 'Q' },
13681ad8388SMartin Matuska 		{ "help",         no_argument,         NULL, 'h' },
13781ad8388SMartin Matuska 		{ "version",      no_argument,         NULL, 'V' },
13881ad8388SMartin Matuska 		{ NULL,           0,                   NULL, 0   }
13981ad8388SMartin Matuska 	};
14081ad8388SMartin Matuska 
14181ad8388SMartin Matuska 	int c;
14281ad8388SMartin Matuska 
14381ad8388SMartin Matuska 	while ((c = getopt_long(argc, argv, short_opts, long_opts, NULL))
14481ad8388SMartin Matuska 			!= -1) {
14581ad8388SMartin Matuska 		switch (c) {
14681ad8388SMartin Matuska 		case 'c':
14781ad8388SMartin Matuska 		case 'd':
14881ad8388SMartin Matuska 		case 'k':
14981ad8388SMartin Matuska 		case 'Q':
15081ad8388SMartin Matuska 			break;
15181ad8388SMartin Matuska 
15281ad8388SMartin Matuska 		case 'q':
15381ad8388SMartin Matuska 			if (display_errors > 0)
15481ad8388SMartin Matuska 				--display_errors;
15581ad8388SMartin Matuska 
15681ad8388SMartin Matuska 			break;
15781ad8388SMartin Matuska 
15881ad8388SMartin Matuska 		case 'h':
15981ad8388SMartin Matuska 			help();
16081ad8388SMartin Matuska 
16181ad8388SMartin Matuska 		case 'V':
16281ad8388SMartin Matuska 			version();
16381ad8388SMartin Matuska 
16481ad8388SMartin Matuska 		default:
16581ad8388SMartin Matuska 			exit(EXIT_FAILURE);
16681ad8388SMartin Matuska 		}
16781ad8388SMartin Matuska 	}
16881ad8388SMartin Matuska 
16981ad8388SMartin Matuska 	return;
17081ad8388SMartin Matuska }
17181ad8388SMartin Matuska 
17281ad8388SMartin Matuska 
17381ad8388SMartin Matuska static void
17481ad8388SMartin Matuska uncompress(lzma_stream *strm, FILE *file, const char *filename)
17581ad8388SMartin Matuska {
17681ad8388SMartin Matuska 	lzma_ret ret;
17781ad8388SMartin Matuska 
17881ad8388SMartin Matuska 	// Initialize the decoder
17981ad8388SMartin Matuska #ifdef LZMADEC
180e0f0e66dSMartin Matuska 	ret = lzma_alone_decoder(strm, UINT64_MAX);
18181ad8388SMartin Matuska #else
182e0f0e66dSMartin Matuska 	ret = lzma_stream_decoder(strm, UINT64_MAX, LZMA_CONCATENATED);
18381ad8388SMartin Matuska #endif
18481ad8388SMartin Matuska 
18581ad8388SMartin Matuska 	// The only reasonable error here is LZMA_MEM_ERROR.
18681ad8388SMartin Matuska 	if (ret != LZMA_OK) {
18781ad8388SMartin Matuska 		my_errorf("%s", ret == LZMA_MEM_ERROR ? strerror(ENOMEM)
18881ad8388SMartin Matuska 				: "Internal error (bug)");
18981ad8388SMartin Matuska 		exit(EXIT_FAILURE);
19081ad8388SMartin Matuska 	}
19181ad8388SMartin Matuska 
19281ad8388SMartin Matuska 	// Input and output buffers
19381ad8388SMartin Matuska 	uint8_t in_buf[BUFSIZ];
19481ad8388SMartin Matuska 	uint8_t out_buf[BUFSIZ];
19581ad8388SMartin Matuska 
19681ad8388SMartin Matuska 	strm->avail_in = 0;
19781ad8388SMartin Matuska 	strm->next_out = out_buf;
19881ad8388SMartin Matuska 	strm->avail_out = BUFSIZ;
19981ad8388SMartin Matuska 
20081ad8388SMartin Matuska 	lzma_action action = LZMA_RUN;
20181ad8388SMartin Matuska 
20281ad8388SMartin Matuska 	while (true) {
20381ad8388SMartin Matuska 		if (strm->avail_in == 0) {
20481ad8388SMartin Matuska 			strm->next_in = in_buf;
20581ad8388SMartin Matuska 			strm->avail_in = fread(in_buf, 1, BUFSIZ, file);
20681ad8388SMartin Matuska 
20781ad8388SMartin Matuska 			if (ferror(file)) {
20881ad8388SMartin Matuska 				// POSIX says that fread() sets errno if
20981ad8388SMartin Matuska 				// an error occurred. ferror() doesn't
21081ad8388SMartin Matuska 				// touch errno.
21181ad8388SMartin Matuska 				my_errorf("%s: Error reading input file: %s",
21281ad8388SMartin Matuska 						filename, strerror(errno));
21381ad8388SMartin Matuska 				exit(EXIT_FAILURE);
21481ad8388SMartin Matuska 			}
21581ad8388SMartin Matuska 
21681ad8388SMartin Matuska #ifndef LZMADEC
21781ad8388SMartin Matuska 			// When using LZMA_CONCATENATED, we need to tell
21881ad8388SMartin Matuska 			// liblzma when it has got all the input.
21981ad8388SMartin Matuska 			if (feof(file))
22081ad8388SMartin Matuska 				action = LZMA_FINISH;
22181ad8388SMartin Matuska #endif
22281ad8388SMartin Matuska 		}
22381ad8388SMartin Matuska 
22481ad8388SMartin Matuska 		ret = lzma_code(strm, action);
22581ad8388SMartin Matuska 
22681ad8388SMartin Matuska 		// Write and check write error before checking decoder error.
22781ad8388SMartin Matuska 		// This way as much data as possible gets written to output
22881ad8388SMartin Matuska 		// even if decoder detected an error.
22981ad8388SMartin Matuska 		if (strm->avail_out == 0 || ret != LZMA_OK) {
23081ad8388SMartin Matuska 			const size_t write_size = BUFSIZ - strm->avail_out;
23181ad8388SMartin Matuska 
23281ad8388SMartin Matuska 			if (fwrite(out_buf, 1, write_size, stdout)
23381ad8388SMartin Matuska 					!= write_size) {
23481ad8388SMartin Matuska 				// Wouldn't be a surprise if writing to stderr
23581ad8388SMartin Matuska 				// would fail too but at least try to show an
23681ad8388SMartin Matuska 				// error message.
23781ad8388SMartin Matuska 				my_errorf("Cannot write to standard output: "
23881ad8388SMartin Matuska 						"%s", strerror(errno));
23981ad8388SMartin Matuska 				exit(EXIT_FAILURE);
24081ad8388SMartin Matuska 			}
24181ad8388SMartin Matuska 
24281ad8388SMartin Matuska 			strm->next_out = out_buf;
24381ad8388SMartin Matuska 			strm->avail_out = BUFSIZ;
24481ad8388SMartin Matuska 		}
24581ad8388SMartin Matuska 
24681ad8388SMartin Matuska 		if (ret != LZMA_OK) {
24781ad8388SMartin Matuska 			if (ret == LZMA_STREAM_END) {
24881ad8388SMartin Matuska #ifdef LZMADEC
24981ad8388SMartin Matuska 				// Check that there's no trailing garbage.
25081ad8388SMartin Matuska 				if (strm->avail_in != 0
25181ad8388SMartin Matuska 						|| fread(in_buf, 1, 1, file)
25281ad8388SMartin Matuska 							!= 0
25381ad8388SMartin Matuska 						|| !feof(file))
25481ad8388SMartin Matuska 					ret = LZMA_DATA_ERROR;
25581ad8388SMartin Matuska 				else
25681ad8388SMartin Matuska 					return;
25781ad8388SMartin Matuska #else
25881ad8388SMartin Matuska 				// lzma_stream_decoder() already guarantees
25981ad8388SMartin Matuska 				// that there's no trailing garbage.
26081ad8388SMartin Matuska 				assert(strm->avail_in == 0);
26181ad8388SMartin Matuska 				assert(action == LZMA_FINISH);
26281ad8388SMartin Matuska 				assert(feof(file));
26381ad8388SMartin Matuska 				return;
26481ad8388SMartin Matuska #endif
26581ad8388SMartin Matuska 			}
26681ad8388SMartin Matuska 
26781ad8388SMartin Matuska 			const char *msg;
26881ad8388SMartin Matuska 			switch (ret) {
26981ad8388SMartin Matuska 			case LZMA_MEM_ERROR:
27081ad8388SMartin Matuska 				msg = strerror(ENOMEM);
27181ad8388SMartin Matuska 				break;
27281ad8388SMartin Matuska 
27381ad8388SMartin Matuska 			case LZMA_FORMAT_ERROR:
27481ad8388SMartin Matuska 				msg = "File format not recognized";
27581ad8388SMartin Matuska 				break;
27681ad8388SMartin Matuska 
27781ad8388SMartin Matuska 			case LZMA_OPTIONS_ERROR:
27881ad8388SMartin Matuska 				// FIXME: Better message?
27981ad8388SMartin Matuska 				msg = "Unsupported compression options";
28081ad8388SMartin Matuska 				break;
28181ad8388SMartin Matuska 
28281ad8388SMartin Matuska 			case LZMA_DATA_ERROR:
28381ad8388SMartin Matuska 				msg = "File is corrupt";
28481ad8388SMartin Matuska 				break;
28581ad8388SMartin Matuska 
28681ad8388SMartin Matuska 			case LZMA_BUF_ERROR:
28781ad8388SMartin Matuska 				msg = "Unexpected end of input";
28881ad8388SMartin Matuska 				break;
28981ad8388SMartin Matuska 
29081ad8388SMartin Matuska 			default:
29181ad8388SMartin Matuska 				msg = "Internal error (bug)";
29281ad8388SMartin Matuska 				break;
29381ad8388SMartin Matuska 			}
29481ad8388SMartin Matuska 
29581ad8388SMartin Matuska 			my_errorf("%s: %s", filename, msg);
29681ad8388SMartin Matuska 			exit(EXIT_FAILURE);
29781ad8388SMartin Matuska 		}
29881ad8388SMartin Matuska 	}
29981ad8388SMartin Matuska }
30081ad8388SMartin Matuska 
30181ad8388SMartin Matuska 
3023b35e7eeSXin LI #ifdef ENABLE_SANDBOX
3033b35e7eeSXin LI static void
3043b35e7eeSXin LI sandbox_enter(int src_fd)
3053b35e7eeSXin LI {
3063b35e7eeSXin LI #if defined(HAVE_CAP_RIGHTS_LIMIT)
3073b35e7eeSXin LI 	// Capsicum needs FreeBSD 10.2 or later.
3083b35e7eeSXin LI 	cap_rights_t rights;
3093b35e7eeSXin LI 
3103b35e7eeSXin LI 	if (cap_enter())
3113b35e7eeSXin LI 		goto error;
3123b35e7eeSXin LI 
3133b35e7eeSXin LI 	if (cap_rights_limit(src_fd, cap_rights_init(&rights, CAP_READ)))
3143b35e7eeSXin LI 		goto error;
3153b35e7eeSXin LI 
3163b35e7eeSXin LI 	// If not reading from stdin, remove all capabilities from it.
3173b35e7eeSXin LI 	if (src_fd != STDIN_FILENO && cap_rights_limit(
3183b35e7eeSXin LI 			STDIN_FILENO, cap_rights_clear(&rights)))
3193b35e7eeSXin LI 		goto error;
3203b35e7eeSXin LI 
3213b35e7eeSXin LI 	if (cap_rights_limit(STDOUT_FILENO, cap_rights_init(&rights,
3223b35e7eeSXin LI 			CAP_WRITE)))
3233b35e7eeSXin LI 		goto error;
3243b35e7eeSXin LI 
3253b35e7eeSXin LI 	if (cap_rights_limit(STDERR_FILENO, cap_rights_init(&rights,
3263b35e7eeSXin LI 			CAP_WRITE)))
3273b35e7eeSXin LI 		goto error;
3283b35e7eeSXin LI 
3293b35e7eeSXin LI #elif defined(HAVE_PLEDGE)
3303b35e7eeSXin LI 	// pledge() was introduced in OpenBSD 5.9.
3313b35e7eeSXin LI 	if (pledge("stdio", ""))
3323b35e7eeSXin LI 		goto error;
3333b35e7eeSXin LI 
3343b35e7eeSXin LI 	(void)src_fd;
3353b35e7eeSXin LI 
3363b35e7eeSXin LI #elif defined(HAVE_LINUX_LANDLOCK)
3373b35e7eeSXin LI 	int landlock_abi = syscall(SYS_landlock_create_ruleset,
3383b35e7eeSXin LI 			(void *)NULL, 0, LANDLOCK_CREATE_RULESET_VERSION);
3393b35e7eeSXin LI 
3403b35e7eeSXin LI 	if (landlock_abi > 0) {
3413b35e7eeSXin LI 		if (landlock_abi > LANDLOCK_ABI_MAX)
3423b35e7eeSXin LI 			landlock_abi = LANDLOCK_ABI_MAX;
3433b35e7eeSXin LI 
3443b35e7eeSXin LI 		const struct landlock_ruleset_attr attr = {
3453b35e7eeSXin LI 			.handled_access_fs = (1ULL
3463b35e7eeSXin LI 				<< (12 + my_min(3, landlock_abi))) - 1,
3473b35e7eeSXin LI #	if LANDLOCK_ABI_MAX >= 4
3483b35e7eeSXin LI 			.handled_access_net = landlock_abi < 4 ? 0 :
3493b35e7eeSXin LI 				(LANDLOCK_ACCESS_NET_BIND_TCP
3503b35e7eeSXin LI 				| LANDLOCK_ACCESS_NET_CONNECT_TCP),
3513b35e7eeSXin LI #	endif
3523b35e7eeSXin LI 		};
3533b35e7eeSXin LI 
3543b35e7eeSXin LI 		const int ruleset_fd = syscall(SYS_landlock_create_ruleset,
3553b35e7eeSXin LI 				&attr, sizeof(attr), 0U);
3563b35e7eeSXin LI 		if (ruleset_fd < 0)
3573b35e7eeSXin LI 			goto error;
3583b35e7eeSXin LI 
3593b35e7eeSXin LI 		// All files we need should have already been opened. Thus,
3603b35e7eeSXin LI 		// we don't need to add any rules using landlock_add_rule(2)
3613b35e7eeSXin LI 		// before activating the sandbox.
3623b35e7eeSXin LI 		if (syscall(SYS_landlock_restrict_self, ruleset_fd, 0U) != 0)
3633b35e7eeSXin LI 			goto error;
3643b35e7eeSXin LI 	}
3653b35e7eeSXin LI 
3663b35e7eeSXin LI 	(void)src_fd;
3673b35e7eeSXin LI 
3683b35e7eeSXin LI #else
3693b35e7eeSXin LI #	error ENABLE_SANDBOX is defined but no sandboxing method was found.
3703b35e7eeSXin LI #endif
3713b35e7eeSXin LI 
3723b35e7eeSXin LI 	return;
3733b35e7eeSXin LI 
3743b35e7eeSXin LI error:
3753b35e7eeSXin LI #ifdef HAVE_CAP_RIGHTS_LIMIT
3763b35e7eeSXin LI 	// If a kernel is configured without capability mode support or
3773b35e7eeSXin LI 	// used in an emulator that does not implement the capability
3783b35e7eeSXin LI 	// system calls, then the Capsicum system calls will fail and set
3793b35e7eeSXin LI 	// errno to ENOSYS. In that case xzdec will silently run without
3803b35e7eeSXin LI 	// the sandbox.
3813b35e7eeSXin LI 	if (errno == ENOSYS)
3823b35e7eeSXin LI 		return;
3833b35e7eeSXin LI #endif
3843b35e7eeSXin LI 
3853b35e7eeSXin LI 	my_errorf("Failed to enable the sandbox");
3863b35e7eeSXin LI 	exit(EXIT_FAILURE);
3873b35e7eeSXin LI }
3883b35e7eeSXin LI #endif
3893b35e7eeSXin LI 
3903b35e7eeSXin LI 
39181ad8388SMartin Matuska int
39281ad8388SMartin Matuska main(int argc, char **argv)
39381ad8388SMartin Matuska {
3943b35e7eeSXin LI #ifdef HAVE_PLEDGE
3953b35e7eeSXin LI 	// OpenBSD's pledge(2) sandbox.
3963b35e7eeSXin LI 	// Initially enable the sandbox slightly more relaxed so that
3973b35e7eeSXin LI 	// the process can still open files. This allows the sandbox to
3983b35e7eeSXin LI 	// be enabled when parsing command line arguments and decompressing
3993b35e7eeSXin LI 	// all files (the more strict sandbox only restricts the last file
4003b35e7eeSXin LI 	// that is decompressed).
4013b35e7eeSXin LI 	if (pledge("stdio rpath", "")) {
4023b35e7eeSXin LI 		my_errorf("Failed to enable the sandbox");
4033b35e7eeSXin LI 		exit(EXIT_FAILURE);
4043b35e7eeSXin LI 	}
4053b35e7eeSXin LI #endif
4063b35e7eeSXin LI 
4073b35e7eeSXin LI #ifdef HAVE_LINUX_LANDLOCK
4083b35e7eeSXin LI 	// Prevent the process from gaining new privileges. This must be done
4093b35e7eeSXin LI 	// before landlock_restrict_self(2) but since we will never need new
4103b35e7eeSXin LI 	// privileges, this call can be done here already.
4113b35e7eeSXin LI 	//
4123b35e7eeSXin LI 	// This is supported since Linux 3.5. Ignore the return value to
4133b35e7eeSXin LI 	// keep compatibility with old kernels. landlock_restrict_self(2)
4143b35e7eeSXin LI 	// will fail if the no_new_privs attribute isn't set, thus if prctl()
4153b35e7eeSXin LI 	// fails here the error will still be detected when it matters.
4163b35e7eeSXin LI 	(void)prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
4173b35e7eeSXin LI #endif
4183b35e7eeSXin LI 
41981ad8388SMartin Matuska 	// Initialize progname which we will be used in error messages.
42081ad8388SMartin Matuska 	tuklib_progname_init(argv);
42181ad8388SMartin Matuska 
42281ad8388SMartin Matuska 	// Parse the command line options.
42381ad8388SMartin Matuska 	parse_options(argc, argv);
42481ad8388SMartin Matuska 
42581ad8388SMartin Matuska 	// The same lzma_stream is used for all files that we decode. This way
42681ad8388SMartin Matuska 	// we don't need to reallocate memory for every file if they use same
42781ad8388SMartin Matuska 	// compression settings.
42881ad8388SMartin Matuska 	lzma_stream strm = LZMA_STREAM_INIT;
42981ad8388SMartin Matuska 
43081ad8388SMartin Matuska 	// Some systems require setting stdin and stdout to binary mode.
43181ad8388SMartin Matuska #ifdef TUKLIB_DOSLIKE
43281ad8388SMartin Matuska 	setmode(fileno(stdin), O_BINARY);
43381ad8388SMartin Matuska 	setmode(fileno(stdout), O_BINARY);
43481ad8388SMartin Matuska #endif
43581ad8388SMartin Matuska 
43681ad8388SMartin Matuska 	if (optind == argc) {
43781ad8388SMartin Matuska 		// No filenames given, decode from stdin.
4383b35e7eeSXin LI #ifdef ENABLE_SANDBOX
4393b35e7eeSXin LI 		sandbox_enter(STDIN_FILENO);
4403b35e7eeSXin LI #endif
44181ad8388SMartin Matuska 		uncompress(&strm, stdin, "(stdin)");
44281ad8388SMartin Matuska 	} else {
44381ad8388SMartin Matuska 		// Loop through the filenames given on the command line.
44481ad8388SMartin Matuska 		do {
4453b35e7eeSXin LI 			FILE *src_file;
4463b35e7eeSXin LI 			const char *src_name;
4473b35e7eeSXin LI 
44881ad8388SMartin Matuska 			// "-" indicates stdin.
44981ad8388SMartin Matuska 			if (strcmp(argv[optind], "-") == 0) {
4503b35e7eeSXin LI 				src_file = stdin;
4513b35e7eeSXin LI 				src_name = "(stdin)";
45281ad8388SMartin Matuska 			} else {
4533b35e7eeSXin LI 				src_name = argv[optind];
4543b35e7eeSXin LI 				src_file = fopen(src_name, "rb");
4553b35e7eeSXin LI 				if (src_file == NULL) {
4563b35e7eeSXin LI 					my_errorf("%s: %s", src_name,
45781ad8388SMartin Matuska 							strerror(errno));
45881ad8388SMartin Matuska 					exit(EXIT_FAILURE);
45981ad8388SMartin Matuska 				}
4602f9cd13dSXin LI 			}
4613b35e7eeSXin LI #ifdef ENABLE_SANDBOX
4623b35e7eeSXin LI 			// Enable the strict sandbox for the last file.
4633b35e7eeSXin LI 			// Then the process can no longer open additional
4643b35e7eeSXin LI 			// files. The typical xzdec use case is to decompress
4653b35e7eeSXin LI 			// a single file so this way the strictest sandboxing
4663b35e7eeSXin LI 			// is used in most cases.
4673b35e7eeSXin LI 			if (optind == argc - 1)
4683b35e7eeSXin LI 				sandbox_enter(fileno(src_file));
4693b35e7eeSXin LI #endif
4703b35e7eeSXin LI 			uncompress(&strm, src_file, src_name);
4713b35e7eeSXin LI 
4723b35e7eeSXin LI 			if (src_file != stdin)
4733b35e7eeSXin LI 				(void)fclose(src_file);
47481ad8388SMartin Matuska 		} while (++optind < argc);
47581ad8388SMartin Matuska 	}
47681ad8388SMartin Matuska 
47781ad8388SMartin Matuska #ifndef NDEBUG
47881ad8388SMartin Matuska 	// Free the memory only when debugging. Freeing wastes some time,
47981ad8388SMartin Matuska 	// but allows detecting possible memory leaks with Valgrind.
48081ad8388SMartin Matuska 	lzma_end(&strm);
48181ad8388SMartin Matuska #endif
48281ad8388SMartin Matuska 
48381ad8388SMartin Matuska 	tuklib_exit(EXIT_SUCCESS, EXIT_FAILURE, display_errors);
48481ad8388SMartin Matuska }
485