diff options
Diffstat (limited to 'src/xz')
| -rw-r--r-- | src/xz/args.c | 16 | ||||
| -rw-r--r-- | src/xz/coder.c | 33 | ||||
| -rw-r--r-- | src/xz/file_io.c | 140 | ||||
| -rw-r--r-- | src/xz/file_io.h | 6 | ||||
| -rw-r--r-- | src/xz/main.c | 27 | ||||
| -rw-r--r-- | src/xz/private.h | 9 |
6 files changed, 205 insertions, 26 deletions
diff --git a/src/xz/args.c b/src/xz/args.c index 041c80073e6d..341f29e1b0e9 100644 --- a/src/xz/args.c +++ b/src/xz/args.c @@ -635,6 +635,22 @@ args_parse(args_info *args, int argc, char **argv) // Then from the command line parse_real(args, argc, argv); + // If encoder or decoder support was omitted at build time, + // show an error now so that the rest of the code can rely on + // that whatever is in opt_mode is also supported. +#ifndef HAVE_ENCODERS + if (opt_mode == MODE_COMPRESS) + message_fatal(_("Compression support was disabled " + "at build time")); +#endif +#ifndef HAVE_DECODERS + // Even MODE_LIST cannot work without decoder support so MODE_COMPRESS + // is the only valid choice. + if (opt_mode != MODE_COMPRESS) + message_fatal(_("Decompression support was disabled " + "at build time")); +#endif + // Never remove the source file when the destination is not on disk. // In test mode the data is written nowhere, but setting opt_stdout // will make the rest of the code behave well. diff --git a/src/xz/coder.c b/src/xz/coder.c index a94bdb83266f..3c6a01cbbdbe 100644 --- a/src/xz/coder.c +++ b/src/xz/coder.c @@ -51,7 +51,7 @@ static lzma_check check; /// This becomes false if the --check=CHECK option is used. static bool check_default = true; -#ifdef MYTHREAD_ENABLED +#if defined(HAVE_ENCODERS) && defined(MYTHREAD_ENABLED) static lzma_mt mt_options = { .flags = 0, .timeout = 300, @@ -221,9 +221,10 @@ coder_set_compression_settings(void) // Get the memory usage. Note that if --format=raw was used, // we can be decompressing. const uint64_t memory_limit = hardware_memlimit_get(opt_mode); - uint64_t memory_usage; + uint64_t memory_usage = UINT64_MAX; if (opt_mode == MODE_COMPRESS) { -#ifdef MYTHREAD_ENABLED +#ifdef HAVE_ENCODERS +# ifdef MYTHREAD_ENABLED if (opt_format == FORMAT_XZ && hardware_threads_get() > 1) { mt_options.threads = hardware_threads_get(); mt_options.block_size = opt_block_size; @@ -235,12 +236,15 @@ coder_set_compression_settings(void) " threads."), mt_options.threads); } else -#endif +# endif { memory_usage = lzma_raw_encoder_memusage(filters); } +#endif } else { +#ifdef HAVE_DECODERS memory_usage = lzma_raw_decoder_memusage(filters); +#endif } if (memory_usage == UINT64_MAX) @@ -248,7 +252,11 @@ coder_set_compression_settings(void) // Print memory usage info before possible dictionary // size auto-adjusting. + // + // NOTE: If only encoder support was built, we cannot show the + // what the decoder memory usage will be. message_mem_needed(V_DEBUG, memory_usage); +#ifdef HAVE_DECODERS if (opt_mode == MODE_COMPRESS) { const uint64_t decmem = lzma_raw_decoder_memusage(filters); if (decmem != UINT64_MAX) @@ -256,6 +264,7 @@ coder_set_compression_settings(void) "%s MiB of memory."), uint64_to_str( round_up_to_mib(decmem), 0)); } +#endif if (memory_usage <= memory_limit) return; @@ -268,7 +277,8 @@ coder_set_compression_settings(void) assert(opt_mode == MODE_COMPRESS); -#ifdef MYTHREAD_ENABLED +#ifdef HAVE_ENCODERS +# ifdef MYTHREAD_ENABLED if (opt_format == FORMAT_XZ && mt_options.threads > 1) { // Try to reduce the number of threads before // adjusting the compression settings down. @@ -295,7 +305,7 @@ coder_set_compression_settings(void) uint64_to_str(round_up_to_mib( memory_limit), 2)); } -#endif +# endif if (memory_usage <= memory_limit) return; @@ -349,11 +359,13 @@ coder_set_compression_settings(void) uint64_to_str(orig_dict_size >> 20, 0), uint64_to_str(opt->dict_size >> 20, 1), uint64_to_str(round_up_to_mib(memory_limit), 2)); +#endif return; } +#ifdef HAVE_DECODERS /// Return true if the data in in_buf seems to be in the .xz format. static bool is_format_xz(void) @@ -411,6 +423,7 @@ is_format_lzma(void) return true; } +#endif /// Detect the input file type (for now, this done only when decompressing), @@ -424,6 +437,7 @@ coder_init(file_pair *pair) lzma_ret ret = LZMA_PROG_ERROR; if (opt_mode == MODE_COMPRESS) { +#ifdef HAVE_ENCODERS switch (opt_format) { case FORMAT_AUTO: // args.c ensures this. @@ -431,12 +445,12 @@ coder_init(file_pair *pair) break; case FORMAT_XZ: -#ifdef MYTHREAD_ENABLED +# ifdef MYTHREAD_ENABLED if (hardware_threads_get() > 1) ret = lzma_stream_encoder_mt( &strm, &mt_options); else -#endif +# endif ret = lzma_stream_encoder( &strm, filters, check); break; @@ -449,7 +463,9 @@ coder_init(file_pair *pair) ret = lzma_raw_encoder(&strm, filters); break; } +#endif } else { +#ifdef HAVE_DECODERS uint32_t flags = 0; // It seems silly to warn about unsupported check if the @@ -531,6 +547,7 @@ coder_init(file_pair *pair) strm.avail_out = 0; ret = lzma_code(&strm, LZMA_RUN); } +#endif } if (ret != LZMA_OK) { diff --git a/src/xz/file_io.c b/src/xz/file_io.c index 9bd515ddc894..c01f4e8bb99d 100644 --- a/src/xz/file_io.c +++ b/src/xz/file_io.c @@ -23,10 +23,20 @@ static bool warn_fchown; #if defined(HAVE_FUTIMES) || defined(HAVE_FUTIMESAT) || defined(HAVE_UTIMES) # include <sys/time.h> +#elif defined(HAVE__FUTIME) +# include <sys/utime.h> #elif defined(HAVE_UTIME) # include <utime.h> #endif +#ifdef HAVE_CAPSICUM +# ifdef HAVE_SYS_CAPSICUM_H +# include <sys/capsicum.h> +# else +# include <sys/capability.h> +# endif +#endif + #include "tuklib_open_stdxxx.h" #ifndef O_BINARY @@ -37,6 +47,14 @@ static bool warn_fchown; # define O_NOCTTY 0 #endif +// Using this macro to silence a warning from gcc -Wlogical-op. +#if EAGAIN == EWOULDBLOCK +# define IS_EAGAIN_OR_EWOULDBLOCK(e) ((e) == EAGAIN) +#else +# define IS_EAGAIN_OR_EWOULDBLOCK(e) \ + ((e) == EAGAIN || (e) == EWOULDBLOCK) +#endif + typedef enum { IO_WAIT_MORE, // Reading or writing is possible. @@ -48,6 +66,11 @@ typedef enum { /// If true, try to create sparse files when decompressing. static bool try_sparse = true; +#ifdef ENABLE_SANDBOX +/// True if the conditions for sandboxing (described in main()) have been met. +static bool sandbox_allowed = false; +#endif + #ifndef TUKLIB_DOSLIKE /// File status flags of standard input. This is used by io_open_src() /// and io_close_src(). @@ -132,6 +155,73 @@ io_no_sparse(void) } +#ifdef ENABLE_SANDBOX +extern void +io_allow_sandbox(void) +{ + sandbox_allowed = true; + return; +} + + +/// Enables operating-system-specific sandbox if it is possible. +/// src_fd is the file descriptor of the input file. +static void +io_sandbox_enter(int src_fd) +{ + if (!sandbox_allowed) { + message(V_DEBUG, _("Sandbox is disabled due " + "to incompatible command line arguments")); + return; + } + + const char dummy_str[] = "x"; + + // Try to ensure that both libc and xz locale files have been + // loaded when NLS is enabled. + snprintf(NULL, 0, "%s%s", _(dummy_str), strerror(EINVAL)); + + // Try to ensure that iconv data files needed for handling multibyte + // characters have been loaded. This is needed at least with glibc. + tuklib_mbstr_width(dummy_str, NULL); + +#ifdef HAVE_CAPSICUM + // Capsicum needs FreeBSD 10.0 or later. + cap_rights_t rights; + + if (cap_rights_limit(src_fd, cap_rights_init(&rights, + CAP_EVENT, CAP_FCNTL, CAP_LOOKUP, CAP_READ, CAP_SEEK))) + goto error; + + if (cap_rights_limit(STDOUT_FILENO, cap_rights_init(&rights, + CAP_EVENT, CAP_FCNTL, CAP_FSTAT, CAP_LOOKUP, + CAP_WRITE, CAP_SEEK))) + goto error; + + if (cap_rights_limit(user_abort_pipe[0], cap_rights_init(&rights, + CAP_EVENT))) + goto error; + + if (cap_rights_limit(user_abort_pipe[1], cap_rights_init(&rights, + CAP_WRITE))) + goto error; + + if (cap_enter()) + goto error; + +#else +# error ENABLE_SANDBOX is defined but no sandboxing method was found. +#endif + + message(V_DEBUG, _("Sandbox was successfully enabled")); + return; + +error: + message(V_DEBUG, _("Failed to enable the sandbox")); +} +#endif // ENABLE_SANDBOX + + #ifndef TUKLIB_DOSLIKE /// \brief Waits for input or output to become available or for a signal /// @@ -369,6 +459,22 @@ io_copy_attrs(const file_pair *pair) (void)utimes(pair->dest_name, tv); # endif +#elif defined(HAVE__FUTIME) + // Use one-second precision with Windows-specific _futime(). + // We could use utime() too except that for some reason the + // timestamp will get reset at close(). With _futime() it works. + // This struct cannot be const as _futime() takes a non-const pointer. + struct _utimbuf buf = { + .actime = pair->src_st.st_atime, + .modtime = pair->src_st.st_mtime, + }; + + // Avoid warnings. + (void)atime_nsec; + (void)mtime_nsec; + + (void)_futime(pair->dest_fd, &buf); + #elif defined(HAVE_UTIME) // Use one-second precision. utime() doesn't support using file // descriptor either. Some systems have broken utime() prototype @@ -649,6 +755,11 @@ io_open_src(const char *src_name) const bool error = io_open_src_real(&pair); signals_unblock(); +#ifdef ENABLE_SANDBOX + if (!error) + io_sandbox_enter(pair.src_fd); +#endif + return error ? NULL : &pair; } @@ -675,23 +786,22 @@ io_close_src(file_pair *pair, bool success) #endif if (pair->src_fd != STDIN_FILENO && pair->src_fd != -1) { -#ifdef TUKLIB_DOSLIKE + // Close the file before possibly unlinking it. On DOS-like + // systems this is always required since unlinking will fail + // if the file is open. On POSIX systems it usually works + // to unlink open files, but in some cases it doesn't and + // one gets EBUSY in errno. + // + // xz 5.2.2 and older unlinked the file before closing it + // (except on DOS-like systems). The old code didn't handle + // EBUSY and could fail e.g. on some CIFS shares. The + // advantage of unlinking before closing is negligible + // (avoids a race between close() and stat()/lstat() and + // unlink()), so let's keep this simple. (void)close(pair->src_fd); -#endif - // If we are going to unlink(), do it before closing the file. - // This way there's no risk that someone replaces the file and - // happens to get same inode number, which would make us - // unlink() wrong file. - // - // NOTE: DOS-like systems are an exception to this, because - // they don't allow unlinking files that are open. *sigh* if (success && !opt_keep_original) io_unlink(pair->src_name, &pair->src_st); - -#ifndef TUKLIB_DOSLIKE - (void)close(pair->src_fd); -#endif } return; @@ -1018,7 +1128,7 @@ io_read(file_pair *pair, io_buf *buf_union, size_t size) } #ifndef TUKLIB_DOSLIKE - if (errno == EAGAIN || errno == EWOULDBLOCK) { + if (IS_EAGAIN_OR_EWOULDBLOCK(errno)) { const io_wait_ret ret = io_wait(pair, mytime_get_flush_timeout(), true); @@ -1106,7 +1216,7 @@ io_write_buf(file_pair *pair, const uint8_t *buf, size_t size) } #ifndef TUKLIB_DOSLIKE - if (errno == EAGAIN || errno == EWOULDBLOCK) { + if (IS_EAGAIN_OR_EWOULDBLOCK(errno)) { if (io_wait(pair, -1, false) == IO_WAIT_MORE) continue; diff --git a/src/xz/file_io.h b/src/xz/file_io.h index 2de3379238d6..6722aef84092 100644 --- a/src/xz/file_io.h +++ b/src/xz/file_io.h @@ -80,6 +80,12 @@ extern void io_write_to_user_abort_pipe(void); extern void io_no_sparse(void); +#ifdef ENABLE_SANDBOX +/// \brief main() calls this if conditions for sandboxing have been met. +extern void io_allow_sandbox(void); +#endif + + /// \brief Open the source file extern file_pair *io_open_src(const char *src_name); diff --git a/src/xz/main.c b/src/xz/main.c index 5608229d54c5..af550c4585d9 100644 --- a/src/xz/main.c +++ b/src/xz/main.c @@ -205,10 +205,31 @@ main(int argc, char **argv) if (opt_mode != MODE_LIST) signals_init(); +#ifdef ENABLE_SANDBOX + // Set a flag that sandboxing is allowed if all these are true: + // - --files or --files0 wasn't used. + // - There is exactly one input file or we are reading from stdin. + // - We won't create any files: output goes to stdout or --test + // or --list was used. Note that --test implies opt_stdout = true + // but --list doesn't. + // + // This is obviously not ideal but it was easy to implement and + // it covers the most common use cases. + // + // TODO: Make sandboxing work for other situations too. + if (args.files_name == NULL && args.arg_count == 1 + && (opt_stdout || strcmp("-", args.arg_names[0]) == 0 + || opt_mode == MODE_LIST)) + io_allow_sandbox(); +#endif + // coder_run() handles compression, decompression, and testing. // list_file() is for --list. - void (*run)(const char *filename) = opt_mode == MODE_LIST - ? &list_file : &coder_run; + void (*run)(const char *filename) = &coder_run; +#ifdef HAVE_DECODERS + if (opt_mode == MODE_LIST) + run = &list_file; +#endif // Process the files given on the command line. Note that if no names // were given, args_parse() gave us a fake "-" filename. @@ -267,6 +288,7 @@ main(int argc, char **argv) (void)fclose(args.files_file); } +#ifdef HAVE_DECODERS // All files have now been handled. If in --list mode, display // the totals before exiting. We don't have signal handlers // enabled in --list mode, so we don't need to check user_abort. @@ -274,6 +296,7 @@ main(int argc, char **argv) assert(!user_abort); list_totals(); } +#endif #ifndef NDEBUG coder_free(); diff --git a/src/xz/private.h b/src/xz/private.h index 4acfa8dc4558..e61563ac72af 100644 --- a/src/xz/private.h +++ b/src/xz/private.h @@ -45,6 +45,10 @@ # define STDERR_FILENO (fileno(stderr)) #endif +#ifdef HAVE_CAPSICUM +# define ENABLE_SANDBOX 1 +#endif + #include "main.h" #include "mytime.h" #include "coder.h" @@ -56,4 +60,7 @@ #include "signals.h" #include "suffix.h" #include "util.h" -#include "list.h" + +#ifdef HAVE_DECODERS +# include "list.h" +#endif |
