file_io.c: Cleanup
[wimlib] / src / file_io.c
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 part of wimlib, a library for working with WIM files.
9  *
10  * wimlib is free software; you can redistribute it and/or modify it under the
11  * terms of the GNU General Public License as published by the Free
12  * Software Foundation; either version 3 of the License, or (at your option)
13  * any later version.
14  *
15  * wimlib is distributed in the hope that it will be useful, but WITHOUT ANY
16  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
17  * A PARTICULAR PURPOSE. See the GNU General Public License for more
18  * details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with wimlib; if not, see http://www.gnu.org/licenses/.
22  */
23
24 #ifdef HAVE_CONFIG_H
25 #  include "config.h"
26 #endif
27
28 #include "wimlib/error.h"
29 #include "wimlib/file_io.h"
30 #include "wimlib/util.h"
31 #ifdef __WIN32__
32 #  include "wimlib/win32.h" /* For pread(), pwrite() replacements */
33 #else
34 #  include <sys/uio.h> /* for writev() and `struct iovec' */
35 #endif
36
37 #include <errno.h>
38 #include <unistd.h>
39
40
41 /* Wrapper around read() that checks for errors keeps retrying until all
42  * requested bytes have been read or until end-of file has occurred.
43  *
44  * Return values:
45  *      WIMLIB_ERR_SUCCESS                      (0)
46  *      WIMLIB_ERR_READ                         (errno set)
47  *      WIMLIB_ERR_UNEXPECTED_END_OF_FILE       (errno set to 0)
48  */
49 int
50 full_read(struct filedes *fd, void *buf, size_t count)
51 {
52         ssize_t bytes_read;
53         size_t bytes_remaining;
54
55         for (bytes_remaining = count;
56              bytes_remaining != 0;
57              bytes_remaining -= bytes_read, buf += bytes_read)
58         {
59                 bytes_read = read(fd->fd, buf, bytes_remaining);
60                 if (unlikely(bytes_read <= 0)) {
61                         if (bytes_read == 0) {
62                                 errno = 0;
63                                 return WIMLIB_ERR_UNEXPECTED_END_OF_FILE;
64                         } else if (errno == EINTR) {
65                                 continue;
66                         } else {
67                                 return WIMLIB_ERR_READ;
68                         }
69                 }
70         }
71         count -= bytes_remaining;
72         fd->offset += count;
73         return 0;
74 }
75
76 static int
77 pipe_read(struct filedes *fd, void *buf, size_t count, off_t offset)
78 {
79         int ret;
80
81         /* Verify the offset.  */
82         if (offset < fd->offset) {
83                 ERROR("Can't seek backwards in pipe "
84                       "(offset %"PRIu64" => %"PRIu64").\n"
85                       "        Make sure the WIM was captured as "
86                       "pipable.", fd->offset, offset);
87                 errno = ESPIPE;
88                 return WIMLIB_ERR_RESOURCE_ORDER;
89         }
90
91         /* Manually seek to the requested position.  */
92         while (fd->offset != offset) {
93                 size_t bytes_to_read = min(offset - fd->offset, BUFFER_SIZE);
94                 u8 dummy[bytes_to_read];
95
96                 ret = full_read(fd, dummy, bytes_to_read);
97                 if (ret)
98                         return ret;
99         }
100
101         /* Do the actual read.  */
102         return full_read(fd, buf, count);
103 }
104
105 /* Wrapper around pread() that checks for errors and keeps retrying until all
106  * requested bytes have been read or until end-of file has occurred.  This also
107  * transparently handle reading from pipe files, but the caller needs to be sure
108  * the requested offset is greater than or equal to the current offset, or else
109  * WIMLIB_ERR_RESOURCE_ORDER will be returned.
110  *
111  * Return values:
112  *      WIMLIB_ERR_SUCCESS                      (0)
113  *      WIMLIB_ERR_READ                         (errno set)
114  *      WIMLIB_ERR_UNEXPECTED_END_OF_FILE       (errno set to 0)
115  *      WIMLIB_ERR_RESOURCE_ORDER               (errno set to ESPIPE)
116  */
117 int
118 full_pread(struct filedes *fd, void *buf, size_t count, off_t offset)
119 {
120         ssize_t bytes_read;
121         size_t bytes_remaining;
122
123         if (fd->is_pipe)
124                 goto is_pipe;
125
126         for (bytes_remaining = count;
127              bytes_remaining != 0;
128              bytes_remaining -= bytes_read, buf += bytes_read,
129                 offset += bytes_read)
130         {
131                 bytes_read = pread(fd->fd, buf, bytes_remaining, offset);
132                 if (unlikely(bytes_read <= 0)) {
133                         if (bytes_read == 0) {
134                                 errno = 0;
135                                 return WIMLIB_ERR_UNEXPECTED_END_OF_FILE;
136                         } else if (errno == EINTR) {
137                                 continue;
138                         } else if (errno == ESPIPE) {
139                                 fd->is_pipe = 1;
140                                 goto is_pipe;
141                         } else {
142                                 return WIMLIB_ERR_READ;
143                         }
144                 }
145         }
146         return 0;
147
148 is_pipe:
149         return pipe_read(fd, buf, count, offset);
150 }
151
152 /* Wrapper around write() that checks for errors and keeps retrying until all
153  * requested bytes have been written.
154  *
155  * Return values:
156  *      WIMLIB_ERR_SUCCESS                      (0)
157  *      WIMLIB_ERR_WRITE                        (errno set)
158  */
159 int
160 full_write(struct filedes *fd, const void *buf, size_t count)
161 {
162         ssize_t bytes_written;
163         size_t bytes_remaining;
164
165         for (bytes_remaining = count;
166              bytes_remaining != 0;
167              bytes_remaining -= bytes_written, buf += bytes_written)
168         {
169                 bytes_written = write(fd->fd, buf, bytes_remaining);
170                 if (unlikely(bytes_written < 0)) {
171                         if (errno == EINTR)
172                                 continue;
173                         return WIMLIB_ERR_WRITE;
174                 }
175         }
176         fd->offset += count;
177         return 0;
178 }
179
180
181 /* Wrapper around pwrite() that checks for errors and keeps retrying until all
182  * requested bytes have been written.
183  *
184  * Return values:
185  *      WIMLIB_ERR_SUCCESS      (0)
186  *      WIMLIB_ERR_WRITE        (errno set)
187  * */
188 int
189 full_pwrite(struct filedes *fd, const void *buf, size_t count, off_t offset)
190 {
191         ssize_t bytes_written;
192         size_t bytes_remaining;
193
194         for (bytes_remaining = count;
195              bytes_remaining != 0;
196              bytes_remaining -= bytes_written, buf += bytes_written,
197                 offset += bytes_written)
198         {
199                 bytes_written = pwrite(fd->fd, buf, bytes_remaining, offset);
200                 if (unlikely(bytes_written < 0)) {
201                         if (errno == EINTR)
202                                 continue;
203                         return WIMLIB_ERR_WRITE;
204                 }
205         }
206         return 0;
207 }
208
209 /* Wrapper around writev() that checks for errors and keep retrying until all
210  * requested bytes have been written.
211  *
212  * Return values:
213  *      WIMLIB_ERR_SUCCESS      (0)
214  *      WIMLIB_ERR_WRITE        (errno set)
215  * */
216 int
217 full_writev(struct filedes *fd, struct iovec *iov, int iovcnt)
218 {
219         size_t total_bytes_written = 0;
220         while (iovcnt > 0) {
221                 ssize_t bytes_written;
222
223                 bytes_written = writev(fd->fd, iov, iovcnt);
224                 if (unlikely(bytes_written < 0)) {
225                         if (errno == EINTR)
226                                 continue;
227                         return WIMLIB_ERR_WRITE;
228                 }
229                 total_bytes_written += bytes_written;
230                 while (bytes_written) {
231                         if (bytes_written >= iov[0].iov_len) {
232                                 bytes_written -= iov[0].iov_len;
233                                 iov++;
234                                 iovcnt--;
235                         } else {
236                                 iov[0].iov_base += bytes_written;
237                                 iov[0].iov_len -= bytes_written;
238                                 bytes_written = 0;
239                         }
240                 }
241         }
242         fd->offset += total_bytes_written;
243         return 0;
244 }
245
246 ssize_t
247 raw_pread(struct filedes *fd, void *buf, size_t count, off_t offset)
248 {
249         return pread(fd->fd, buf, count, offset);
250 }
251
252 ssize_t
253 raw_pwrite(struct filedes *fd, const void *buf, size_t count, off_t offset)
254 {
255         return pwrite(fd->fd, buf, count, offset);
256 }
257
258 off_t filedes_seek(struct filedes *fd, off_t offset)
259 {
260         if (fd->is_pipe) {
261                 errno = ESPIPE;
262                 return -1;
263         }
264         if (fd->offset != offset) {
265                 if (lseek(fd->fd, offset, SEEK_SET) == -1)
266                         return -1;
267                 fd->offset = offset;
268         }
269         return offset;
270 }
271
272 bool filedes_is_seekable(struct filedes *fd)
273 {
274         return !fd->is_pipe && lseek(fd->fd, 0, SEEK_CUR) != -1;
275 }