qemu/hw/9pfs/virtio-9p-proxy.c
<<
>>
Prefs
   1/*
   2 * Virtio 9p Proxy callback
   3 *
   4 * Copyright IBM, Corp. 2011
   5 *
   6 * Authors:
   7 * M. Mohan Kumar <mohan@in.ibm.com>
   8 *
   9 * This work is licensed under the terms of the GNU GPL, version 2.  See
  10 * the COPYING file in the top-level directory.
  11 */
  12#include <sys/socket.h>
  13#include <sys/un.h>
  14#include "hw/virtio/virtio.h"
  15#include "virtio-9p.h"
  16#include "qemu/error-report.h"
  17#include "fsdev/qemu-fsdev.h"
  18#include "virtio-9p-proxy.h"
  19
  20typedef struct V9fsProxy {
  21    int sockfd;
  22    QemuMutex mutex;
  23    struct iovec in_iovec;
  24    struct iovec out_iovec;
  25} V9fsProxy;
  26
  27/*
  28 * Return received file descriptor on success in *status.
  29 * errno is also returned on *status (which will be < 0)
  30 * return < 0 on transport error.
  31 */
  32static int v9fs_receivefd(int sockfd, int *status)
  33{
  34    struct iovec iov;
  35    struct msghdr msg;
  36    struct cmsghdr *cmsg;
  37    int retval, data, fd;
  38    union MsgControl msg_control;
  39
  40    iov.iov_base = &data;
  41    iov.iov_len = sizeof(data);
  42
  43    memset(&msg, 0, sizeof(msg));
  44    msg.msg_iov = &iov;
  45    msg.msg_iovlen = 1;
  46    msg.msg_control = &msg_control;
  47    msg.msg_controllen = sizeof(msg_control);
  48
  49    do {
  50        retval = recvmsg(sockfd, &msg, 0);
  51    } while (retval < 0 && errno == EINTR);
  52    if (retval <= 0) {
  53        return retval;
  54    }
  55    /*
  56     * data is set to V9FS_FD_VALID, if ancillary data is sent.  If this
  57     * request doesn't need ancillary data (fd) or an error occurred,
  58     * data is set to negative errno value.
  59     */
  60    if (data != V9FS_FD_VALID) {
  61        *status = data;
  62        return 0;
  63    }
  64    /*
  65     * File descriptor (fd) is sent in the ancillary data. Check if we
  66     * indeed received it. One of the reasons to fail to receive it is if
  67     * we exceeded the maximum number of file descriptors!
  68     */
  69    for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
  70        if (cmsg->cmsg_len != CMSG_LEN(sizeof(int)) ||
  71            cmsg->cmsg_level != SOL_SOCKET ||
  72            cmsg->cmsg_type != SCM_RIGHTS) {
  73            continue;
  74        }
  75        fd = *((int *)CMSG_DATA(cmsg));
  76        *status = fd;
  77        return 0;
  78    }
  79    *status = -ENFILE;  /* Ancillary data sent but not received */
  80    return 0;
  81}
  82
  83static ssize_t socket_read(int sockfd, void *buff, size_t size)
  84{
  85    ssize_t retval, total = 0;
  86
  87    while (size) {
  88        retval = read(sockfd, buff, size);
  89        if (retval == 0) {
  90            return -EIO;
  91        }
  92        if (retval < 0) {
  93            if (errno == EINTR) {
  94                continue;
  95            }
  96            return -errno;
  97        }
  98        size -= retval;
  99        buff += retval;
 100        total += retval;
 101    }
 102    return total;
 103}
 104
 105/* Converts proxy_statfs to VFS statfs structure */
 106static void prstatfs_to_statfs(struct statfs *stfs, ProxyStatFS *prstfs)
 107{
 108    memset(stfs, 0, sizeof(*stfs));
 109    stfs->f_type = prstfs->f_type;
 110    stfs->f_bsize = prstfs->f_bsize;
 111    stfs->f_blocks = prstfs->f_blocks;
 112    stfs->f_bfree = prstfs->f_bfree;
 113    stfs->f_bavail = prstfs->f_bavail;
 114    stfs->f_files = prstfs->f_files;
 115    stfs->f_ffree = prstfs->f_ffree;
 116    stfs->f_fsid.__val[0] = prstfs->f_fsid[0] & 0xFFFFFFFFU;
 117    stfs->f_fsid.__val[1] = prstfs->f_fsid[1] >> 32 & 0xFFFFFFFFU;
 118    stfs->f_namelen = prstfs->f_namelen;
 119    stfs->f_frsize = prstfs->f_frsize;
 120}
 121
 122/* Converts proxy_stat structure to VFS stat structure */
 123static void prstat_to_stat(struct stat *stbuf, ProxyStat *prstat)
 124{
 125   memset(stbuf, 0, sizeof(*stbuf));
 126   stbuf->st_dev = prstat->st_dev;
 127   stbuf->st_ino = prstat->st_ino;
 128   stbuf->st_nlink = prstat->st_nlink;
 129   stbuf->st_mode = prstat->st_mode;
 130   stbuf->st_uid = prstat->st_uid;
 131   stbuf->st_gid = prstat->st_gid;
 132   stbuf->st_rdev = prstat->st_rdev;
 133   stbuf->st_size = prstat->st_size;
 134   stbuf->st_blksize = prstat->st_blksize;
 135   stbuf->st_blocks = prstat->st_blocks;
 136   stbuf->st_atim.tv_sec = prstat->st_atim_sec;
 137   stbuf->st_atim.tv_nsec = prstat->st_atim_nsec;
 138   stbuf->st_mtime = prstat->st_mtim_sec;
 139   stbuf->st_mtim.tv_nsec = prstat->st_mtim_nsec;
 140   stbuf->st_ctime = prstat->st_ctim_sec;
 141   stbuf->st_ctim.tv_nsec = prstat->st_ctim_nsec;
 142}
 143
 144/*
 145 * Response contains two parts
 146 * {header, data}
 147 * header.type == T_ERROR, data -> -errno
 148 * header.type == T_SUCCESS, data -> response
 149 * size of errno/response is given by header.size
 150 * returns < 0, on transport error. response is
 151 * valid only if status >= 0.
 152 */
 153static int v9fs_receive_response(V9fsProxy *proxy, int type,
 154                                 int *status, void *response)
 155{
 156    int retval;
 157    ProxyHeader header;
 158    struct iovec *reply = &proxy->in_iovec;
 159
 160    *status = 0;
 161    reply->iov_len = 0;
 162    retval = socket_read(proxy->sockfd, reply->iov_base, PROXY_HDR_SZ);
 163    if (retval < 0) {
 164        return retval;
 165    }
 166    reply->iov_len = PROXY_HDR_SZ;
 167    proxy_unmarshal(reply, 0, "dd", &header.type, &header.size);
 168    /*
 169     * if response size > PROXY_MAX_IO_SZ, read the response but ignore it and
 170     * return -ENOBUFS
 171     */
 172    if (header.size > PROXY_MAX_IO_SZ) {
 173        int count;
 174        while (header.size > 0) {
 175            count = MIN(PROXY_MAX_IO_SZ, header.size);
 176            count = socket_read(proxy->sockfd, reply->iov_base, count);
 177            if (count < 0) {
 178                return count;
 179            }
 180            header.size -= count;
 181        }
 182        *status = -ENOBUFS;
 183        return 0;
 184    }
 185
 186    retval = socket_read(proxy->sockfd,
 187                         reply->iov_base + PROXY_HDR_SZ, header.size);
 188    if (retval < 0) {
 189        return retval;
 190    }
 191    reply->iov_len += header.size;
 192    /* there was an error during processing request */
 193    if (header.type == T_ERROR) {
 194        int ret;
 195        ret = proxy_unmarshal(reply, PROXY_HDR_SZ, "d", status);
 196        if (ret < 0) {
 197            *status = ret;
 198        }
 199        return 0;
 200    }
 201
 202    switch (type) {
 203    case T_LSTAT: {
 204        ProxyStat prstat;
 205        retval = proxy_unmarshal(reply, PROXY_HDR_SZ,
 206                                 "qqqdddqqqqqqqqqq", &prstat.st_dev,
 207                                 &prstat.st_ino, &prstat.st_nlink,
 208                                 &prstat.st_mode, &prstat.st_uid,
 209                                 &prstat.st_gid, &prstat.st_rdev,
 210                                 &prstat.st_size, &prstat.st_blksize,
 211                                 &prstat.st_blocks,
 212                                 &prstat.st_atim_sec, &prstat.st_atim_nsec,
 213                                 &prstat.st_mtim_sec, &prstat.st_mtim_nsec,
 214                                 &prstat.st_ctim_sec, &prstat.st_ctim_nsec);
 215        prstat_to_stat(response, &prstat);
 216        break;
 217    }
 218    case T_STATFS: {
 219        ProxyStatFS prstfs;
 220        retval = proxy_unmarshal(reply, PROXY_HDR_SZ,
 221                                 "qqqqqqqqqqq", &prstfs.f_type,
 222                                 &prstfs.f_bsize, &prstfs.f_blocks,
 223                                 &prstfs.f_bfree, &prstfs.f_bavail,
 224                                 &prstfs.f_files, &prstfs.f_ffree,
 225                                 &prstfs.f_fsid[0], &prstfs.f_fsid[1],
 226                                 &prstfs.f_namelen, &prstfs.f_frsize);
 227        prstatfs_to_statfs(response, &prstfs);
 228        break;
 229    }
 230    case T_READLINK: {
 231        V9fsString target;
 232        v9fs_string_init(&target);
 233        retval = proxy_unmarshal(reply, PROXY_HDR_SZ, "s", &target);
 234        strcpy(response, target.data);
 235        v9fs_string_free(&target);
 236        break;
 237    }
 238    case T_LGETXATTR:
 239    case T_LLISTXATTR: {
 240        V9fsString xattr;
 241        v9fs_string_init(&xattr);
 242        retval = proxy_unmarshal(reply, PROXY_HDR_SZ, "s", &xattr);
 243        memcpy(response, xattr.data, xattr.size);
 244        v9fs_string_free(&xattr);
 245        break;
 246    }
 247    case T_GETVERSION:
 248        proxy_unmarshal(reply, PROXY_HDR_SZ, "q", response);
 249        break;
 250    default:
 251        return -1;
 252    }
 253    if (retval < 0) {
 254        *status  = retval;
 255    }
 256    return 0;
 257}
 258
 259/*
 260 * return < 0 on transport error.
 261 * *status is valid only if return >= 0
 262 */
 263static int v9fs_receive_status(V9fsProxy *proxy,
 264                               struct iovec *reply, int *status)
 265{
 266    int retval;
 267    ProxyHeader header;
 268
 269    *status = 0;
 270    reply->iov_len = 0;
 271    retval = socket_read(proxy->sockfd, reply->iov_base, PROXY_HDR_SZ);
 272    if (retval < 0) {
 273        return retval;
 274    }
 275    reply->iov_len = PROXY_HDR_SZ;
 276    proxy_unmarshal(reply, 0, "dd", &header.type, &header.size);
 277    if (header.size != sizeof(int)) {
 278        *status = -ENOBUFS;
 279        return 0;
 280    }
 281    retval = socket_read(proxy->sockfd,
 282                         reply->iov_base + PROXY_HDR_SZ, header.size);
 283    if (retval < 0) {
 284        return retval;
 285    }
 286    reply->iov_len += header.size;
 287    proxy_unmarshal(reply, PROXY_HDR_SZ, "d", status);
 288    return 0;
 289}
 290
 291/*
 292 * Proxy->header and proxy->request written to socket by QEMU process.
 293 * This request read by proxy helper process
 294 * returns 0 on success and -errno on error
 295 */
 296static int v9fs_request(V9fsProxy *proxy, int type,
 297                        void *response, const char *fmt, ...)
 298{
 299    dev_t rdev;
 300    va_list ap;
 301    int size = 0;
 302    int retval = 0;
 303    uint64_t offset;
 304    ProxyHeader header = { 0, 0};
 305    struct timespec spec[2];
 306    int flags, mode, uid, gid;
 307    V9fsString *name, *value;
 308    V9fsString *path, *oldpath;
 309    struct iovec *iovec = NULL, *reply = NULL;
 310
 311    qemu_mutex_lock(&proxy->mutex);
 312
 313    if (proxy->sockfd == -1) {
 314        retval = -EIO;
 315        goto err_out;
 316    }
 317    iovec = &proxy->out_iovec;
 318    reply = &proxy->in_iovec;
 319    va_start(ap, fmt);
 320    switch (type) {
 321    case T_OPEN:
 322        path = va_arg(ap, V9fsString *);
 323        flags = va_arg(ap, int);
 324        retval = proxy_marshal(iovec, PROXY_HDR_SZ, "sd", path, flags);
 325        if (retval > 0) {
 326            header.size = retval;
 327            header.type = T_OPEN;
 328        }
 329        break;
 330    case T_CREATE:
 331        path = va_arg(ap, V9fsString *);
 332        flags = va_arg(ap, int);
 333        mode = va_arg(ap, int);
 334        uid = va_arg(ap, int);
 335        gid = va_arg(ap, int);
 336        retval = proxy_marshal(iovec, PROXY_HDR_SZ, "sdddd", path,
 337                                    flags, mode, uid, gid);
 338        if (retval > 0) {
 339            header.size = retval;
 340            header.type = T_CREATE;
 341        }
 342        break;
 343    case T_MKNOD:
 344        path = va_arg(ap, V9fsString *);
 345        mode = va_arg(ap, int);
 346        rdev = va_arg(ap, long int);
 347        uid = va_arg(ap, int);
 348        gid = va_arg(ap, int);
 349        retval = proxy_marshal(iovec, PROXY_HDR_SZ, "ddsdq",
 350                                    uid, gid, path, mode, rdev);
 351        if (retval > 0) {
 352            header.size = retval;
 353            header.type = T_MKNOD;
 354        }
 355        break;
 356    case T_MKDIR:
 357        path = va_arg(ap, V9fsString *);
 358        mode = va_arg(ap, int);
 359        uid = va_arg(ap, int);
 360        gid = va_arg(ap, int);
 361        retval = proxy_marshal(iovec, PROXY_HDR_SZ, "ddsd",
 362                                    uid, gid, path, mode);
 363        if (retval > 0) {
 364            header.size = retval;
 365            header.type = T_MKDIR;
 366        }
 367        break;
 368    case T_SYMLINK:
 369        oldpath = va_arg(ap, V9fsString *);
 370        path = va_arg(ap, V9fsString *);
 371        uid = va_arg(ap, int);
 372        gid = va_arg(ap, int);
 373        retval = proxy_marshal(iovec, PROXY_HDR_SZ, "ddss",
 374                                    uid, gid, oldpath, path);
 375        if (retval > 0) {
 376            header.size = retval;
 377            header.type = T_SYMLINK;
 378        }
 379        break;
 380    case T_LINK:
 381        oldpath = va_arg(ap, V9fsString *);
 382        path = va_arg(ap, V9fsString *);
 383        retval = proxy_marshal(iovec, PROXY_HDR_SZ, "ss",
 384                                    oldpath, path);
 385        if (retval > 0) {
 386            header.size = retval;
 387            header.type = T_LINK;
 388        }
 389        break;
 390    case T_LSTAT:
 391        path = va_arg(ap, V9fsString *);
 392        retval = proxy_marshal(iovec, PROXY_HDR_SZ, "s", path);
 393        if (retval > 0) {
 394            header.size = retval;
 395            header.type = T_LSTAT;
 396        }
 397        break;
 398    case T_READLINK:
 399        path = va_arg(ap, V9fsString *);
 400        size = va_arg(ap, int);
 401        retval = proxy_marshal(iovec, PROXY_HDR_SZ, "sd", path, size);
 402        if (retval > 0) {
 403            header.size = retval;
 404            header.type = T_READLINK;
 405        }
 406        break;
 407    case T_STATFS:
 408        path = va_arg(ap, V9fsString *);
 409        retval = proxy_marshal(iovec, PROXY_HDR_SZ, "s", path);
 410        if (retval > 0) {
 411            header.size = retval;
 412            header.type = T_STATFS;
 413        }
 414        break;
 415    case T_CHMOD:
 416        path = va_arg(ap, V9fsString *);
 417        mode = va_arg(ap, int);
 418        retval = proxy_marshal(iovec, PROXY_HDR_SZ, "sd", path, mode);
 419        if (retval > 0) {
 420            header.size = retval;
 421            header.type = T_CHMOD;
 422        }
 423        break;
 424    case T_CHOWN:
 425        path = va_arg(ap, V9fsString *);
 426        uid = va_arg(ap, int);
 427        gid = va_arg(ap, int);
 428        retval = proxy_marshal(iovec, PROXY_HDR_SZ, "sdd", path, uid, gid);
 429        if (retval > 0) {
 430            header.size = retval;
 431            header.type = T_CHOWN;
 432        }
 433        break;
 434    case T_TRUNCATE:
 435        path = va_arg(ap, V9fsString *);
 436        offset = va_arg(ap, uint64_t);
 437        retval = proxy_marshal(iovec, PROXY_HDR_SZ, "sq", path, offset);
 438        if (retval > 0) {
 439            header.size = retval;
 440            header.type = T_TRUNCATE;
 441        }
 442        break;
 443    case T_UTIME:
 444        path = va_arg(ap, V9fsString *);
 445        spec[0].tv_sec = va_arg(ap, long);
 446        spec[0].tv_nsec = va_arg(ap, long);
 447        spec[1].tv_sec = va_arg(ap, long);
 448        spec[1].tv_nsec = va_arg(ap, long);
 449        retval = proxy_marshal(iovec, PROXY_HDR_SZ, "sqqqq", path,
 450                                    spec[0].tv_sec, spec[1].tv_nsec,
 451                                    spec[1].tv_sec, spec[1].tv_nsec);
 452        if (retval > 0) {
 453            header.size = retval;
 454            header.type = T_UTIME;
 455        }
 456        break;
 457    case T_RENAME:
 458        oldpath = va_arg(ap, V9fsString *);
 459        path = va_arg(ap, V9fsString *);
 460        retval = proxy_marshal(iovec, PROXY_HDR_SZ, "ss", oldpath, path);
 461        if (retval > 0) {
 462            header.size = retval;
 463            header.type = T_RENAME;
 464        }
 465        break;
 466    case T_REMOVE:
 467        path = va_arg(ap, V9fsString *);
 468        retval = proxy_marshal(iovec, PROXY_HDR_SZ, "s", path);
 469        if (retval > 0) {
 470            header.size = retval;
 471            header.type = T_REMOVE;
 472        }
 473        break;
 474    case T_LGETXATTR:
 475        size = va_arg(ap, int);
 476        path = va_arg(ap, V9fsString *);
 477        name = va_arg(ap, V9fsString *);
 478        retval = proxy_marshal(iovec, PROXY_HDR_SZ,
 479                                    "dss", size, path, name);
 480        if (retval > 0) {
 481            header.size = retval;
 482            header.type = T_LGETXATTR;
 483        }
 484        break;
 485    case T_LLISTXATTR:
 486        size = va_arg(ap, int);
 487        path = va_arg(ap, V9fsString *);
 488        retval = proxy_marshal(iovec, PROXY_HDR_SZ, "ds", size, path);
 489        if (retval > 0) {
 490            header.size = retval;
 491            header.type = T_LLISTXATTR;
 492        }
 493        break;
 494    case T_LSETXATTR:
 495        path = va_arg(ap, V9fsString *);
 496        name = va_arg(ap, V9fsString *);
 497        value = va_arg(ap, V9fsString *);
 498        size = va_arg(ap, int);
 499        flags = va_arg(ap, int);
 500        retval = proxy_marshal(iovec, PROXY_HDR_SZ, "sssdd",
 501                                    path, name, value, size, flags);
 502        if (retval > 0) {
 503            header.size = retval;
 504            header.type = T_LSETXATTR;
 505        }
 506        break;
 507    case T_LREMOVEXATTR:
 508        path = va_arg(ap, V9fsString *);
 509        name = va_arg(ap, V9fsString *);
 510        retval = proxy_marshal(iovec, PROXY_HDR_SZ, "ss", path, name);
 511        if (retval > 0) {
 512            header.size = retval;
 513            header.type = T_LREMOVEXATTR;
 514        }
 515        break;
 516    case T_GETVERSION:
 517        path = va_arg(ap, V9fsString *);
 518        retval = proxy_marshal(iovec, PROXY_HDR_SZ, "s", path);
 519        if (retval > 0) {
 520            header.size = retval;
 521            header.type = T_GETVERSION;
 522        }
 523        break;
 524    default:
 525        error_report("Invalid type %d", type);
 526        retval = -EINVAL;
 527        break;
 528    }
 529    va_end(ap);
 530
 531    if (retval < 0) {
 532        goto err_out;
 533    }
 534
 535    /* marshal the header details */
 536    proxy_marshal(iovec, 0, "dd", header.type, header.size);
 537    header.size += PROXY_HDR_SZ;
 538
 539    retval = qemu_write_full(proxy->sockfd, iovec->iov_base, header.size);
 540    if (retval != header.size) {
 541        goto close_error;
 542    }
 543
 544    switch (type) {
 545    case T_OPEN:
 546    case T_CREATE:
 547        /*
 548         * A file descriptor is returned as response for
 549         * T_OPEN,T_CREATE on success
 550         */
 551        if (v9fs_receivefd(proxy->sockfd, &retval) < 0) {
 552            goto close_error;
 553        }
 554        break;
 555    case T_MKNOD:
 556    case T_MKDIR:
 557    case T_SYMLINK:
 558    case T_LINK:
 559    case T_CHMOD:
 560    case T_CHOWN:
 561    case T_RENAME:
 562    case T_TRUNCATE:
 563    case T_UTIME:
 564    case T_REMOVE:
 565    case T_LSETXATTR:
 566    case T_LREMOVEXATTR:
 567        if (v9fs_receive_status(proxy, reply, &retval) < 0) {
 568            goto close_error;
 569        }
 570        break;
 571    case T_LSTAT:
 572    case T_READLINK:
 573    case T_STATFS:
 574    case T_GETVERSION:
 575        if (v9fs_receive_response(proxy, type, &retval, response) < 0) {
 576            goto close_error;
 577        }
 578        break;
 579    case T_LGETXATTR:
 580    case T_LLISTXATTR:
 581        if (!size) {
 582            if (v9fs_receive_status(proxy, reply, &retval) < 0) {
 583                goto close_error;
 584            }
 585        } else {
 586            if (v9fs_receive_response(proxy, type, &retval, response) < 0) {
 587                goto close_error;
 588            }
 589        }
 590        break;
 591    }
 592
 593err_out:
 594    qemu_mutex_unlock(&proxy->mutex);
 595    return retval;
 596
 597close_error:
 598    close(proxy->sockfd);
 599    proxy->sockfd = -1;
 600    qemu_mutex_unlock(&proxy->mutex);
 601    return -EIO;
 602}
 603
 604static int proxy_lstat(FsContext *fs_ctx, V9fsPath *fs_path, struct stat *stbuf)
 605{
 606    int retval;
 607    retval = v9fs_request(fs_ctx->private, T_LSTAT, stbuf, "s", fs_path);
 608    if (retval < 0) {
 609        errno = -retval;
 610        return -1;
 611    }
 612    return retval;
 613}
 614
 615static ssize_t proxy_readlink(FsContext *fs_ctx, V9fsPath *fs_path,
 616                              char *buf, size_t bufsz)
 617{
 618    int retval;
 619    retval = v9fs_request(fs_ctx->private, T_READLINK, buf, "sd",
 620                          fs_path, bufsz);
 621    if (retval < 0) {
 622        errno = -retval;
 623        return -1;
 624    }
 625    return strlen(buf);
 626}
 627
 628static int proxy_close(FsContext *ctx, V9fsFidOpenState *fs)
 629{
 630    return close(fs->fd);
 631}
 632
 633static int proxy_closedir(FsContext *ctx, V9fsFidOpenState *fs)
 634{
 635    return closedir(fs->dir);
 636}
 637
 638static int proxy_open(FsContext *ctx, V9fsPath *fs_path,
 639                      int flags, V9fsFidOpenState *fs)
 640{
 641    fs->fd = v9fs_request(ctx->private, T_OPEN, NULL, "sd", fs_path, flags);
 642    if (fs->fd < 0) {
 643        errno = -fs->fd;
 644        fs->fd = -1;
 645    }
 646    return fs->fd;
 647}
 648
 649static int proxy_opendir(FsContext *ctx,
 650                         V9fsPath *fs_path, V9fsFidOpenState *fs)
 651{
 652    int serrno, fd;
 653
 654    fs->dir = NULL;
 655    fd = v9fs_request(ctx->private, T_OPEN, NULL, "sd", fs_path, O_DIRECTORY);
 656    if (fd < 0) {
 657        errno = -fd;
 658        return -1;
 659    }
 660    fs->dir = fdopendir(fd);
 661    if (!fs->dir) {
 662        serrno = errno;
 663        close(fd);
 664        errno = serrno;
 665        return -1;
 666    }
 667    return 0;
 668}
 669
 670static void proxy_rewinddir(FsContext *ctx, V9fsFidOpenState *fs)
 671{
 672    return rewinddir(fs->dir);
 673}
 674
 675static off_t proxy_telldir(FsContext *ctx, V9fsFidOpenState *fs)
 676{
 677    return telldir(fs->dir);
 678}
 679
 680static int proxy_readdir_r(FsContext *ctx, V9fsFidOpenState *fs,
 681                           struct dirent *entry,
 682                           struct dirent **result)
 683{
 684    return readdir_r(fs->dir, entry, result);
 685}
 686
 687static void proxy_seekdir(FsContext *ctx, V9fsFidOpenState *fs, off_t off)
 688{
 689    return seekdir(fs->dir, off);
 690}
 691
 692static ssize_t proxy_preadv(FsContext *ctx, V9fsFidOpenState *fs,
 693                            const struct iovec *iov,
 694                            int iovcnt, off_t offset)
 695{
 696#ifdef CONFIG_PREADV
 697    return preadv(fs->fd, iov, iovcnt, offset);
 698#else
 699    int err = lseek(fs->fd, offset, SEEK_SET);
 700    if (err == -1) {
 701        return err;
 702    } else {
 703        return readv(fs->fd, iov, iovcnt);
 704    }
 705#endif
 706}
 707
 708static ssize_t proxy_pwritev(FsContext *ctx, V9fsFidOpenState *fs,
 709                             const struct iovec *iov,
 710                             int iovcnt, off_t offset)
 711{
 712    ssize_t ret;
 713
 714#ifdef CONFIG_PREADV
 715    ret = pwritev(fs->fd, iov, iovcnt, offset);
 716#else
 717    int err = lseek(fs->fd, offset, SEEK_SET);
 718    if (err == -1) {
 719        return err;
 720    } else {
 721        ret = writev(fs->fd, iov, iovcnt);
 722    }
 723#endif
 724#ifdef CONFIG_SYNC_FILE_RANGE
 725    if (ret > 0 && ctx->export_flags & V9FS_IMMEDIATE_WRITEOUT) {
 726        /*
 727         * Initiate a writeback. This is not a data integrity sync.
 728         * We want to ensure that we don't leave dirty pages in the cache
 729         * after write when writeout=immediate is sepcified.
 730         */
 731        sync_file_range(fs->fd, offset, ret,
 732                        SYNC_FILE_RANGE_WAIT_BEFORE | SYNC_FILE_RANGE_WRITE);
 733    }
 734#endif
 735    return ret;
 736}
 737
 738static int proxy_chmod(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp)
 739{
 740    int retval;
 741    retval = v9fs_request(fs_ctx->private, T_CHMOD, NULL, "sd",
 742                          fs_path, credp->fc_mode);
 743    if (retval < 0) {
 744        errno = -retval;
 745    }
 746    return retval;
 747}
 748
 749static int proxy_mknod(FsContext *fs_ctx, V9fsPath *dir_path,
 750                       const char *name, FsCred *credp)
 751{
 752    int retval;
 753    V9fsString fullname;
 754
 755    v9fs_string_init(&fullname);
 756    v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name);
 757
 758    retval = v9fs_request(fs_ctx->private, T_MKNOD, NULL, "sdqdd",
 759                          &fullname, credp->fc_mode, credp->fc_rdev,
 760                          credp->fc_uid, credp->fc_gid);
 761    v9fs_string_free(&fullname);
 762    if (retval < 0) {
 763        errno = -retval;
 764        retval = -1;
 765    }
 766    return retval;
 767}
 768
 769static int proxy_mkdir(FsContext *fs_ctx, V9fsPath *dir_path,
 770                       const char *name, FsCred *credp)
 771{
 772    int retval;
 773    V9fsString fullname;
 774
 775    v9fs_string_init(&fullname);
 776    v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name);
 777
 778    retval = v9fs_request(fs_ctx->private, T_MKDIR, NULL, "sddd", &fullname,
 779                          credp->fc_mode, credp->fc_uid, credp->fc_gid);
 780    v9fs_string_free(&fullname);
 781    if (retval < 0) {
 782        errno = -retval;
 783        retval = -1;
 784    }
 785    v9fs_string_free(&fullname);
 786    return retval;
 787}
 788
 789static int proxy_fstat(FsContext *fs_ctx, int fid_type,
 790                       V9fsFidOpenState *fs, struct stat *stbuf)
 791{
 792    int fd;
 793
 794    if (fid_type == P9_FID_DIR) {
 795        fd = dirfd(fs->dir);
 796    } else {
 797        fd = fs->fd;
 798    }
 799    return fstat(fd, stbuf);
 800}
 801
 802static int proxy_open2(FsContext *fs_ctx, V9fsPath *dir_path, const char *name,
 803                       int flags, FsCred *credp, V9fsFidOpenState *fs)
 804{
 805    V9fsString fullname;
 806
 807    v9fs_string_init(&fullname);
 808    v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name);
 809
 810    fs->fd = v9fs_request(fs_ctx->private, T_CREATE, NULL, "sdddd",
 811                          &fullname, flags, credp->fc_mode,
 812                          credp->fc_uid, credp->fc_gid);
 813    v9fs_string_free(&fullname);
 814    if (fs->fd < 0) {
 815        errno = -fs->fd;
 816        fs->fd = -1;
 817    }
 818    return fs->fd;
 819}
 820
 821static int proxy_symlink(FsContext *fs_ctx, const char *oldpath,
 822                         V9fsPath *dir_path, const char *name, FsCred *credp)
 823{
 824    int retval;
 825    V9fsString fullname, target;
 826
 827    v9fs_string_init(&fullname);
 828    v9fs_string_init(&target);
 829
 830    v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name);
 831    v9fs_string_sprintf(&target, "%s", oldpath);
 832
 833    retval = v9fs_request(fs_ctx->private, T_SYMLINK, NULL, "ssdd",
 834                          &target, &fullname, credp->fc_uid, credp->fc_gid);
 835    v9fs_string_free(&fullname);
 836    v9fs_string_free(&target);
 837    if (retval < 0) {
 838        errno = -retval;
 839        retval = -1;
 840    }
 841    return retval;
 842}
 843
 844static int proxy_link(FsContext *ctx, V9fsPath *oldpath,
 845                      V9fsPath *dirpath, const char *name)
 846{
 847    int retval;
 848    V9fsString newpath;
 849
 850    v9fs_string_init(&newpath);
 851    v9fs_string_sprintf(&newpath, "%s/%s", dirpath->data, name);
 852
 853    retval = v9fs_request(ctx->private, T_LINK, NULL, "ss", oldpath, &newpath);
 854    v9fs_string_free(&newpath);
 855    if (retval < 0) {
 856        errno = -retval;
 857        retval = -1;
 858    }
 859    return retval;
 860}
 861
 862static int proxy_truncate(FsContext *ctx, V9fsPath *fs_path, off_t size)
 863{
 864    int retval;
 865
 866    retval = v9fs_request(ctx->private, T_TRUNCATE, NULL, "sq", fs_path, size);
 867    if (retval < 0) {
 868        errno = -retval;
 869        return -1;
 870    }
 871    return 0;
 872}
 873
 874static int proxy_rename(FsContext *ctx, const char *oldpath,
 875                        const char *newpath)
 876{
 877    int retval;
 878    V9fsString oldname, newname;
 879
 880    v9fs_string_init(&oldname);
 881    v9fs_string_init(&newname);
 882
 883    v9fs_string_sprintf(&oldname, "%s", oldpath);
 884    v9fs_string_sprintf(&newname, "%s", newpath);
 885    retval = v9fs_request(ctx->private, T_RENAME, NULL, "ss",
 886                          &oldname, &newname);
 887    v9fs_string_free(&oldname);
 888    v9fs_string_free(&newname);
 889    if (retval < 0) {
 890        errno = -retval;
 891    }
 892    return retval;
 893}
 894
 895static int proxy_chown(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp)
 896{
 897    int retval;
 898    retval = v9fs_request(fs_ctx->private, T_CHOWN, NULL, "sdd",
 899                          fs_path, credp->fc_uid, credp->fc_gid);
 900    if (retval < 0) {
 901        errno = -retval;
 902    }
 903    return retval;
 904}
 905
 906static int proxy_utimensat(FsContext *s, V9fsPath *fs_path,
 907                           const struct timespec *buf)
 908{
 909    int retval;
 910    retval = v9fs_request(s->private, T_UTIME, NULL, "sqqqq",
 911                          fs_path,
 912                          buf[0].tv_sec, buf[0].tv_nsec,
 913                          buf[1].tv_sec, buf[1].tv_nsec);
 914    if (retval < 0) {
 915        errno = -retval;
 916    }
 917    return retval;
 918}
 919
 920static int proxy_remove(FsContext *ctx, const char *path)
 921{
 922    int retval;
 923    V9fsString name;
 924    v9fs_string_init(&name);
 925    v9fs_string_sprintf(&name, "%s", path);
 926    retval = v9fs_request(ctx->private, T_REMOVE, NULL, "s", &name);
 927    v9fs_string_free(&name);
 928    if (retval < 0) {
 929        errno = -retval;
 930    }
 931    return retval;
 932}
 933
 934static int proxy_fsync(FsContext *ctx, int fid_type,
 935                       V9fsFidOpenState *fs, int datasync)
 936{
 937    int fd;
 938
 939    if (fid_type == P9_FID_DIR) {
 940        fd = dirfd(fs->dir);
 941    } else {
 942        fd = fs->fd;
 943    }
 944
 945    if (datasync) {
 946        return qemu_fdatasync(fd);
 947    } else {
 948        return fsync(fd);
 949    }
 950}
 951
 952static int proxy_statfs(FsContext *s, V9fsPath *fs_path, struct statfs *stbuf)
 953{
 954    int retval;
 955    retval = v9fs_request(s->private, T_STATFS, stbuf, "s", fs_path);
 956    if (retval < 0) {
 957        errno = -retval;
 958        return -1;
 959    }
 960    return retval;
 961}
 962
 963static ssize_t proxy_lgetxattr(FsContext *ctx, V9fsPath *fs_path,
 964                               const char *name, void *value, size_t size)
 965{
 966    int retval;
 967    V9fsString xname;
 968
 969    v9fs_string_init(&xname);
 970    v9fs_string_sprintf(&xname, "%s", name);
 971    retval = v9fs_request(ctx->private, T_LGETXATTR, value, "dss", size,
 972                          fs_path, &xname);
 973    v9fs_string_free(&xname);
 974    if (retval < 0) {
 975        errno = -retval;
 976    }
 977    return retval;
 978}
 979
 980static ssize_t proxy_llistxattr(FsContext *ctx, V9fsPath *fs_path,
 981                                void *value, size_t size)
 982{
 983    int retval;
 984    retval = v9fs_request(ctx->private, T_LLISTXATTR, value, "ds", size,
 985                        fs_path);
 986    if (retval < 0) {
 987        errno = -retval;
 988    }
 989    return retval;
 990}
 991
 992static int proxy_lsetxattr(FsContext *ctx, V9fsPath *fs_path, const char *name,
 993                           void *value, size_t size, int flags)
 994{
 995    int retval;
 996    V9fsString xname, xvalue;
 997
 998    v9fs_string_init(&xname);
 999    v9fs_string_sprintf(&xname, "%s", name);
1000
1001    v9fs_string_init(&xvalue);
1002    xvalue.size = size;
1003    xvalue.data = g_malloc(size);
1004    memcpy(xvalue.data, value, size);
1005
1006    retval = v9fs_request(ctx->private, T_LSETXATTR, value, "sssdd",
1007                          fs_path, &xname, &xvalue, size, flags);
1008    v9fs_string_free(&xname);
1009    v9fs_string_free(&xvalue);
1010    if (retval < 0) {
1011        errno = -retval;
1012    }
1013    return retval;
1014}
1015
1016static int proxy_lremovexattr(FsContext *ctx, V9fsPath *fs_path,
1017                              const char *name)
1018{
1019    int retval;
1020    V9fsString xname;
1021
1022    v9fs_string_init(&xname);
1023    v9fs_string_sprintf(&xname, "%s", name);
1024    retval = v9fs_request(ctx->private, T_LREMOVEXATTR, NULL, "ss",
1025                          fs_path, &xname);
1026    v9fs_string_free(&xname);
1027    if (retval < 0) {
1028        errno = -retval;
1029    }
1030    return retval;
1031}
1032
1033static int proxy_name_to_path(FsContext *ctx, V9fsPath *dir_path,
1034                              const char *name, V9fsPath *target)
1035{
1036    if (dir_path) {
1037        v9fs_string_sprintf((V9fsString *)target, "%s/%s",
1038                            dir_path->data, name);
1039    } else {
1040        v9fs_string_sprintf((V9fsString *)target, "%s", name);
1041    }
1042    /* Bump the size for including terminating NULL */
1043    target->size++;
1044    return 0;
1045}
1046
1047static int proxy_renameat(FsContext *ctx, V9fsPath *olddir,
1048                          const char *old_name, V9fsPath *newdir,
1049                          const char *new_name)
1050{
1051    int ret;
1052    V9fsString old_full_name, new_full_name;
1053
1054    v9fs_string_init(&old_full_name);
1055    v9fs_string_init(&new_full_name);
1056
1057    v9fs_string_sprintf(&old_full_name, "%s/%s", olddir->data, old_name);
1058    v9fs_string_sprintf(&new_full_name, "%s/%s", newdir->data, new_name);
1059
1060    ret = proxy_rename(ctx, old_full_name.data, new_full_name.data);
1061    v9fs_string_free(&old_full_name);
1062    v9fs_string_free(&new_full_name);
1063    return ret;
1064}
1065
1066static int proxy_unlinkat(FsContext *ctx, V9fsPath *dir,
1067                          const char *name, int flags)
1068{
1069    int ret;
1070    V9fsString fullname;
1071    v9fs_string_init(&fullname);
1072
1073    v9fs_string_sprintf(&fullname, "%s/%s", dir->data, name);
1074    ret = proxy_remove(ctx, fullname.data);
1075    v9fs_string_free(&fullname);
1076
1077    return ret;
1078}
1079
1080static int proxy_ioc_getversion(FsContext *fs_ctx, V9fsPath *path,
1081                                mode_t st_mode, uint64_t *st_gen)
1082{
1083    int err;
1084
1085    /* Do not try to open special files like device nodes, fifos etc
1086     * we can get fd for regular files and directories only
1087     */
1088    if (!S_ISREG(st_mode) && !S_ISDIR(st_mode)) {
1089        return 0;
1090    }
1091    err = v9fs_request(fs_ctx->private, T_GETVERSION, st_gen, "s", path);
1092    if (err < 0) {
1093        errno = -err;
1094        err = -1;
1095    }
1096    return err;
1097}
1098
1099static int connect_namedsocket(const char *path)
1100{
1101    int sockfd, size;
1102    struct sockaddr_un helper;
1103
1104    sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
1105    if (sockfd < 0) {
1106        fprintf(stderr, "socket %s\n", strerror(errno));
1107        return -1;
1108    }
1109    strcpy(helper.sun_path, path);
1110    helper.sun_family = AF_UNIX;
1111    size = strlen(helper.sun_path) + sizeof(helper.sun_family);
1112    if (connect(sockfd, (struct sockaddr *)&helper, size) < 0) {
1113        fprintf(stderr, "socket error\n");
1114        return -1;
1115    }
1116
1117    /* remove the socket for security reasons */
1118    unlink(path);
1119    return sockfd;
1120}
1121
1122static int proxy_parse_opts(QemuOpts *opts, struct FsDriverEntry *fs)
1123{
1124    const char *socket = qemu_opt_get(opts, "socket");
1125    const char *sock_fd = qemu_opt_get(opts, "sock_fd");
1126
1127    if (!socket && !sock_fd) {
1128        fprintf(stderr, "socket and sock_fd none of the option specified\n");
1129        return -1;
1130    }
1131    if (socket && sock_fd) {
1132        fprintf(stderr, "Both socket and sock_fd options specified\n");
1133        return -1;
1134    }
1135    if (socket) {
1136        fs->path = g_strdup(socket);
1137        fs->export_flags = V9FS_PROXY_SOCK_NAME;
1138    } else {
1139        fs->path = g_strdup(sock_fd);
1140        fs->export_flags = V9FS_PROXY_SOCK_FD;
1141    }
1142    return 0;
1143}
1144
1145static int proxy_init(FsContext *ctx)
1146{
1147    V9fsProxy *proxy = g_malloc(sizeof(V9fsProxy));
1148    int sock_id;
1149
1150    if (ctx->export_flags & V9FS_PROXY_SOCK_NAME) {
1151        sock_id = connect_namedsocket(ctx->fs_root);
1152    } else {
1153        sock_id = atoi(ctx->fs_root);
1154        if (sock_id < 0) {
1155            fprintf(stderr, "socket descriptor not initialized\n");
1156            g_free(proxy);
1157            return -1;
1158        }
1159    }
1160    g_free(ctx->fs_root);
1161    ctx->fs_root = NULL;
1162
1163    proxy->in_iovec.iov_base  = g_malloc(PROXY_MAX_IO_SZ + PROXY_HDR_SZ);
1164    proxy->in_iovec.iov_len   = PROXY_MAX_IO_SZ + PROXY_HDR_SZ;
1165    proxy->out_iovec.iov_base = g_malloc(PROXY_MAX_IO_SZ + PROXY_HDR_SZ);
1166    proxy->out_iovec.iov_len  = PROXY_MAX_IO_SZ + PROXY_HDR_SZ;
1167
1168    ctx->private = proxy;
1169    proxy->sockfd = sock_id;
1170    qemu_mutex_init(&proxy->mutex);
1171
1172    ctx->export_flags |= V9FS_PATHNAME_FSCONTEXT;
1173    ctx->exops.get_st_gen = proxy_ioc_getversion;
1174    return 0;
1175}
1176
1177FileOperations proxy_ops = {
1178    .parse_opts   = proxy_parse_opts,
1179    .init         = proxy_init,
1180    .lstat        = proxy_lstat,
1181    .readlink     = proxy_readlink,
1182    .close        = proxy_close,
1183    .closedir     = proxy_closedir,
1184    .open         = proxy_open,
1185    .opendir      = proxy_opendir,
1186    .rewinddir    = proxy_rewinddir,
1187    .telldir      = proxy_telldir,
1188    .readdir_r    = proxy_readdir_r,
1189    .seekdir      = proxy_seekdir,
1190    .preadv       = proxy_preadv,
1191    .pwritev      = proxy_pwritev,
1192    .chmod        = proxy_chmod,
1193    .mknod        = proxy_mknod,
1194    .mkdir        = proxy_mkdir,
1195    .fstat        = proxy_fstat,
1196    .open2        = proxy_open2,
1197    .symlink      = proxy_symlink,
1198    .link         = proxy_link,
1199    .truncate     = proxy_truncate,
1200    .rename       = proxy_rename,
1201    .chown        = proxy_chown,
1202    .utimensat    = proxy_utimensat,
1203    .remove       = proxy_remove,
1204    .fsync        = proxy_fsync,
1205    .statfs       = proxy_statfs,
1206    .lgetxattr    = proxy_lgetxattr,
1207    .llistxattr   = proxy_llistxattr,
1208    .lsetxattr    = proxy_lsetxattr,
1209    .lremovexattr = proxy_lremovexattr,
1210    .name_to_path = proxy_name_to_path,
1211    .renameat     = proxy_renameat,
1212    .unlinkat     = proxy_unlinkat,
1213};
1214