qemu/migration/qemu-file-unix.c
<<
>>
Prefs
   1/*
   2 * QEMU System Emulator
   3 *
   4 * Copyright (c) 2003-2008 Fabrice Bellard
   5 *
   6 * Permission is hereby granted, free of charge, to any person obtaining a copy
   7 * of this software and associated documentation files (the "Software"), to deal
   8 * in the Software without restriction, including without limitation the rights
   9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10 * copies of the Software, and to permit persons to whom the Software is
  11 * furnished to do so, subject to the following conditions:
  12 *
  13 * The above copyright notice and this permission notice shall be included in
  14 * all copies or substantial portions of the Software.
  15 *
  16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22 * THE SOFTWARE.
  23 */
  24#include "qemu-common.h"
  25#include "qemu/iov.h"
  26#include "qemu/sockets.h"
  27#include "block/coroutine.h"
  28#include "migration/qemu-file.h"
  29
  30typedef struct QEMUFileSocket {
  31    int fd;
  32    QEMUFile *file;
  33} QEMUFileSocket;
  34
  35static ssize_t socket_writev_buffer(void *opaque, struct iovec *iov, int iovcnt,
  36                                    int64_t pos)
  37{
  38    QEMUFileSocket *s = opaque;
  39    ssize_t len;
  40    ssize_t size = iov_size(iov, iovcnt);
  41
  42    len = iov_send(s->fd, iov, iovcnt, 0, size);
  43    if (len < size) {
  44        len = -socket_error();
  45    }
  46    return len;
  47}
  48
  49static int socket_get_fd(void *opaque)
  50{
  51    QEMUFileSocket *s = opaque;
  52
  53    return s->fd;
  54}
  55
  56static int socket_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size)
  57{
  58    QEMUFileSocket *s = opaque;
  59    ssize_t len;
  60
  61    for (;;) {
  62        len = qemu_recv(s->fd, buf, size, 0);
  63        if (len != -1) {
  64            break;
  65        }
  66        if (socket_error() == EAGAIN) {
  67            yield_until_fd_readable(s->fd);
  68        } else if (socket_error() != EINTR) {
  69            break;
  70        }
  71    }
  72
  73    if (len == -1) {
  74        len = -socket_error();
  75    }
  76    return len;
  77}
  78
  79static int socket_close(void *opaque)
  80{
  81    QEMUFileSocket *s = opaque;
  82    closesocket(s->fd);
  83    g_free(s);
  84    return 0;
  85}
  86
  87static ssize_t unix_writev_buffer(void *opaque, struct iovec *iov, int iovcnt,
  88                                  int64_t pos)
  89{
  90    QEMUFileSocket *s = opaque;
  91    ssize_t len, offset;
  92    ssize_t size = iov_size(iov, iovcnt);
  93    ssize_t total = 0;
  94
  95    assert(iovcnt > 0);
  96    offset = 0;
  97    while (size > 0) {
  98        /* Find the next start position; skip all full-sized vector elements  */
  99        while (offset >= iov[0].iov_len) {
 100            offset -= iov[0].iov_len;
 101            iov++, iovcnt--;
 102        }
 103
 104        /* skip `offset' bytes from the (now) first element, undo it on exit */
 105        assert(iovcnt > 0);
 106        iov[0].iov_base += offset;
 107        iov[0].iov_len -= offset;
 108
 109        do {
 110            len = writev(s->fd, iov, iovcnt);
 111        } while (len == -1 && errno == EINTR);
 112        if (len == -1) {
 113            return -errno;
 114        }
 115
 116        /* Undo the changes above */
 117        iov[0].iov_base -= offset;
 118        iov[0].iov_len += offset;
 119
 120        /* Prepare for the next iteration */
 121        offset += len;
 122        total += len;
 123        size -= len;
 124    }
 125
 126    return total;
 127}
 128
 129static int unix_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size)
 130{
 131    QEMUFileSocket *s = opaque;
 132    ssize_t len;
 133
 134    for (;;) {
 135        len = read(s->fd, buf, size);
 136        if (len != -1) {
 137            break;
 138        }
 139        if (errno == EAGAIN) {
 140            yield_until_fd_readable(s->fd);
 141        } else if (errno != EINTR) {
 142            break;
 143        }
 144    }
 145
 146    if (len == -1) {
 147        len = -errno;
 148    }
 149    return len;
 150}
 151
 152static int unix_close(void *opaque)
 153{
 154    QEMUFileSocket *s = opaque;
 155    close(s->fd);
 156    g_free(s);
 157    return 0;
 158}
 159
 160static const QEMUFileOps unix_read_ops = {
 161    .get_fd =     socket_get_fd,
 162    .get_buffer = unix_get_buffer,
 163    .close =      unix_close
 164};
 165
 166static const QEMUFileOps unix_write_ops = {
 167    .get_fd =     socket_get_fd,
 168    .writev_buffer = unix_writev_buffer,
 169    .close =      unix_close
 170};
 171
 172QEMUFile *qemu_fdopen(int fd, const char *mode)
 173{
 174    QEMUFileSocket *s;
 175
 176    if (mode == NULL ||
 177        (mode[0] != 'r' && mode[0] != 'w') ||
 178        mode[1] != 'b' || mode[2] != 0) {
 179        fprintf(stderr, "qemu_fdopen: Argument validity check failed\n");
 180        return NULL;
 181    }
 182
 183    s = g_malloc0(sizeof(QEMUFileSocket));
 184    s->fd = fd;
 185
 186    if (mode[0] == 'r') {
 187        s->file = qemu_fopen_ops(s, &unix_read_ops);
 188    } else {
 189        s->file = qemu_fopen_ops(s, &unix_write_ops);
 190    }
 191    return s->file;
 192}
 193
 194static const QEMUFileOps socket_read_ops = {
 195    .get_fd =     socket_get_fd,
 196    .get_buffer = socket_get_buffer,
 197    .close =      socket_close
 198};
 199
 200static const QEMUFileOps socket_write_ops = {
 201    .get_fd =     socket_get_fd,
 202    .writev_buffer = socket_writev_buffer,
 203    .close =      socket_close
 204};
 205
 206QEMUFile *qemu_fopen_socket(int fd, const char *mode)
 207{
 208    QEMUFileSocket *s;
 209
 210    if (qemu_file_mode_is_not_valid(mode)) {
 211        return NULL;
 212    }
 213
 214    s = g_malloc0(sizeof(QEMUFileSocket));
 215    s->fd = fd;
 216    if (mode[0] == 'w') {
 217        qemu_set_block(s->fd);
 218        s->file = qemu_fopen_ops(s, &socket_write_ops);
 219    } else {
 220        s->file = qemu_fopen_ops(s, &socket_read_ops);
 221    }
 222    return s->file;
 223}
 224