qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

buffer.c (8129B)


      1 /*
      2  * FUSE: Filesystem in Userspace
      3  * Copyright (C) 2010  Miklos Szeredi <miklos@szeredi.hu>
      4  *
      5  * Functions for dealing with `struct fuse_buf` and `struct
      6  * fuse_bufvec`.
      7  *
      8  * This program can be distributed under the terms of the GNU LGPLv2.
      9  * See the file COPYING.LIB
     10  */
     11 
     12 #include "qemu/osdep.h"
     13 #include "fuse_i.h"
     14 #include "fuse_lowlevel.h"
     15 
     16 size_t fuse_buf_size(const struct fuse_bufvec *bufv)
     17 {
     18     size_t i;
     19     size_t size = 0;
     20 
     21     for (i = 0; i < bufv->count; i++) {
     22         if (bufv->buf[i].size == SIZE_MAX) {
     23             size = SIZE_MAX;
     24         } else {
     25             size += bufv->buf[i].size;
     26         }
     27     }
     28 
     29     return size;
     30 }
     31 
     32 static ssize_t fuse_buf_writev(struct fuse_buf *out_buf,
     33                                struct fuse_bufvec *in_buf)
     34 {
     35     ssize_t res, i, j;
     36     size_t iovcnt = in_buf->count;
     37     struct iovec *iov;
     38     int fd = out_buf->fd;
     39 
     40     iov = g_try_new0(struct iovec, iovcnt);
     41     if (!iov) {
     42         return -ENOMEM;
     43     }
     44 
     45     for (i = 0, j = 0; i < iovcnt; i++) {
     46         /* Skip the buf with 0 size */
     47         if (in_buf->buf[i].size) {
     48             iov[j].iov_base = in_buf->buf[i].mem;
     49             iov[j].iov_len = in_buf->buf[i].size;
     50             j++;
     51         }
     52     }
     53 
     54     if (out_buf->flags & FUSE_BUF_FD_SEEK) {
     55         res = pwritev(fd, iov, iovcnt, out_buf->pos);
     56     } else {
     57         res = writev(fd, iov, iovcnt);
     58     }
     59 
     60     if (res == -1) {
     61         res = -errno;
     62     }
     63 
     64     g_free(iov);
     65     return res;
     66 }
     67 
     68 static size_t min_size(size_t s1, size_t s2)
     69 {
     70     return s1 < s2 ? s1 : s2;
     71 }
     72 
     73 static ssize_t fuse_buf_write(const struct fuse_buf *dst, size_t dst_off,
     74                               const struct fuse_buf *src, size_t src_off,
     75                               size_t len)
     76 {
     77     ssize_t res = 0;
     78     size_t copied = 0;
     79 
     80     while (len) {
     81         if (dst->flags & FUSE_BUF_FD_SEEK) {
     82             res = pwrite(dst->fd, (char *)src->mem + src_off, len,
     83                          dst->pos + dst_off);
     84         } else {
     85             res = write(dst->fd, (char *)src->mem + src_off, len);
     86         }
     87         if (res == -1) {
     88             if (!copied) {
     89                 return -errno;
     90             }
     91             break;
     92         }
     93         if (res == 0) {
     94             break;
     95         }
     96 
     97         copied += res;
     98         if (!(dst->flags & FUSE_BUF_FD_RETRY)) {
     99             break;
    100         }
    101 
    102         src_off += res;
    103         dst_off += res;
    104         len -= res;
    105     }
    106 
    107     return copied;
    108 }
    109 
    110 static ssize_t fuse_buf_read(const struct fuse_buf *dst, size_t dst_off,
    111                              const struct fuse_buf *src, size_t src_off,
    112                              size_t len)
    113 {
    114     ssize_t res = 0;
    115     size_t copied = 0;
    116 
    117     while (len) {
    118         if (src->flags & FUSE_BUF_FD_SEEK) {
    119             res = pread(src->fd, (char *)dst->mem + dst_off, len,
    120                         src->pos + src_off);
    121         } else {
    122             res = read(src->fd, (char *)dst->mem + dst_off, len);
    123         }
    124         if (res == -1) {
    125             if (!copied) {
    126                 return -errno;
    127             }
    128             break;
    129         }
    130         if (res == 0) {
    131             break;
    132         }
    133 
    134         copied += res;
    135         if (!(src->flags & FUSE_BUF_FD_RETRY)) {
    136             break;
    137         }
    138 
    139         dst_off += res;
    140         src_off += res;
    141         len -= res;
    142     }
    143 
    144     return copied;
    145 }
    146 
    147 static ssize_t fuse_buf_fd_to_fd(const struct fuse_buf *dst, size_t dst_off,
    148                                  const struct fuse_buf *src, size_t src_off,
    149                                  size_t len)
    150 {
    151     char buf[4096];
    152     struct fuse_buf tmp = {
    153         .size = sizeof(buf),
    154         .flags = 0,
    155     };
    156     ssize_t res;
    157     size_t copied = 0;
    158 
    159     tmp.mem = buf;
    160 
    161     while (len) {
    162         size_t this_len = min_size(tmp.size, len);
    163         size_t read_len;
    164 
    165         res = fuse_buf_read(&tmp, 0, src, src_off, this_len);
    166         if (res < 0) {
    167             if (!copied) {
    168                 return res;
    169             }
    170             break;
    171         }
    172         if (res == 0) {
    173             break;
    174         }
    175 
    176         read_len = res;
    177         res = fuse_buf_write(dst, dst_off, &tmp, 0, read_len);
    178         if (res < 0) {
    179             if (!copied) {
    180                 return res;
    181             }
    182             break;
    183         }
    184         if (res == 0) {
    185             break;
    186         }
    187 
    188         copied += res;
    189 
    190         if (res < this_len) {
    191             break;
    192         }
    193 
    194         dst_off += res;
    195         src_off += res;
    196         len -= res;
    197     }
    198 
    199     return copied;
    200 }
    201 
    202 static ssize_t fuse_buf_copy_one(const struct fuse_buf *dst, size_t dst_off,
    203                                  const struct fuse_buf *src, size_t src_off,
    204                                  size_t len)
    205 {
    206     int src_is_fd = src->flags & FUSE_BUF_IS_FD;
    207     int dst_is_fd = dst->flags & FUSE_BUF_IS_FD;
    208 
    209     if (!src_is_fd && !dst_is_fd) {
    210         char *dstmem = (char *)dst->mem + dst_off;
    211         char *srcmem = (char *)src->mem + src_off;
    212 
    213         if (dstmem != srcmem) {
    214             if (dstmem + len <= srcmem || srcmem + len <= dstmem) {
    215                 memcpy(dstmem, srcmem, len);
    216             } else {
    217                 memmove(dstmem, srcmem, len);
    218             }
    219         }
    220 
    221         return len;
    222     } else if (!src_is_fd) {
    223         return fuse_buf_write(dst, dst_off, src, src_off, len);
    224     } else if (!dst_is_fd) {
    225         return fuse_buf_read(dst, dst_off, src, src_off, len);
    226     } else {
    227         return fuse_buf_fd_to_fd(dst, dst_off, src, src_off, len);
    228     }
    229 }
    230 
    231 static const struct fuse_buf *fuse_bufvec_current(struct fuse_bufvec *bufv)
    232 {
    233     if (bufv->idx < bufv->count) {
    234         return &bufv->buf[bufv->idx];
    235     } else {
    236         return NULL;
    237     }
    238 }
    239 
    240 static int fuse_bufvec_advance(struct fuse_bufvec *bufv, size_t len)
    241 {
    242     const struct fuse_buf *buf = fuse_bufvec_current(bufv);
    243 
    244     if (!buf) {
    245         return 0;
    246     }
    247 
    248     bufv->off += len;
    249     assert(bufv->off <= buf->size);
    250     if (bufv->off == buf->size) {
    251         assert(bufv->idx < bufv->count);
    252         bufv->idx++;
    253         if (bufv->idx == bufv->count) {
    254             return 0;
    255         }
    256         bufv->off = 0;
    257     }
    258     return 1;
    259 }
    260 
    261 ssize_t fuse_buf_copy(struct fuse_bufvec *dstv, struct fuse_bufvec *srcv)
    262 {
    263     size_t copied = 0, i;
    264 
    265     if (dstv == srcv) {
    266         return fuse_buf_size(dstv);
    267     }
    268 
    269     /*
    270      * use writev to improve bandwidth when all the
    271      * src buffers already mapped by the daemon
    272      * process
    273      */
    274     for (i = 0; i < srcv->count; i++) {
    275         if (srcv->buf[i].flags & FUSE_BUF_IS_FD) {
    276             break;
    277         }
    278     }
    279     if ((i == srcv->count) && (dstv->count == 1) &&
    280         (dstv->idx == 0) &&
    281         (dstv->buf[0].flags & FUSE_BUF_IS_FD)) {
    282         dstv->buf[0].pos += dstv->off;
    283         return fuse_buf_writev(&dstv->buf[0], srcv);
    284     }
    285 
    286     for (;;) {
    287         const struct fuse_buf *src = fuse_bufvec_current(srcv);
    288         const struct fuse_buf *dst = fuse_bufvec_current(dstv);
    289         size_t src_len;
    290         size_t dst_len;
    291         size_t len;
    292         ssize_t res;
    293 
    294         if (src == NULL || dst == NULL) {
    295             break;
    296         }
    297 
    298         src_len = src->size - srcv->off;
    299         dst_len = dst->size - dstv->off;
    300         len = min_size(src_len, dst_len);
    301 
    302         res = fuse_buf_copy_one(dst, dstv->off, src, srcv->off, len);
    303         if (res < 0) {
    304             if (!copied) {
    305                 return res;
    306             }
    307             break;
    308         }
    309         copied += res;
    310 
    311         if (!fuse_bufvec_advance(srcv, res) ||
    312             !fuse_bufvec_advance(dstv, res)) {
    313             break;
    314         }
    315 
    316         if (res < len) {
    317             break;
    318         }
    319     }
    320 
    321     return copied;
    322 }
    323 
    324 void *fuse_mbuf_iter_advance(struct fuse_mbuf_iter *iter, size_t len)
    325 {
    326     void *ptr;
    327 
    328     if (len > iter->size - iter->pos) {
    329         return NULL;
    330     }
    331 
    332     ptr = iter->mem + iter->pos;
    333     iter->pos += len;
    334     return ptr;
    335 }
    336 
    337 const char *fuse_mbuf_iter_advance_str(struct fuse_mbuf_iter *iter)
    338 {
    339     const char *str = iter->mem + iter->pos;
    340     size_t remaining = iter->size - iter->pos;
    341     size_t i;
    342 
    343     for (i = 0; i < remaining; i++) {
    344         if (str[i] == '\0') {
    345             iter->pos += i + 1;
    346             return str;
    347         }
    348     }
    349     return NULL;
    350 }