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