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