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