]> wimlib.net Git - wimlib/blob - src/file_io.c
9455ed63ad0cd6cec4abdd5f9d645738059d2d33
[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 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 #ifdef __WIN32__
33 #  include "wimlib/win32.h" /* For pread(), pwrite() replacements */
34 #endif
35
36 /* Wrapper around read() that checks for errors keeps retrying until all
37  * requested bytes have been read or until end-of file has occurred.
38  *
39  * Return values:
40  *      WIMLIB_ERR_SUCCESS                      (0)
41  *      WIMLIB_ERR_READ                         (errno set)
42  *      WIMLIB_ERR_UNEXPECTED_END_OF_FILE       (errno set to 0)
43  */
44 int
45 full_read(struct filedes *fd, void *buf, size_t count)
46 {
47         ssize_t bytes_read;
48         size_t bytes_remaining;
49
50         for (bytes_remaining = count;
51              bytes_remaining != 0;
52              bytes_remaining -= bytes_read, buf += bytes_read)
53         {
54                 bytes_read = read(fd->fd, buf, bytes_remaining);
55                 if (unlikely(bytes_read <= 0)) {
56                         if (bytes_read == 0) {
57                                 errno = 0;
58                                 return WIMLIB_ERR_UNEXPECTED_END_OF_FILE;
59                         } else if (errno == EINTR) {
60                                 continue;
61                         } else {
62                                 return WIMLIB_ERR_READ;
63                         }
64                 }
65         }
66         count -= bytes_remaining;
67         fd->offset += count;
68         return 0;
69 }
70
71 static int
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 /* Wrapper around pread() that checks for errors and keeps retrying until all
101  * requested bytes have been read or until end-of file has occurred.  This also
102  * transparently handle reading from pipe files, but the caller needs to be sure
103  * the requested offset is greater than or equal to the current offset, or else
104  * WIMLIB_ERR_RESOURCE_ORDER will be returned.
105  *
106  * Return values:
107  *      WIMLIB_ERR_SUCCESS                      (0)
108  *      WIMLIB_ERR_READ                         (errno set)
109  *      WIMLIB_ERR_UNEXPECTED_END_OF_FILE       (errno set to 0)
110  *      WIMLIB_ERR_RESOURCE_ORDER               (errno set to ESPIPE)
111  */
112 int
113 full_pread(struct filedes *fd, void *buf, size_t count, off_t offset)
114 {
115         ssize_t bytes_read;
116         size_t bytes_remaining;
117
118         if (fd->is_pipe)
119                 goto is_pipe;
120
121         for (bytes_remaining = count;
122              bytes_remaining != 0;
123              bytes_remaining -= bytes_read, buf += bytes_read,
124                 offset += bytes_read)
125         {
126                 bytes_read = pread(fd->fd, buf, bytes_remaining, offset);
127                 if (unlikely(bytes_read <= 0)) {
128                         if (bytes_read == 0) {
129                                 errno = 0;
130                                 return WIMLIB_ERR_UNEXPECTED_END_OF_FILE;
131                         } else if (errno == EINTR) {
132                                 continue;
133                         } else if (errno == ESPIPE) {
134                                 fd->is_pipe = 1;
135                                 goto is_pipe;
136                         } else {
137                                 return WIMLIB_ERR_READ;
138                         }
139                 }
140         }
141         return 0;
142
143 is_pipe:
144         return pipe_read(fd, buf, count, offset);
145 }
146
147 /* Wrapper around write() that checks for errors and keeps retrying until all
148  * requested bytes have been written.
149  *
150  * Return values:
151  *      WIMLIB_ERR_SUCCESS                      (0)
152  *      WIMLIB_ERR_WRITE                        (errno set)
153  */
154 int
155 full_write(struct filedes *fd, const void *buf, size_t count)
156 {
157         ssize_t bytes_written;
158         size_t bytes_remaining;
159
160         for (bytes_remaining = count;
161              bytes_remaining != 0;
162              bytes_remaining -= bytes_written, buf += bytes_written)
163         {
164                 bytes_written = write(fd->fd, buf, bytes_remaining);
165                 if (unlikely(bytes_written < 0)) {
166                         if (errno == EINTR)
167                                 continue;
168                         return WIMLIB_ERR_WRITE;
169                 }
170         }
171         fd->offset += count;
172         return 0;
173 }
174
175
176 /* Wrapper around pwrite() that checks for errors and keeps retrying until all
177  * requested bytes have been written.
178  *
179  * Return values:
180  *      WIMLIB_ERR_SUCCESS      (0)
181  *      WIMLIB_ERR_WRITE        (errno set)
182  * */
183 int
184 full_pwrite(struct filedes *fd, const void *buf, size_t count, off_t offset)
185 {
186         ssize_t bytes_written;
187         size_t bytes_remaining;
188
189         for (bytes_remaining = count;
190              bytes_remaining != 0;
191              bytes_remaining -= bytes_written, buf += bytes_written,
192                 offset += bytes_written)
193         {
194                 bytes_written = pwrite(fd->fd, buf, bytes_remaining, offset);
195                 if (unlikely(bytes_written < 0)) {
196                         if (errno == EINTR)
197                                 continue;
198                         return WIMLIB_ERR_WRITE;
199                 }
200         }
201         return 0;
202 }
203
204 ssize_t
205 raw_pread(struct filedes *fd, void *buf, size_t count, off_t offset)
206 {
207         return pread(fd->fd, buf, count, offset);
208 }
209
210 ssize_t
211 raw_pwrite(struct filedes *fd, const void *buf, size_t count, off_t offset)
212 {
213         return pwrite(fd->fd, buf, count, offset);
214 }
215
216 off_t filedes_seek(struct filedes *fd, off_t offset)
217 {
218         if (fd->is_pipe) {
219                 errno = ESPIPE;
220                 return -1;
221         }
222         if (fd->offset != offset) {
223                 if (lseek(fd->fd, offset, SEEK_SET) == -1)
224                         return -1;
225                 fd->offset = offset;
226         }
227         return offset;
228 }
229
230 bool filedes_is_seekable(struct filedes *fd)
231 {
232         return !fd->is_pipe && lseek(fd->fd, 0, SEEK_CUR) != -1;
233 }