1 /* $NetBSD: buf.c,v 1.2 2022/01/22 13:25:55 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.2 2022/01/22 13:25:55 pho Exp $");
35 #endif /* !lint */
36
37 #include <assert.h>
38 #include <errno.h>
39 #include <fuse_internal.h>
40 #include <stdbool.h>
41 #include <stdint.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include <sys/param.h> /* for MIN(a, b) */
45 #include <unistd.h>
46
47 size_t
fuse_buf_size(const struct fuse_bufvec * bufv)48 fuse_buf_size(const struct fuse_bufvec *bufv) {
49 size_t i;
50 size_t total = 0;
51
52 for (i = 0; i < bufv->count; i++) {
53 total += bufv->buf[i].size;
54 }
55
56 return total;
57 }
58
59 /* Return the pointer to the current buffer in a buffer vector, or
60 * NULL if we have reached the end of the vector. */
61 static const struct fuse_buf*
fuse_buf_current(const struct fuse_bufvec * bufv)62 fuse_buf_current(const struct fuse_bufvec *bufv) {
63 if (bufv->idx < bufv->count)
64 return &bufv->buf[bufv->idx];
65 else
66 return NULL;
67 }
68
69 /* Copy data from one fd to a memory buffer, and return the number of
70 * octets that have been copied, or -1 on failure. */
71 static ssize_t
fuse_buf_read_fd_to_mem(const struct fuse_buf * dst,size_t dst_off,const struct fuse_buf * src,size_t src_off,size_t len)72 fuse_buf_read_fd_to_mem(const struct fuse_buf *dst, size_t dst_off,
73 const struct fuse_buf *src, size_t src_off,
74 size_t len) {
75 ssize_t total = 0;
76
77 while (len > 0) {
78 ssize_t n_read;
79
80 if (src->flags & FUSE_BUF_FD_SEEK)
81 n_read = pread(src->fd, (uint8_t*)dst->mem + dst_off, len,
82 src->pos + (off_t)src_off);
83 else
84 n_read = read(src->fd, (uint8_t*)dst->mem + dst_off, len);
85
86 if (n_read == -1) {
87 if (errno == EINTR)
88 continue;
89 else if (total == 0)
90 return -1;
91 else
92 /* The last pread(2) or read(2) failed but we have
93 * already copied some data. */
94 break;
95 }
96 else if (n_read == 0) {
97 /* Reached EOF */
98 break;
99 }
100 else {
101 total += n_read;
102 dst_off += (size_t)n_read;
103 src_off += (size_t)n_read;
104 len -= (size_t)n_read;
105
106 if (src->flags & FUSE_BUF_FD_RETRY)
107 continue;
108 }
109 }
110
111 return total;
112 }
113
114 /* Copy data from one memory buffer to an fd, and return the number of
115 * octets that have been copied, or -1 on failure. */
116 static ssize_t
fuse_buf_write_mem_to_fd(const struct fuse_buf * dst,size_t dst_off,const struct fuse_buf * src,size_t src_off,size_t len)117 fuse_buf_write_mem_to_fd(const struct fuse_buf *dst, size_t dst_off,
118 const struct fuse_buf *src, size_t src_off,
119 size_t len) {
120 ssize_t total = 0;
121
122 while (len > 0) {
123 ssize_t n_wrote;
124
125 if (dst->flags & FUSE_BUF_FD_SEEK)
126 n_wrote = pwrite(dst->fd, (uint8_t*)src->mem + src_off, len,
127 dst->pos + (off_t)dst_off);
128 else
129 n_wrote = write(dst->fd, (uint8_t*)src->mem + src_off, len);
130
131 if (n_wrote == -1) {
132 if (errno == EINTR)
133 continue;
134 else if (total == 0)
135 return -1;
136 else
137 /* The last pwrite(2) or write(2) failed but we have
138 * already copied some data. */
139 break;
140 }
141 else if (n_wrote == 0) {
142 break;
143 }
144 else {
145 total += n_wrote;
146 dst_off += (size_t)n_wrote;
147 src_off += (size_t)n_wrote;
148 len -= (size_t)n_wrote;
149
150 if (dst->flags & FUSE_BUF_FD_RETRY)
151 continue;
152 }
153 }
154
155 return total;
156 }
157
158 /* Copy data from one fd to another, and return the number of octets
159 * that have been copied, or -1 on failure. */
160 static ssize_t
fuse_buf_copy_fd_to_fd(const struct fuse_buf * dst,size_t dst_off,const struct fuse_buf * src,size_t src_off,size_t len)161 fuse_buf_copy_fd_to_fd(const struct fuse_buf *dst, size_t dst_off,
162 const struct fuse_buf *src, size_t src_off,
163 size_t len) {
164 ssize_t total = 0;
165 struct fuse_buf tmp;
166
167 tmp.size = (size_t)sysconf(_SC_PAGESIZE);
168 tmp.flags = (enum fuse_buf_flags)0;
169 tmp.mem = malloc(tmp.size);
170
171 if (tmp.mem == NULL) {
172 return -1;
173 }
174
175 while (len) {
176 size_t n_to_read = MIN(tmp.size, len);
177 ssize_t n_read;
178 ssize_t n_wrote;
179
180 n_read = fuse_buf_read_fd_to_mem(&tmp, 0, src, src_off, n_to_read);
181 if (n_read == -1) {
182 if (total == 0) {
183 free(tmp.mem);
184 return -1;
185 }
186 else {
187 /* We have already copied some data. */
188 break;
189 }
190 }
191 else if (n_read == 0) {
192 /* Reached EOF */
193 break;
194 }
195
196 n_wrote = fuse_buf_write_mem_to_fd(dst, dst_off, &tmp, 0, (size_t)n_read);
197 if (n_wrote == -1) {
198 if (total == 0) {
199 free(tmp.mem);
200 return -1;
201 }
202 else {
203 /* We have already copied some data. */
204 break;
205 }
206 }
207 else if (n_wrote == 0) {
208 break;
209 }
210
211 total += n_wrote;
212 dst_off += (size_t)n_wrote;
213 src_off += (size_t)n_wrote;
214 len -= (size_t)n_wrote;
215
216 if (n_wrote < n_read)
217 /* Now we have some data that were read but couldn't be
218 * written, and can't do anything about it. */
219 break;
220 }
221
222 free(tmp.mem);
223 return total;
224 }
225
226 /* Copy data from one buffer to another, and return the number of
227 * octets that have been copied. */
228 static ssize_t
fuse_buf_copy_one(const struct fuse_buf * dst,size_t dst_off,const struct fuse_buf * src,size_t src_off,size_t len,enum fuse_buf_copy_flags flags)229 fuse_buf_copy_one(const struct fuse_buf *dst, size_t dst_off,
230 const struct fuse_buf *src, size_t src_off,
231 size_t len,
232 enum fuse_buf_copy_flags flags __attribute__((__unused__))) {
233
234 const bool dst_is_fd = !!(dst->flags & FUSE_BUF_IS_FD);
235 const bool src_is_fd = !!(src->flags & FUSE_BUF_IS_FD);
236
237 if (!dst_is_fd && !src_is_fd) {
238 void* dst_mem = (uint8_t*)dst->mem + dst_off;
239 void* src_mem = (uint8_t*)src->mem + src_off;
240
241 memmove(dst_mem, src_mem, len);
242
243 return (ssize_t)len;
244 }
245 else if (!dst_is_fd) {
246 return fuse_buf_read_fd_to_mem(dst, dst_off, src, src_off, len);
247 }
248 else if (!src_is_fd) {
249 return fuse_buf_write_mem_to_fd(dst, dst_off, src, src_off, len);
250 }
251 else {
252 return fuse_buf_copy_fd_to_fd(dst, dst_off, src, src_off, len);
253 }
254 }
255
256 /* Advance the buffer by a given number of octets. Return 0 on
257 * success, or -1 otherwise. Reaching the end of the buffer vector
258 * counts as a failure. */
259 static int
fuse_buf_advance(struct fuse_bufvec * bufv,size_t len)260 fuse_buf_advance(struct fuse_bufvec *bufv, size_t len) {
261 const struct fuse_buf *buf = fuse_buf_current(bufv);
262
263 assert(bufv->off + len <= buf->size);
264 bufv->off += len;
265 if (bufv->off == buf->size) {
266 /* Done with the current buffer. Advance to the next one. */
267 assert(bufv->idx < bufv->count);
268 bufv->idx++;
269 if (bufv->idx == bufv->count)
270 return -1; /* No more buffers in the vector. */
271 bufv->off = 0;
272 }
273 return 0;
274 }
275
276 ssize_t
fuse_buf_copy(struct fuse_bufvec * dstv,struct fuse_bufvec * srcv,enum fuse_buf_copy_flags flags)277 fuse_buf_copy(struct fuse_bufvec *dstv, struct fuse_bufvec *srcv,
278 enum fuse_buf_copy_flags flags) {
279 ssize_t total = 0;
280
281 while (true) {
282 const struct fuse_buf* dst;
283 const struct fuse_buf* src;
284 size_t src_len;
285 size_t dst_len;
286 size_t len;
287 ssize_t n_copied;
288
289 dst = fuse_buf_current(dstv);
290 src = fuse_buf_current(srcv);
291 if (src == NULL || dst == NULL)
292 break;
293
294 src_len = src->size - srcv->off;
295 dst_len = dst->size - dstv->off;
296 len = MIN(src_len, dst_len);
297
298 n_copied = fuse_buf_copy_one(dst, dstv->off, src, srcv->off, len, flags);
299 if (n_copied == -1) {
300 if (total == 0)
301 return -1;
302 else
303 /* Failed to copy the current buffer but we have
304 * already copied some part of the vector. It is
305 * therefore inappropriate to return an error. */
306 break;
307 }
308 total += n_copied;
309
310 if (fuse_buf_advance(srcv, (size_t)n_copied) != 0 ||
311 fuse_buf_advance(dstv, (size_t)n_copied) != 0)
312 break;
313
314 if ((size_t)n_copied < len)
315 break;
316 }
317
318 return total;
319 }
320