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