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