1 /*
2  * file_io.c - Helper functions for reading and writing to file descriptors.
3  */
4 
5 /*
6  * Copyright (C) 2013 Eric Biggers
7  *
8  * This file is free software; you can redistribute it and/or modify it under
9  * the terms of the GNU Lesser General Public License as published by the Free
10  * Software Foundation; either version 3 of the License, or (at your option) any
11  * later version.
12  *
13  * This file is distributed in the hope that it will be useful, but WITHOUT
14  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
15  * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU Lesser General Public License
19  * along with this file; if not, see http://www.gnu.org/licenses/.
20  */
21 
22 #ifdef HAVE_CONFIG_H
23 #  include "config.h"
24 #endif
25 
26 #include <errno.h>
27 #include <unistd.h>
28 
29 #include "wimlib/error.h"
30 #include "wimlib/file_io.h"
31 #include "wimlib/util.h"
32 
33 #ifdef __WIN32__
34 #  include "wimlib/win32.h"
35 #  define read win32_read
36 #  define write win32_write
37 #  define pread win32_pread
38 #  define pwrite win32_pwrite
39 #endif
40 
41 /*
42  * Wrapper around read() that checks for errors and keeps retrying until all
43  * requested bytes have been read or until end-of file has occurred.
44  *
45  * Return values:
46  *	WIMLIB_ERR_SUCCESS			(0)
47  *	WIMLIB_ERR_READ				(errno set)
48  *	WIMLIB_ERR_UNEXPECTED_END_OF_FILE	(errno set to EINVAL)
49  */
50 int
full_read(struct filedes * fd,void * buf,size_t count)51 full_read(struct filedes *fd, void *buf, size_t count)
52 {
53 	while (count) {
54 		ssize_t ret = read(fd->fd, buf, count);
55 		if (unlikely(ret <= 0)) {
56 			if (ret == 0) {
57 				errno = EINVAL;
58 				return WIMLIB_ERR_UNEXPECTED_END_OF_FILE;
59 			}
60 			if (errno == EINTR)
61 				continue;
62 			return WIMLIB_ERR_READ;
63 		}
64 		buf += ret;
65 		count -= ret;
66 		fd->offset += ret;
67 	}
68 	return 0;
69 }
70 
71 static int
pipe_read(struct filedes * fd,void * buf,size_t count,off_t offset)72 pipe_read(struct filedes *fd, void *buf, size_t count, off_t offset)
73 {
74 	int ret;
75 
76 	/* Verify the offset.  */
77 	if (offset < fd->offset) {
78 		ERROR("Can't seek backwards in pipe "
79 		      "(offset %"PRIu64" => %"PRIu64").\n"
80 		      "        Make sure the WIM was captured as "
81 		      "pipable.", fd->offset, offset);
82 		errno = ESPIPE;
83 		return WIMLIB_ERR_RESOURCE_ORDER;
84 	}
85 
86 	/* Manually seek to the requested position.  */
87 	while (fd->offset != offset) {
88 		size_t bytes_to_read = min(offset - fd->offset, BUFFER_SIZE);
89 		u8 dummy[bytes_to_read];
90 
91 		ret = full_read(fd, dummy, bytes_to_read);
92 		if (ret)
93 			return ret;
94 	}
95 
96 	/* Do the actual read.  */
97 	return full_read(fd, buf, count);
98 }
99 
100 /*
101  * Wrapper around pread() that checks for errors and keeps retrying until all
102  * requested bytes have been read or until end-of file has occurred.  This also
103  * transparently handle reading from pipe files, but the caller needs to be sure
104  * the requested offset is greater than or equal to the current offset, or else
105  * WIMLIB_ERR_RESOURCE_ORDER will be returned.
106  *
107  * Return values:
108  *	WIMLIB_ERR_SUCCESS			(0)
109  *	WIMLIB_ERR_READ				(errno set)
110  *	WIMLIB_ERR_UNEXPECTED_END_OF_FILE	(errno set to EINVAL)
111  *	WIMLIB_ERR_RESOURCE_ORDER		(errno set to ESPIPE)
112  */
113 int
full_pread(struct filedes * fd,void * buf,size_t count,off_t offset)114 full_pread(struct filedes *fd, void *buf, size_t count, off_t offset)
115 {
116 	if (fd->is_pipe)
117 		goto is_pipe;
118 
119 	while (count) {
120 		ssize_t ret = pread(fd->fd, buf, count, offset);
121 		if (unlikely(ret <= 0)) {
122 			if (ret == 0) {
123 				errno = EINVAL;
124 				return WIMLIB_ERR_UNEXPECTED_END_OF_FILE;
125 			}
126 			if (errno == EINTR)
127 				continue;
128 			if (errno == ESPIPE) {
129 				fd->is_pipe = 1;
130 				goto is_pipe;
131 			}
132 			return WIMLIB_ERR_READ;
133 		}
134 		buf += ret;
135 		count -= ret;
136 		offset += ret;
137 	}
138 	return 0;
139 
140 is_pipe:
141 	return pipe_read(fd, buf, count, offset);
142 }
143 
144 /*
145  * Wrapper around write() that checks for errors and keeps retrying until all
146  * requested bytes have been written.
147  *
148  * Return values:
149  *	WIMLIB_ERR_SUCCESS			(0)
150  *	WIMLIB_ERR_WRITE			(errno set)
151  */
152 int
full_write(struct filedes * fd,const void * buf,size_t count)153 full_write(struct filedes *fd, const void *buf, size_t count)
154 {
155 	while (count) {
156 		ssize_t ret = write(fd->fd, buf, count);
157 		if (unlikely(ret < 0)) {
158 			if (errno == EINTR)
159 				continue;
160 			return WIMLIB_ERR_WRITE;
161 		}
162 		buf += ret;
163 		count -= ret;
164 		fd->offset += ret;
165 	}
166 	return 0;
167 }
168 
169 
170 /*
171  * Wrapper around pwrite() that checks for errors and keeps retrying until all
172  * requested bytes have been written.
173  *
174  * Return values:
175  *	WIMLIB_ERR_SUCCESS	(0)
176  *	WIMLIB_ERR_WRITE	(errno set)
177  */
178 int
full_pwrite(struct filedes * fd,const void * buf,size_t count,off_t offset)179 full_pwrite(struct filedes *fd, const void *buf, size_t count, off_t offset)
180 {
181 	while (count) {
182 		ssize_t ret = pwrite(fd->fd, buf, count, offset);
183 		if (unlikely(ret < 0)) {
184 			if (errno == EINTR)
185 				continue;
186 			return WIMLIB_ERR_WRITE;
187 		}
188 		buf += ret;
189 		count -= ret;
190 		offset += ret;
191 	}
192 	return 0;
193 }
194 
filedes_seek(struct filedes * fd,off_t offset)195 off_t filedes_seek(struct filedes *fd, off_t offset)
196 {
197 	if (fd->is_pipe) {
198 		errno = ESPIPE;
199 		return -1;
200 	}
201 	if (fd->offset != offset) {
202 		if (lseek(fd->fd, offset, SEEK_SET) == -1)
203 			return -1;
204 		fd->offset = offset;
205 	}
206 	return offset;
207 }
208 
filedes_is_seekable(struct filedes * fd)209 bool filedes_is_seekable(struct filedes *fd)
210 {
211 	return !fd->is_pipe && lseek(fd->fd, 0, SEEK_CUR) != -1;
212 }
213