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