mirror of
https://git.tukaani.org/xz.git
synced 2024-04-04 12:36:23 +02:00
xz: Use non-blocking I/O for the input file.
This commit is contained in:
parent
78673a08be
commit
3541bc79d0
1 changed files with 111 additions and 45 deletions
152
src/xz/file_io.c
152
src/xz/file_io.c
|
@ -17,6 +17,7 @@
|
||||||
#ifdef TUKLIB_DOSLIKE
|
#ifdef TUKLIB_DOSLIKE
|
||||||
# include <io.h>
|
# include <io.h>
|
||||||
#else
|
#else
|
||||||
|
# include <poll.h>
|
||||||
static bool warn_fchown;
|
static bool warn_fchown;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -41,6 +42,11 @@ static bool warn_fchown;
|
||||||
static bool try_sparse = true;
|
static bool try_sparse = true;
|
||||||
|
|
||||||
#ifndef TUKLIB_DOSLIKE
|
#ifndef TUKLIB_DOSLIKE
|
||||||
|
/// File status flags of standard input. This is used by io_open_src()
|
||||||
|
/// and io_close_src().
|
||||||
|
static int stdin_flags;
|
||||||
|
static bool restore_stdin_flags = false;
|
||||||
|
|
||||||
/// Original file status flags of standard output. This is used by
|
/// Original file status flags of standard output. This is used by
|
||||||
/// io_open_dest() and io_close_dest() to save and restore the flags.
|
/// io_open_dest() and io_close_dest() to save and restore the flags.
|
||||||
static int stdout_flags;
|
static int stdout_flags;
|
||||||
|
@ -84,6 +90,47 @@ io_no_sparse(void)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef TUKLIB_DOSLIKE
|
||||||
|
/// \brief Waits for input or output to become available
|
||||||
|
static bool
|
||||||
|
io_wait(file_pair *pair, bool is_reading)
|
||||||
|
{
|
||||||
|
struct pollfd pfd[1];
|
||||||
|
|
||||||
|
if (is_reading) {
|
||||||
|
pfd[0].fd = pair->src_fd;
|
||||||
|
pfd[0].events = POLLIN;
|
||||||
|
} else {
|
||||||
|
pfd[0].fd = pair->dest_fd;
|
||||||
|
pfd[0].events = POLLOUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
const int ret = poll(pfd, 1, -1);
|
||||||
|
|
||||||
|
if (ret == -1) {
|
||||||
|
if (errno == EINTR) {
|
||||||
|
if (user_abort)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (errno == EAGAIN)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
message_error(_("%s: poll() failed: %s"),
|
||||||
|
is_reading ? pair->src_name
|
||||||
|
: pair->dest_name,
|
||||||
|
strerror(errno));
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
/// \brief Unlink a file
|
/// \brief Unlink a file
|
||||||
///
|
///
|
||||||
/// This tries to verify that the file being unlinked really is the file that
|
/// This tries to verify that the file being unlinked really is the file that
|
||||||
|
@ -293,6 +340,27 @@ io_open_src_real(file_pair *pair)
|
||||||
pair->src_fd = STDIN_FILENO;
|
pair->src_fd = STDIN_FILENO;
|
||||||
#ifdef TUKLIB_DOSLIKE
|
#ifdef TUKLIB_DOSLIKE
|
||||||
setmode(STDIN_FILENO, O_BINARY);
|
setmode(STDIN_FILENO, O_BINARY);
|
||||||
|
#else
|
||||||
|
// Enable O_NONBLOCK for stdin.
|
||||||
|
stdin_flags = fcntl(STDIN_FILENO, F_GETFL);
|
||||||
|
if (stdin_flags == -1) {
|
||||||
|
message_error(_("Error getting the file status flags "
|
||||||
|
"from standard input: %s"),
|
||||||
|
strerror(errno));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((stdin_flags & O_NONBLOCK) == 0) {
|
||||||
|
if (fcntl(STDIN_FILENO, F_SETFL,
|
||||||
|
stdin_flags | O_NONBLOCK) == -1) {
|
||||||
|
message_error(_("Error setting O_NONBLOCK "
|
||||||
|
"on standard input: %s"),
|
||||||
|
strerror(errno));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
restore_stdin_flags = true;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
#ifdef HAVE_POSIX_FADVISE
|
#ifdef HAVE_POSIX_FADVISE
|
||||||
// It will fail if stdin is a pipe and that's fine.
|
// It will fail if stdin is a pipe and that's fine.
|
||||||
|
@ -314,12 +382,11 @@ io_open_src_real(file_pair *pair)
|
||||||
int flags = O_RDONLY | O_BINARY | O_NOCTTY;
|
int flags = O_RDONLY | O_BINARY | O_NOCTTY;
|
||||||
|
|
||||||
#ifndef TUKLIB_DOSLIKE
|
#ifndef TUKLIB_DOSLIKE
|
||||||
// If we accept only regular files, we need to be careful to avoid
|
// Use non-blocking I/O:
|
||||||
// problems with special files like devices and FIFOs. O_NONBLOCK
|
// - It prevents blocking when opening FIFOs and some other
|
||||||
// prevents blocking when opening such files. When we want to accept
|
// special files, which is good if we want to accept only
|
||||||
// special files, we must not use O_NONBLOCK, or otherwise we won't
|
// regular files.
|
||||||
// block waiting e.g. FIFOs to become readable.
|
// - It can help avoiding some race conditions with signal handling.
|
||||||
if (reg_files_only)
|
|
||||||
flags |= O_NONBLOCK;
|
flags |= O_NONBLOCK;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -348,30 +415,13 @@ io_open_src_real(file_pair *pair)
|
||||||
(void)follow_symlinks;
|
(void)follow_symlinks;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Try to open the file. If we are accepting non-regular files,
|
// Try to open the file. Signals have been blocked so EINTR shouldn't
|
||||||
// unblock the caught signals so that open() can be interrupted
|
// be possible.
|
||||||
// if it blocks e.g. due to a FIFO file.
|
|
||||||
if (!reg_files_only)
|
|
||||||
signals_unblock();
|
|
||||||
|
|
||||||
// Maybe this wouldn't need a loop, since all the signal handlers for
|
|
||||||
// which we don't use SA_RESTART set user_abort to true. But it
|
|
||||||
// doesn't hurt to have it just in case.
|
|
||||||
do {
|
|
||||||
pair->src_fd = open(pair->src_name, flags);
|
pair->src_fd = open(pair->src_name, flags);
|
||||||
} while (pair->src_fd == -1 && errno == EINTR && !user_abort);
|
|
||||||
|
|
||||||
if (!reg_files_only)
|
|
||||||
signals_block();
|
|
||||||
|
|
||||||
if (pair->src_fd == -1) {
|
if (pair->src_fd == -1) {
|
||||||
// If we were interrupted, don't display any error message.
|
// Signals (that have a signal handler) have been blocked.
|
||||||
if (errno == EINTR) {
|
assert(errno != EINTR);
|
||||||
// All the signals that don't have SA_RESTART
|
|
||||||
// set user_abort.
|
|
||||||
assert(user_abort);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef O_NOFOLLOW
|
#ifdef O_NOFOLLOW
|
||||||
// Give an understandable error message if the reason
|
// Give an understandable error message if the reason
|
||||||
|
@ -430,22 +480,6 @@ io_open_src_real(file_pair *pair)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef TUKLIB_DOSLIKE
|
|
||||||
// Drop O_NONBLOCK, which is used only when we are accepting only
|
|
||||||
// regular files. After the open() call, we want things to block
|
|
||||||
// instead of giving EAGAIN.
|
|
||||||
if (reg_files_only) {
|
|
||||||
flags = fcntl(pair->src_fd, F_GETFL);
|
|
||||||
if (flags == -1)
|
|
||||||
goto error_msg;
|
|
||||||
|
|
||||||
flags &= ~O_NONBLOCK;
|
|
||||||
|
|
||||||
if (fcntl(pair->src_fd, F_SETFL, flags) == -1)
|
|
||||||
goto error_msg;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Stat the source file. We need the result also when we copy
|
// Stat the source file. We need the result also when we copy
|
||||||
// the permissions, and when unlinking.
|
// the permissions, and when unlinking.
|
||||||
//
|
//
|
||||||
|
@ -505,6 +539,18 @@ io_open_src_real(file_pair *pair)
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If it is something else than a regular file, wait until
|
||||||
|
// there is input available. This way reading from FIFOs
|
||||||
|
// will work when open() is used with O_NONBLOCK.
|
||||||
|
if (!S_ISREG(pair->src_st.st_mode)) {
|
||||||
|
signals_unblock();
|
||||||
|
const bool ret = io_wait(pair, true);
|
||||||
|
signals_block();
|
||||||
|
|
||||||
|
if (ret)
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef HAVE_POSIX_FADVISE
|
#ifdef HAVE_POSIX_FADVISE
|
||||||
|
@ -560,6 +606,17 @@ io_open_src(const char *src_name)
|
||||||
static void
|
static void
|
||||||
io_close_src(file_pair *pair, bool success)
|
io_close_src(file_pair *pair, bool success)
|
||||||
{
|
{
|
||||||
|
if (restore_stdin_flags) {
|
||||||
|
assert(pair->src_fd == STDIN_FILENO);
|
||||||
|
|
||||||
|
restore_stdin_flags = false;
|
||||||
|
|
||||||
|
if (fcntl(STDIN_FILENO, F_SETFL, stdin_flags) == -1)
|
||||||
|
message_error(_("Error restoring the status flags "
|
||||||
|
"to standard input: %s"),
|
||||||
|
strerror(errno));
|
||||||
|
}
|
||||||
|
|
||||||
if (pair->src_fd != STDIN_FILENO && pair->src_fd != -1) {
|
if (pair->src_fd != STDIN_FILENO && pair->src_fd != -1) {
|
||||||
#ifdef TUKLIB_DOSLIKE
|
#ifdef TUKLIB_DOSLIKE
|
||||||
(void)close(pair->src_fd);
|
(void)close(pair->src_fd);
|
||||||
|
@ -872,6 +929,15 @@ io_read(file_pair *pair, io_buf *buf_union, size_t size)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef TUKLIB_DOSLIKE
|
||||||
|
if (errno == EAGAIN || errno == EWOULDBLOCK) {
|
||||||
|
if (!io_wait(pair, true))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
return SIZE_MAX;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
message_error(_("%s: Read error: %s"),
|
message_error(_("%s: Read error: %s"),
|
||||||
pair->src_name, strerror(errno));
|
pair->src_name, strerror(errno));
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue