xref: /openbmc/qemu/hw/9pfs/9p-proxy.c (revision fbc04127)
1494a8ebeSWei Liu /*
2494a8ebeSWei Liu  * 9p Proxy callback
3494a8ebeSWei Liu  *
4494a8ebeSWei Liu  * Copyright IBM, Corp. 2011
5494a8ebeSWei Liu  *
6494a8ebeSWei Liu  * Authors:
7494a8ebeSWei Liu  * M. Mohan Kumar <mohan@in.ibm.com>
8494a8ebeSWei Liu  *
9494a8ebeSWei Liu  * This work is licensed under the terms of the GNU GPL, version 2.  See
10494a8ebeSWei Liu  * the COPYING file in the top-level directory.
11494a8ebeSWei Liu  */
12*fbc04127SPeter Maydell #include "qemu/osdep.h"
13494a8ebeSWei Liu #include <sys/socket.h>
14494a8ebeSWei Liu #include <sys/un.h>
15ebe74f8bSWei Liu #include "9p.h"
16494a8ebeSWei Liu #include "qemu/error-report.h"
17494a8ebeSWei Liu #include "fsdev/qemu-fsdev.h"
18494a8ebeSWei Liu #include "9p-proxy.h"
19494a8ebeSWei Liu 
20494a8ebeSWei Liu typedef struct V9fsProxy {
21494a8ebeSWei Liu     int sockfd;
22494a8ebeSWei Liu     QemuMutex mutex;
23494a8ebeSWei Liu     struct iovec in_iovec;
24494a8ebeSWei Liu     struct iovec out_iovec;
25494a8ebeSWei Liu } V9fsProxy;
26494a8ebeSWei Liu 
27494a8ebeSWei Liu /*
28494a8ebeSWei Liu  * Return received file descriptor on success in *status.
29494a8ebeSWei Liu  * errno is also returned on *status (which will be < 0)
30494a8ebeSWei Liu  * return < 0 on transport error.
31494a8ebeSWei Liu  */
32494a8ebeSWei Liu static int v9fs_receivefd(int sockfd, int *status)
33494a8ebeSWei Liu {
34494a8ebeSWei Liu     struct iovec iov;
35494a8ebeSWei Liu     struct msghdr msg;
36494a8ebeSWei Liu     struct cmsghdr *cmsg;
37494a8ebeSWei Liu     int retval, data, fd;
38494a8ebeSWei Liu     union MsgControl msg_control;
39494a8ebeSWei Liu 
40494a8ebeSWei Liu     iov.iov_base = &data;
41494a8ebeSWei Liu     iov.iov_len = sizeof(data);
42494a8ebeSWei Liu 
43494a8ebeSWei Liu     memset(&msg, 0, sizeof(msg));
44494a8ebeSWei Liu     msg.msg_iov = &iov;
45494a8ebeSWei Liu     msg.msg_iovlen = 1;
46494a8ebeSWei Liu     msg.msg_control = &msg_control;
47494a8ebeSWei Liu     msg.msg_controllen = sizeof(msg_control);
48494a8ebeSWei Liu 
49494a8ebeSWei Liu     do {
50494a8ebeSWei Liu         retval = recvmsg(sockfd, &msg, 0);
51494a8ebeSWei Liu     } while (retval < 0 && errno == EINTR);
52494a8ebeSWei Liu     if (retval <= 0) {
53494a8ebeSWei Liu         return retval;
54494a8ebeSWei Liu     }
55494a8ebeSWei Liu     /*
56494a8ebeSWei Liu      * data is set to V9FS_FD_VALID, if ancillary data is sent.  If this
57494a8ebeSWei Liu      * request doesn't need ancillary data (fd) or an error occurred,
58494a8ebeSWei Liu      * data is set to negative errno value.
59494a8ebeSWei Liu      */
60494a8ebeSWei Liu     if (data != V9FS_FD_VALID) {
61494a8ebeSWei Liu         *status = data;
62494a8ebeSWei Liu         return 0;
63494a8ebeSWei Liu     }
64494a8ebeSWei Liu     /*
65494a8ebeSWei Liu      * File descriptor (fd) is sent in the ancillary data. Check if we
66494a8ebeSWei Liu      * indeed received it. One of the reasons to fail to receive it is if
67494a8ebeSWei Liu      * we exceeded the maximum number of file descriptors!
68494a8ebeSWei Liu      */
69494a8ebeSWei Liu     for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
70494a8ebeSWei Liu         if (cmsg->cmsg_len != CMSG_LEN(sizeof(int)) ||
71494a8ebeSWei Liu             cmsg->cmsg_level != SOL_SOCKET ||
72494a8ebeSWei Liu             cmsg->cmsg_type != SCM_RIGHTS) {
73494a8ebeSWei Liu             continue;
74494a8ebeSWei Liu         }
75494a8ebeSWei Liu         fd = *((int *)CMSG_DATA(cmsg));
76494a8ebeSWei Liu         *status = fd;
77494a8ebeSWei Liu         return 0;
78494a8ebeSWei Liu     }
79494a8ebeSWei Liu     *status = -ENFILE;  /* Ancillary data sent but not received */
80494a8ebeSWei Liu     return 0;
81494a8ebeSWei Liu }
82494a8ebeSWei Liu 
83494a8ebeSWei Liu static ssize_t socket_read(int sockfd, void *buff, size_t size)
84494a8ebeSWei Liu {
85494a8ebeSWei Liu     ssize_t retval, total = 0;
86494a8ebeSWei Liu 
87494a8ebeSWei Liu     while (size) {
88494a8ebeSWei Liu         retval = read(sockfd, buff, size);
89494a8ebeSWei Liu         if (retval == 0) {
90494a8ebeSWei Liu             return -EIO;
91494a8ebeSWei Liu         }
92494a8ebeSWei Liu         if (retval < 0) {
93494a8ebeSWei Liu             if (errno == EINTR) {
94494a8ebeSWei Liu                 continue;
95494a8ebeSWei Liu             }
96494a8ebeSWei Liu             return -errno;
97494a8ebeSWei Liu         }
98494a8ebeSWei Liu         size -= retval;
99494a8ebeSWei Liu         buff += retval;
100494a8ebeSWei Liu         total += retval;
101494a8ebeSWei Liu     }
102494a8ebeSWei Liu     return total;
103494a8ebeSWei Liu }
104494a8ebeSWei Liu 
105494a8ebeSWei Liu /* Converts proxy_statfs to VFS statfs structure */
106494a8ebeSWei Liu static void prstatfs_to_statfs(struct statfs *stfs, ProxyStatFS *prstfs)
107494a8ebeSWei Liu {
108494a8ebeSWei Liu     memset(stfs, 0, sizeof(*stfs));
109494a8ebeSWei Liu     stfs->f_type = prstfs->f_type;
110494a8ebeSWei Liu     stfs->f_bsize = prstfs->f_bsize;
111494a8ebeSWei Liu     stfs->f_blocks = prstfs->f_blocks;
112494a8ebeSWei Liu     stfs->f_bfree = prstfs->f_bfree;
113494a8ebeSWei Liu     stfs->f_bavail = prstfs->f_bavail;
114494a8ebeSWei Liu     stfs->f_files = prstfs->f_files;
115494a8ebeSWei Liu     stfs->f_ffree = prstfs->f_ffree;
116494a8ebeSWei Liu     stfs->f_fsid.__val[0] = prstfs->f_fsid[0] & 0xFFFFFFFFU;
117494a8ebeSWei Liu     stfs->f_fsid.__val[1] = prstfs->f_fsid[1] >> 32 & 0xFFFFFFFFU;
118494a8ebeSWei Liu     stfs->f_namelen = prstfs->f_namelen;
119494a8ebeSWei Liu     stfs->f_frsize = prstfs->f_frsize;
120494a8ebeSWei Liu }
121494a8ebeSWei Liu 
122494a8ebeSWei Liu /* Converts proxy_stat structure to VFS stat structure */
123494a8ebeSWei Liu static void prstat_to_stat(struct stat *stbuf, ProxyStat *prstat)
124494a8ebeSWei Liu {
125494a8ebeSWei Liu    memset(stbuf, 0, sizeof(*stbuf));
126494a8ebeSWei Liu    stbuf->st_dev = prstat->st_dev;
127494a8ebeSWei Liu    stbuf->st_ino = prstat->st_ino;
128494a8ebeSWei Liu    stbuf->st_nlink = prstat->st_nlink;
129494a8ebeSWei Liu    stbuf->st_mode = prstat->st_mode;
130494a8ebeSWei Liu    stbuf->st_uid = prstat->st_uid;
131494a8ebeSWei Liu    stbuf->st_gid = prstat->st_gid;
132494a8ebeSWei Liu    stbuf->st_rdev = prstat->st_rdev;
133494a8ebeSWei Liu    stbuf->st_size = prstat->st_size;
134494a8ebeSWei Liu    stbuf->st_blksize = prstat->st_blksize;
135494a8ebeSWei Liu    stbuf->st_blocks = prstat->st_blocks;
136494a8ebeSWei Liu    stbuf->st_atim.tv_sec = prstat->st_atim_sec;
137494a8ebeSWei Liu    stbuf->st_atim.tv_nsec = prstat->st_atim_nsec;
138494a8ebeSWei Liu    stbuf->st_mtime = prstat->st_mtim_sec;
139494a8ebeSWei Liu    stbuf->st_mtim.tv_nsec = prstat->st_mtim_nsec;
140494a8ebeSWei Liu    stbuf->st_ctime = prstat->st_ctim_sec;
141494a8ebeSWei Liu    stbuf->st_ctim.tv_nsec = prstat->st_ctim_nsec;
142494a8ebeSWei Liu }
143494a8ebeSWei Liu 
144494a8ebeSWei Liu /*
145494a8ebeSWei Liu  * Response contains two parts
146494a8ebeSWei Liu  * {header, data}
147494a8ebeSWei Liu  * header.type == T_ERROR, data -> -errno
148494a8ebeSWei Liu  * header.type == T_SUCCESS, data -> response
149494a8ebeSWei Liu  * size of errno/response is given by header.size
150494a8ebeSWei Liu  * returns < 0, on transport error. response is
151494a8ebeSWei Liu  * valid only if status >= 0.
152494a8ebeSWei Liu  */
153494a8ebeSWei Liu static int v9fs_receive_response(V9fsProxy *proxy, int type,
154494a8ebeSWei Liu                                  int *status, void *response)
155494a8ebeSWei Liu {
156494a8ebeSWei Liu     int retval;
157494a8ebeSWei Liu     ProxyHeader header;
158494a8ebeSWei Liu     struct iovec *reply = &proxy->in_iovec;
159494a8ebeSWei Liu 
160494a8ebeSWei Liu     *status = 0;
161494a8ebeSWei Liu     reply->iov_len = 0;
162494a8ebeSWei Liu     retval = socket_read(proxy->sockfd, reply->iov_base, PROXY_HDR_SZ);
163494a8ebeSWei Liu     if (retval < 0) {
164494a8ebeSWei Liu         return retval;
165494a8ebeSWei Liu     }
166494a8ebeSWei Liu     reply->iov_len = PROXY_HDR_SZ;
167494a8ebeSWei Liu     proxy_unmarshal(reply, 0, "dd", &header.type, &header.size);
168494a8ebeSWei Liu     /*
169494a8ebeSWei Liu      * if response size > PROXY_MAX_IO_SZ, read the response but ignore it and
170494a8ebeSWei Liu      * return -ENOBUFS
171494a8ebeSWei Liu      */
172494a8ebeSWei Liu     if (header.size > PROXY_MAX_IO_SZ) {
173494a8ebeSWei Liu         int count;
174494a8ebeSWei Liu         while (header.size > 0) {
175494a8ebeSWei Liu             count = MIN(PROXY_MAX_IO_SZ, header.size);
176494a8ebeSWei Liu             count = socket_read(proxy->sockfd, reply->iov_base, count);
177494a8ebeSWei Liu             if (count < 0) {
178494a8ebeSWei Liu                 return count;
179494a8ebeSWei Liu             }
180494a8ebeSWei Liu             header.size -= count;
181494a8ebeSWei Liu         }
182494a8ebeSWei Liu         *status = -ENOBUFS;
183494a8ebeSWei Liu         return 0;
184494a8ebeSWei Liu     }
185494a8ebeSWei Liu 
186494a8ebeSWei Liu     retval = socket_read(proxy->sockfd,
187494a8ebeSWei Liu                          reply->iov_base + PROXY_HDR_SZ, header.size);
188494a8ebeSWei Liu     if (retval < 0) {
189494a8ebeSWei Liu         return retval;
190494a8ebeSWei Liu     }
191494a8ebeSWei Liu     reply->iov_len += header.size;
192494a8ebeSWei Liu     /* there was an error during processing request */
193494a8ebeSWei Liu     if (header.type == T_ERROR) {
194494a8ebeSWei Liu         int ret;
195494a8ebeSWei Liu         ret = proxy_unmarshal(reply, PROXY_HDR_SZ, "d", status);
196494a8ebeSWei Liu         if (ret < 0) {
197494a8ebeSWei Liu             *status = ret;
198494a8ebeSWei Liu         }
199494a8ebeSWei Liu         return 0;
200494a8ebeSWei Liu     }
201494a8ebeSWei Liu 
202494a8ebeSWei Liu     switch (type) {
203494a8ebeSWei Liu     case T_LSTAT: {
204494a8ebeSWei Liu         ProxyStat prstat;
205494a8ebeSWei Liu         retval = proxy_unmarshal(reply, PROXY_HDR_SZ,
206494a8ebeSWei Liu                                  "qqqdddqqqqqqqqqq", &prstat.st_dev,
207494a8ebeSWei Liu                                  &prstat.st_ino, &prstat.st_nlink,
208494a8ebeSWei Liu                                  &prstat.st_mode, &prstat.st_uid,
209494a8ebeSWei Liu                                  &prstat.st_gid, &prstat.st_rdev,
210494a8ebeSWei Liu                                  &prstat.st_size, &prstat.st_blksize,
211494a8ebeSWei Liu                                  &prstat.st_blocks,
212494a8ebeSWei Liu                                  &prstat.st_atim_sec, &prstat.st_atim_nsec,
213494a8ebeSWei Liu                                  &prstat.st_mtim_sec, &prstat.st_mtim_nsec,
214494a8ebeSWei Liu                                  &prstat.st_ctim_sec, &prstat.st_ctim_nsec);
215494a8ebeSWei Liu         prstat_to_stat(response, &prstat);
216494a8ebeSWei Liu         break;
217494a8ebeSWei Liu     }
218494a8ebeSWei Liu     case T_STATFS: {
219494a8ebeSWei Liu         ProxyStatFS prstfs;
220494a8ebeSWei Liu         retval = proxy_unmarshal(reply, PROXY_HDR_SZ,
221494a8ebeSWei Liu                                  "qqqqqqqqqqq", &prstfs.f_type,
222494a8ebeSWei Liu                                  &prstfs.f_bsize, &prstfs.f_blocks,
223494a8ebeSWei Liu                                  &prstfs.f_bfree, &prstfs.f_bavail,
224494a8ebeSWei Liu                                  &prstfs.f_files, &prstfs.f_ffree,
225494a8ebeSWei Liu                                  &prstfs.f_fsid[0], &prstfs.f_fsid[1],
226494a8ebeSWei Liu                                  &prstfs.f_namelen, &prstfs.f_frsize);
227494a8ebeSWei Liu         prstatfs_to_statfs(response, &prstfs);
228494a8ebeSWei Liu         break;
229494a8ebeSWei Liu     }
230494a8ebeSWei Liu     case T_READLINK: {
231494a8ebeSWei Liu         V9fsString target;
232494a8ebeSWei Liu         v9fs_string_init(&target);
233494a8ebeSWei Liu         retval = proxy_unmarshal(reply, PROXY_HDR_SZ, "s", &target);
234494a8ebeSWei Liu         strcpy(response, target.data);
235494a8ebeSWei Liu         v9fs_string_free(&target);
236494a8ebeSWei Liu         break;
237494a8ebeSWei Liu     }
238494a8ebeSWei Liu     case T_LGETXATTR:
239494a8ebeSWei Liu     case T_LLISTXATTR: {
240494a8ebeSWei Liu         V9fsString xattr;
241494a8ebeSWei Liu         v9fs_string_init(&xattr);
242494a8ebeSWei Liu         retval = proxy_unmarshal(reply, PROXY_HDR_SZ, "s", &xattr);
243494a8ebeSWei Liu         memcpy(response, xattr.data, xattr.size);
244494a8ebeSWei Liu         v9fs_string_free(&xattr);
245494a8ebeSWei Liu         break;
246494a8ebeSWei Liu     }
247494a8ebeSWei Liu     case T_GETVERSION:
248494a8ebeSWei Liu         proxy_unmarshal(reply, PROXY_HDR_SZ, "q", response);
249494a8ebeSWei Liu         break;
250494a8ebeSWei Liu     default:
251494a8ebeSWei Liu         return -1;
252494a8ebeSWei Liu     }
253494a8ebeSWei Liu     if (retval < 0) {
254494a8ebeSWei Liu         *status  = retval;
255494a8ebeSWei Liu     }
256494a8ebeSWei Liu     return 0;
257494a8ebeSWei Liu }
258494a8ebeSWei Liu 
259494a8ebeSWei Liu /*
260494a8ebeSWei Liu  * return < 0 on transport error.
261494a8ebeSWei Liu  * *status is valid only if return >= 0
262494a8ebeSWei Liu  */
263494a8ebeSWei Liu static int v9fs_receive_status(V9fsProxy *proxy,
264494a8ebeSWei Liu                                struct iovec *reply, int *status)
265494a8ebeSWei Liu {
266494a8ebeSWei Liu     int retval;
267494a8ebeSWei Liu     ProxyHeader header;
268494a8ebeSWei Liu 
269494a8ebeSWei Liu     *status = 0;
270494a8ebeSWei Liu     reply->iov_len = 0;
271494a8ebeSWei Liu     retval = socket_read(proxy->sockfd, reply->iov_base, PROXY_HDR_SZ);
272494a8ebeSWei Liu     if (retval < 0) {
273494a8ebeSWei Liu         return retval;
274494a8ebeSWei Liu     }
275494a8ebeSWei Liu     reply->iov_len = PROXY_HDR_SZ;
276494a8ebeSWei Liu     proxy_unmarshal(reply, 0, "dd", &header.type, &header.size);
277494a8ebeSWei Liu     if (header.size != sizeof(int)) {
278494a8ebeSWei Liu         *status = -ENOBUFS;
279494a8ebeSWei Liu         return 0;
280494a8ebeSWei Liu     }
281494a8ebeSWei Liu     retval = socket_read(proxy->sockfd,
282494a8ebeSWei Liu                          reply->iov_base + PROXY_HDR_SZ, header.size);
283494a8ebeSWei Liu     if (retval < 0) {
284494a8ebeSWei Liu         return retval;
285494a8ebeSWei Liu     }
286494a8ebeSWei Liu     reply->iov_len += header.size;
287494a8ebeSWei Liu     proxy_unmarshal(reply, PROXY_HDR_SZ, "d", status);
288494a8ebeSWei Liu     return 0;
289494a8ebeSWei Liu }
290494a8ebeSWei Liu 
291494a8ebeSWei Liu /*
292494a8ebeSWei Liu  * Proxy->header and proxy->request written to socket by QEMU process.
293494a8ebeSWei Liu  * This request read by proxy helper process
294494a8ebeSWei Liu  * returns 0 on success and -errno on error
295494a8ebeSWei Liu  */
296494a8ebeSWei Liu static int v9fs_request(V9fsProxy *proxy, int type,
297494a8ebeSWei Liu                         void *response, const char *fmt, ...)
298494a8ebeSWei Liu {
299494a8ebeSWei Liu     dev_t rdev;
300494a8ebeSWei Liu     va_list ap;
301494a8ebeSWei Liu     int size = 0;
302494a8ebeSWei Liu     int retval = 0;
303494a8ebeSWei Liu     uint64_t offset;
304494a8ebeSWei Liu     ProxyHeader header = { 0, 0};
305494a8ebeSWei Liu     struct timespec spec[2];
306494a8ebeSWei Liu     int flags, mode, uid, gid;
307494a8ebeSWei Liu     V9fsString *name, *value;
308494a8ebeSWei Liu     V9fsString *path, *oldpath;
309494a8ebeSWei Liu     struct iovec *iovec = NULL, *reply = NULL;
310494a8ebeSWei Liu 
311494a8ebeSWei Liu     qemu_mutex_lock(&proxy->mutex);
312494a8ebeSWei Liu 
313494a8ebeSWei Liu     if (proxy->sockfd == -1) {
314494a8ebeSWei Liu         retval = -EIO;
315494a8ebeSWei Liu         goto err_out;
316494a8ebeSWei Liu     }
317494a8ebeSWei Liu     iovec = &proxy->out_iovec;
318494a8ebeSWei Liu     reply = &proxy->in_iovec;
319494a8ebeSWei Liu     va_start(ap, fmt);
320494a8ebeSWei Liu     switch (type) {
321494a8ebeSWei Liu     case T_OPEN:
322494a8ebeSWei Liu         path = va_arg(ap, V9fsString *);
323494a8ebeSWei Liu         flags = va_arg(ap, int);
324494a8ebeSWei Liu         retval = proxy_marshal(iovec, PROXY_HDR_SZ, "sd", path, flags);
325494a8ebeSWei Liu         if (retval > 0) {
326494a8ebeSWei Liu             header.size = retval;
327494a8ebeSWei Liu             header.type = T_OPEN;
328494a8ebeSWei Liu         }
329494a8ebeSWei Liu         break;
330494a8ebeSWei Liu     case T_CREATE:
331494a8ebeSWei Liu         path = va_arg(ap, V9fsString *);
332494a8ebeSWei Liu         flags = va_arg(ap, int);
333494a8ebeSWei Liu         mode = va_arg(ap, int);
334494a8ebeSWei Liu         uid = va_arg(ap, int);
335494a8ebeSWei Liu         gid = va_arg(ap, int);
336494a8ebeSWei Liu         retval = proxy_marshal(iovec, PROXY_HDR_SZ, "sdddd", path,
337494a8ebeSWei Liu                                     flags, mode, uid, gid);
338494a8ebeSWei Liu         if (retval > 0) {
339494a8ebeSWei Liu             header.size = retval;
340494a8ebeSWei Liu             header.type = T_CREATE;
341494a8ebeSWei Liu         }
342494a8ebeSWei Liu         break;
343494a8ebeSWei Liu     case T_MKNOD:
344494a8ebeSWei Liu         path = va_arg(ap, V9fsString *);
345494a8ebeSWei Liu         mode = va_arg(ap, int);
346494a8ebeSWei Liu         rdev = va_arg(ap, long int);
347494a8ebeSWei Liu         uid = va_arg(ap, int);
348494a8ebeSWei Liu         gid = va_arg(ap, int);
349494a8ebeSWei Liu         retval = proxy_marshal(iovec, PROXY_HDR_SZ, "ddsdq",
350494a8ebeSWei Liu                                     uid, gid, path, mode, rdev);
351494a8ebeSWei Liu         if (retval > 0) {
352494a8ebeSWei Liu             header.size = retval;
353494a8ebeSWei Liu             header.type = T_MKNOD;
354494a8ebeSWei Liu         }
355494a8ebeSWei Liu         break;
356494a8ebeSWei Liu     case T_MKDIR:
357494a8ebeSWei Liu         path = va_arg(ap, V9fsString *);
358494a8ebeSWei Liu         mode = va_arg(ap, int);
359494a8ebeSWei Liu         uid = va_arg(ap, int);
360494a8ebeSWei Liu         gid = va_arg(ap, int);
361494a8ebeSWei Liu         retval = proxy_marshal(iovec, PROXY_HDR_SZ, "ddsd",
362494a8ebeSWei Liu                                     uid, gid, path, mode);
363494a8ebeSWei Liu         if (retval > 0) {
364494a8ebeSWei Liu             header.size = retval;
365494a8ebeSWei Liu             header.type = T_MKDIR;
366494a8ebeSWei Liu         }
367494a8ebeSWei Liu         break;
368494a8ebeSWei Liu     case T_SYMLINK:
369494a8ebeSWei Liu         oldpath = va_arg(ap, V9fsString *);
370494a8ebeSWei Liu         path = va_arg(ap, V9fsString *);
371494a8ebeSWei Liu         uid = va_arg(ap, int);
372494a8ebeSWei Liu         gid = va_arg(ap, int);
373494a8ebeSWei Liu         retval = proxy_marshal(iovec, PROXY_HDR_SZ, "ddss",
374494a8ebeSWei Liu                                     uid, gid, oldpath, path);
375494a8ebeSWei Liu         if (retval > 0) {
376494a8ebeSWei Liu             header.size = retval;
377494a8ebeSWei Liu             header.type = T_SYMLINK;
378494a8ebeSWei Liu         }
379494a8ebeSWei Liu         break;
380494a8ebeSWei Liu     case T_LINK:
381494a8ebeSWei Liu         oldpath = va_arg(ap, V9fsString *);
382494a8ebeSWei Liu         path = va_arg(ap, V9fsString *);
383494a8ebeSWei Liu         retval = proxy_marshal(iovec, PROXY_HDR_SZ, "ss",
384494a8ebeSWei Liu                                     oldpath, path);
385494a8ebeSWei Liu         if (retval > 0) {
386494a8ebeSWei Liu             header.size = retval;
387494a8ebeSWei Liu             header.type = T_LINK;
388494a8ebeSWei Liu         }
389494a8ebeSWei Liu         break;
390494a8ebeSWei Liu     case T_LSTAT:
391494a8ebeSWei Liu         path = va_arg(ap, V9fsString *);
392494a8ebeSWei Liu         retval = proxy_marshal(iovec, PROXY_HDR_SZ, "s", path);
393494a8ebeSWei Liu         if (retval > 0) {
394494a8ebeSWei Liu             header.size = retval;
395494a8ebeSWei Liu             header.type = T_LSTAT;
396494a8ebeSWei Liu         }
397494a8ebeSWei Liu         break;
398494a8ebeSWei Liu     case T_READLINK:
399494a8ebeSWei Liu         path = va_arg(ap, V9fsString *);
400494a8ebeSWei Liu         size = va_arg(ap, int);
401494a8ebeSWei Liu         retval = proxy_marshal(iovec, PROXY_HDR_SZ, "sd", path, size);
402494a8ebeSWei Liu         if (retval > 0) {
403494a8ebeSWei Liu             header.size = retval;
404494a8ebeSWei Liu             header.type = T_READLINK;
405494a8ebeSWei Liu         }
406494a8ebeSWei Liu         break;
407494a8ebeSWei Liu     case T_STATFS:
408494a8ebeSWei Liu         path = va_arg(ap, V9fsString *);
409494a8ebeSWei Liu         retval = proxy_marshal(iovec, PROXY_HDR_SZ, "s", path);
410494a8ebeSWei Liu         if (retval > 0) {
411494a8ebeSWei Liu             header.size = retval;
412494a8ebeSWei Liu             header.type = T_STATFS;
413494a8ebeSWei Liu         }
414494a8ebeSWei Liu         break;
415494a8ebeSWei Liu     case T_CHMOD:
416494a8ebeSWei Liu         path = va_arg(ap, V9fsString *);
417494a8ebeSWei Liu         mode = va_arg(ap, int);
418494a8ebeSWei Liu         retval = proxy_marshal(iovec, PROXY_HDR_SZ, "sd", path, mode);
419494a8ebeSWei Liu         if (retval > 0) {
420494a8ebeSWei Liu             header.size = retval;
421494a8ebeSWei Liu             header.type = T_CHMOD;
422494a8ebeSWei Liu         }
423494a8ebeSWei Liu         break;
424494a8ebeSWei Liu     case T_CHOWN:
425494a8ebeSWei Liu         path = va_arg(ap, V9fsString *);
426494a8ebeSWei Liu         uid = va_arg(ap, int);
427494a8ebeSWei Liu         gid = va_arg(ap, int);
428494a8ebeSWei Liu         retval = proxy_marshal(iovec, PROXY_HDR_SZ, "sdd", path, uid, gid);
429494a8ebeSWei Liu         if (retval > 0) {
430494a8ebeSWei Liu             header.size = retval;
431494a8ebeSWei Liu             header.type = T_CHOWN;
432494a8ebeSWei Liu         }
433494a8ebeSWei Liu         break;
434494a8ebeSWei Liu     case T_TRUNCATE:
435494a8ebeSWei Liu         path = va_arg(ap, V9fsString *);
436494a8ebeSWei Liu         offset = va_arg(ap, uint64_t);
437494a8ebeSWei Liu         retval = proxy_marshal(iovec, PROXY_HDR_SZ, "sq", path, offset);
438494a8ebeSWei Liu         if (retval > 0) {
439494a8ebeSWei Liu             header.size = retval;
440494a8ebeSWei Liu             header.type = T_TRUNCATE;
441494a8ebeSWei Liu         }
442494a8ebeSWei Liu         break;
443494a8ebeSWei Liu     case T_UTIME:
444494a8ebeSWei Liu         path = va_arg(ap, V9fsString *);
445494a8ebeSWei Liu         spec[0].tv_sec = va_arg(ap, long);
446494a8ebeSWei Liu         spec[0].tv_nsec = va_arg(ap, long);
447494a8ebeSWei Liu         spec[1].tv_sec = va_arg(ap, long);
448494a8ebeSWei Liu         spec[1].tv_nsec = va_arg(ap, long);
449494a8ebeSWei Liu         retval = proxy_marshal(iovec, PROXY_HDR_SZ, "sqqqq", path,
450494a8ebeSWei Liu                                     spec[0].tv_sec, spec[1].tv_nsec,
451494a8ebeSWei Liu                                     spec[1].tv_sec, spec[1].tv_nsec);
452494a8ebeSWei Liu         if (retval > 0) {
453494a8ebeSWei Liu             header.size = retval;
454494a8ebeSWei Liu             header.type = T_UTIME;
455494a8ebeSWei Liu         }
456494a8ebeSWei Liu         break;
457494a8ebeSWei Liu     case T_RENAME:
458494a8ebeSWei Liu         oldpath = va_arg(ap, V9fsString *);
459494a8ebeSWei Liu         path = va_arg(ap, V9fsString *);
460494a8ebeSWei Liu         retval = proxy_marshal(iovec, PROXY_HDR_SZ, "ss", oldpath, path);
461494a8ebeSWei Liu         if (retval > 0) {
462494a8ebeSWei Liu             header.size = retval;
463494a8ebeSWei Liu             header.type = T_RENAME;
464494a8ebeSWei Liu         }
465494a8ebeSWei Liu         break;
466494a8ebeSWei Liu     case T_REMOVE:
467494a8ebeSWei Liu         path = va_arg(ap, V9fsString *);
468494a8ebeSWei Liu         retval = proxy_marshal(iovec, PROXY_HDR_SZ, "s", path);
469494a8ebeSWei Liu         if (retval > 0) {
470494a8ebeSWei Liu             header.size = retval;
471494a8ebeSWei Liu             header.type = T_REMOVE;
472494a8ebeSWei Liu         }
473494a8ebeSWei Liu         break;
474494a8ebeSWei Liu     case T_LGETXATTR:
475494a8ebeSWei Liu         size = va_arg(ap, int);
476494a8ebeSWei Liu         path = va_arg(ap, V9fsString *);
477494a8ebeSWei Liu         name = va_arg(ap, V9fsString *);
478494a8ebeSWei Liu         retval = proxy_marshal(iovec, PROXY_HDR_SZ,
479494a8ebeSWei Liu                                     "dss", size, path, name);
480494a8ebeSWei Liu         if (retval > 0) {
481494a8ebeSWei Liu             header.size = retval;
482494a8ebeSWei Liu             header.type = T_LGETXATTR;
483494a8ebeSWei Liu         }
484494a8ebeSWei Liu         break;
485494a8ebeSWei Liu     case T_LLISTXATTR:
486494a8ebeSWei Liu         size = va_arg(ap, int);
487494a8ebeSWei Liu         path = va_arg(ap, V9fsString *);
488494a8ebeSWei Liu         retval = proxy_marshal(iovec, PROXY_HDR_SZ, "ds", size, path);
489494a8ebeSWei Liu         if (retval > 0) {
490494a8ebeSWei Liu             header.size = retval;
491494a8ebeSWei Liu             header.type = T_LLISTXATTR;
492494a8ebeSWei Liu         }
493494a8ebeSWei Liu         break;
494494a8ebeSWei Liu     case T_LSETXATTR:
495494a8ebeSWei Liu         path = va_arg(ap, V9fsString *);
496494a8ebeSWei Liu         name = va_arg(ap, V9fsString *);
497494a8ebeSWei Liu         value = va_arg(ap, V9fsString *);
498494a8ebeSWei Liu         size = va_arg(ap, int);
499494a8ebeSWei Liu         flags = va_arg(ap, int);
500494a8ebeSWei Liu         retval = proxy_marshal(iovec, PROXY_HDR_SZ, "sssdd",
501494a8ebeSWei Liu                                     path, name, value, size, flags);
502494a8ebeSWei Liu         if (retval > 0) {
503494a8ebeSWei Liu             header.size = retval;
504494a8ebeSWei Liu             header.type = T_LSETXATTR;
505494a8ebeSWei Liu         }
506494a8ebeSWei Liu         break;
507494a8ebeSWei Liu     case T_LREMOVEXATTR:
508494a8ebeSWei Liu         path = va_arg(ap, V9fsString *);
509494a8ebeSWei Liu         name = va_arg(ap, V9fsString *);
510494a8ebeSWei Liu         retval = proxy_marshal(iovec, PROXY_HDR_SZ, "ss", path, name);
511494a8ebeSWei Liu         if (retval > 0) {
512494a8ebeSWei Liu             header.size = retval;
513494a8ebeSWei Liu             header.type = T_LREMOVEXATTR;
514494a8ebeSWei Liu         }
515494a8ebeSWei Liu         break;
516494a8ebeSWei Liu     case T_GETVERSION:
517494a8ebeSWei Liu         path = va_arg(ap, V9fsString *);
518494a8ebeSWei Liu         retval = proxy_marshal(iovec, PROXY_HDR_SZ, "s", path);
519494a8ebeSWei Liu         if (retval > 0) {
520494a8ebeSWei Liu             header.size = retval;
521494a8ebeSWei Liu             header.type = T_GETVERSION;
522494a8ebeSWei Liu         }
523494a8ebeSWei Liu         break;
524494a8ebeSWei Liu     default:
525494a8ebeSWei Liu         error_report("Invalid type %d", type);
526494a8ebeSWei Liu         retval = -EINVAL;
527494a8ebeSWei Liu         break;
528494a8ebeSWei Liu     }
529494a8ebeSWei Liu     va_end(ap);
530494a8ebeSWei Liu 
531494a8ebeSWei Liu     if (retval < 0) {
532494a8ebeSWei Liu         goto err_out;
533494a8ebeSWei Liu     }
534494a8ebeSWei Liu 
535494a8ebeSWei Liu     /* marshal the header details */
536494a8ebeSWei Liu     proxy_marshal(iovec, 0, "dd", header.type, header.size);
537494a8ebeSWei Liu     header.size += PROXY_HDR_SZ;
538494a8ebeSWei Liu 
539494a8ebeSWei Liu     retval = qemu_write_full(proxy->sockfd, iovec->iov_base, header.size);
540494a8ebeSWei Liu     if (retval != header.size) {
541494a8ebeSWei Liu         goto close_error;
542494a8ebeSWei Liu     }
543494a8ebeSWei Liu 
544494a8ebeSWei Liu     switch (type) {
545494a8ebeSWei Liu     case T_OPEN:
546494a8ebeSWei Liu     case T_CREATE:
547494a8ebeSWei Liu         /*
548494a8ebeSWei Liu          * A file descriptor is returned as response for
549494a8ebeSWei Liu          * T_OPEN,T_CREATE on success
550494a8ebeSWei Liu          */
551494a8ebeSWei Liu         if (v9fs_receivefd(proxy->sockfd, &retval) < 0) {
552494a8ebeSWei Liu             goto close_error;
553494a8ebeSWei Liu         }
554494a8ebeSWei Liu         break;
555494a8ebeSWei Liu     case T_MKNOD:
556494a8ebeSWei Liu     case T_MKDIR:
557494a8ebeSWei Liu     case T_SYMLINK:
558494a8ebeSWei Liu     case T_LINK:
559494a8ebeSWei Liu     case T_CHMOD:
560494a8ebeSWei Liu     case T_CHOWN:
561494a8ebeSWei Liu     case T_RENAME:
562494a8ebeSWei Liu     case T_TRUNCATE:
563494a8ebeSWei Liu     case T_UTIME:
564494a8ebeSWei Liu     case T_REMOVE:
565494a8ebeSWei Liu     case T_LSETXATTR:
566494a8ebeSWei Liu     case T_LREMOVEXATTR:
567494a8ebeSWei Liu         if (v9fs_receive_status(proxy, reply, &retval) < 0) {
568494a8ebeSWei Liu             goto close_error;
569494a8ebeSWei Liu         }
570494a8ebeSWei Liu         break;
571494a8ebeSWei Liu     case T_LSTAT:
572494a8ebeSWei Liu     case T_READLINK:
573494a8ebeSWei Liu     case T_STATFS:
574494a8ebeSWei Liu     case T_GETVERSION:
575494a8ebeSWei Liu         if (v9fs_receive_response(proxy, type, &retval, response) < 0) {
576494a8ebeSWei Liu             goto close_error;
577494a8ebeSWei Liu         }
578494a8ebeSWei Liu         break;
579494a8ebeSWei Liu     case T_LGETXATTR:
580494a8ebeSWei Liu     case T_LLISTXATTR:
581494a8ebeSWei Liu         if (!size) {
582494a8ebeSWei Liu             if (v9fs_receive_status(proxy, reply, &retval) < 0) {
583494a8ebeSWei Liu                 goto close_error;
584494a8ebeSWei Liu             }
585494a8ebeSWei Liu         } else {
586494a8ebeSWei Liu             if (v9fs_receive_response(proxy, type, &retval, response) < 0) {
587494a8ebeSWei Liu                 goto close_error;
588494a8ebeSWei Liu             }
589494a8ebeSWei Liu         }
590494a8ebeSWei Liu         break;
591494a8ebeSWei Liu     }
592494a8ebeSWei Liu 
593494a8ebeSWei Liu err_out:
594494a8ebeSWei Liu     qemu_mutex_unlock(&proxy->mutex);
595494a8ebeSWei Liu     return retval;
596494a8ebeSWei Liu 
597494a8ebeSWei Liu close_error:
598494a8ebeSWei Liu     close(proxy->sockfd);
599494a8ebeSWei Liu     proxy->sockfd = -1;
600494a8ebeSWei Liu     qemu_mutex_unlock(&proxy->mutex);
601494a8ebeSWei Liu     return -EIO;
602494a8ebeSWei Liu }
603494a8ebeSWei Liu 
604494a8ebeSWei Liu static int proxy_lstat(FsContext *fs_ctx, V9fsPath *fs_path, struct stat *stbuf)
605494a8ebeSWei Liu {
606494a8ebeSWei Liu     int retval;
607494a8ebeSWei Liu     retval = v9fs_request(fs_ctx->private, T_LSTAT, stbuf, "s", fs_path);
608494a8ebeSWei Liu     if (retval < 0) {
609494a8ebeSWei Liu         errno = -retval;
610494a8ebeSWei Liu         return -1;
611494a8ebeSWei Liu     }
612494a8ebeSWei Liu     return retval;
613494a8ebeSWei Liu }
614494a8ebeSWei Liu 
615494a8ebeSWei Liu static ssize_t proxy_readlink(FsContext *fs_ctx, V9fsPath *fs_path,
616494a8ebeSWei Liu                               char *buf, size_t bufsz)
617494a8ebeSWei Liu {
618494a8ebeSWei Liu     int retval;
619494a8ebeSWei Liu     retval = v9fs_request(fs_ctx->private, T_READLINK, buf, "sd",
620494a8ebeSWei Liu                           fs_path, bufsz);
621494a8ebeSWei Liu     if (retval < 0) {
622494a8ebeSWei Liu         errno = -retval;
623494a8ebeSWei Liu         return -1;
624494a8ebeSWei Liu     }
625494a8ebeSWei Liu     return strlen(buf);
626494a8ebeSWei Liu }
627494a8ebeSWei Liu 
628494a8ebeSWei Liu static int proxy_close(FsContext *ctx, V9fsFidOpenState *fs)
629494a8ebeSWei Liu {
630494a8ebeSWei Liu     return close(fs->fd);
631494a8ebeSWei Liu }
632494a8ebeSWei Liu 
633494a8ebeSWei Liu static int proxy_closedir(FsContext *ctx, V9fsFidOpenState *fs)
634494a8ebeSWei Liu {
635494a8ebeSWei Liu     return closedir(fs->dir);
636494a8ebeSWei Liu }
637494a8ebeSWei Liu 
638494a8ebeSWei Liu static int proxy_open(FsContext *ctx, V9fsPath *fs_path,
639494a8ebeSWei Liu                       int flags, V9fsFidOpenState *fs)
640494a8ebeSWei Liu {
641494a8ebeSWei Liu     fs->fd = v9fs_request(ctx->private, T_OPEN, NULL, "sd", fs_path, flags);
642494a8ebeSWei Liu     if (fs->fd < 0) {
643494a8ebeSWei Liu         errno = -fs->fd;
644494a8ebeSWei Liu         fs->fd = -1;
645494a8ebeSWei Liu     }
646494a8ebeSWei Liu     return fs->fd;
647494a8ebeSWei Liu }
648494a8ebeSWei Liu 
649494a8ebeSWei Liu static int proxy_opendir(FsContext *ctx,
650494a8ebeSWei Liu                          V9fsPath *fs_path, V9fsFidOpenState *fs)
651494a8ebeSWei Liu {
652494a8ebeSWei Liu     int serrno, fd;
653494a8ebeSWei Liu 
654494a8ebeSWei Liu     fs->dir = NULL;
655494a8ebeSWei Liu     fd = v9fs_request(ctx->private, T_OPEN, NULL, "sd", fs_path, O_DIRECTORY);
656494a8ebeSWei Liu     if (fd < 0) {
657494a8ebeSWei Liu         errno = -fd;
658494a8ebeSWei Liu         return -1;
659494a8ebeSWei Liu     }
660494a8ebeSWei Liu     fs->dir = fdopendir(fd);
661494a8ebeSWei Liu     if (!fs->dir) {
662494a8ebeSWei Liu         serrno = errno;
663494a8ebeSWei Liu         close(fd);
664494a8ebeSWei Liu         errno = serrno;
665494a8ebeSWei Liu         return -1;
666494a8ebeSWei Liu     }
667494a8ebeSWei Liu     return 0;
668494a8ebeSWei Liu }
669494a8ebeSWei Liu 
670494a8ebeSWei Liu static void proxy_rewinddir(FsContext *ctx, V9fsFidOpenState *fs)
671494a8ebeSWei Liu {
672494a8ebeSWei Liu     rewinddir(fs->dir);
673494a8ebeSWei Liu }
674494a8ebeSWei Liu 
675494a8ebeSWei Liu static off_t proxy_telldir(FsContext *ctx, V9fsFidOpenState *fs)
676494a8ebeSWei Liu {
677494a8ebeSWei Liu     return telldir(fs->dir);
678494a8ebeSWei Liu }
679494a8ebeSWei Liu 
680494a8ebeSWei Liu static int proxy_readdir_r(FsContext *ctx, V9fsFidOpenState *fs,
681494a8ebeSWei Liu                            struct dirent *entry,
682494a8ebeSWei Liu                            struct dirent **result)
683494a8ebeSWei Liu {
684494a8ebeSWei Liu     return readdir_r(fs->dir, entry, result);
685494a8ebeSWei Liu }
686494a8ebeSWei Liu 
687494a8ebeSWei Liu static void proxy_seekdir(FsContext *ctx, V9fsFidOpenState *fs, off_t off)
688494a8ebeSWei Liu {
689494a8ebeSWei Liu     seekdir(fs->dir, off);
690494a8ebeSWei Liu }
691494a8ebeSWei Liu 
692494a8ebeSWei Liu static ssize_t proxy_preadv(FsContext *ctx, V9fsFidOpenState *fs,
693494a8ebeSWei Liu                             const struct iovec *iov,
694494a8ebeSWei Liu                             int iovcnt, off_t offset)
695494a8ebeSWei Liu {
696494a8ebeSWei Liu     ssize_t ret;
697494a8ebeSWei Liu #ifdef CONFIG_PREADV
698494a8ebeSWei Liu     ret = preadv(fs->fd, iov, iovcnt, offset);
699494a8ebeSWei Liu #else
700494a8ebeSWei Liu     ret = lseek(fs->fd, offset, SEEK_SET);
701494a8ebeSWei Liu     if (ret >= 0) {
702494a8ebeSWei Liu         ret = readv(fs->fd, iov, iovcnt);
703494a8ebeSWei Liu     }
704494a8ebeSWei Liu #endif
705494a8ebeSWei Liu     return ret;
706494a8ebeSWei Liu }
707494a8ebeSWei Liu 
708494a8ebeSWei Liu static ssize_t proxy_pwritev(FsContext *ctx, V9fsFidOpenState *fs,
709494a8ebeSWei Liu                              const struct iovec *iov,
710494a8ebeSWei Liu                              int iovcnt, off_t offset)
711494a8ebeSWei Liu {
712494a8ebeSWei Liu     ssize_t ret;
713494a8ebeSWei Liu 
714494a8ebeSWei Liu #ifdef CONFIG_PREADV
715494a8ebeSWei Liu     ret = pwritev(fs->fd, iov, iovcnt, offset);
716494a8ebeSWei Liu #else
717494a8ebeSWei Liu     ret = lseek(fs->fd, offset, SEEK_SET);
718494a8ebeSWei Liu     if (ret >= 0) {
719494a8ebeSWei Liu         ret = writev(fs->fd, iov, iovcnt);
720494a8ebeSWei Liu     }
721494a8ebeSWei Liu #endif
722494a8ebeSWei Liu #ifdef CONFIG_SYNC_FILE_RANGE
723494a8ebeSWei Liu     if (ret > 0 && ctx->export_flags & V9FS_IMMEDIATE_WRITEOUT) {
724494a8ebeSWei Liu         /*
725494a8ebeSWei Liu          * Initiate a writeback. This is not a data integrity sync.
726494a8ebeSWei Liu          * We want to ensure that we don't leave dirty pages in the cache
727494a8ebeSWei Liu          * after write when writeout=immediate is sepcified.
728494a8ebeSWei Liu          */
729494a8ebeSWei Liu         sync_file_range(fs->fd, offset, ret,
730494a8ebeSWei Liu                         SYNC_FILE_RANGE_WAIT_BEFORE | SYNC_FILE_RANGE_WRITE);
731494a8ebeSWei Liu     }
732494a8ebeSWei Liu #endif
733494a8ebeSWei Liu     return ret;
734494a8ebeSWei Liu }
735494a8ebeSWei Liu 
736494a8ebeSWei Liu static int proxy_chmod(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp)
737494a8ebeSWei Liu {
738494a8ebeSWei Liu     int retval;
739494a8ebeSWei Liu     retval = v9fs_request(fs_ctx->private, T_CHMOD, NULL, "sd",
740494a8ebeSWei Liu                           fs_path, credp->fc_mode);
741494a8ebeSWei Liu     if (retval < 0) {
742494a8ebeSWei Liu         errno = -retval;
743494a8ebeSWei Liu     }
744494a8ebeSWei Liu     return retval;
745494a8ebeSWei Liu }
746494a8ebeSWei Liu 
747494a8ebeSWei Liu static int proxy_mknod(FsContext *fs_ctx, V9fsPath *dir_path,
748494a8ebeSWei Liu                        const char *name, FsCred *credp)
749494a8ebeSWei Liu {
750494a8ebeSWei Liu     int retval;
751494a8ebeSWei Liu     V9fsString fullname;
752494a8ebeSWei Liu 
753494a8ebeSWei Liu     v9fs_string_init(&fullname);
754494a8ebeSWei Liu     v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name);
755494a8ebeSWei Liu 
756494a8ebeSWei Liu     retval = v9fs_request(fs_ctx->private, T_MKNOD, NULL, "sdqdd",
757494a8ebeSWei Liu                           &fullname, credp->fc_mode, credp->fc_rdev,
758494a8ebeSWei Liu                           credp->fc_uid, credp->fc_gid);
759494a8ebeSWei Liu     v9fs_string_free(&fullname);
760494a8ebeSWei Liu     if (retval < 0) {
761494a8ebeSWei Liu         errno = -retval;
762494a8ebeSWei Liu         retval = -1;
763494a8ebeSWei Liu     }
764494a8ebeSWei Liu     return retval;
765494a8ebeSWei Liu }
766494a8ebeSWei Liu 
767494a8ebeSWei Liu static int proxy_mkdir(FsContext *fs_ctx, V9fsPath *dir_path,
768494a8ebeSWei Liu                        const char *name, FsCred *credp)
769494a8ebeSWei Liu {
770494a8ebeSWei Liu     int retval;
771494a8ebeSWei Liu     V9fsString fullname;
772494a8ebeSWei Liu 
773494a8ebeSWei Liu     v9fs_string_init(&fullname);
774494a8ebeSWei Liu     v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name);
775494a8ebeSWei Liu 
776494a8ebeSWei Liu     retval = v9fs_request(fs_ctx->private, T_MKDIR, NULL, "sddd", &fullname,
777494a8ebeSWei Liu                           credp->fc_mode, credp->fc_uid, credp->fc_gid);
778494a8ebeSWei Liu     v9fs_string_free(&fullname);
779494a8ebeSWei Liu     if (retval < 0) {
780494a8ebeSWei Liu         errno = -retval;
781494a8ebeSWei Liu         retval = -1;
782494a8ebeSWei Liu     }
783494a8ebeSWei Liu     v9fs_string_free(&fullname);
784494a8ebeSWei Liu     return retval;
785494a8ebeSWei Liu }
786494a8ebeSWei Liu 
787494a8ebeSWei Liu static int proxy_fstat(FsContext *fs_ctx, int fid_type,
788494a8ebeSWei Liu                        V9fsFidOpenState *fs, struct stat *stbuf)
789494a8ebeSWei Liu {
790494a8ebeSWei Liu     int fd;
791494a8ebeSWei Liu 
792494a8ebeSWei Liu     if (fid_type == P9_FID_DIR) {
793494a8ebeSWei Liu         fd = dirfd(fs->dir);
794494a8ebeSWei Liu     } else {
795494a8ebeSWei Liu         fd = fs->fd;
796494a8ebeSWei Liu     }
797494a8ebeSWei Liu     return fstat(fd, stbuf);
798494a8ebeSWei Liu }
799494a8ebeSWei Liu 
800494a8ebeSWei Liu static int proxy_open2(FsContext *fs_ctx, V9fsPath *dir_path, const char *name,
801494a8ebeSWei Liu                        int flags, FsCred *credp, V9fsFidOpenState *fs)
802494a8ebeSWei Liu {
803494a8ebeSWei Liu     V9fsString fullname;
804494a8ebeSWei Liu 
805494a8ebeSWei Liu     v9fs_string_init(&fullname);
806494a8ebeSWei Liu     v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name);
807494a8ebeSWei Liu 
808494a8ebeSWei Liu     fs->fd = v9fs_request(fs_ctx->private, T_CREATE, NULL, "sdddd",
809494a8ebeSWei Liu                           &fullname, flags, credp->fc_mode,
810494a8ebeSWei Liu                           credp->fc_uid, credp->fc_gid);
811494a8ebeSWei Liu     v9fs_string_free(&fullname);
812494a8ebeSWei Liu     if (fs->fd < 0) {
813494a8ebeSWei Liu         errno = -fs->fd;
814494a8ebeSWei Liu         fs->fd = -1;
815494a8ebeSWei Liu     }
816494a8ebeSWei Liu     return fs->fd;
817494a8ebeSWei Liu }
818494a8ebeSWei Liu 
819494a8ebeSWei Liu static int proxy_symlink(FsContext *fs_ctx, const char *oldpath,
820494a8ebeSWei Liu                          V9fsPath *dir_path, const char *name, FsCred *credp)
821494a8ebeSWei Liu {
822494a8ebeSWei Liu     int retval;
823494a8ebeSWei Liu     V9fsString fullname, target;
824494a8ebeSWei Liu 
825494a8ebeSWei Liu     v9fs_string_init(&fullname);
826494a8ebeSWei Liu     v9fs_string_init(&target);
827494a8ebeSWei Liu 
828494a8ebeSWei Liu     v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name);
829494a8ebeSWei Liu     v9fs_string_sprintf(&target, "%s", oldpath);
830494a8ebeSWei Liu 
831494a8ebeSWei Liu     retval = v9fs_request(fs_ctx->private, T_SYMLINK, NULL, "ssdd",
832494a8ebeSWei Liu                           &target, &fullname, credp->fc_uid, credp->fc_gid);
833494a8ebeSWei Liu     v9fs_string_free(&fullname);
834494a8ebeSWei Liu     v9fs_string_free(&target);
835494a8ebeSWei Liu     if (retval < 0) {
836494a8ebeSWei Liu         errno = -retval;
837494a8ebeSWei Liu         retval = -1;
838494a8ebeSWei Liu     }
839494a8ebeSWei Liu     return retval;
840494a8ebeSWei Liu }
841494a8ebeSWei Liu 
842494a8ebeSWei Liu static int proxy_link(FsContext *ctx, V9fsPath *oldpath,
843494a8ebeSWei Liu                       V9fsPath *dirpath, const char *name)
844494a8ebeSWei Liu {
845494a8ebeSWei Liu     int retval;
846494a8ebeSWei Liu     V9fsString newpath;
847494a8ebeSWei Liu 
848494a8ebeSWei Liu     v9fs_string_init(&newpath);
849494a8ebeSWei Liu     v9fs_string_sprintf(&newpath, "%s/%s", dirpath->data, name);
850494a8ebeSWei Liu 
851494a8ebeSWei Liu     retval = v9fs_request(ctx->private, T_LINK, NULL, "ss", oldpath, &newpath);
852494a8ebeSWei Liu     v9fs_string_free(&newpath);
853494a8ebeSWei Liu     if (retval < 0) {
854494a8ebeSWei Liu         errno = -retval;
855494a8ebeSWei Liu         retval = -1;
856494a8ebeSWei Liu     }
857494a8ebeSWei Liu     return retval;
858494a8ebeSWei Liu }
859494a8ebeSWei Liu 
860494a8ebeSWei Liu static int proxy_truncate(FsContext *ctx, V9fsPath *fs_path, off_t size)
861494a8ebeSWei Liu {
862494a8ebeSWei Liu     int retval;
863494a8ebeSWei Liu 
864494a8ebeSWei Liu     retval = v9fs_request(ctx->private, T_TRUNCATE, NULL, "sq", fs_path, size);
865494a8ebeSWei Liu     if (retval < 0) {
866494a8ebeSWei Liu         errno = -retval;
867494a8ebeSWei Liu         return -1;
868494a8ebeSWei Liu     }
869494a8ebeSWei Liu     return 0;
870494a8ebeSWei Liu }
871494a8ebeSWei Liu 
872494a8ebeSWei Liu static int proxy_rename(FsContext *ctx, const char *oldpath,
873494a8ebeSWei Liu                         const char *newpath)
874494a8ebeSWei Liu {
875494a8ebeSWei Liu     int retval;
876494a8ebeSWei Liu     V9fsString oldname, newname;
877494a8ebeSWei Liu 
878494a8ebeSWei Liu     v9fs_string_init(&oldname);
879494a8ebeSWei Liu     v9fs_string_init(&newname);
880494a8ebeSWei Liu 
881494a8ebeSWei Liu     v9fs_string_sprintf(&oldname, "%s", oldpath);
882494a8ebeSWei Liu     v9fs_string_sprintf(&newname, "%s", newpath);
883494a8ebeSWei Liu     retval = v9fs_request(ctx->private, T_RENAME, NULL, "ss",
884494a8ebeSWei Liu                           &oldname, &newname);
885494a8ebeSWei Liu     v9fs_string_free(&oldname);
886494a8ebeSWei Liu     v9fs_string_free(&newname);
887494a8ebeSWei Liu     if (retval < 0) {
888494a8ebeSWei Liu         errno = -retval;
889494a8ebeSWei Liu     }
890494a8ebeSWei Liu     return retval;
891494a8ebeSWei Liu }
892494a8ebeSWei Liu 
893494a8ebeSWei Liu static int proxy_chown(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp)
894494a8ebeSWei Liu {
895494a8ebeSWei Liu     int retval;
896494a8ebeSWei Liu     retval = v9fs_request(fs_ctx->private, T_CHOWN, NULL, "sdd",
897494a8ebeSWei Liu                           fs_path, credp->fc_uid, credp->fc_gid);
898494a8ebeSWei Liu     if (retval < 0) {
899494a8ebeSWei Liu         errno = -retval;
900494a8ebeSWei Liu     }
901494a8ebeSWei Liu     return retval;
902494a8ebeSWei Liu }
903494a8ebeSWei Liu 
904494a8ebeSWei Liu static int proxy_utimensat(FsContext *s, V9fsPath *fs_path,
905494a8ebeSWei Liu                            const struct timespec *buf)
906494a8ebeSWei Liu {
907494a8ebeSWei Liu     int retval;
908494a8ebeSWei Liu     retval = v9fs_request(s->private, T_UTIME, NULL, "sqqqq",
909494a8ebeSWei Liu                           fs_path,
910494a8ebeSWei Liu                           buf[0].tv_sec, buf[0].tv_nsec,
911494a8ebeSWei Liu                           buf[1].tv_sec, buf[1].tv_nsec);
912494a8ebeSWei Liu     if (retval < 0) {
913494a8ebeSWei Liu         errno = -retval;
914494a8ebeSWei Liu     }
915494a8ebeSWei Liu     return retval;
916494a8ebeSWei Liu }
917494a8ebeSWei Liu 
918494a8ebeSWei Liu static int proxy_remove(FsContext *ctx, const char *path)
919494a8ebeSWei Liu {
920494a8ebeSWei Liu     int retval;
921494a8ebeSWei Liu     V9fsString name;
922494a8ebeSWei Liu     v9fs_string_init(&name);
923494a8ebeSWei Liu     v9fs_string_sprintf(&name, "%s", path);
924494a8ebeSWei Liu     retval = v9fs_request(ctx->private, T_REMOVE, NULL, "s", &name);
925494a8ebeSWei Liu     v9fs_string_free(&name);
926494a8ebeSWei Liu     if (retval < 0) {
927494a8ebeSWei Liu         errno = -retval;
928494a8ebeSWei Liu     }
929494a8ebeSWei Liu     return retval;
930494a8ebeSWei Liu }
931494a8ebeSWei Liu 
932494a8ebeSWei Liu static int proxy_fsync(FsContext *ctx, int fid_type,
933494a8ebeSWei Liu                        V9fsFidOpenState *fs, int datasync)
934494a8ebeSWei Liu {
935494a8ebeSWei Liu     int fd;
936494a8ebeSWei Liu 
937494a8ebeSWei Liu     if (fid_type == P9_FID_DIR) {
938494a8ebeSWei Liu         fd = dirfd(fs->dir);
939494a8ebeSWei Liu     } else {
940494a8ebeSWei Liu         fd = fs->fd;
941494a8ebeSWei Liu     }
942494a8ebeSWei Liu 
943494a8ebeSWei Liu     if (datasync) {
944494a8ebeSWei Liu         return qemu_fdatasync(fd);
945494a8ebeSWei Liu     } else {
946494a8ebeSWei Liu         return fsync(fd);
947494a8ebeSWei Liu     }
948494a8ebeSWei Liu }
949494a8ebeSWei Liu 
950494a8ebeSWei Liu static int proxy_statfs(FsContext *s, V9fsPath *fs_path, struct statfs *stbuf)
951494a8ebeSWei Liu {
952494a8ebeSWei Liu     int retval;
953494a8ebeSWei Liu     retval = v9fs_request(s->private, T_STATFS, stbuf, "s", fs_path);
954494a8ebeSWei Liu     if (retval < 0) {
955494a8ebeSWei Liu         errno = -retval;
956494a8ebeSWei Liu         return -1;
957494a8ebeSWei Liu     }
958494a8ebeSWei Liu     return retval;
959494a8ebeSWei Liu }
960494a8ebeSWei Liu 
961494a8ebeSWei Liu static ssize_t proxy_lgetxattr(FsContext *ctx, V9fsPath *fs_path,
962494a8ebeSWei Liu                                const char *name, void *value, size_t size)
963494a8ebeSWei Liu {
964494a8ebeSWei Liu     int retval;
965494a8ebeSWei Liu     V9fsString xname;
966494a8ebeSWei Liu 
967494a8ebeSWei Liu     v9fs_string_init(&xname);
968494a8ebeSWei Liu     v9fs_string_sprintf(&xname, "%s", name);
969494a8ebeSWei Liu     retval = v9fs_request(ctx->private, T_LGETXATTR, value, "dss", size,
970494a8ebeSWei Liu                           fs_path, &xname);
971494a8ebeSWei Liu     v9fs_string_free(&xname);
972494a8ebeSWei Liu     if (retval < 0) {
973494a8ebeSWei Liu         errno = -retval;
974494a8ebeSWei Liu     }
975494a8ebeSWei Liu     return retval;
976494a8ebeSWei Liu }
977494a8ebeSWei Liu 
978494a8ebeSWei Liu static ssize_t proxy_llistxattr(FsContext *ctx, V9fsPath *fs_path,
979494a8ebeSWei Liu                                 void *value, size_t size)
980494a8ebeSWei Liu {
981494a8ebeSWei Liu     int retval;
982494a8ebeSWei Liu     retval = v9fs_request(ctx->private, T_LLISTXATTR, value, "ds", size,
983494a8ebeSWei Liu                         fs_path);
984494a8ebeSWei Liu     if (retval < 0) {
985494a8ebeSWei Liu         errno = -retval;
986494a8ebeSWei Liu     }
987494a8ebeSWei Liu     return retval;
988494a8ebeSWei Liu }
989494a8ebeSWei Liu 
990494a8ebeSWei Liu static int proxy_lsetxattr(FsContext *ctx, V9fsPath *fs_path, const char *name,
991494a8ebeSWei Liu                            void *value, size_t size, int flags)
992494a8ebeSWei Liu {
993494a8ebeSWei Liu     int retval;
994494a8ebeSWei Liu     V9fsString xname, xvalue;
995494a8ebeSWei Liu 
996494a8ebeSWei Liu     v9fs_string_init(&xname);
997494a8ebeSWei Liu     v9fs_string_sprintf(&xname, "%s", name);
998494a8ebeSWei Liu 
999494a8ebeSWei Liu     v9fs_string_init(&xvalue);
1000494a8ebeSWei Liu     xvalue.size = size;
1001494a8ebeSWei Liu     xvalue.data = g_malloc(size);
1002494a8ebeSWei Liu     memcpy(xvalue.data, value, size);
1003494a8ebeSWei Liu 
1004494a8ebeSWei Liu     retval = v9fs_request(ctx->private, T_LSETXATTR, value, "sssdd",
1005494a8ebeSWei Liu                           fs_path, &xname, &xvalue, size, flags);
1006494a8ebeSWei Liu     v9fs_string_free(&xname);
1007494a8ebeSWei Liu     v9fs_string_free(&xvalue);
1008494a8ebeSWei Liu     if (retval < 0) {
1009494a8ebeSWei Liu         errno = -retval;
1010494a8ebeSWei Liu     }
1011494a8ebeSWei Liu     return retval;
1012494a8ebeSWei Liu }
1013494a8ebeSWei Liu 
1014494a8ebeSWei Liu static int proxy_lremovexattr(FsContext *ctx, V9fsPath *fs_path,
1015494a8ebeSWei Liu                               const char *name)
1016494a8ebeSWei Liu {
1017494a8ebeSWei Liu     int retval;
1018494a8ebeSWei Liu     V9fsString xname;
1019494a8ebeSWei Liu 
1020494a8ebeSWei Liu     v9fs_string_init(&xname);
1021494a8ebeSWei Liu     v9fs_string_sprintf(&xname, "%s", name);
1022494a8ebeSWei Liu     retval = v9fs_request(ctx->private, T_LREMOVEXATTR, NULL, "ss",
1023494a8ebeSWei Liu                           fs_path, &xname);
1024494a8ebeSWei Liu     v9fs_string_free(&xname);
1025494a8ebeSWei Liu     if (retval < 0) {
1026494a8ebeSWei Liu         errno = -retval;
1027494a8ebeSWei Liu     }
1028494a8ebeSWei Liu     return retval;
1029494a8ebeSWei Liu }
1030494a8ebeSWei Liu 
1031494a8ebeSWei Liu static int proxy_name_to_path(FsContext *ctx, V9fsPath *dir_path,
1032494a8ebeSWei Liu                               const char *name, V9fsPath *target)
1033494a8ebeSWei Liu {
1034494a8ebeSWei Liu     if (dir_path) {
1035494a8ebeSWei Liu         v9fs_string_sprintf((V9fsString *)target, "%s/%s",
1036494a8ebeSWei Liu                             dir_path->data, name);
1037494a8ebeSWei Liu     } else {
1038494a8ebeSWei Liu         v9fs_string_sprintf((V9fsString *)target, "%s", name);
1039494a8ebeSWei Liu     }
1040494a8ebeSWei Liu     /* Bump the size for including terminating NULL */
1041494a8ebeSWei Liu     target->size++;
1042494a8ebeSWei Liu     return 0;
1043494a8ebeSWei Liu }
1044494a8ebeSWei Liu 
1045494a8ebeSWei Liu static int proxy_renameat(FsContext *ctx, V9fsPath *olddir,
1046494a8ebeSWei Liu                           const char *old_name, V9fsPath *newdir,
1047494a8ebeSWei Liu                           const char *new_name)
1048494a8ebeSWei Liu {
1049494a8ebeSWei Liu     int ret;
1050494a8ebeSWei Liu     V9fsString old_full_name, new_full_name;
1051494a8ebeSWei Liu 
1052494a8ebeSWei Liu     v9fs_string_init(&old_full_name);
1053494a8ebeSWei Liu     v9fs_string_init(&new_full_name);
1054494a8ebeSWei Liu 
1055494a8ebeSWei Liu     v9fs_string_sprintf(&old_full_name, "%s/%s", olddir->data, old_name);
1056494a8ebeSWei Liu     v9fs_string_sprintf(&new_full_name, "%s/%s", newdir->data, new_name);
1057494a8ebeSWei Liu 
1058494a8ebeSWei Liu     ret = proxy_rename(ctx, old_full_name.data, new_full_name.data);
1059494a8ebeSWei Liu     v9fs_string_free(&old_full_name);
1060494a8ebeSWei Liu     v9fs_string_free(&new_full_name);
1061494a8ebeSWei Liu     return ret;
1062494a8ebeSWei Liu }
1063494a8ebeSWei Liu 
1064494a8ebeSWei Liu static int proxy_unlinkat(FsContext *ctx, V9fsPath *dir,
1065494a8ebeSWei Liu                           const char *name, int flags)
1066494a8ebeSWei Liu {
1067494a8ebeSWei Liu     int ret;
1068494a8ebeSWei Liu     V9fsString fullname;
1069494a8ebeSWei Liu     v9fs_string_init(&fullname);
1070494a8ebeSWei Liu 
1071494a8ebeSWei Liu     v9fs_string_sprintf(&fullname, "%s/%s", dir->data, name);
1072494a8ebeSWei Liu     ret = proxy_remove(ctx, fullname.data);
1073494a8ebeSWei Liu     v9fs_string_free(&fullname);
1074494a8ebeSWei Liu 
1075494a8ebeSWei Liu     return ret;
1076494a8ebeSWei Liu }
1077494a8ebeSWei Liu 
1078494a8ebeSWei Liu static int proxy_ioc_getversion(FsContext *fs_ctx, V9fsPath *path,
1079494a8ebeSWei Liu                                 mode_t st_mode, uint64_t *st_gen)
1080494a8ebeSWei Liu {
1081494a8ebeSWei Liu     int err;
1082494a8ebeSWei Liu 
1083494a8ebeSWei Liu     /* Do not try to open special files like device nodes, fifos etc
1084494a8ebeSWei Liu      * we can get fd for regular files and directories only
1085494a8ebeSWei Liu      */
1086494a8ebeSWei Liu     if (!S_ISREG(st_mode) && !S_ISDIR(st_mode)) {
1087494a8ebeSWei Liu         errno = ENOTTY;
1088494a8ebeSWei Liu         return -1;
1089494a8ebeSWei Liu     }
1090494a8ebeSWei Liu     err = v9fs_request(fs_ctx->private, T_GETVERSION, st_gen, "s", path);
1091494a8ebeSWei Liu     if (err < 0) {
1092494a8ebeSWei Liu         errno = -err;
1093494a8ebeSWei Liu         err = -1;
1094494a8ebeSWei Liu     }
1095494a8ebeSWei Liu     return err;
1096494a8ebeSWei Liu }
1097494a8ebeSWei Liu 
1098494a8ebeSWei Liu static int connect_namedsocket(const char *path)
1099494a8ebeSWei Liu {
1100494a8ebeSWei Liu     int sockfd, size;
1101494a8ebeSWei Liu     struct sockaddr_un helper;
1102494a8ebeSWei Liu 
1103494a8ebeSWei Liu     if (strlen(path) >= sizeof(helper.sun_path)) {
110463325b18SGreg Kurz         error_report("Socket name too long");
1105494a8ebeSWei Liu         return -1;
1106494a8ebeSWei Liu     }
1107494a8ebeSWei Liu     sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
1108494a8ebeSWei Liu     if (sockfd < 0) {
110963325b18SGreg Kurz         error_report("Failed to create socket: %s", strerror(errno));
1110494a8ebeSWei Liu         return -1;
1111494a8ebeSWei Liu     }
1112494a8ebeSWei Liu     strcpy(helper.sun_path, path);
1113494a8ebeSWei Liu     helper.sun_family = AF_UNIX;
1114494a8ebeSWei Liu     size = strlen(helper.sun_path) + sizeof(helper.sun_family);
1115494a8ebeSWei Liu     if (connect(sockfd, (struct sockaddr *)&helper, size) < 0) {
111663325b18SGreg Kurz         error_report("Failed to connect to %s: %s", path, strerror(errno));
1117494a8ebeSWei Liu         close(sockfd);
1118494a8ebeSWei Liu         return -1;
1119494a8ebeSWei Liu     }
1120494a8ebeSWei Liu 
1121494a8ebeSWei Liu     /* remove the socket for security reasons */
1122494a8ebeSWei Liu     unlink(path);
1123494a8ebeSWei Liu     return sockfd;
1124494a8ebeSWei Liu }
1125494a8ebeSWei Liu 
1126494a8ebeSWei Liu static int proxy_parse_opts(QemuOpts *opts, struct FsDriverEntry *fs)
1127494a8ebeSWei Liu {
1128494a8ebeSWei Liu     const char *socket = qemu_opt_get(opts, "socket");
1129494a8ebeSWei Liu     const char *sock_fd = qemu_opt_get(opts, "sock_fd");
1130494a8ebeSWei Liu 
1131494a8ebeSWei Liu     if (!socket && !sock_fd) {
113263325b18SGreg Kurz         error_report("Must specify either socket or sock_fd");
1133494a8ebeSWei Liu         return -1;
1134494a8ebeSWei Liu     }
1135494a8ebeSWei Liu     if (socket && sock_fd) {
113663325b18SGreg Kurz         error_report("Both socket and sock_fd options specified");
1137494a8ebeSWei Liu         return -1;
1138494a8ebeSWei Liu     }
1139494a8ebeSWei Liu     if (socket) {
1140494a8ebeSWei Liu         fs->path = g_strdup(socket);
1141494a8ebeSWei Liu         fs->export_flags = V9FS_PROXY_SOCK_NAME;
1142494a8ebeSWei Liu     } else {
1143494a8ebeSWei Liu         fs->path = g_strdup(sock_fd);
1144494a8ebeSWei Liu         fs->export_flags = V9FS_PROXY_SOCK_FD;
1145494a8ebeSWei Liu     }
1146494a8ebeSWei Liu     return 0;
1147494a8ebeSWei Liu }
1148494a8ebeSWei Liu 
1149494a8ebeSWei Liu static int proxy_init(FsContext *ctx)
1150494a8ebeSWei Liu {
1151494a8ebeSWei Liu     V9fsProxy *proxy = g_malloc(sizeof(V9fsProxy));
1152494a8ebeSWei Liu     int sock_id;
1153494a8ebeSWei Liu 
1154494a8ebeSWei Liu     if (ctx->export_flags & V9FS_PROXY_SOCK_NAME) {
1155494a8ebeSWei Liu         sock_id = connect_namedsocket(ctx->fs_root);
1156494a8ebeSWei Liu     } else {
1157494a8ebeSWei Liu         sock_id = atoi(ctx->fs_root);
1158494a8ebeSWei Liu         if (sock_id < 0) {
115963325b18SGreg Kurz             error_report("Socket descriptor not initialized");
1160494a8ebeSWei Liu         }
1161494a8ebeSWei Liu     }
1162494a8ebeSWei Liu     if (sock_id < 0) {
1163494a8ebeSWei Liu         g_free(proxy);
1164494a8ebeSWei Liu         return -1;
1165494a8ebeSWei Liu     }
1166494a8ebeSWei Liu     g_free(ctx->fs_root);
1167494a8ebeSWei Liu     ctx->fs_root = NULL;
1168494a8ebeSWei Liu 
1169494a8ebeSWei Liu     proxy->in_iovec.iov_base  = g_malloc(PROXY_MAX_IO_SZ + PROXY_HDR_SZ);
1170494a8ebeSWei Liu     proxy->in_iovec.iov_len   = PROXY_MAX_IO_SZ + PROXY_HDR_SZ;
1171494a8ebeSWei Liu     proxy->out_iovec.iov_base = g_malloc(PROXY_MAX_IO_SZ + PROXY_HDR_SZ);
1172494a8ebeSWei Liu     proxy->out_iovec.iov_len  = PROXY_MAX_IO_SZ + PROXY_HDR_SZ;
1173494a8ebeSWei Liu 
1174494a8ebeSWei Liu     ctx->private = proxy;
1175494a8ebeSWei Liu     proxy->sockfd = sock_id;
1176494a8ebeSWei Liu     qemu_mutex_init(&proxy->mutex);
1177494a8ebeSWei Liu 
1178494a8ebeSWei Liu     ctx->export_flags |= V9FS_PATHNAME_FSCONTEXT;
1179494a8ebeSWei Liu     ctx->exops.get_st_gen = proxy_ioc_getversion;
1180494a8ebeSWei Liu     return 0;
1181494a8ebeSWei Liu }
1182494a8ebeSWei Liu 
1183494a8ebeSWei Liu FileOperations proxy_ops = {
1184494a8ebeSWei Liu     .parse_opts   = proxy_parse_opts,
1185494a8ebeSWei Liu     .init         = proxy_init,
1186494a8ebeSWei Liu     .lstat        = proxy_lstat,
1187494a8ebeSWei Liu     .readlink     = proxy_readlink,
1188494a8ebeSWei Liu     .close        = proxy_close,
1189494a8ebeSWei Liu     .closedir     = proxy_closedir,
1190494a8ebeSWei Liu     .open         = proxy_open,
1191494a8ebeSWei Liu     .opendir      = proxy_opendir,
1192494a8ebeSWei Liu     .rewinddir    = proxy_rewinddir,
1193494a8ebeSWei Liu     .telldir      = proxy_telldir,
1194494a8ebeSWei Liu     .readdir_r    = proxy_readdir_r,
1195494a8ebeSWei Liu     .seekdir      = proxy_seekdir,
1196494a8ebeSWei Liu     .preadv       = proxy_preadv,
1197494a8ebeSWei Liu     .pwritev      = proxy_pwritev,
1198494a8ebeSWei Liu     .chmod        = proxy_chmod,
1199494a8ebeSWei Liu     .mknod        = proxy_mknod,
1200494a8ebeSWei Liu     .mkdir        = proxy_mkdir,
1201494a8ebeSWei Liu     .fstat        = proxy_fstat,
1202494a8ebeSWei Liu     .open2        = proxy_open2,
1203494a8ebeSWei Liu     .symlink      = proxy_symlink,
1204494a8ebeSWei Liu     .link         = proxy_link,
1205494a8ebeSWei Liu     .truncate     = proxy_truncate,
1206494a8ebeSWei Liu     .rename       = proxy_rename,
1207494a8ebeSWei Liu     .chown        = proxy_chown,
1208494a8ebeSWei Liu     .utimensat    = proxy_utimensat,
1209494a8ebeSWei Liu     .remove       = proxy_remove,
1210494a8ebeSWei Liu     .fsync        = proxy_fsync,
1211494a8ebeSWei Liu     .statfs       = proxy_statfs,
1212494a8ebeSWei Liu     .lgetxattr    = proxy_lgetxattr,
1213494a8ebeSWei Liu     .llistxattr   = proxy_llistxattr,
1214494a8ebeSWei Liu     .lsetxattr    = proxy_lsetxattr,
1215494a8ebeSWei Liu     .lremovexattr = proxy_lremovexattr,
1216494a8ebeSWei Liu     .name_to_path = proxy_name_to_path,
1217494a8ebeSWei Liu     .renameat     = proxy_renameat,
1218494a8ebeSWei Liu     .unlinkat     = proxy_unlinkat,
1219494a8ebeSWei Liu };
1220