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
full_read(struct filedes * fd,void * buf,size_t count)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
pipe_read(struct filedes * fd,void * buf,size_t count,off_t offset)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
full_pread(struct filedes * fd,void * buf,size_t count,off_t offset)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
full_write(struct filedes * fd,const void * buf,size_t count)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
full_pwrite(struct filedes * fd,const void * buf,size_t count,off_t offset)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
filedes_seek(struct filedes * fd,off_t offset)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
filedes_is_seekable(struct filedes * fd)209 bool filedes_is_seekable(struct filedes *fd)
210 {
211 return !fd->is_pipe && lseek(fd->fd, 0, SEEK_CUR) != -1;
212 }
213