xref: /netbsd-src/lib/librefuse/refuse/buf.c (revision b2cb7fcce3663dae46a3988a78b7cb0484374ed4)
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