qemu/hw/9pfs/9p-proxy.c
<<
>>
Prefs
   1/*
   2 * 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 "qemu/osdep.h"
  13#include <sys/socket.h>
  14#include <sys/un.h>
  15#include "9p.h"
  16#include "qemu/cutils.h"
  17#include "qemu/error-report.h"
  18#include "fsdev/qemu-fsdev.h"
  19#include "9p-proxy.h"
  20
  21typedef struct V9fsProxy {
  22    int sockfd;
  23    QemuMutex mutex;
  24    struct iovec in_iovec;
  25    struct iovec out_iovec;
  26} V9fsProxy;
  27
  28/*
  29 * Return received file descriptor on success in *status.
  30 * errno is also returned on *status (which will be < 0)
  31 * return < 0 on transport error.
  32 */
  33static int v9fs_receivefd(int sockfd, int *status)
  34{
  35    struct iovec iov;
  36    struct msghdr msg;
  37    struct cmsghdr *cmsg;
  38    int retval, data, fd;
  39    union MsgControl msg_control;
  40
  41    iov.iov_base = &data;
  42    iov.iov_len = sizeof(data);
  43
  44    memset(&msg, 0, sizeof(msg));
  45    msg.msg_iov = &iov;
  46    msg.msg_iovlen = 1;
  47    msg.msg_control = &msg_control;
  48    msg.msg_controllen = sizeof(msg_control);
  49
  50    do {
  51        retval = recvmsg(sockfd, &msg, 0);
  52    } while (retval < 0 && errno == EINTR);
  53    if (retval <= 0) {
  54        return retval;
  55    }
  56    /*
  57     * data is set to V9FS_FD_VALID, if ancillary data is sent.  If this
  58     * request doesn't need ancillary data (fd) or an error occurred,
  59     * data is set to negative errno value.
  60     */
  61    if (data != V9FS_FD_VALID) {
  62        *status = data;
  63        return 0;
  64    }
  65    /*
  66     * File descriptor (fd) is sent in the ancillary data. Check if we
  67     * indeed received it. One of the reasons to fail to receive it is if
  68     * we exceeded the maximum number of file descriptors!
  69     */
  70    for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
  71        if (cmsg->cmsg_len != CMSG_LEN(sizeof(int)) ||
  72            cmsg->cmsg_level != SOL_SOCKET ||
  73            cmsg->cmsg_type != SCM_RIGHTS) {
  74            continue;
  75        }
  76        fd = *((int *)CMSG_DATA(cmsg));
  77        *status = fd;
  78        return 0;
  79    }
  80    *status = -ENFILE;  /* Ancillary data sent but not received */
  81    return 0;
  82}
  83
  84static ssize_t socket_read(int sockfd, void *buff, size_t size)
  85{
  86    ssize_t retval, total = 0;
  87
  88    while (size) {
  89        retval = read(sockfd, buff, size);
  90        if (retval == 0) {
  91            return -EIO;
  92        }
  93        if (retval < 0) {
  94            if (errno == EINTR) {
  95                continue;
  96            }
  97            return -errno;
  98        }
  99        size -= retval;
 100        buff += retval;
 101        total += retval;
 102    }
 103    return total;
 104}
 105
 106/* Converts proxy_statfs to VFS statfs structure */
 107static void prstatfs_to_statfs(struct statfs *stfs, ProxyStatFS *prstfs)
 108{
 109    memset(stfs, 0, sizeof(*stfs));
 110    stfs->f_type = prstfs->f_type;
 111    stfs->f_bsize = prstfs->f_bsize;
 112    stfs->f_blocks = prstfs->f_blocks;
 113    stfs->f_bfree = prstfs->f_bfree;
 114    stfs->f_bavail = prstfs->f_bavail;
 115    stfs->f_files = prstfs->f_files;
 116    stfs->f_ffree = prstfs->f_ffree;
 117    stfs->f_fsid.__val[0] = prstfs->f_fsid[0] & 0xFFFFFFFFU;
 118    stfs->f_fsid.__val[1] = prstfs->f_fsid[1] >> 32 & 0xFFFFFFFFU;
 119    stfs->f_namelen = prstfs->f_namelen;
 120    stfs->f_frsize = prstfs->f_frsize;
 121}
 122
 123/* Converts proxy_stat structure to VFS stat structure */
 124static void prstat_to_stat(struct stat *stbuf, ProxyStat *prstat)
 125{
 126   memset(stbuf, 0, sizeof(*stbuf));
 127   stbuf->st_dev = prstat->st_dev;
 128   stbuf->st_ino = prstat->st_ino;
 129   stbuf->st_nlink = prstat->st_nlink;
 130   stbuf->st_mode = prstat->st_mode;
 131   stbuf->st_uid = prstat->st_uid;
 132   stbuf->st_gid = prstat->st_gid;
 133   stbuf->st_rdev = prstat->st_rdev;
 134   stbuf->st_size = prstat->st_size;
 135   stbuf->st_blksize = prstat->st_blksize;
 136   stbuf->st_blocks = prstat->st_blocks;
 137   stbuf->st_atim.tv_sec = prstat->st_atim_sec;
 138   stbuf->st_atim.tv_nsec = prstat->st_atim_nsec;
 139   stbuf->st_mtime = prstat->st_mtim_sec;
 140   stbuf->st_mtim.tv_nsec = prstat->st_mtim_nsec;
 141   stbuf->st_ctime = prstat->st_ctim_sec;
 142   stbuf->st_ctim.tv_nsec = prstat->st_ctim_nsec;
 143}
 144
 145/*
 146 * Response contains two parts
 147 * {header, data}
 148 * header.type == T_ERROR, data -> -errno
 149 * header.type == T_SUCCESS, data -> response
 150 * size of errno/response is given by header.size
 151 * returns < 0, on transport error. response is
 152 * valid only if status >= 0.
 153 */
 154static int v9fs_receive_response(V9fsProxy *proxy, int type,
 155                                 int *status, void *response)
 156{
 157    int retval;
 158    ProxyHeader header;
 159    struct iovec *reply = &proxy->in_iovec;
 160
 161    *status = 0;
 162    reply->iov_len = 0;
 163    retval = socket_read(proxy->sockfd, reply->iov_base, PROXY_HDR_SZ);
 164    if (retval < 0) {
 165        return retval;
 166    }
 167    reply->iov_len = PROXY_HDR_SZ;
 168    retval = proxy_unmarshal(reply, 0, "dd", &header.type, &header.size);
 169    assert(retval == 4 * 2);
 170    /*
 171     * if response size > PROXY_MAX_IO_SZ, read the response but ignore it and
 172     * return -ENOBUFS
 173     */
 174    if (header.size > PROXY_MAX_IO_SZ) {
 175        int count;
 176        while (header.size > 0) {
 177            count = MIN(PROXY_MAX_IO_SZ, header.size);
 178            count = socket_read(proxy->sockfd, reply->iov_base, count);
 179            if (count < 0) {
 180                return count;
 181            }
 182            header.size -= count;
 183        }
 184        *status = -ENOBUFS;
 185        return 0;
 186    }
 187
 188    retval = socket_read(proxy->sockfd,
 189                         reply->iov_base + PROXY_HDR_SZ, header.size);
 190    if (retval < 0) {
 191        return retval;
 192    }
 193    reply->iov_len += header.size;
 194    /* there was an error during processing request */
 195    if (header.type == T_ERROR) {
 196        int ret;
 197        ret = proxy_unmarshal(reply, PROXY_HDR_SZ, "d", status);
 198        assert(ret == 4);
 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        assert(retval == 8 * 3 + 4 * 3 + 8 * 10);
 216        prstat_to_stat(response, &prstat);
 217        break;
 218    }
 219    case T_STATFS: {
 220        ProxyStatFS prstfs;
 221        retval = proxy_unmarshal(reply, PROXY_HDR_SZ,
 222                                 "qqqqqqqqqqq", &prstfs.f_type,
 223                                 &prstfs.f_bsize, &prstfs.f_blocks,
 224                                 &prstfs.f_bfree, &prstfs.f_bavail,
 225                                 &prstfs.f_files, &prstfs.f_ffree,
 226                                 &prstfs.f_fsid[0], &prstfs.f_fsid[1],
 227                                 &prstfs.f_namelen, &prstfs.f_frsize);
 228        assert(retval == 8 * 11);
 229        prstatfs_to_statfs(response, &prstfs);
 230        break;
 231    }
 232    case T_READLINK: {
 233        V9fsString target;
 234        v9fs_string_init(&target);
 235        retval = proxy_unmarshal(reply, PROXY_HDR_SZ, "s", &target);
 236        strcpy(response, target.data);
 237        v9fs_string_free(&target);
 238        break;
 239    }
 240    case T_LGETXATTR:
 241    case T_LLISTXATTR: {
 242        V9fsString xattr;
 243        v9fs_string_init(&xattr);
 244        retval = proxy_unmarshal(reply, PROXY_HDR_SZ, "s", &xattr);
 245        memcpy(response, xattr.data, xattr.size);
 246        v9fs_string_free(&xattr);
 247        break;
 248    }
 249    case T_GETVERSION:
 250        retval = proxy_unmarshal(reply, PROXY_HDR_SZ, "q", response);
 251        assert(retval == 8);
 252        break;
 253    default:
 254        return -1;
 255    }
 256    if (retval < 0) {
 257        *status  = retval;
 258    }
 259    return 0;
 260}
 261
 262/*
 263 * return < 0 on transport error.
 264 * *status is valid only if return >= 0
 265 */
 266static int v9fs_receive_status(V9fsProxy *proxy,
 267                               struct iovec *reply, int *status)
 268{
 269    int retval;
 270    ProxyHeader header;
 271
 272    *status = 0;
 273    reply->iov_len = 0;
 274    retval = socket_read(proxy->sockfd, reply->iov_base, PROXY_HDR_SZ);
 275    if (retval < 0) {
 276        return retval;
 277    }
 278    reply->iov_len = PROXY_HDR_SZ;
 279    retval = proxy_unmarshal(reply, 0, "dd", &header.type, &header.size);
 280    assert(retval == 4 * 2);
 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    retval = proxy_unmarshal(reply, PROXY_HDR_SZ, "d", status);
 288    assert(retval == 4);
 289    return 0;
 290}
 291
 292/*
 293 * Proxy->header and proxy->request written to socket by QEMU process.
 294 * This request read by proxy helper process
 295 * returns 0 on success and -errno on error
 296 */
 297static int v9fs_request(V9fsProxy *proxy, int type, void *response, ...)
 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, response);
 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, 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, fs_path, bufsz);
 620    if (retval < 0) {
 621        errno = -retval;
 622        return -1;
 623    }
 624    return strlen(buf);
 625}
 626
 627static int proxy_close(FsContext *ctx, V9fsFidOpenState *fs)
 628{
 629    return close(fs->fd);
 630}
 631
 632static int proxy_closedir(FsContext *ctx, V9fsFidOpenState *fs)
 633{
 634    return closedir(fs->dir.stream);
 635}
 636
 637static int proxy_open(FsContext *ctx, V9fsPath *fs_path,
 638                      int flags, V9fsFidOpenState *fs)
 639{
 640    fs->fd = v9fs_request(ctx->private, T_OPEN, NULL, fs_path, flags);
 641    if (fs->fd < 0) {
 642        errno = -fs->fd;
 643        fs->fd = -1;
 644    }
 645    return fs->fd;
 646}
 647
 648static int proxy_opendir(FsContext *ctx,
 649                         V9fsPath *fs_path, V9fsFidOpenState *fs)
 650{
 651    int serrno, fd;
 652
 653    fs->dir.stream = NULL;
 654    fd = v9fs_request(ctx->private, T_OPEN, NULL, fs_path, O_DIRECTORY);
 655    if (fd < 0) {
 656        errno = -fd;
 657        return -1;
 658    }
 659    fs->dir.stream = fdopendir(fd);
 660    if (!fs->dir.stream) {
 661        serrno = errno;
 662        close(fd);
 663        errno = serrno;
 664        return -1;
 665    }
 666    return 0;
 667}
 668
 669static void proxy_rewinddir(FsContext *ctx, V9fsFidOpenState *fs)
 670{
 671    rewinddir(fs->dir.stream);
 672}
 673
 674static off_t proxy_telldir(FsContext *ctx, V9fsFidOpenState *fs)
 675{
 676    return telldir(fs->dir.stream);
 677}
 678
 679static struct dirent *proxy_readdir(FsContext *ctx, V9fsFidOpenState *fs)
 680{
 681    return readdir(fs->dir.stream);
 682}
 683
 684static void proxy_seekdir(FsContext *ctx, V9fsFidOpenState *fs, off_t off)
 685{
 686    seekdir(fs->dir.stream, off);
 687}
 688
 689static ssize_t proxy_preadv(FsContext *ctx, V9fsFidOpenState *fs,
 690                            const struct iovec *iov,
 691                            int iovcnt, off_t offset)
 692{
 693    ssize_t ret;
 694#ifdef CONFIG_PREADV
 695    ret = preadv(fs->fd, iov, iovcnt, offset);
 696#else
 697    ret = lseek(fs->fd, offset, SEEK_SET);
 698    if (ret >= 0) {
 699        ret = readv(fs->fd, iov, iovcnt);
 700    }
 701#endif
 702    return ret;
 703}
 704
 705static ssize_t proxy_pwritev(FsContext *ctx, V9fsFidOpenState *fs,
 706                             const struct iovec *iov,
 707                             int iovcnt, off_t offset)
 708{
 709    ssize_t ret;
 710
 711#ifdef CONFIG_PREADV
 712    ret = pwritev(fs->fd, iov, iovcnt, offset);
 713#else
 714    ret = lseek(fs->fd, offset, SEEK_SET);
 715    if (ret >= 0) {
 716        ret = writev(fs->fd, iov, iovcnt);
 717    }
 718#endif
 719#ifdef CONFIG_SYNC_FILE_RANGE
 720    if (ret > 0 && ctx->export_flags & V9FS_IMMEDIATE_WRITEOUT) {
 721        /*
 722         * Initiate a writeback. This is not a data integrity sync.
 723         * We want to ensure that we don't leave dirty pages in the cache
 724         * after write when writeout=immediate is sepcified.
 725         */
 726        sync_file_range(fs->fd, offset, ret,
 727                        SYNC_FILE_RANGE_WAIT_BEFORE | SYNC_FILE_RANGE_WRITE);
 728    }
 729#endif
 730    return ret;
 731}
 732
 733static int proxy_chmod(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp)
 734{
 735    int retval;
 736    retval = v9fs_request(fs_ctx->private, T_CHMOD, NULL, fs_path,
 737                          credp->fc_mode);
 738    if (retval < 0) {
 739        errno = -retval;
 740    }
 741    return retval;
 742}
 743
 744static int proxy_mknod(FsContext *fs_ctx, V9fsPath *dir_path,
 745                       const char *name, FsCred *credp)
 746{
 747    int retval;
 748    V9fsString fullname;
 749
 750    v9fs_string_init(&fullname);
 751    v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name);
 752
 753    retval = v9fs_request(fs_ctx->private, T_MKNOD, NULL, &fullname,
 754                          credp->fc_mode, credp->fc_rdev,
 755                          credp->fc_uid, credp->fc_gid);
 756    v9fs_string_free(&fullname);
 757    if (retval < 0) {
 758        errno = -retval;
 759        retval = -1;
 760    }
 761    return retval;
 762}
 763
 764static int proxy_mkdir(FsContext *fs_ctx, V9fsPath *dir_path,
 765                       const char *name, FsCred *credp)
 766{
 767    int retval;
 768    V9fsString fullname;
 769
 770    v9fs_string_init(&fullname);
 771    v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name);
 772
 773    retval = v9fs_request(fs_ctx->private, T_MKDIR, NULL, &fullname,
 774                          credp->fc_mode, credp->fc_uid, credp->fc_gid);
 775    v9fs_string_free(&fullname);
 776    if (retval < 0) {
 777        errno = -retval;
 778        retval = -1;
 779    }
 780    return retval;
 781}
 782
 783static int proxy_fstat(FsContext *fs_ctx, int fid_type,
 784                       V9fsFidOpenState *fs, struct stat *stbuf)
 785{
 786    int fd;
 787
 788    if (fid_type == P9_FID_DIR) {
 789        fd = dirfd(fs->dir.stream);
 790    } else {
 791        fd = fs->fd;
 792    }
 793    return fstat(fd, stbuf);
 794}
 795
 796static int proxy_open2(FsContext *fs_ctx, V9fsPath *dir_path, const char *name,
 797                       int flags, FsCred *credp, V9fsFidOpenState *fs)
 798{
 799    V9fsString fullname;
 800
 801    v9fs_string_init(&fullname);
 802    v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name);
 803
 804    fs->fd = v9fs_request(fs_ctx->private, T_CREATE, NULL, &fullname, flags,
 805                          credp->fc_mode, credp->fc_uid, credp->fc_gid);
 806    v9fs_string_free(&fullname);
 807    if (fs->fd < 0) {
 808        errno = -fs->fd;
 809        fs->fd = -1;
 810    }
 811    return fs->fd;
 812}
 813
 814static int proxy_symlink(FsContext *fs_ctx, const char *oldpath,
 815                         V9fsPath *dir_path, const char *name, FsCred *credp)
 816{
 817    int retval;
 818    V9fsString fullname, target;
 819
 820    v9fs_string_init(&fullname);
 821    v9fs_string_init(&target);
 822
 823    v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name);
 824    v9fs_string_sprintf(&target, "%s", oldpath);
 825
 826    retval = v9fs_request(fs_ctx->private, T_SYMLINK, NULL, &target, &fullname,
 827                          credp->fc_uid, credp->fc_gid);
 828    v9fs_string_free(&fullname);
 829    v9fs_string_free(&target);
 830    if (retval < 0) {
 831        errno = -retval;
 832        retval = -1;
 833    }
 834    return retval;
 835}
 836
 837static int proxy_link(FsContext *ctx, V9fsPath *oldpath,
 838                      V9fsPath *dirpath, const char *name)
 839{
 840    int retval;
 841    V9fsString newpath;
 842
 843    v9fs_string_init(&newpath);
 844    v9fs_string_sprintf(&newpath, "%s/%s", dirpath->data, name);
 845
 846    retval = v9fs_request(ctx->private, T_LINK, NULL, oldpath, &newpath);
 847    v9fs_string_free(&newpath);
 848    if (retval < 0) {
 849        errno = -retval;
 850        retval = -1;
 851    }
 852    return retval;
 853}
 854
 855static int proxy_truncate(FsContext *ctx, V9fsPath *fs_path, off_t size)
 856{
 857    int retval;
 858
 859    retval = v9fs_request(ctx->private, T_TRUNCATE, NULL, fs_path, size);
 860    if (retval < 0) {
 861        errno = -retval;
 862        return -1;
 863    }
 864    return 0;
 865}
 866
 867static int proxy_rename(FsContext *ctx, const char *oldpath,
 868                        const char *newpath)
 869{
 870    int retval;
 871    V9fsString oldname, newname;
 872
 873    v9fs_string_init(&oldname);
 874    v9fs_string_init(&newname);
 875
 876    v9fs_string_sprintf(&oldname, "%s", oldpath);
 877    v9fs_string_sprintf(&newname, "%s", newpath);
 878    retval = v9fs_request(ctx->private, T_RENAME, NULL, &oldname, &newname);
 879    v9fs_string_free(&oldname);
 880    v9fs_string_free(&newname);
 881    if (retval < 0) {
 882        errno = -retval;
 883    }
 884    return retval;
 885}
 886
 887static int proxy_chown(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp)
 888{
 889    int retval;
 890    retval = v9fs_request(fs_ctx->private, T_CHOWN, NULL, fs_path,
 891                          credp->fc_uid, credp->fc_gid);
 892    if (retval < 0) {
 893        errno = -retval;
 894    }
 895    return retval;
 896}
 897
 898static int proxy_utimensat(FsContext *s, V9fsPath *fs_path,
 899                           const struct timespec *buf)
 900{
 901    int retval;
 902    retval = v9fs_request(s->private, T_UTIME, NULL, fs_path,
 903                          buf[0].tv_sec, buf[0].tv_nsec,
 904                          buf[1].tv_sec, buf[1].tv_nsec);
 905    if (retval < 0) {
 906        errno = -retval;
 907    }
 908    return retval;
 909}
 910
 911static int proxy_remove(FsContext *ctx, const char *path)
 912{
 913    int retval;
 914    V9fsString name;
 915    v9fs_string_init(&name);
 916    v9fs_string_sprintf(&name, "%s", path);
 917    retval = v9fs_request(ctx->private, T_REMOVE, NULL, &name);
 918    v9fs_string_free(&name);
 919    if (retval < 0) {
 920        errno = -retval;
 921    }
 922    return retval;
 923}
 924
 925static int proxy_fsync(FsContext *ctx, int fid_type,
 926                       V9fsFidOpenState *fs, int datasync)
 927{
 928    int fd;
 929
 930    if (fid_type == P9_FID_DIR) {
 931        fd = dirfd(fs->dir.stream);
 932    } else {
 933        fd = fs->fd;
 934    }
 935
 936    if (datasync) {
 937        return qemu_fdatasync(fd);
 938    } else {
 939        return fsync(fd);
 940    }
 941}
 942
 943static int proxy_statfs(FsContext *s, V9fsPath *fs_path, struct statfs *stbuf)
 944{
 945    int retval;
 946    retval = v9fs_request(s->private, T_STATFS, stbuf, fs_path);
 947    if (retval < 0) {
 948        errno = -retval;
 949        return -1;
 950    }
 951    return retval;
 952}
 953
 954static ssize_t proxy_lgetxattr(FsContext *ctx, V9fsPath *fs_path,
 955                               const char *name, void *value, size_t size)
 956{
 957    int retval;
 958    V9fsString xname;
 959
 960    v9fs_string_init(&xname);
 961    v9fs_string_sprintf(&xname, "%s", name);
 962    retval = v9fs_request(ctx->private, T_LGETXATTR, value, size, fs_path,
 963                          &xname);
 964    v9fs_string_free(&xname);
 965    if (retval < 0) {
 966        errno = -retval;
 967    }
 968    return retval;
 969}
 970
 971static ssize_t proxy_llistxattr(FsContext *ctx, V9fsPath *fs_path,
 972                                void *value, size_t size)
 973{
 974    int retval;
 975    retval = v9fs_request(ctx->private, T_LLISTXATTR, value, size, fs_path);
 976    if (retval < 0) {
 977        errno = -retval;
 978    }
 979    return retval;
 980}
 981
 982static int proxy_lsetxattr(FsContext *ctx, V9fsPath *fs_path, const char *name,
 983                           void *value, size_t size, int flags)
 984{
 985    int retval;
 986    V9fsString xname, xvalue;
 987
 988    v9fs_string_init(&xname);
 989    v9fs_string_sprintf(&xname, "%s", name);
 990
 991    v9fs_string_init(&xvalue);
 992    xvalue.size = size;
 993    xvalue.data = g_malloc(size);
 994    memcpy(xvalue.data, value, size);
 995
 996    retval = v9fs_request(ctx->private, T_LSETXATTR, value, fs_path, &xname,
 997                          &xvalue, size, flags);
 998    v9fs_string_free(&xname);
 999    v9fs_string_free(&xvalue);
1000    if (retval < 0) {
1001        errno = -retval;
1002    }
1003    return retval;
1004}
1005
1006static int proxy_lremovexattr(FsContext *ctx, V9fsPath *fs_path,
1007                              const char *name)
1008{
1009    int retval;
1010    V9fsString xname;
1011
1012    v9fs_string_init(&xname);
1013    v9fs_string_sprintf(&xname, "%s", name);
1014    retval = v9fs_request(ctx->private, T_LREMOVEXATTR, NULL, fs_path, &xname);
1015    v9fs_string_free(&xname);
1016    if (retval < 0) {
1017        errno = -retval;
1018    }
1019    return retval;
1020}
1021
1022static int proxy_name_to_path(FsContext *ctx, V9fsPath *dir_path,
1023                              const char *name, V9fsPath *target)
1024{
1025    if (dir_path) {
1026        v9fs_path_sprintf(target, "%s/%s", dir_path->data, name);
1027    } else {
1028        v9fs_path_sprintf(target, "%s", name);
1029    }
1030    return 0;
1031}
1032
1033static int proxy_renameat(FsContext *ctx, V9fsPath *olddir,
1034                          const char *old_name, V9fsPath *newdir,
1035                          const char *new_name)
1036{
1037    int ret;
1038    V9fsString old_full_name, new_full_name;
1039
1040    v9fs_string_init(&old_full_name);
1041    v9fs_string_init(&new_full_name);
1042
1043    v9fs_string_sprintf(&old_full_name, "%s/%s", olddir->data, old_name);
1044    v9fs_string_sprintf(&new_full_name, "%s/%s", newdir->data, new_name);
1045
1046    ret = proxy_rename(ctx, old_full_name.data, new_full_name.data);
1047    v9fs_string_free(&old_full_name);
1048    v9fs_string_free(&new_full_name);
1049    return ret;
1050}
1051
1052static int proxy_unlinkat(FsContext *ctx, V9fsPath *dir,
1053                          const char *name, int flags)
1054{
1055    int ret;
1056    V9fsString fullname;
1057    v9fs_string_init(&fullname);
1058
1059    v9fs_string_sprintf(&fullname, "%s/%s", dir->data, name);
1060    ret = proxy_remove(ctx, fullname.data);
1061    v9fs_string_free(&fullname);
1062
1063    return ret;
1064}
1065
1066static int proxy_ioc_getversion(FsContext *fs_ctx, V9fsPath *path,
1067                                mode_t st_mode, uint64_t *st_gen)
1068{
1069    int err;
1070
1071    /* Do not try to open special files like device nodes, fifos etc
1072     * we can get fd for regular files and directories only
1073     */
1074    if (!S_ISREG(st_mode) && !S_ISDIR(st_mode)) {
1075        errno = ENOTTY;
1076        return -1;
1077    }
1078    err = v9fs_request(fs_ctx->private, T_GETVERSION, st_gen, path);
1079    if (err < 0) {
1080        errno = -err;
1081        err = -1;
1082    }
1083    return err;
1084}
1085
1086static int connect_namedsocket(const char *path)
1087{
1088    int sockfd, size;
1089    struct sockaddr_un helper;
1090
1091    if (strlen(path) >= sizeof(helper.sun_path)) {
1092        error_report("Socket name too long");
1093        return -1;
1094    }
1095    sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
1096    if (sockfd < 0) {
1097        error_report("Failed to create socket: %s", strerror(errno));
1098        return -1;
1099    }
1100    strcpy(helper.sun_path, path);
1101    helper.sun_family = AF_UNIX;
1102    size = strlen(helper.sun_path) + sizeof(helper.sun_family);
1103    if (connect(sockfd, (struct sockaddr *)&helper, size) < 0) {
1104        error_report("Failed to connect to %s: %s", path, strerror(errno));
1105        close(sockfd);
1106        return -1;
1107    }
1108
1109    /* remove the socket for security reasons */
1110    unlink(path);
1111    return sockfd;
1112}
1113
1114static int proxy_parse_opts(QemuOpts *opts, struct FsDriverEntry *fs)
1115{
1116    const char *socket = qemu_opt_get(opts, "socket");
1117    const char *sock_fd = qemu_opt_get(opts, "sock_fd");
1118
1119    if (!socket && !sock_fd) {
1120        error_report("Must specify either socket or sock_fd");
1121        return -1;
1122    }
1123    if (socket && sock_fd) {
1124        error_report("Both socket and sock_fd options specified");
1125        return -1;
1126    }
1127    if (socket) {
1128        fs->path = g_strdup(socket);
1129        fs->export_flags = V9FS_PROXY_SOCK_NAME;
1130    } else {
1131        fs->path = g_strdup(sock_fd);
1132        fs->export_flags = V9FS_PROXY_SOCK_FD;
1133    }
1134    return 0;
1135}
1136
1137static int proxy_init(FsContext *ctx)
1138{
1139    V9fsProxy *proxy = g_malloc(sizeof(V9fsProxy));
1140    int sock_id;
1141
1142    if (ctx->export_flags & V9FS_PROXY_SOCK_NAME) {
1143        sock_id = connect_namedsocket(ctx->fs_root);
1144    } else {
1145        sock_id = atoi(ctx->fs_root);
1146        if (sock_id < 0) {
1147            error_report("Socket descriptor not initialized");
1148        }
1149    }
1150    if (sock_id < 0) {
1151        g_free(proxy);
1152        return -1;
1153    }
1154    g_free(ctx->fs_root);
1155    ctx->fs_root = NULL;
1156
1157    proxy->in_iovec.iov_base  = g_malloc(PROXY_MAX_IO_SZ + PROXY_HDR_SZ);
1158    proxy->in_iovec.iov_len   = PROXY_MAX_IO_SZ + PROXY_HDR_SZ;
1159    proxy->out_iovec.iov_base = g_malloc(PROXY_MAX_IO_SZ + PROXY_HDR_SZ);
1160    proxy->out_iovec.iov_len  = PROXY_MAX_IO_SZ + PROXY_HDR_SZ;
1161
1162    ctx->private = proxy;
1163    proxy->sockfd = sock_id;
1164    qemu_mutex_init(&proxy->mutex);
1165
1166    ctx->export_flags |= V9FS_PATHNAME_FSCONTEXT;
1167    ctx->exops.get_st_gen = proxy_ioc_getversion;
1168    return 0;
1169}
1170
1171static void proxy_cleanup(FsContext *ctx)
1172{
1173    V9fsProxy *proxy = ctx->private;
1174
1175    g_free(proxy->out_iovec.iov_base);
1176    g_free(proxy->in_iovec.iov_base);
1177    if (ctx->export_flags & V9FS_PROXY_SOCK_NAME) {
1178        close(proxy->sockfd);
1179    }
1180    g_free(proxy);
1181}
1182
1183FileOperations proxy_ops = {
1184    .parse_opts   = proxy_parse_opts,
1185    .init         = proxy_init,
1186    .cleanup      = proxy_cleanup,
1187    .lstat        = proxy_lstat,
1188    .readlink     = proxy_readlink,
1189    .close        = proxy_close,
1190    .closedir     = proxy_closedir,
1191    .open         = proxy_open,
1192    .opendir      = proxy_opendir,
1193    .rewinddir    = proxy_rewinddir,
1194    .telldir      = proxy_telldir,
1195    .readdir      = proxy_readdir,
1196    .seekdir      = proxy_seekdir,
1197    .preadv       = proxy_preadv,
1198    .pwritev      = proxy_pwritev,
1199    .chmod        = proxy_chmod,
1200    .mknod        = proxy_mknod,
1201    .mkdir        = proxy_mkdir,
1202    .fstat        = proxy_fstat,
1203    .open2        = proxy_open2,
1204    .symlink      = proxy_symlink,
1205    .link         = proxy_link,
1206    .truncate     = proxy_truncate,
1207    .rename       = proxy_rename,
1208    .chown        = proxy_chown,
1209    .utimensat    = proxy_utimensat,
1210    .remove       = proxy_remove,
1211    .fsync        = proxy_fsync,
1212    .statfs       = proxy_statfs,
1213    .lgetxattr    = proxy_lgetxattr,
1214    .llistxattr   = proxy_llistxattr,
1215    .lsetxattr    = proxy_lsetxattr,
1216    .lremovexattr = proxy_lremovexattr,
1217    .name_to_path = proxy_name_to_path,
1218    .renameat     = proxy_renameat,
1219    .unlinkat     = proxy_unlinkat,
1220};
1221