qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

9p-proxy.c (36591B)


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