qemu/tools/virtiofsd/buffer.c
<<
>>
Prefs
   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
  16size_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
  32static 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
  68static size_t min_size(size_t s1, size_t s2)
  69{
  70    return s1 < s2 ? s1 : s2;
  71}
  72
  73static 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
 110static 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
 147static 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
 202static 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
 231static 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
 240static 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
 261ssize_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
 324void *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
 337const 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}
 351