1207753Smm///////////////////////////////////////////////////////////////////////////////
2207753Smm//
3207753Smm/// \file       file_io.c
4207753Smm/// \brief      File opening, unlinking, and closing
5207753Smm//
6207753Smm//  Author:     Lasse Collin
7207753Smm//
8207753Smm//  This file has been put into the public domain.
9207753Smm//  You can do whatever you want with this file.
10207753Smm//
11207753Smm///////////////////////////////////////////////////////////////////////////////
12207753Smm
13207753Smm#include "private.h"
14207753Smm
15207753Smm#include <fcntl.h>
16207753Smm
17207753Smm#ifdef TUKLIB_DOSLIKE
18207753Smm#	include <io.h>
19207753Smm#else
20292588Sdelphij#	include <poll.h>
21207753Smmstatic bool warn_fchown;
22207753Smm#endif
23207753Smm
24207753Smm#if defined(HAVE_FUTIMES) || defined(HAVE_FUTIMESAT) || defined(HAVE_UTIMES)
25207753Smm#	include <sys/time.h>
26207753Smm#elif defined(HAVE_UTIME)
27207753Smm#	include <utime.h>
28207753Smm#endif
29207753Smm
30207753Smm#include "tuklib_open_stdxxx.h"
31207753Smm
32207753Smm#ifndef O_BINARY
33207753Smm#	define O_BINARY 0
34207753Smm#endif
35207753Smm
36207753Smm#ifndef O_NOCTTY
37207753Smm#	define O_NOCTTY 0
38207753Smm#endif
39207753Smm
40207753Smm
41292588Sdelphijtypedef enum {
42292588Sdelphij	IO_WAIT_MORE,    // Reading or writing is possible.
43292588Sdelphij	IO_WAIT_ERROR,   // Error or user_abort
44292588Sdelphij	IO_WAIT_TIMEOUT, // poll() timed out
45292588Sdelphij} io_wait_ret;
46292588Sdelphij
47292588Sdelphij
48207753Smm/// If true, try to create sparse files when decompressing.
49207753Smmstatic bool try_sparse = true;
50207753Smm
51207753Smm#ifndef TUKLIB_DOSLIKE
52292588Sdelphij/// File status flags of standard input. This is used by io_open_src()
53292588Sdelphij/// and io_close_src().
54292588Sdelphijstatic int stdin_flags;
55292588Sdelphijstatic bool restore_stdin_flags = false;
56292588Sdelphij
57263285Sdelphij/// Original file status flags of standard output. This is used by
58263285Sdelphij/// io_open_dest() and io_close_dest() to save and restore the flags.
59263285Sdelphijstatic int stdout_flags;
60263285Sdelphijstatic bool restore_stdout_flags = false;
61292588Sdelphij
62292588Sdelphij/// Self-pipe used together with the user_abort variable to avoid
63292588Sdelphij/// race conditions with signal handling.
64292588Sdelphijstatic int user_abort_pipe[2];
65207753Smm#endif
66207753Smm
67207753Smm
68207753Smmstatic bool io_write_buf(file_pair *pair, const uint8_t *buf, size_t size);
69207753Smm
70207753Smm
71207753Smmextern void
72207753Smmio_init(void)
73207753Smm{
74223935Smm	// Make sure that stdin, stdout, and stderr are connected to
75207753Smm	// a valid file descriptor. Exit immediately with exit code ERROR
76207753Smm	// if we cannot make the file descriptors valid. Maybe we should
77207753Smm	// print an error message, but our stderr could be screwed anyway.
78207753Smm	tuklib_open_stdxxx(E_ERROR);
79207753Smm
80207753Smm#ifndef TUKLIB_DOSLIKE
81207753Smm	// If fchown() fails setting the owner, we warn about it only if
82207753Smm	// we are root.
83207753Smm	warn_fchown = geteuid() == 0;
84292588Sdelphij
85292588Sdelphij	// Create a pipe for the self-pipe trick.
86292588Sdelphij	if (pipe(user_abort_pipe))
87292588Sdelphij		message_fatal(_("Error creating a pipe: %s"),
88292588Sdelphij				strerror(errno));
89292588Sdelphij
90292588Sdelphij	// Make both ends of the pipe non-blocking.
91292588Sdelphij	for (unsigned i = 0; i < 2; ++i) {
92292588Sdelphij		int flags = fcntl(user_abort_pipe[i], F_GETFL);
93292588Sdelphij		if (flags == -1 || fcntl(user_abort_pipe[i], F_SETFL,
94292588Sdelphij				flags | O_NONBLOCK) == -1)
95292588Sdelphij			message_fatal(_("Error creating a pipe: %s"),
96292588Sdelphij					strerror(errno));
97292588Sdelphij	}
98207753Smm#endif
99207753Smm
100207753Smm#ifdef __DJGPP__
101207753Smm	// Avoid doing useless things when statting files.
102207753Smm	// This isn't important but doesn't hurt.
103292588Sdelphij	_djstat_flags = _STAT_EXEC_EXT | _STAT_EXEC_MAGIC | _STAT_DIRSIZE;
104207753Smm#endif
105207753Smm
106207753Smm	return;
107207753Smm}
108207753Smm
109207753Smm
110292588Sdelphij#ifndef TUKLIB_DOSLIKE
111207753Smmextern void
112292588Sdelphijio_write_to_user_abort_pipe(void)
113292588Sdelphij{
114292588Sdelphij	// If the write() fails, it's probably due to the pipe being full.
115292588Sdelphij	// Failing in that case is fine. If the reason is something else,
116292588Sdelphij	// there's not much we can do since this is called in a signal
117292588Sdelphij	// handler. So ignore the errors and try to avoid warnings with
118292588Sdelphij	// GCC and glibc when _FORTIFY_SOURCE=2 is used.
119292588Sdelphij	uint8_t b = '\0';
120292588Sdelphij	const int ret = write(user_abort_pipe[1], &b, 1);
121292588Sdelphij	(void)ret;
122292588Sdelphij	return;
123292588Sdelphij}
124292588Sdelphij#endif
125292588Sdelphij
126292588Sdelphij
127292588Sdelphijextern void
128207753Smmio_no_sparse(void)
129207753Smm{
130207753Smm	try_sparse = false;
131207753Smm	return;
132207753Smm}
133207753Smm
134207753Smm
135292588Sdelphij#ifndef TUKLIB_DOSLIKE
136292588Sdelphij/// \brief      Waits for input or output to become available or for a signal
137292588Sdelphij///
138292588Sdelphij/// This uses the self-pipe trick to avoid a race condition that can occur
139292588Sdelphij/// if a signal is caught after user_abort has been checked but before e.g.
140292588Sdelphij/// read() has been called. In that situation read() could block unless
141292588Sdelphij/// non-blocking I/O is used. With non-blocking I/O something like select()
142292588Sdelphij/// or poll() is needed to avoid a busy-wait loop, and the same race condition
143292588Sdelphij/// pops up again. There are pselect() (POSIX-1.2001) and ppoll() (not in
144292588Sdelphij/// POSIX) but neither is portable enough in 2013. The self-pipe trick is
145292588Sdelphij/// old and very portable.
146292588Sdelphijstatic io_wait_ret
147292588Sdelphijio_wait(file_pair *pair, int timeout, bool is_reading)
148292588Sdelphij{
149292588Sdelphij	struct pollfd pfd[2];
150292588Sdelphij
151292588Sdelphij	if (is_reading) {
152292588Sdelphij		pfd[0].fd = pair->src_fd;
153292588Sdelphij		pfd[0].events = POLLIN;
154292588Sdelphij	} else {
155292588Sdelphij		pfd[0].fd = pair->dest_fd;
156292588Sdelphij		pfd[0].events = POLLOUT;
157292588Sdelphij	}
158292588Sdelphij
159292588Sdelphij	pfd[1].fd = user_abort_pipe[0];
160292588Sdelphij	pfd[1].events = POLLIN;
161292588Sdelphij
162292588Sdelphij	while (true) {
163292588Sdelphij		const int ret = poll(pfd, 2, timeout);
164292588Sdelphij
165292588Sdelphij		if (user_abort)
166292588Sdelphij			return IO_WAIT_ERROR;
167292588Sdelphij
168292588Sdelphij		if (ret == -1) {
169292588Sdelphij			if (errno == EINTR || errno == EAGAIN)
170292588Sdelphij				continue;
171292588Sdelphij
172292588Sdelphij			message_error(_("%s: poll() failed: %s"),
173292588Sdelphij					is_reading ? pair->src_name
174292588Sdelphij						: pair->dest_name,
175292588Sdelphij					strerror(errno));
176292588Sdelphij			return IO_WAIT_ERROR;
177292588Sdelphij		}
178292588Sdelphij
179292588Sdelphij		if (ret == 0) {
180292588Sdelphij			assert(opt_flush_timeout != 0);
181292588Sdelphij			flush_needed = true;
182292588Sdelphij			return IO_WAIT_TIMEOUT;
183292588Sdelphij		}
184292588Sdelphij
185292588Sdelphij		if (pfd[0].revents != 0)
186292588Sdelphij			return IO_WAIT_MORE;
187292588Sdelphij	}
188292588Sdelphij}
189292588Sdelphij#endif
190292588Sdelphij
191292588Sdelphij
192207753Smm/// \brief      Unlink a file
193207753Smm///
194207753Smm/// This tries to verify that the file being unlinked really is the file that
195207753Smm/// we want to unlink by verifying device and inode numbers. There's still
196207753Smm/// a small unavoidable race, but this is much better than nothing (the file
197207753Smm/// could have been moved/replaced even hours earlier).
198207753Smmstatic void
199207753Smmio_unlink(const char *name, const struct stat *known_st)
200207753Smm{
201207753Smm#if defined(TUKLIB_DOSLIKE)
202207753Smm	// On DOS-like systems, st_ino is meaningless, so don't bother
203207753Smm	// testing it. Just silence a compiler warning.
204207753Smm	(void)known_st;
205207753Smm#else
206207753Smm	struct stat new_st;
207207753Smm
208207753Smm	// If --force was used, use stat() instead of lstat(). This way
209207753Smm	// (de)compressing symlinks works correctly. However, it also means
210207753Smm	// that xz cannot detect if a regular file foo is renamed to bar
211207753Smm	// and then a symlink foo -> bar is created. Because of stat()
212207753Smm	// instead of lstat(), xz will think that foo hasn't been replaced
213207753Smm	// with another file. Thus, xz will remove foo even though it no
214207753Smm	// longer is the same file that xz used when it started compressing.
215207753Smm	// Probably it's not too bad though, so this doesn't need a more
216207753Smm	// complex fix.
217207753Smm	const int stat_ret = opt_force
218207753Smm			? stat(name, &new_st) : lstat(name, &new_st);
219207753Smm
220207753Smm	if (stat_ret
221207753Smm#	ifdef __VMS
222207753Smm			// st_ino is an array, and we don't want to
223207753Smm			// compare st_dev at all.
224207753Smm			|| memcmp(&new_st.st_ino, &known_st->st_ino,
225207753Smm				sizeof(new_st.st_ino)) != 0
226207753Smm#	else
227207753Smm			// Typical POSIX-like system
228207753Smm			|| new_st.st_dev != known_st->st_dev
229207753Smm			|| new_st.st_ino != known_st->st_ino
230207753Smm#	endif
231207753Smm			)
232207753Smm		// TRANSLATORS: When compression or decompression finishes,
233207753Smm		// and xz is going to remove the source file, xz first checks
234207753Smm		// if the source file still exists, and if it does, does its
235207753Smm		// device and inode numbers match what xz saw when it opened
236207753Smm		// the source file. If these checks fail, this message is
237207753Smm		// shown, %s being the filename, and the file is not deleted.
238207753Smm		// The check for device and inode numbers is there, because
239207753Smm		// it is possible that the user has put a new file in place
240207753Smm		// of the original file, and in that case it obviously
241207753Smm		// shouldn't be removed.
242207753Smm		message_error(_("%s: File seems to have been moved, "
243207753Smm				"not removing"), name);
244207753Smm	else
245207753Smm#endif
246207753Smm		// There's a race condition between lstat() and unlink()
247207753Smm		// but at least we have tried to avoid removing wrong file.
248207753Smm		if (unlink(name))
249207753Smm			message_error(_("%s: Cannot remove: %s"),
250207753Smm					name, strerror(errno));
251207753Smm
252207753Smm	return;
253207753Smm}
254207753Smm
255207753Smm
256207753Smm/// \brief      Copies owner/group and permissions
257207753Smm///
258207753Smm/// \todo       ACL and EA support
259207753Smm///
260207753Smmstatic void
261207753Smmio_copy_attrs(const file_pair *pair)
262207753Smm{
263207753Smm	// Skip chown and chmod on Windows.
264207753Smm#ifndef TUKLIB_DOSLIKE
265207753Smm	// This function is more tricky than you may think at first.
266207753Smm	// Blindly copying permissions may permit users to access the
267207753Smm	// destination file who didn't have permission to access the
268207753Smm	// source file.
269207753Smm
270207753Smm	// Try changing the owner of the file. If we aren't root or the owner
271207753Smm	// isn't already us, fchown() probably doesn't succeed. We warn
272207753Smm	// about failing fchown() only if we are root.
273207753Smm	if (fchown(pair->dest_fd, pair->src_st.st_uid, -1) && warn_fchown)
274207753Smm		message_warning(_("%s: Cannot set the file owner: %s"),
275207753Smm				pair->dest_name, strerror(errno));
276207753Smm
277207753Smm	mode_t mode;
278207753Smm
279207753Smm	if (fchown(pair->dest_fd, -1, pair->src_st.st_gid)) {
280207753Smm		message_warning(_("%s: Cannot set the file group: %s"),
281207753Smm				pair->dest_name, strerror(errno));
282207753Smm		// We can still safely copy some additional permissions:
283207753Smm		// `group' must be at least as strict as `other' and
284207753Smm		// also vice versa.
285207753Smm		//
286207753Smm		// NOTE: After this, the owner of the source file may
287207753Smm		// get additional permissions. This shouldn't be too bad,
288207753Smm		// because the owner would have had permission to chmod
289207753Smm		// the original file anyway.
290207753Smm		mode = ((pair->src_st.st_mode & 0070) >> 3)
291207753Smm				& (pair->src_st.st_mode & 0007);
292207753Smm		mode = (pair->src_st.st_mode & 0700) | (mode << 3) | mode;
293207753Smm	} else {
294207753Smm		// Drop the setuid, setgid, and sticky bits.
295207753Smm		mode = pair->src_st.st_mode & 0777;
296207753Smm	}
297207753Smm
298207753Smm	if (fchmod(pair->dest_fd, mode))
299207753Smm		message_warning(_("%s: Cannot set the file permissions: %s"),
300207753Smm				pair->dest_name, strerror(errno));
301207753Smm#endif
302207753Smm
303207753Smm	// Copy the timestamps. We have several possible ways to do this, of
304207753Smm	// which some are better in both security and precision.
305207753Smm	//
306207753Smm	// First, get the nanosecond part of the timestamps. As of writing,
307207753Smm	// it's not standardized by POSIX, and there are several names for
308207753Smm	// the same thing in struct stat.
309207753Smm	long atime_nsec;
310207753Smm	long mtime_nsec;
311207753Smm
312207753Smm#	if defined(HAVE_STRUCT_STAT_ST_ATIM_TV_NSEC)
313207753Smm	// GNU and Solaris
314207753Smm	atime_nsec = pair->src_st.st_atim.tv_nsec;
315207753Smm	mtime_nsec = pair->src_st.st_mtim.tv_nsec;
316207753Smm
317207753Smm#	elif defined(HAVE_STRUCT_STAT_ST_ATIMESPEC_TV_NSEC)
318207753Smm	// BSD
319207753Smm	atime_nsec = pair->src_st.st_atimespec.tv_nsec;
320207753Smm	mtime_nsec = pair->src_st.st_mtimespec.tv_nsec;
321207753Smm
322207753Smm#	elif defined(HAVE_STRUCT_STAT_ST_ATIMENSEC)
323207753Smm	// GNU and BSD without extensions
324207753Smm	atime_nsec = pair->src_st.st_atimensec;
325207753Smm	mtime_nsec = pair->src_st.st_mtimensec;
326207753Smm
327207753Smm#	elif defined(HAVE_STRUCT_STAT_ST_UATIME)
328207753Smm	// Tru64
329207753Smm	atime_nsec = pair->src_st.st_uatime * 1000;
330207753Smm	mtime_nsec = pair->src_st.st_umtime * 1000;
331207753Smm
332207753Smm#	elif defined(HAVE_STRUCT_STAT_ST_ATIM_ST__TIM_TV_NSEC)
333207753Smm	// UnixWare
334207753Smm	atime_nsec = pair->src_st.st_atim.st__tim.tv_nsec;
335207753Smm	mtime_nsec = pair->src_st.st_mtim.st__tim.tv_nsec;
336207753Smm
337207753Smm#	else
338207753Smm	// Safe fallback
339207753Smm	atime_nsec = 0;
340207753Smm	mtime_nsec = 0;
341207753Smm#	endif
342207753Smm
343207753Smm	// Construct a structure to hold the timestamps and call appropriate
344207753Smm	// function to set the timestamps.
345207753Smm#if defined(HAVE_FUTIMENS)
346207753Smm	// Use nanosecond precision.
347207753Smm	struct timespec tv[2];
348207753Smm	tv[0].tv_sec = pair->src_st.st_atime;
349207753Smm	tv[0].tv_nsec = atime_nsec;
350207753Smm	tv[1].tv_sec = pair->src_st.st_mtime;
351207753Smm	tv[1].tv_nsec = mtime_nsec;
352207753Smm
353207753Smm	(void)futimens(pair->dest_fd, tv);
354207753Smm
355207753Smm#elif defined(HAVE_FUTIMES) || defined(HAVE_FUTIMESAT) || defined(HAVE_UTIMES)
356207753Smm	// Use microsecond precision.
357207753Smm	struct timeval tv[2];
358207753Smm	tv[0].tv_sec = pair->src_st.st_atime;
359207753Smm	tv[0].tv_usec = atime_nsec / 1000;
360207753Smm	tv[1].tv_sec = pair->src_st.st_mtime;
361207753Smm	tv[1].tv_usec = mtime_nsec / 1000;
362207753Smm
363207753Smm#	if defined(HAVE_FUTIMES)
364207753Smm	(void)futimes(pair->dest_fd, tv);
365207753Smm#	elif defined(HAVE_FUTIMESAT)
366207753Smm	(void)futimesat(pair->dest_fd, NULL, tv);
367207753Smm#	else
368207753Smm	// Argh, no function to use a file descriptor to set the timestamp.
369207753Smm	(void)utimes(pair->dest_name, tv);
370207753Smm#	endif
371207753Smm
372207753Smm#elif defined(HAVE_UTIME)
373207753Smm	// Use one-second precision. utime() doesn't support using file
374207753Smm	// descriptor either. Some systems have broken utime() prototype
375207753Smm	// so don't make this const.
376207753Smm	struct utimbuf buf = {
377207753Smm		.actime = pair->src_st.st_atime,
378207753Smm		.modtime = pair->src_st.st_mtime,
379207753Smm	};
380207753Smm
381207753Smm	// Avoid warnings.
382207753Smm	(void)atime_nsec;
383207753Smm	(void)mtime_nsec;
384207753Smm
385207753Smm	(void)utime(pair->dest_name, &buf);
386207753Smm#endif
387207753Smm
388207753Smm	return;
389207753Smm}
390207753Smm
391207753Smm
392207753Smm/// Opens the source file. Returns false on success, true on error.
393207753Smmstatic bool
394207753Smmio_open_src_real(file_pair *pair)
395207753Smm{
396207753Smm	// There's nothing to open when reading from stdin.
397207753Smm	if (pair->src_name == stdin_filename) {
398207753Smm		pair->src_fd = STDIN_FILENO;
399207753Smm#ifdef TUKLIB_DOSLIKE
400207753Smm		setmode(STDIN_FILENO, O_BINARY);
401292588Sdelphij#else
402292588Sdelphij		// Try to set stdin to non-blocking mode. It won't work
403292588Sdelphij		// e.g. on OpenBSD if stdout is e.g. /dev/null. In such
404292588Sdelphij		// case we proceed as if stdin were non-blocking anyway
405292588Sdelphij		// (in case of /dev/null it will be in practice). The
406292588Sdelphij		// same applies to stdout in io_open_dest_real().
407292588Sdelphij		stdin_flags = fcntl(STDIN_FILENO, F_GETFL);
408292588Sdelphij		if (stdin_flags == -1) {
409292588Sdelphij			message_error(_("Error getting the file status flags "
410292588Sdelphij					"from standard input: %s"),
411292588Sdelphij					strerror(errno));
412292588Sdelphij			return true;
413292588Sdelphij		}
414292588Sdelphij
415292588Sdelphij		if ((stdin_flags & O_NONBLOCK) == 0
416292588Sdelphij				&& fcntl(STDIN_FILENO, F_SETFL,
417292588Sdelphij					stdin_flags | O_NONBLOCK) != -1)
418292588Sdelphij			restore_stdin_flags = true;
419207753Smm#endif
420292588Sdelphij#ifdef HAVE_POSIX_FADVISE
421292588Sdelphij		// It will fail if stdin is a pipe and that's fine.
422292588Sdelphij		(void)posix_fadvise(STDIN_FILENO, 0, 0, POSIX_FADV_SEQUENTIAL);
423292588Sdelphij#endif
424207753Smm		return false;
425207753Smm	}
426207753Smm
427207753Smm	// Symlinks are not followed unless writing to stdout or --force
428207753Smm	// was used.
429207753Smm	const bool follow_symlinks = opt_stdout || opt_force;
430207753Smm
431207753Smm	// We accept only regular files if we are writing the output
432207753Smm	// to disk too. bzip2 allows overriding this with --force but
433207753Smm	// gzip and xz don't.
434207753Smm	const bool reg_files_only = !opt_stdout;
435207753Smm
436207753Smm	// Flags for open()
437207753Smm	int flags = O_RDONLY | O_BINARY | O_NOCTTY;
438207753Smm
439207753Smm#ifndef TUKLIB_DOSLIKE
440292588Sdelphij	// Use non-blocking I/O:
441292588Sdelphij	//   - It prevents blocking when opening FIFOs and some other
442292588Sdelphij	//     special files, which is good if we want to accept only
443292588Sdelphij	//     regular files.
444292588Sdelphij	//   - It can help avoiding some race conditions with signal handling.
445292588Sdelphij	flags |= O_NONBLOCK;
446207753Smm#endif
447207753Smm
448207753Smm#if defined(O_NOFOLLOW)
449207753Smm	if (!follow_symlinks)
450207753Smm		flags |= O_NOFOLLOW;
451207753Smm#elif !defined(TUKLIB_DOSLIKE)
452207753Smm	// Some POSIX-like systems lack O_NOFOLLOW (it's not required
453207753Smm	// by POSIX). Check for symlinks with a separate lstat() on
454207753Smm	// these systems.
455207753Smm	if (!follow_symlinks) {
456207753Smm		struct stat st;
457207753Smm		if (lstat(pair->src_name, &st)) {
458207753Smm			message_error("%s: %s", pair->src_name,
459207753Smm					strerror(errno));
460207753Smm			return true;
461207753Smm
462207753Smm		} else if (S_ISLNK(st.st_mode)) {
463207753Smm			message_warning(_("%s: Is a symbolic link, "
464207753Smm					"skipping"), pair->src_name);
465207753Smm			return true;
466207753Smm		}
467207753Smm	}
468207753Smm#else
469207753Smm	// Avoid warnings.
470207753Smm	(void)follow_symlinks;
471207753Smm#endif
472207753Smm
473292588Sdelphij	// Try to open the file. Signals have been blocked so EINTR shouldn't
474292588Sdelphij	// be possible.
475292588Sdelphij	pair->src_fd = open(pair->src_name, flags);
476207753Smm
477207753Smm	if (pair->src_fd == -1) {
478292588Sdelphij		// Signals (that have a signal handler) have been blocked.
479292588Sdelphij		assert(errno != EINTR);
480207753Smm
481207753Smm#ifdef O_NOFOLLOW
482213700Smm		// Give an understandable error message if the reason
483207753Smm		// for failing was that the file was a symbolic link.
484207753Smm		//
485207753Smm		// Note that at least Linux, OpenBSD, Solaris, and Darwin
486213700Smm		// use ELOOP to indicate that O_NOFOLLOW was the reason
487207753Smm		// that open() failed. Because there may be
488207753Smm		// directories in the pathname, ELOOP may occur also
489207753Smm		// because of a symlink loop in the directory part.
490213700Smm		// So ELOOP doesn't tell us what actually went wrong,
491213700Smm		// and this stupidity went into POSIX-1.2008 too.
492207753Smm		//
493207753Smm		// FreeBSD associates EMLINK with O_NOFOLLOW and
494207753Smm		// Tru64 uses ENOTSUP. We use these directly here
495207753Smm		// and skip the lstat() call and the associated race.
496207753Smm		// I want to hear if there are other kernels that
497207753Smm		// fail with something else than ELOOP with O_NOFOLLOW.
498207753Smm		bool was_symlink = false;
499207753Smm
500207753Smm#	if defined(__FreeBSD__) || defined(__DragonFly__)
501207753Smm		if (errno == EMLINK)
502207753Smm			was_symlink = true;
503207753Smm
504207753Smm#	elif defined(__digital__) && defined(__unix__)
505207753Smm		if (errno == ENOTSUP)
506207753Smm			was_symlink = true;
507207753Smm
508207753Smm#	elif defined(__NetBSD__)
509207753Smm		if (errno == EFTYPE)
510207753Smm			was_symlink = true;
511207753Smm
512207753Smm#	else
513207753Smm		if (errno == ELOOP && !follow_symlinks) {
514207753Smm			const int saved_errno = errno;
515207753Smm			struct stat st;
516207753Smm			if (lstat(pair->src_name, &st) == 0
517207753Smm					&& S_ISLNK(st.st_mode))
518207753Smm				was_symlink = true;
519207753Smm
520207753Smm			errno = saved_errno;
521207753Smm		}
522207753Smm#	endif
523207753Smm
524207753Smm		if (was_symlink)
525207753Smm			message_warning(_("%s: Is a symbolic link, "
526207753Smm					"skipping"), pair->src_name);
527207753Smm		else
528207753Smm#endif
529207753Smm			// Something else than O_NOFOLLOW failing
530207753Smm			// (assuming that the race conditions didn't
531207753Smm			// confuse us).
532207753Smm			message_error("%s: %s", pair->src_name,
533207753Smm					strerror(errno));
534207753Smm
535207753Smm		return true;
536207753Smm	}
537207753Smm
538207753Smm	// Stat the source file. We need the result also when we copy
539207753Smm	// the permissions, and when unlinking.
540292588Sdelphij	//
541292588Sdelphij	// NOTE: Use stat() instead of fstat() with DJGPP, because
542292588Sdelphij	// then we have a better chance to get st_ino value that can
543292588Sdelphij	// be used in io_open_dest_real() to prevent overwriting the
544292588Sdelphij	// source file.
545292588Sdelphij#ifdef __DJGPP__
546292588Sdelphij	if (stat(pair->src_name, &pair->src_st))
547292588Sdelphij		goto error_msg;
548292588Sdelphij#else
549207753Smm	if (fstat(pair->src_fd, &pair->src_st))
550207753Smm		goto error_msg;
551292588Sdelphij#endif
552207753Smm
553207753Smm	if (S_ISDIR(pair->src_st.st_mode)) {
554207753Smm		message_warning(_("%s: Is a directory, skipping"),
555207753Smm				pair->src_name);
556207753Smm		goto error;
557207753Smm	}
558207753Smm
559219001Smm	if (reg_files_only && !S_ISREG(pair->src_st.st_mode)) {
560219001Smm		message_warning(_("%s: Not a regular file, skipping"),
561219001Smm				pair->src_name);
562219001Smm		goto error;
563219001Smm	}
564207753Smm
565207753Smm#ifndef TUKLIB_DOSLIKE
566219001Smm	if (reg_files_only && !opt_force) {
567207753Smm		if (pair->src_st.st_mode & (S_ISUID | S_ISGID)) {
568207753Smm			// gzip rejects setuid and setgid files even
569207753Smm			// when --force was used. bzip2 doesn't check
570207753Smm			// for them, but calls fchown() after fchmod(),
571207753Smm			// and many systems automatically drop setuid
572207753Smm			// and setgid bits there.
573207753Smm			//
574207753Smm			// We accept setuid and setgid files if
575207753Smm			// --force was used. We drop these bits
576207753Smm			// explicitly in io_copy_attr().
577207753Smm			message_warning(_("%s: File has setuid or "
578207753Smm					"setgid bit set, skipping"),
579207753Smm					pair->src_name);
580207753Smm			goto error;
581207753Smm		}
582207753Smm
583207753Smm		if (pair->src_st.st_mode & S_ISVTX) {
584207753Smm			message_warning(_("%s: File has sticky bit "
585207753Smm					"set, skipping"),
586207753Smm					pair->src_name);
587207753Smm			goto error;
588207753Smm		}
589207753Smm
590207753Smm		if (pair->src_st.st_nlink > 1) {
591207753Smm			message_warning(_("%s: Input file has more "
592207753Smm					"than one hard link, "
593207753Smm					"skipping"), pair->src_name);
594207753Smm			goto error;
595207753Smm		}
596219001Smm	}
597292588Sdelphij
598292588Sdelphij	// If it is something else than a regular file, wait until
599292588Sdelphij	// there is input available. This way reading from FIFOs
600292588Sdelphij	// will work when open() is used with O_NONBLOCK.
601292588Sdelphij	if (!S_ISREG(pair->src_st.st_mode)) {
602292588Sdelphij		signals_unblock();
603292588Sdelphij		const io_wait_ret ret = io_wait(pair, -1, true);
604292588Sdelphij		signals_block();
605292588Sdelphij
606292588Sdelphij		if (ret != IO_WAIT_MORE)
607292588Sdelphij			goto error;
608292588Sdelphij	}
609207753Smm#endif
610207753Smm
611292588Sdelphij#ifdef HAVE_POSIX_FADVISE
612292588Sdelphij	// It will fail with some special files like FIFOs but that is fine.
613292588Sdelphij	(void)posix_fadvise(pair->src_fd, 0, 0, POSIX_FADV_SEQUENTIAL);
614292588Sdelphij#endif
615292588Sdelphij
616207753Smm	return false;
617207753Smm
618207753Smmerror_msg:
619207753Smm	message_error("%s: %s", pair->src_name, strerror(errno));
620207753Smmerror:
621207753Smm	(void)close(pair->src_fd);
622207753Smm	return true;
623207753Smm}
624207753Smm
625207753Smm
626207753Smmextern file_pair *
627207753Smmio_open_src(const char *src_name)
628207753Smm{
629207753Smm	if (is_empty_filename(src_name))
630207753Smm		return NULL;
631207753Smm
632207753Smm	// Since we have only one file open at a time, we can use
633207753Smm	// a statically allocated structure.
634207753Smm	static file_pair pair;
635207753Smm
636207753Smm	pair = (file_pair){
637207753Smm		.src_name = src_name,
638207753Smm		.dest_name = NULL,
639207753Smm		.src_fd = -1,
640207753Smm		.dest_fd = -1,
641207753Smm		.src_eof = false,
642207753Smm		.dest_try_sparse = false,
643207753Smm		.dest_pending_sparse = 0,
644207753Smm	};
645207753Smm
646207753Smm	// Block the signals, for which we have a custom signal handler, so
647207753Smm	// that we don't need to worry about EINTR.
648207753Smm	signals_block();
649207753Smm	const bool error = io_open_src_real(&pair);
650207753Smm	signals_unblock();
651207753Smm
652207753Smm	return error ? NULL : &pair;
653207753Smm}
654207753Smm
655207753Smm
656207753Smm/// \brief      Closes source file of the file_pair structure
657207753Smm///
658207753Smm/// \param      pair    File whose src_fd should be closed
659207753Smm/// \param      success If true, the file will be removed from the disk if
660207753Smm///                     closing succeeds and --keep hasn't been used.
661207753Smmstatic void
662207753Smmio_close_src(file_pair *pair, bool success)
663207753Smm{
664292588Sdelphij#ifndef TUKLIB_DOSLIKE
665292588Sdelphij	if (restore_stdin_flags) {
666292588Sdelphij		assert(pair->src_fd == STDIN_FILENO);
667292588Sdelphij
668292588Sdelphij		restore_stdin_flags = false;
669292588Sdelphij
670292588Sdelphij		if (fcntl(STDIN_FILENO, F_SETFL, stdin_flags) == -1)
671292588Sdelphij			message_error(_("Error restoring the status flags "
672292588Sdelphij					"to standard input: %s"),
673292588Sdelphij					strerror(errno));
674292588Sdelphij	}
675292588Sdelphij#endif
676292588Sdelphij
677207753Smm	if (pair->src_fd != STDIN_FILENO && pair->src_fd != -1) {
678207753Smm#ifdef TUKLIB_DOSLIKE
679207753Smm		(void)close(pair->src_fd);
680207753Smm#endif
681207753Smm
682207753Smm		// If we are going to unlink(), do it before closing the file.
683207753Smm		// This way there's no risk that someone replaces the file and
684207753Smm		// happens to get same inode number, which would make us
685207753Smm		// unlink() wrong file.
686207753Smm		//
687207753Smm		// NOTE: DOS-like systems are an exception to this, because
688207753Smm		// they don't allow unlinking files that are open. *sigh*
689207753Smm		if (success && !opt_keep_original)
690207753Smm			io_unlink(pair->src_name, &pair->src_st);
691207753Smm
692207753Smm#ifndef TUKLIB_DOSLIKE
693207753Smm		(void)close(pair->src_fd);
694207753Smm#endif
695207753Smm	}
696207753Smm
697207753Smm	return;
698207753Smm}
699207753Smm
700207753Smm
701207753Smmstatic bool
702207753Smmio_open_dest_real(file_pair *pair)
703207753Smm{
704207753Smm	if (opt_stdout || pair->src_fd == STDIN_FILENO) {
705207753Smm		// We don't modify or free() this.
706207753Smm		pair->dest_name = (char *)"(stdout)";
707207753Smm		pair->dest_fd = STDOUT_FILENO;
708207753Smm#ifdef TUKLIB_DOSLIKE
709207753Smm		setmode(STDOUT_FILENO, O_BINARY);
710292588Sdelphij#else
711292588Sdelphij		// Try to set O_NONBLOCK if it isn't already set.
712292588Sdelphij		// If it fails, we assume that stdout is non-blocking
713292588Sdelphij		// in practice. See the comments in io_open_src_real()
714292588Sdelphij		// for similar situation with stdin.
715292588Sdelphij		//
716292588Sdelphij		// NOTE: O_APPEND may be unset later in this function
717292588Sdelphij		// and it relies on stdout_flags being set here.
718292588Sdelphij		stdout_flags = fcntl(STDOUT_FILENO, F_GETFL);
719292588Sdelphij		if (stdout_flags == -1) {
720292588Sdelphij			message_error(_("Error getting the file status flags "
721292588Sdelphij					"from standard output: %s"),
722292588Sdelphij					strerror(errno));
723292588Sdelphij			return true;
724292588Sdelphij		}
725292588Sdelphij
726292588Sdelphij		if ((stdout_flags & O_NONBLOCK) == 0
727292588Sdelphij				&& fcntl(STDOUT_FILENO, F_SETFL,
728292588Sdelphij					stdout_flags | O_NONBLOCK) != -1)
729292588Sdelphij				restore_stdout_flags = true;
730207753Smm#endif
731207753Smm	} else {
732207753Smm		pair->dest_name = suffix_get_dest_name(pair->src_name);
733207753Smm		if (pair->dest_name == NULL)
734207753Smm			return true;
735207753Smm
736292588Sdelphij#ifdef __DJGPP__
737292588Sdelphij		struct stat st;
738292588Sdelphij		if (stat(pair->dest_name, &st) == 0) {
739292588Sdelphij			// Check that it isn't a special file like "prn".
740292588Sdelphij			if (st.st_dev == -1) {
741292588Sdelphij				message_error("%s: Refusing to write to "
742292588Sdelphij						"a DOS special file",
743292588Sdelphij						pair->dest_name);
744292588Sdelphij				free(pair->dest_name);
745292588Sdelphij				return true;
746292588Sdelphij			}
747292588Sdelphij
748292588Sdelphij			// Check that we aren't overwriting the source file.
749292588Sdelphij			if (st.st_dev == pair->src_st.st_dev
750292588Sdelphij					&& st.st_ino == pair->src_st.st_ino) {
751292588Sdelphij				message_error("%s: Output file is the same "
752292588Sdelphij						"as the input file",
753292588Sdelphij						pair->dest_name);
754292588Sdelphij				free(pair->dest_name);
755292588Sdelphij				return true;
756292588Sdelphij			}
757292588Sdelphij		}
758292588Sdelphij#endif
759292588Sdelphij
760207753Smm		// If --force was used, unlink the target file first.
761207753Smm		if (opt_force && unlink(pair->dest_name) && errno != ENOENT) {
762207753Smm			message_error(_("%s: Cannot remove: %s"),
763207753Smm					pair->dest_name, strerror(errno));
764207753Smm			free(pair->dest_name);
765207753Smm			return true;
766207753Smm		}
767207753Smm
768207753Smm		// Open the file.
769292588Sdelphij		int flags = O_WRONLY | O_BINARY | O_NOCTTY
770207753Smm				| O_CREAT | O_EXCL;
771292588Sdelphij#ifndef TUKLIB_DOSLIKE
772292588Sdelphij		flags |= O_NONBLOCK;
773292588Sdelphij#endif
774207753Smm		const mode_t mode = S_IRUSR | S_IWUSR;
775207753Smm		pair->dest_fd = open(pair->dest_name, flags, mode);
776207753Smm
777207753Smm		if (pair->dest_fd == -1) {
778207753Smm			message_error("%s: %s", pair->dest_name,
779207753Smm					strerror(errno));
780207753Smm			free(pair->dest_name);
781207753Smm			return true;
782207753Smm		}
783207753Smm	}
784207753Smm
785292588Sdelphij#ifndef TUKLIB_DOSLIKE
786292588Sdelphij	// dest_st isn't used on DOS-like systems except as a dummy
787292588Sdelphij	// argument to io_unlink(), so don't fstat() on such systems.
788207753Smm	if (fstat(pair->dest_fd, &pair->dest_st)) {
789292588Sdelphij		// If fstat() really fails, we have a safe fallback here.
790292588Sdelphij#	if defined(__VMS)
791207753Smm		pair->dest_st.st_ino[0] = 0;
792207753Smm		pair->dest_st.st_ino[1] = 0;
793207753Smm		pair->dest_st.st_ino[2] = 0;
794292588Sdelphij#	else
795207753Smm		pair->dest_st.st_dev = 0;
796207753Smm		pair->dest_st.st_ino = 0;
797292588Sdelphij#	endif
798207753Smm	} else if (try_sparse && opt_mode == MODE_DECOMPRESS) {
799207753Smm		// When writing to standard output, we need to be extra
800207753Smm		// careful:
801207753Smm		//  - It may be connected to something else than
802207753Smm		//    a regular file.
803207753Smm		//  - We aren't necessarily writing to a new empty file
804207753Smm		//    or to the end of an existing file.
805207753Smm		//  - O_APPEND may be active.
806207753Smm		//
807207753Smm		// TODO: I'm keeping this disabled for DOS-like systems
808207753Smm		// for now. FAT doesn't support sparse files, but NTFS
809207753Smm		// does, so maybe this should be enabled on Windows after
810207753Smm		// some testing.
811207753Smm		if (pair->dest_fd == STDOUT_FILENO) {
812207753Smm			if (!S_ISREG(pair->dest_st.st_mode))
813207753Smm				return false;
814207753Smm
815263285Sdelphij			if (stdout_flags & O_APPEND) {
816207753Smm				// Creating a sparse file is not possible
817207753Smm				// when O_APPEND is active (it's used by
818207753Smm				// shell's >> redirection). As I understand
819207753Smm				// it, it is safe to temporarily disable
820207753Smm				// O_APPEND in xz, because if someone
821207753Smm				// happened to write to the same file at the
822207753Smm				// same time, results would be bad anyway
823207753Smm				// (users shouldn't assume that xz uses any
824207753Smm				// specific block size when writing data).
825207753Smm				//
826207753Smm				// The write position may be something else
827207753Smm				// than the end of the file, so we must fix
828207753Smm				// it to start writing at the end of the file
829207753Smm				// to imitate O_APPEND.
830207753Smm				if (lseek(STDOUT_FILENO, 0, SEEK_END) == -1)
831207753Smm					return false;
832207753Smm
833292588Sdelphij				// Construct the new file status flags.
834292588Sdelphij				// If O_NONBLOCK was set earlier in this
835292588Sdelphij				// function, it must be kept here too.
836292588Sdelphij				int flags = stdout_flags & ~O_APPEND;
837292588Sdelphij				if (restore_stdout_flags)
838292588Sdelphij					flags |= O_NONBLOCK;
839292588Sdelphij
840292588Sdelphij				// If this fcntl() fails, we continue but won't
841292588Sdelphij				// try to create sparse output. The original
842292588Sdelphij				// flags will still be restored if needed (to
843292588Sdelphij				// unset O_NONBLOCK) when the file is finished.
844292588Sdelphij				if (fcntl(STDOUT_FILENO, F_SETFL, flags) == -1)
845207753Smm					return false;
846207753Smm
847263285Sdelphij				// Disabling O_APPEND succeeded. Mark
848263285Sdelphij				// that the flags should be restored
849292588Sdelphij				// in io_close_dest(). (This may have already
850292588Sdelphij				// been set when enabling O_NONBLOCK.)
851263285Sdelphij				restore_stdout_flags = true;
852207753Smm
853207753Smm			} else if (lseek(STDOUT_FILENO, 0, SEEK_CUR)
854207753Smm					!= pair->dest_st.st_size) {
855207753Smm				// Writing won't start exactly at the end
856207753Smm				// of the file. We cannot use sparse output,
857207753Smm				// because it would probably corrupt the file.
858207753Smm				return false;
859207753Smm			}
860207753Smm		}
861207753Smm
862207753Smm		pair->dest_try_sparse = true;
863292588Sdelphij	}
864207753Smm#endif
865207753Smm
866207753Smm	return false;
867207753Smm}
868207753Smm
869207753Smm
870207753Smmextern bool
871207753Smmio_open_dest(file_pair *pair)
872207753Smm{
873207753Smm	signals_block();
874207753Smm	const bool ret = io_open_dest_real(pair);
875207753Smm	signals_unblock();
876207753Smm	return ret;
877207753Smm}
878207753Smm
879207753Smm
880207753Smm/// \brief      Closes destination file of the file_pair structure
881207753Smm///
882207753Smm/// \param      pair    File whose dest_fd should be closed
883207753Smm/// \param      success If false, the file will be removed from the disk.
884207753Smm///
885207753Smm/// \return     Zero if closing succeeds. On error, -1 is returned and
886207753Smm///             error message printed.
887207753Smmstatic bool
888207753Smmio_close_dest(file_pair *pair, bool success)
889207753Smm{
890207753Smm#ifndef TUKLIB_DOSLIKE
891207753Smm	// If io_open_dest() has disabled O_APPEND, restore it here.
892263285Sdelphij	if (restore_stdout_flags) {
893207753Smm		assert(pair->dest_fd == STDOUT_FILENO);
894207753Smm
895263285Sdelphij		restore_stdout_flags = false;
896207753Smm
897263285Sdelphij		if (fcntl(STDOUT_FILENO, F_SETFL, stdout_flags) == -1) {
898207753Smm			message_error(_("Error restoring the O_APPEND flag "
899207753Smm					"to standard output: %s"),
900207753Smm					strerror(errno));
901207753Smm			return true;
902207753Smm		}
903207753Smm	}
904207753Smm#endif
905207753Smm
906207753Smm	if (pair->dest_fd == -1 || pair->dest_fd == STDOUT_FILENO)
907207753Smm		return false;
908207753Smm
909207753Smm	if (close(pair->dest_fd)) {
910207753Smm		message_error(_("%s: Closing the file failed: %s"),
911207753Smm				pair->dest_name, strerror(errno));
912207753Smm
913207753Smm		// Closing destination file failed, so we cannot trust its
914207753Smm		// contents. Get rid of junk:
915207753Smm		io_unlink(pair->dest_name, &pair->dest_st);
916207753Smm		free(pair->dest_name);
917207753Smm		return true;
918207753Smm	}
919207753Smm
920207753Smm	// If the operation using this file wasn't successful, we git rid
921207753Smm	// of the junk file.
922207753Smm	if (!success)
923207753Smm		io_unlink(pair->dest_name, &pair->dest_st);
924207753Smm
925207753Smm	free(pair->dest_name);
926207753Smm
927207753Smm	return false;
928207753Smm}
929207753Smm
930207753Smm
931207753Smmextern void
932207753Smmio_close(file_pair *pair, bool success)
933207753Smm{
934207753Smm	// Take care of sparseness at the end of the output file.
935207753Smm	if (success && pair->dest_try_sparse
936207753Smm			&& pair->dest_pending_sparse > 0) {
937207753Smm		// Seek forward one byte less than the size of the pending
938207753Smm		// hole, then write one zero-byte. This way the file grows
939207753Smm		// to its correct size. An alternative would be to use
940207753Smm		// ftruncate() but that isn't portable enough (e.g. it
941207753Smm		// doesn't work with FAT on Linux; FAT isn't that important
942207753Smm		// since it doesn't support sparse files anyway, but we don't
943207753Smm		// want to create corrupt files on it).
944207753Smm		if (lseek(pair->dest_fd, pair->dest_pending_sparse - 1,
945207753Smm				SEEK_CUR) == -1) {
946207753Smm			message_error(_("%s: Seeking failed when trying "
947207753Smm					"to create a sparse file: %s"),
948207753Smm					pair->dest_name, strerror(errno));
949207753Smm			success = false;
950207753Smm		} else {
951207753Smm			const uint8_t zero[1] = { '\0' };
952207753Smm			if (io_write_buf(pair, zero, 1))
953207753Smm				success = false;
954207753Smm		}
955207753Smm	}
956207753Smm
957207753Smm	signals_block();
958207753Smm
959207753Smm	// Copy the file attributes. We need to skip this if destination
960207753Smm	// file isn't open or it is standard output.
961207753Smm	if (success && pair->dest_fd != -1 && pair->dest_fd != STDOUT_FILENO)
962207753Smm		io_copy_attrs(pair);
963207753Smm
964207753Smm	// Close the destination first. If it fails, we must not remove
965207753Smm	// the source file!
966207753Smm	if (io_close_dest(pair, success))
967207753Smm		success = false;
968207753Smm
969207753Smm	// Close the source file, and unlink it if the operation using this
970207753Smm	// file pair was successful and we haven't requested to keep the
971207753Smm	// source file.
972207753Smm	io_close_src(pair, success);
973207753Smm
974207753Smm	signals_unblock();
975207753Smm
976207753Smm	return;
977207753Smm}
978207753Smm
979207753Smm
980292588Sdelphijextern void
981292588Sdelphijio_fix_src_pos(file_pair *pair, size_t rewind_size)
982292588Sdelphij{
983292588Sdelphij	assert(rewind_size <= IO_BUFFER_SIZE);
984292588Sdelphij
985292588Sdelphij	if (rewind_size > 0) {
986292588Sdelphij		// This doesn't need to work on unseekable file descriptors,
987292588Sdelphij		// so just ignore possible errors.
988292588Sdelphij		(void)lseek(pair->src_fd, -(off_t)(rewind_size), SEEK_CUR);
989292588Sdelphij	}
990292588Sdelphij
991292588Sdelphij	return;
992292588Sdelphij}
993292588Sdelphij
994292588Sdelphij
995207753Smmextern size_t
996207753Smmio_read(file_pair *pair, io_buf *buf_union, size_t size)
997207753Smm{
998207753Smm	// We use small buffers here.
999207753Smm	assert(size < SSIZE_MAX);
1000207753Smm
1001207753Smm	uint8_t *buf = buf_union->u8;
1002207753Smm	size_t left = size;
1003207753Smm
1004207753Smm	while (left > 0) {
1005207753Smm		const ssize_t amount = read(pair->src_fd, buf, left);
1006207753Smm
1007207753Smm		if (amount == 0) {
1008207753Smm			pair->src_eof = true;
1009207753Smm			break;
1010207753Smm		}
1011207753Smm
1012207753Smm		if (amount == -1) {
1013207753Smm			if (errno == EINTR) {
1014207753Smm				if (user_abort)
1015207753Smm					return SIZE_MAX;
1016207753Smm
1017207753Smm				continue;
1018207753Smm			}
1019207753Smm
1020292588Sdelphij#ifndef TUKLIB_DOSLIKE
1021292588Sdelphij			if (errno == EAGAIN || errno == EWOULDBLOCK) {
1022292588Sdelphij				const io_wait_ret ret = io_wait(pair,
1023292588Sdelphij						mytime_get_flush_timeout(),
1024292588Sdelphij						true);
1025292588Sdelphij				switch (ret) {
1026292588Sdelphij				case IO_WAIT_MORE:
1027292588Sdelphij					continue;
1028292588Sdelphij
1029292588Sdelphij				case IO_WAIT_ERROR:
1030292588Sdelphij					return SIZE_MAX;
1031292588Sdelphij
1032292588Sdelphij				case IO_WAIT_TIMEOUT:
1033292588Sdelphij					return size - left;
1034292588Sdelphij
1035292588Sdelphij				default:
1036292588Sdelphij					message_bug();
1037292588Sdelphij				}
1038292588Sdelphij			}
1039292588Sdelphij#endif
1040292588Sdelphij
1041207753Smm			message_error(_("%s: Read error: %s"),
1042207753Smm					pair->src_name, strerror(errno));
1043207753Smm
1044207753Smm			return SIZE_MAX;
1045207753Smm		}
1046207753Smm
1047207753Smm		buf += (size_t)(amount);
1048207753Smm		left -= (size_t)(amount);
1049207753Smm	}
1050207753Smm
1051207753Smm	return size - left;
1052207753Smm}
1053207753Smm
1054207753Smm
1055207753Smmextern bool
1056207753Smmio_pread(file_pair *pair, io_buf *buf, size_t size, off_t pos)
1057207753Smm{
1058207753Smm	// Using lseek() and read() is more portable than pread() and
1059207753Smm	// for us it is as good as real pread().
1060207753Smm	if (lseek(pair->src_fd, pos, SEEK_SET) != pos) {
1061207753Smm		message_error(_("%s: Error seeking the file: %s"),
1062207753Smm				pair->src_name, strerror(errno));
1063207753Smm		return true;
1064207753Smm	}
1065207753Smm
1066207753Smm	const size_t amount = io_read(pair, buf, size);
1067207753Smm	if (amount == SIZE_MAX)
1068207753Smm		return true;
1069207753Smm
1070207753Smm	if (amount != size) {
1071207753Smm		message_error(_("%s: Unexpected end of file"),
1072207753Smm				pair->src_name);
1073207753Smm		return true;
1074207753Smm	}
1075207753Smm
1076207753Smm	return false;
1077207753Smm}
1078207753Smm
1079207753Smm
1080207753Smmstatic bool
1081207753Smmis_sparse(const io_buf *buf)
1082207753Smm{
1083207753Smm	assert(IO_BUFFER_SIZE % sizeof(uint64_t) == 0);
1084207753Smm
1085207753Smm	for (size_t i = 0; i < ARRAY_SIZE(buf->u64); ++i)
1086207753Smm		if (buf->u64[i] != 0)
1087207753Smm			return false;
1088207753Smm
1089207753Smm	return true;
1090207753Smm}
1091207753Smm
1092207753Smm
1093207753Smmstatic bool
1094207753Smmio_write_buf(file_pair *pair, const uint8_t *buf, size_t size)
1095207753Smm{
1096207753Smm	assert(size < SSIZE_MAX);
1097207753Smm
1098207753Smm	while (size > 0) {
1099207753Smm		const ssize_t amount = write(pair->dest_fd, buf, size);
1100207753Smm		if (amount == -1) {
1101207753Smm			if (errno == EINTR) {
1102207753Smm				if (user_abort)
1103263285Sdelphij					return true;
1104207753Smm
1105207753Smm				continue;
1106207753Smm			}
1107207753Smm
1108292588Sdelphij#ifndef TUKLIB_DOSLIKE
1109292588Sdelphij			if (errno == EAGAIN || errno == EWOULDBLOCK) {
1110292588Sdelphij				if (io_wait(pair, -1, false) == IO_WAIT_MORE)
1111292588Sdelphij					continue;
1112292588Sdelphij
1113292588Sdelphij				return true;
1114292588Sdelphij			}
1115292588Sdelphij#endif
1116292588Sdelphij
1117207753Smm			// Handle broken pipe specially. gzip and bzip2
1118207753Smm			// don't print anything on SIGPIPE. In addition,
1119207753Smm			// gzip --quiet uses exit status 2 (warning) on
1120207753Smm			// broken pipe instead of whatever raise(SIGPIPE)
1121207753Smm			// would make it return. It is there to hide "Broken
1122207753Smm			// pipe" message on some old shells (probably old
1123207753Smm			// GNU bash).
1124207753Smm			//
1125207753Smm			// We don't do anything special with --quiet, which
1126207753Smm			// is what bzip2 does too. If we get SIGPIPE, we
1127207753Smm			// will handle it like other signals by setting
1128207753Smm			// user_abort, and get EPIPE here.
1129207753Smm			if (errno != EPIPE)
1130207753Smm				message_error(_("%s: Write error: %s"),
1131207753Smm					pair->dest_name, strerror(errno));
1132207753Smm
1133207753Smm			return true;
1134207753Smm		}
1135207753Smm
1136207753Smm		buf += (size_t)(amount);
1137207753Smm		size -= (size_t)(amount);
1138207753Smm	}
1139207753Smm
1140207753Smm	return false;
1141207753Smm}
1142207753Smm
1143207753Smm
1144207753Smmextern bool
1145207753Smmio_write(file_pair *pair, const io_buf *buf, size_t size)
1146207753Smm{
1147207753Smm	assert(size <= IO_BUFFER_SIZE);
1148207753Smm
1149207753Smm	if (pair->dest_try_sparse) {
1150207753Smm		// Check if the block is sparse (contains only zeros). If it
1151207753Smm		// sparse, we just store the amount and return. We will take
1152207753Smm		// care of actually skipping over the hole when we hit the
1153207753Smm		// next data block or close the file.
1154207753Smm		//
1155207753Smm		// Since io_close() requires that dest_pending_sparse > 0
1156207753Smm		// if the file ends with sparse block, we must also return
1157207753Smm		// if size == 0 to avoid doing the lseek().
1158207753Smm		if (size == IO_BUFFER_SIZE) {
1159207753Smm			if (is_sparse(buf)) {
1160207753Smm				pair->dest_pending_sparse += size;
1161207753Smm				return false;
1162207753Smm			}
1163207753Smm		} else if (size == 0) {
1164207753Smm			return false;
1165207753Smm		}
1166207753Smm
1167207753Smm		// This is not a sparse block. If we have a pending hole,
1168207753Smm		// skip it now.
1169207753Smm		if (pair->dest_pending_sparse > 0) {
1170207753Smm			if (lseek(pair->dest_fd, pair->dest_pending_sparse,
1171207753Smm					SEEK_CUR) == -1) {
1172207753Smm				message_error(_("%s: Seeking failed when "
1173207753Smm						"trying to create a sparse "
1174207753Smm						"file: %s"), pair->dest_name,
1175207753Smm						strerror(errno));
1176207753Smm				return true;
1177207753Smm			}
1178207753Smm
1179207753Smm			pair->dest_pending_sparse = 0;
1180207753Smm		}
1181207753Smm	}
1182207753Smm
1183207753Smm	return io_write_buf(pair, buf->u8, size);
1184207753Smm}
1185