]> wimlib.net Git - wimlib/blob - src/file_io.c
Use LGPLv3+ for src/*.c
[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 "wimlib/error.h"
27 #include "wimlib/file_io.h"
28 #include "wimlib/util.h"
29 #ifdef __WIN32__
30 #  include "wimlib/win32.h" /* For pread(), pwrite() replacements */
31 #endif
32
33 #include <errno.h>
34 #include <unistd.h>
35
36
37 /* Wrapper around read() that checks for errors keeps retrying until all
38  * requested bytes have been read or until end-of file has occurred.
39  *
40  * Return values:
41  *      WIMLIB_ERR_SUCCESS                      (0)
42  *      WIMLIB_ERR_READ                         (errno set)
43  *      WIMLIB_ERR_UNEXPECTED_END_OF_FILE       (errno set to 0)
44  */
45 int
46 full_read(struct filedes *fd, void *buf, size_t count)
47 {
48         ssize_t bytes_read;
49         size_t bytes_remaining;
50
51         for (bytes_remaining = count;
52              bytes_remaining != 0;
53              bytes_remaining -= bytes_read, buf += bytes_read)
54         {
55                 bytes_read = read(fd->fd, buf, bytes_remaining);
56                 if (unlikely(bytes_read <= 0)) {
57                         if (bytes_read == 0) {
58                                 errno = 0;
59                                 return WIMLIB_ERR_UNEXPECTED_END_OF_FILE;
60                         } else if (errno == EINTR) {
61                                 continue;
62                         } else {
63                                 return WIMLIB_ERR_READ;
64                         }
65                 }
66         }
67         count -= bytes_remaining;
68         fd->offset += count;
69         return 0;
70 }
71
72 static int
73 pipe_read(struct filedes *fd, void *buf, size_t count, off_t offset)
74 {
75         int ret;
76
77         /* Verify the offset.  */
78         if (offset < fd->offset) {
79                 ERROR("Can't seek backwards in pipe "
80                       "(offset %"PRIu64" => %"PRIu64").\n"
81                       "        Make sure the WIM was captured as "
82                       "pipable.", fd->offset, offset);
83                 errno = ESPIPE;
84                 return WIMLIB_ERR_RESOURCE_ORDER;
85         }
86
87         /* Manually seek to the requested position.  */
88         while (fd->offset != offset) {
89                 size_t bytes_to_read = min(offset - fd->offset, BUFFER_SIZE);
90                 u8 dummy[bytes_to_read];
91
92                 ret = full_read(fd, dummy, bytes_to_read);
93                 if (ret)
94                         return ret;
95         }
96
97         /* Do the actual read.  */
98         return full_read(fd, buf, count);
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 0)
111  *      WIMLIB_ERR_RESOURCE_ORDER               (errno set to ESPIPE)
112  */
113 int
114 full_pread(struct filedes *fd, void *buf, size_t count, off_t offset)
115 {
116         ssize_t bytes_read;
117         size_t bytes_remaining;
118
119         if (fd->is_pipe)
120                 goto is_pipe;
121
122         for (bytes_remaining = count;
123              bytes_remaining != 0;
124              bytes_remaining -= bytes_read, buf += bytes_read,
125                 offset += bytes_read)
126         {
127                 bytes_read = pread(fd->fd, buf, bytes_remaining, offset);
128                 if (unlikely(bytes_read <= 0)) {
129                         if (bytes_read == 0) {
130                                 errno = 0;
131                                 return WIMLIB_ERR_UNEXPECTED_END_OF_FILE;
132                         } else if (errno == EINTR) {
133                                 continue;
134                         } else if (errno == ESPIPE) {
135                                 fd->is_pipe = 1;
136                                 goto is_pipe;
137                         } else {
138                                 return WIMLIB_ERR_READ;
139                         }
140                 }
141         }
142         return 0;
143
144 is_pipe:
145         return pipe_read(fd, buf, count, offset);
146 }
147
148 /* Wrapper around write() that checks for errors and keeps retrying until all
149  * requested bytes have been written.
150  *
151  * Return values:
152  *      WIMLIB_ERR_SUCCESS                      (0)
153  *      WIMLIB_ERR_WRITE                        (errno set)
154  */
155 int
156 full_write(struct filedes *fd, const void *buf, size_t count)
157 {
158         ssize_t bytes_written;
159         size_t bytes_remaining;
160
161         for (bytes_remaining = count;
162              bytes_remaining != 0;
163              bytes_remaining -= bytes_written, buf += bytes_written)
164         {
165                 bytes_written = write(fd->fd, buf, bytes_remaining);
166                 if (unlikely(bytes_written < 0)) {
167                         if (errno == EINTR)
168                                 continue;
169                         return WIMLIB_ERR_WRITE;
170                 }
171         }
172         fd->offset += count;
173         return 0;
174 }
175
176
177 /* Wrapper around pwrite() that checks for errors and keeps retrying until all
178  * requested bytes have been written.
179  *
180  * Return values:
181  *      WIMLIB_ERR_SUCCESS      (0)
182  *      WIMLIB_ERR_WRITE        (errno set)
183  * */
184 int
185 full_pwrite(struct filedes *fd, const void *buf, size_t count, off_t offset)
186 {
187         ssize_t bytes_written;
188         size_t bytes_remaining;
189
190         for (bytes_remaining = count;
191              bytes_remaining != 0;
192              bytes_remaining -= bytes_written, buf += bytes_written,
193                 offset += bytes_written)
194         {
195                 bytes_written = pwrite(fd->fd, buf, bytes_remaining, offset);
196                 if (unlikely(bytes_written < 0)) {
197                         if (errno == EINTR)
198                                 continue;
199                         return WIMLIB_ERR_WRITE;
200                 }
201         }
202         return 0;
203 }
204
205 ssize_t
206 raw_pread(struct filedes *fd, void *buf, size_t count, off_t offset)
207 {
208         return pread(fd->fd, buf, count, offset);
209 }
210
211 ssize_t
212 raw_pwrite(struct filedes *fd, const void *buf, size_t count, off_t offset)
213 {
214         return pwrite(fd->fd, buf, count, offset);
215 }
216
217 off_t filedes_seek(struct filedes *fd, off_t offset)
218 {
219         if (fd->is_pipe) {
220                 errno = ESPIPE;
221                 return -1;
222         }
223         if (fd->offset != offset) {
224                 if (lseek(fd->fd, offset, SEEK_SET) == -1)
225                         return -1;
226                 fd->offset = offset;
227         }
228         return offset;
229 }
230
231 bool filedes_is_seekable(struct filedes *fd)
232 {
233         return !fd->is_pipe && lseek(fd->fd, 0, SEEK_CUR) != -1;
234 }