Do not use read() and write() from MSVCRT
[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
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
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
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
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
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
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
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
209 bool filedes_is_seekable(struct filedes *fd)
210 {
211         return !fd->is_pipe && lseek(fd->fd, 0, SEEK_CUR) != -1;
212 }