buf.c revision 1.1 1 /* $NetBSD: buf.c,v 1.1 2022/01/22 07:54:57 pho Exp $ */
2
3 /*
4 * Copyright (c) 2021 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. The name of the author may not be used to endorse or promote
16 * products derived from this software without specific prior written
17 * permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
20 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
23 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
25 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
27 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
28 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
29 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 #include <sys/cdefs.h>
33 #if !defined(lint)
34 __RCSID("$NetBSD: buf.c,v 1.1 2022/01/22 07:54:57 pho Exp $");
35 #endif /* !lint */
36
37 #include <assert.h>
38 #include <errno.h>
39 #include <fuse_internal.h>
40 #include <machine/vmparam.h> /* for PAGE_SIZE */
41 #include <stdbool.h>
42 #include <stdint.h>
43 #include <stdlib.h>
44 #include <string.h>
45 #include <sys/param.h> /* for MIN(a, b) */
46 #include <unistd.h>
47
48 size_t
49 fuse_buf_size(const struct fuse_bufvec *bufv) {
50 size_t i;
51 size_t total = 0;
52
53 for (i = 0; i < bufv->count; i++) {
54 total += bufv->buf[i].size;
55 }
56
57 return total;
58 }
59
60 /* Return the pointer to the current buffer in a buffer vector, or
61 * NULL if we have reached the end of the vector. */
62 static const struct fuse_buf*
63 fuse_buf_current(const struct fuse_bufvec *bufv) {
64 if (bufv->idx < bufv->count)
65 return &bufv->buf[bufv->idx];
66 else
67 return NULL;
68 }
69
70 /* Copy data from one fd to a memory buffer, and return the number of
71 * octets that have been copied, or -1 on failure. */
72 static ssize_t
73 fuse_buf_read_fd_to_mem(const struct fuse_buf *dst, size_t dst_off,
74 const struct fuse_buf *src, size_t src_off,
75 size_t len) {
76 ssize_t total = 0;
77
78 while (len > 0) {
79 ssize_t n_read;
80
81 if (src->flags & FUSE_BUF_FD_SEEK)
82 n_read = pread(src->fd, (uint8_t*)dst->mem + dst_off, len,
83 src->pos + (off_t)src_off);
84 else
85 n_read = read(src->fd, (uint8_t*)dst->mem + dst_off, len);
86
87 if (n_read == -1) {
88 if (errno == EINTR)
89 continue;
90 else if (total == 0)
91 return -1;
92 else
93 /* The last pread(2) or read(2) failed but we have
94 * already copied some data. */
95 break;
96 }
97 else if (n_read == 0) {
98 /* Reached EOF */
99 break;
100 }
101 else {
102 total += n_read;
103 dst_off += (size_t)n_read;
104 src_off += (size_t)n_read;
105 len -= (size_t)n_read;
106
107 if (src->flags & FUSE_BUF_FD_RETRY)
108 continue;
109 }
110 }
111
112 return total;
113 }
114
115 /* Copy data from one memory buffer to an fd, and return the number of
116 * octets that have been copied, or -1 on failure. */
117 static ssize_t
118 fuse_buf_write_mem_to_fd(const struct fuse_buf *dst, size_t dst_off,
119 const struct fuse_buf *src, size_t src_off,
120 size_t len) {
121 ssize_t total = 0;
122
123 while (len > 0) {
124 ssize_t n_wrote;
125
126 if (dst->flags & FUSE_BUF_FD_SEEK)
127 n_wrote = pwrite(dst->fd, (uint8_t*)src->mem + src_off, len,
128 dst->pos + (off_t)dst_off);
129 else
130 n_wrote = write(dst->fd, (uint8_t*)src->mem + src_off, len);
131
132 if (n_wrote == -1) {
133 if (errno == EINTR)
134 continue;
135 else if (total == 0)
136 return -1;
137 else
138 /* The last pwrite(2) or write(2) failed but we have
139 * already copied some data. */
140 break;
141 }
142 else if (n_wrote == 0) {
143 break;
144 }
145 else {
146 total += n_wrote;
147 dst_off += (size_t)n_wrote;
148 src_off += (size_t)n_wrote;
149 len -= (size_t)n_wrote;
150
151 if (dst->flags & FUSE_BUF_FD_RETRY)
152 continue;
153 }
154 }
155
156 return total;
157 }
158
159 /* Copy data from one fd to another, and return the number of octets
160 * that have been copied, or -1 on failure. */
161 static ssize_t
162 fuse_buf_copy_fd_to_fd(const struct fuse_buf *dst, size_t dst_off,
163 const struct fuse_buf *src, size_t src_off,
164 size_t len) {
165 ssize_t total = 0;
166 struct fuse_buf tmp;
167
168 tmp.size = PAGE_SIZE;
169 tmp.flags = (enum fuse_buf_flags)0;
170 tmp.mem = malloc(tmp.size);
171
172 if (tmp.mem == NULL) {
173 return -1;
174 }
175
176 while (len) {
177 size_t n_to_read = MIN(tmp.size, len);
178 ssize_t n_read;
179 ssize_t n_wrote;
180
181 n_read = fuse_buf_read_fd_to_mem(&tmp, 0, src, src_off, n_to_read);
182 if (n_read == -1) {
183 if (total == 0) {
184 free(tmp.mem);
185 return -1;
186 }
187 else {
188 /* We have already copied some data. */
189 break;
190 }
191 }
192 else if (n_read == 0) {
193 /* Reached EOF */
194 break;
195 }
196
197 n_wrote = fuse_buf_write_mem_to_fd(dst, dst_off, &tmp, 0, (size_t)n_read);
198 if (n_wrote == -1) {
199 if (total == 0) {
200 free(tmp.mem);
201 return -1;
202 }
203 else {
204 /* We have already copied some data. */
205 break;
206 }
207 }
208 else if (n_wrote == 0) {
209 break;
210 }
211
212 total += n_wrote;
213 dst_off += (size_t)n_wrote;
214 src_off += (size_t)n_wrote;
215 len -= (size_t)n_wrote;
216
217 if (n_wrote < n_read)
218 /* Now we have some data that were read but couldn't be
219 * written, and can't do anything about it. */
220 break;
221 }
222
223 free(tmp.mem);
224 return total;
225 }
226
227 /* Copy data from one buffer to another, and return the number of
228 * octets that have been copied. */
229 static ssize_t
230 fuse_buf_copy_one(const struct fuse_buf *dst, size_t dst_off,
231 const struct fuse_buf *src, size_t src_off,
232 size_t len,
233 enum fuse_buf_copy_flags flags __attribute__((__unused__))) {
234
235 const bool dst_is_fd = !!(dst->flags & FUSE_BUF_IS_FD);
236 const bool src_is_fd = !!(src->flags & FUSE_BUF_IS_FD);
237
238 if (!dst_is_fd && !src_is_fd) {
239 void* dst_mem = (uint8_t*)dst->mem + dst_off;
240 void* src_mem = (uint8_t*)src->mem + src_off;
241
242 memmove(dst_mem, src_mem, len);
243
244 return (ssize_t)len;
245 }
246 else if (!dst_is_fd) {
247 return fuse_buf_read_fd_to_mem(dst, dst_off, src, src_off, len);
248 }
249 else if (!src_is_fd) {
250 return fuse_buf_write_mem_to_fd(dst, dst_off, src, src_off, len);
251 }
252 else {
253 return fuse_buf_copy_fd_to_fd(dst, dst_off, src, src_off, len);
254 }
255 }
256
257 /* Advance the buffer by a given number of octets. Return 0 on
258 * success, or -1 otherwise. Reaching the end of the buffer vector
259 * counts as a failure. */
260 static int
261 fuse_buf_advance(struct fuse_bufvec *bufv, size_t len) {
262 const struct fuse_buf *buf = fuse_buf_current(bufv);
263
264 assert(bufv->off + len <= buf->size);
265 bufv->off += len;
266 if (bufv->off == buf->size) {
267 /* Done with the current buffer. Advance to the next one. */
268 assert(bufv->idx < bufv->count);
269 bufv->idx++;
270 if (bufv->idx == bufv->count)
271 return -1; /* No more buffers in the vector. */
272 bufv->off = 0;
273 }
274 return 0;
275 }
276
277 ssize_t
278 fuse_buf_copy(struct fuse_bufvec *dstv, struct fuse_bufvec *srcv,
279 enum fuse_buf_copy_flags flags) {
280 ssize_t total = 0;
281
282 while (true) {
283 const struct fuse_buf* dst;
284 const struct fuse_buf* src;
285 size_t src_len;
286 size_t dst_len;
287 size_t len;
288 ssize_t n_copied;
289
290 dst = fuse_buf_current(dstv);
291 src = fuse_buf_current(srcv);
292 if (src == NULL || dst == NULL)
293 break;
294
295 src_len = src->size - srcv->off;
296 dst_len = dst->size - dstv->off;
297 len = MIN(src_len, dst_len);
298
299 n_copied = fuse_buf_copy_one(dst, dstv->off, src, srcv->off, len, flags);
300 if (n_copied == -1) {
301 if (total == 0)
302 return -1;
303 else
304 /* Failed to copy the current buffer but we have
305 * already copied some part of the vector. It is
306 * therefore inappropriate to return an error. */
307 break;
308 }
309 total += n_copied;
310
311 if (fuse_buf_advance(srcv, (size_t)n_copied) != 0 ||
312 fuse_buf_advance(dstv, (size_t)n_copied) != 0)
313 break;
314
315 if ((size_t)n_copied < len)
316 break;
317 }
318
319 return total;
320 }
321