Home | History | Annotate | Line # | Download | only in refuse
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