xref: /openbmc/qemu/hw/9pfs/9p-proxy.c (revision 28cbbdd2)
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  */
12e688df6bSMarkus Armbruster 
136f569084SChristian Schoenebeck /*
146f569084SChristian Schoenebeck  * Not so fast! You might want to read the 9p developer docs first:
156f569084SChristian Schoenebeck  * https://wiki.qemu.org/Documentation/9p
166f569084SChristian Schoenebeck  */
176f569084SChristian Schoenebeck 
1871d72eceSChristian Schoenebeck /*
1971d72eceSChristian Schoenebeck  * NOTE: The 9p 'proxy' backend is deprecated (since QEMU 8.1) and will be
2071d72eceSChristian Schoenebeck  * removed in a future version of QEMU!
2171d72eceSChristian Schoenebeck  */
2271d72eceSChristian Schoenebeck 
23fbc04127SPeter Maydell #include "qemu/osdep.h"
24494a8ebeSWei Liu #include <sys/socket.h>
25494a8ebeSWei Liu #include <sys/un.h>
26ebe74f8bSWei Liu #include "9p.h"
27e688df6bSMarkus Armbruster #include "qapi/error.h"
28f348b6d1SVeronia Bahaa #include "qemu/cutils.h"
29494a8ebeSWei Liu #include "qemu/error-report.h"
30922a01a0SMarkus Armbruster #include "qemu/option.h"
31494a8ebeSWei Liu #include "fsdev/qemu-fsdev.h"
32494a8ebeSWei Liu #include "9p-proxy.h"
33494a8ebeSWei Liu 
34494a8ebeSWei Liu typedef struct V9fsProxy {
35494a8ebeSWei Liu     int sockfd;
36494a8ebeSWei Liu     QemuMutex mutex;
37494a8ebeSWei Liu     struct iovec in_iovec;
38494a8ebeSWei Liu     struct iovec out_iovec;
39494a8ebeSWei Liu } V9fsProxy;
40494a8ebeSWei Liu 
41494a8ebeSWei Liu /*
42494a8ebeSWei Liu  * Return received file descriptor on success in *status.
43494a8ebeSWei Liu  * errno is also returned on *status (which will be < 0)
44494a8ebeSWei Liu  * return < 0 on transport error.
45494a8ebeSWei Liu  */
v9fs_receivefd(int sockfd,int * status)46494a8ebeSWei Liu static int v9fs_receivefd(int sockfd, int *status)
47494a8ebeSWei Liu {
48494a8ebeSWei Liu     struct iovec iov;
49494a8ebeSWei Liu     struct msghdr msg;
50494a8ebeSWei Liu     struct cmsghdr *cmsg;
51494a8ebeSWei Liu     int retval, data, fd;
52494a8ebeSWei Liu     union MsgControl msg_control;
53494a8ebeSWei Liu 
54494a8ebeSWei Liu     iov.iov_base = &data;
55494a8ebeSWei Liu     iov.iov_len = sizeof(data);
56494a8ebeSWei Liu 
57494a8ebeSWei Liu     memset(&msg, 0, sizeof(msg));
58494a8ebeSWei Liu     msg.msg_iov = &iov;
59494a8ebeSWei Liu     msg.msg_iovlen = 1;
60494a8ebeSWei Liu     msg.msg_control = &msg_control;
61494a8ebeSWei Liu     msg.msg_controllen = sizeof(msg_control);
62494a8ebeSWei Liu 
63494a8ebeSWei Liu     do {
64494a8ebeSWei Liu         retval = recvmsg(sockfd, &msg, 0);
65494a8ebeSWei Liu     } while (retval < 0 && errno == EINTR);
66494a8ebeSWei Liu     if (retval <= 0) {
67494a8ebeSWei Liu         return retval;
68494a8ebeSWei Liu     }
69494a8ebeSWei Liu     /*
70494a8ebeSWei Liu      * data is set to V9FS_FD_VALID, if ancillary data is sent.  If this
71494a8ebeSWei Liu      * request doesn't need ancillary data (fd) or an error occurred,
72494a8ebeSWei Liu      * data is set to negative errno value.
73494a8ebeSWei Liu      */
74494a8ebeSWei Liu     if (data != V9FS_FD_VALID) {
75494a8ebeSWei Liu         *status = data;
76494a8ebeSWei Liu         return 0;
77494a8ebeSWei Liu     }
78494a8ebeSWei Liu     /*
79494a8ebeSWei Liu      * File descriptor (fd) is sent in the ancillary data. Check if we
80494a8ebeSWei Liu      * indeed received it. One of the reasons to fail to receive it is if
81494a8ebeSWei Liu      * we exceeded the maximum number of file descriptors!
82494a8ebeSWei Liu      */
83494a8ebeSWei Liu     for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
84494a8ebeSWei Liu         if (cmsg->cmsg_len != CMSG_LEN(sizeof(int)) ||
85494a8ebeSWei Liu             cmsg->cmsg_level != SOL_SOCKET ||
86494a8ebeSWei Liu             cmsg->cmsg_type != SCM_RIGHTS) {
87494a8ebeSWei Liu             continue;
88494a8ebeSWei Liu         }
89494a8ebeSWei Liu         fd = *((int *)CMSG_DATA(cmsg));
90494a8ebeSWei Liu         *status = fd;
91494a8ebeSWei Liu         return 0;
92494a8ebeSWei Liu     }
93494a8ebeSWei Liu     *status = -ENFILE;  /* Ancillary data sent but not received */
94494a8ebeSWei Liu     return 0;
95494a8ebeSWei Liu }
96494a8ebeSWei Liu 
socket_read(int sockfd,void * buff,size_t size)97494a8ebeSWei Liu static ssize_t socket_read(int sockfd, void *buff, size_t size)
98494a8ebeSWei Liu {
99494a8ebeSWei Liu     ssize_t retval, total = 0;
100494a8ebeSWei Liu 
101494a8ebeSWei Liu     while (size) {
102494a8ebeSWei Liu         retval = read(sockfd, buff, size);
103494a8ebeSWei Liu         if (retval == 0) {
104494a8ebeSWei Liu             return -EIO;
105494a8ebeSWei Liu         }
106494a8ebeSWei Liu         if (retval < 0) {
107494a8ebeSWei Liu             if (errno == EINTR) {
108494a8ebeSWei Liu                 continue;
109494a8ebeSWei Liu             }
110494a8ebeSWei Liu             return -errno;
111494a8ebeSWei Liu         }
112494a8ebeSWei Liu         size -= retval;
113494a8ebeSWei Liu         buff += retval;
114494a8ebeSWei Liu         total += retval;
115494a8ebeSWei Liu     }
116494a8ebeSWei Liu     return total;
117494a8ebeSWei Liu }
118494a8ebeSWei Liu 
119494a8ebeSWei Liu /* Converts proxy_statfs to VFS statfs structure */
prstatfs_to_statfs(struct statfs * stfs,ProxyStatFS * prstfs)120494a8ebeSWei Liu static void prstatfs_to_statfs(struct statfs *stfs, ProxyStatFS *prstfs)
121494a8ebeSWei Liu {
122494a8ebeSWei Liu     memset(stfs, 0, sizeof(*stfs));
123494a8ebeSWei Liu     stfs->f_type = prstfs->f_type;
124494a8ebeSWei Liu     stfs->f_bsize = prstfs->f_bsize;
125494a8ebeSWei Liu     stfs->f_blocks = prstfs->f_blocks;
126494a8ebeSWei Liu     stfs->f_bfree = prstfs->f_bfree;
127494a8ebeSWei Liu     stfs->f_bavail = prstfs->f_bavail;
128494a8ebeSWei Liu     stfs->f_files = prstfs->f_files;
129494a8ebeSWei Liu     stfs->f_ffree = prstfs->f_ffree;
130f41db099SKeno Fischer #ifdef CONFIG_DARWIN
131f41db099SKeno Fischer     /* f_namelen and f_frsize do not exist on Darwin */
132f41db099SKeno Fischer     stfs->f_fsid.val[0] = prstfs->f_fsid[0] & 0xFFFFFFFFU;
133f41db099SKeno Fischer     stfs->f_fsid.val[1] = prstfs->f_fsid[1] >> 32 & 0xFFFFFFFFU;
134f41db099SKeno Fischer #else
135494a8ebeSWei Liu     stfs->f_fsid.__val[0] = prstfs->f_fsid[0] & 0xFFFFFFFFU;
136494a8ebeSWei Liu     stfs->f_fsid.__val[1] = prstfs->f_fsid[1] >> 32 & 0xFFFFFFFFU;
137494a8ebeSWei Liu     stfs->f_namelen = prstfs->f_namelen;
138494a8ebeSWei Liu     stfs->f_frsize = prstfs->f_frsize;
139f41db099SKeno Fischer #endif
140494a8ebeSWei Liu }
141494a8ebeSWei Liu 
142494a8ebeSWei Liu /* Converts proxy_stat structure to VFS stat structure */
prstat_to_stat(struct stat * stbuf,ProxyStat * prstat)143494a8ebeSWei Liu static void prstat_to_stat(struct stat *stbuf, ProxyStat *prstat)
144494a8ebeSWei Liu {
145494a8ebeSWei Liu    memset(stbuf, 0, sizeof(*stbuf));
146494a8ebeSWei Liu    stbuf->st_dev = prstat->st_dev;
147494a8ebeSWei Liu    stbuf->st_ino = prstat->st_ino;
148494a8ebeSWei Liu    stbuf->st_nlink = prstat->st_nlink;
149494a8ebeSWei Liu    stbuf->st_mode = prstat->st_mode;
150494a8ebeSWei Liu    stbuf->st_uid = prstat->st_uid;
151494a8ebeSWei Liu    stbuf->st_gid = prstat->st_gid;
152494a8ebeSWei Liu    stbuf->st_rdev = prstat->st_rdev;
153494a8ebeSWei Liu    stbuf->st_size = prstat->st_size;
154494a8ebeSWei Liu    stbuf->st_blksize = prstat->st_blksize;
155494a8ebeSWei Liu    stbuf->st_blocks = prstat->st_blocks;
156f41db099SKeno Fischer    stbuf->st_atime = prstat->st_atim_sec;
157494a8ebeSWei Liu    stbuf->st_mtime = prstat->st_mtim_sec;
158494a8ebeSWei Liu    stbuf->st_ctime = prstat->st_ctim_sec;
159f41db099SKeno Fischer #ifdef CONFIG_DARWIN
160f41db099SKeno Fischer    stbuf->st_atimespec.tv_sec = prstat->st_atim_sec;
161f41db099SKeno Fischer    stbuf->st_mtimespec.tv_sec = prstat->st_mtim_sec;
162f41db099SKeno Fischer    stbuf->st_ctimespec.tv_sec = prstat->st_ctim_sec;
163f41db099SKeno Fischer    stbuf->st_atimespec.tv_nsec = prstat->st_atim_nsec;
164f41db099SKeno Fischer    stbuf->st_mtimespec.tv_nsec = prstat->st_mtim_nsec;
165f41db099SKeno Fischer    stbuf->st_ctimespec.tv_nsec = prstat->st_ctim_nsec;
166f41db099SKeno Fischer #else
167f41db099SKeno Fischer    stbuf->st_atim.tv_sec = prstat->st_atim_sec;
168f41db099SKeno Fischer    stbuf->st_mtim.tv_sec = prstat->st_mtim_sec;
169f41db099SKeno Fischer    stbuf->st_ctim.tv_sec = prstat->st_ctim_sec;
170f41db099SKeno Fischer    stbuf->st_atim.tv_nsec = prstat->st_atim_nsec;
171f41db099SKeno Fischer    stbuf->st_mtim.tv_nsec = prstat->st_mtim_nsec;
172494a8ebeSWei Liu    stbuf->st_ctim.tv_nsec = prstat->st_ctim_nsec;
173f41db099SKeno Fischer #endif
174494a8ebeSWei Liu }
175494a8ebeSWei Liu 
176494a8ebeSWei Liu /*
177494a8ebeSWei Liu  * Response contains two parts
178494a8ebeSWei Liu  * {header, data}
179494a8ebeSWei Liu  * header.type == T_ERROR, data -> -errno
180494a8ebeSWei Liu  * header.type == T_SUCCESS, data -> response
181494a8ebeSWei Liu  * size of errno/response is given by header.size
182494a8ebeSWei Liu  * returns < 0, on transport error. response is
183494a8ebeSWei Liu  * valid only if status >= 0.
184494a8ebeSWei Liu  */
v9fs_receive_response(V9fsProxy * proxy,int type,int * status,void * response)185494a8ebeSWei Liu static int v9fs_receive_response(V9fsProxy *proxy, int type,
186494a8ebeSWei Liu                                  int *status, void *response)
187494a8ebeSWei Liu {
188494a8ebeSWei Liu     int retval;
189494a8ebeSWei Liu     ProxyHeader header;
190494a8ebeSWei Liu     struct iovec *reply = &proxy->in_iovec;
191494a8ebeSWei Liu 
192494a8ebeSWei Liu     *status = 0;
193494a8ebeSWei Liu     reply->iov_len = 0;
194494a8ebeSWei Liu     retval = socket_read(proxy->sockfd, reply->iov_base, PROXY_HDR_SZ);
195494a8ebeSWei Liu     if (retval < 0) {
196494a8ebeSWei Liu         return retval;
197494a8ebeSWei Liu     }
198494a8ebeSWei Liu     reply->iov_len = PROXY_HDR_SZ;
199262169abSGreg Kurz     retval = proxy_unmarshal(reply, 0, "dd", &header.type, &header.size);
200262169abSGreg Kurz     assert(retval == 4 * 2);
201494a8ebeSWei Liu     /*
202494a8ebeSWei Liu      * if response size > PROXY_MAX_IO_SZ, read the response but ignore it and
203494a8ebeSWei Liu      * return -ENOBUFS
204494a8ebeSWei Liu      */
205494a8ebeSWei Liu     if (header.size > PROXY_MAX_IO_SZ) {
206494a8ebeSWei Liu         int count;
207494a8ebeSWei Liu         while (header.size > 0) {
208494a8ebeSWei Liu             count = MIN(PROXY_MAX_IO_SZ, header.size);
209494a8ebeSWei Liu             count = socket_read(proxy->sockfd, reply->iov_base, count);
210494a8ebeSWei Liu             if (count < 0) {
211494a8ebeSWei Liu                 return count;
212494a8ebeSWei Liu             }
213494a8ebeSWei Liu             header.size -= count;
214494a8ebeSWei Liu         }
215494a8ebeSWei Liu         *status = -ENOBUFS;
216494a8ebeSWei Liu         return 0;
217494a8ebeSWei Liu     }
218494a8ebeSWei Liu 
219494a8ebeSWei Liu     retval = socket_read(proxy->sockfd,
220494a8ebeSWei Liu                          reply->iov_base + PROXY_HDR_SZ, header.size);
221494a8ebeSWei Liu     if (retval < 0) {
222494a8ebeSWei Liu         return retval;
223494a8ebeSWei Liu     }
224494a8ebeSWei Liu     reply->iov_len += header.size;
225494a8ebeSWei Liu     /* there was an error during processing request */
226494a8ebeSWei Liu     if (header.type == T_ERROR) {
227494a8ebeSWei Liu         int ret;
228494a8ebeSWei Liu         ret = proxy_unmarshal(reply, PROXY_HDR_SZ, "d", status);
229262169abSGreg Kurz         assert(ret == 4);
230494a8ebeSWei Liu         return 0;
231494a8ebeSWei Liu     }
232494a8ebeSWei Liu 
233494a8ebeSWei Liu     switch (type) {
234494a8ebeSWei Liu     case T_LSTAT: {
235494a8ebeSWei Liu         ProxyStat prstat;
236494a8ebeSWei Liu         retval = proxy_unmarshal(reply, PROXY_HDR_SZ,
237494a8ebeSWei Liu                                  "qqqdddqqqqqqqqqq", &prstat.st_dev,
238494a8ebeSWei Liu                                  &prstat.st_ino, &prstat.st_nlink,
239494a8ebeSWei Liu                                  &prstat.st_mode, &prstat.st_uid,
240494a8ebeSWei Liu                                  &prstat.st_gid, &prstat.st_rdev,
241494a8ebeSWei Liu                                  &prstat.st_size, &prstat.st_blksize,
242494a8ebeSWei Liu                                  &prstat.st_blocks,
243494a8ebeSWei Liu                                  &prstat.st_atim_sec, &prstat.st_atim_nsec,
244494a8ebeSWei Liu                                  &prstat.st_mtim_sec, &prstat.st_mtim_nsec,
245494a8ebeSWei Liu                                  &prstat.st_ctim_sec, &prstat.st_ctim_nsec);
246262169abSGreg Kurz         assert(retval == 8 * 3 + 4 * 3 + 8 * 10);
247494a8ebeSWei Liu         prstat_to_stat(response, &prstat);
248494a8ebeSWei Liu         break;
249494a8ebeSWei Liu     }
250494a8ebeSWei Liu     case T_STATFS: {
251494a8ebeSWei Liu         ProxyStatFS prstfs;
252494a8ebeSWei Liu         retval = proxy_unmarshal(reply, PROXY_HDR_SZ,
253494a8ebeSWei Liu                                  "qqqqqqqqqqq", &prstfs.f_type,
254494a8ebeSWei Liu                                  &prstfs.f_bsize, &prstfs.f_blocks,
255494a8ebeSWei Liu                                  &prstfs.f_bfree, &prstfs.f_bavail,
256494a8ebeSWei Liu                                  &prstfs.f_files, &prstfs.f_ffree,
257494a8ebeSWei Liu                                  &prstfs.f_fsid[0], &prstfs.f_fsid[1],
258494a8ebeSWei Liu                                  &prstfs.f_namelen, &prstfs.f_frsize);
259262169abSGreg Kurz         assert(retval == 8 * 11);
260494a8ebeSWei Liu         prstatfs_to_statfs(response, &prstfs);
261494a8ebeSWei Liu         break;
262494a8ebeSWei Liu     }
263494a8ebeSWei Liu     case T_READLINK: {
264494a8ebeSWei Liu         V9fsString target;
265494a8ebeSWei Liu         v9fs_string_init(&target);
266494a8ebeSWei Liu         retval = proxy_unmarshal(reply, PROXY_HDR_SZ, "s", &target);
267494a8ebeSWei Liu         strcpy(response, target.data);
268494a8ebeSWei Liu         v9fs_string_free(&target);
269494a8ebeSWei Liu         break;
270494a8ebeSWei Liu     }
271494a8ebeSWei Liu     case T_LGETXATTR:
272494a8ebeSWei Liu     case T_LLISTXATTR: {
273494a8ebeSWei Liu         V9fsString xattr;
274494a8ebeSWei Liu         v9fs_string_init(&xattr);
275494a8ebeSWei Liu         retval = proxy_unmarshal(reply, PROXY_HDR_SZ, "s", &xattr);
276494a8ebeSWei Liu         memcpy(response, xattr.data, xattr.size);
277494a8ebeSWei Liu         v9fs_string_free(&xattr);
278494a8ebeSWei Liu         break;
279494a8ebeSWei Liu     }
280494a8ebeSWei Liu     case T_GETVERSION:
281262169abSGreg Kurz         retval = proxy_unmarshal(reply, PROXY_HDR_SZ, "q", response);
282262169abSGreg Kurz         assert(retval == 8);
283494a8ebeSWei Liu         break;
284494a8ebeSWei Liu     default:
285494a8ebeSWei Liu         return -1;
286494a8ebeSWei Liu     }
287494a8ebeSWei Liu     if (retval < 0) {
288494a8ebeSWei Liu         *status  = retval;
289494a8ebeSWei Liu     }
290494a8ebeSWei Liu     return 0;
291494a8ebeSWei Liu }
292494a8ebeSWei Liu 
293494a8ebeSWei Liu /*
294494a8ebeSWei Liu  * return < 0 on transport error.
295494a8ebeSWei Liu  * *status is valid only if return >= 0
296494a8ebeSWei Liu  */
v9fs_receive_status(V9fsProxy * proxy,struct iovec * reply,int * status)297494a8ebeSWei Liu static int v9fs_receive_status(V9fsProxy *proxy,
298494a8ebeSWei Liu                                struct iovec *reply, int *status)
299494a8ebeSWei Liu {
300494a8ebeSWei Liu     int retval;
301494a8ebeSWei Liu     ProxyHeader header;
302494a8ebeSWei Liu 
303494a8ebeSWei Liu     *status = 0;
304494a8ebeSWei Liu     reply->iov_len = 0;
305494a8ebeSWei Liu     retval = socket_read(proxy->sockfd, reply->iov_base, PROXY_HDR_SZ);
306494a8ebeSWei Liu     if (retval < 0) {
307494a8ebeSWei Liu         return retval;
308494a8ebeSWei Liu     }
309494a8ebeSWei Liu     reply->iov_len = PROXY_HDR_SZ;
310262169abSGreg Kurz     retval = proxy_unmarshal(reply, 0, "dd", &header.type, &header.size);
311262169abSGreg Kurz     assert(retval == 4 * 2);
312494a8ebeSWei Liu     retval = socket_read(proxy->sockfd,
313494a8ebeSWei Liu                          reply->iov_base + PROXY_HDR_SZ, header.size);
314494a8ebeSWei Liu     if (retval < 0) {
315494a8ebeSWei Liu         return retval;
316494a8ebeSWei Liu     }
317494a8ebeSWei Liu     reply->iov_len += header.size;
318262169abSGreg Kurz     retval = proxy_unmarshal(reply, PROXY_HDR_SZ, "d", status);
319262169abSGreg Kurz     assert(retval == 4);
320494a8ebeSWei Liu     return 0;
321494a8ebeSWei Liu }
322494a8ebeSWei Liu 
323494a8ebeSWei Liu /*
324494a8ebeSWei Liu  * Proxy->header and proxy->request written to socket by QEMU process.
325494a8ebeSWei Liu  * This request read by proxy helper process
326494a8ebeSWei Liu  * returns 0 on success and -errno on error
327494a8ebeSWei Liu  */
v9fs_request(V9fsProxy * proxy,int type,void * response,...)328799fe087SGreg Kurz static int v9fs_request(V9fsProxy *proxy, int type, void *response, ...)
329494a8ebeSWei Liu {
330494a8ebeSWei Liu     dev_t rdev;
331494a8ebeSWei Liu     va_list ap;
332494a8ebeSWei Liu     int size = 0;
333494a8ebeSWei Liu     int retval = 0;
334494a8ebeSWei Liu     uint64_t offset;
335494a8ebeSWei Liu     ProxyHeader header = { 0, 0};
336494a8ebeSWei Liu     struct timespec spec[2];
337494a8ebeSWei Liu     int flags, mode, uid, gid;
338494a8ebeSWei Liu     V9fsString *name, *value;
339494a8ebeSWei Liu     V9fsString *path, *oldpath;
340494a8ebeSWei Liu     struct iovec *iovec = NULL, *reply = NULL;
341494a8ebeSWei Liu 
342494a8ebeSWei Liu     qemu_mutex_lock(&proxy->mutex);
343494a8ebeSWei Liu 
344494a8ebeSWei Liu     if (proxy->sockfd == -1) {
345494a8ebeSWei Liu         retval = -EIO;
346494a8ebeSWei Liu         goto err_out;
347494a8ebeSWei Liu     }
348494a8ebeSWei Liu     iovec = &proxy->out_iovec;
349494a8ebeSWei Liu     reply = &proxy->in_iovec;
350799fe087SGreg Kurz     va_start(ap, response);
351494a8ebeSWei Liu     switch (type) {
352494a8ebeSWei Liu     case T_OPEN:
353494a8ebeSWei Liu         path = va_arg(ap, V9fsString *);
354494a8ebeSWei Liu         flags = va_arg(ap, int);
355494a8ebeSWei Liu         retval = proxy_marshal(iovec, PROXY_HDR_SZ, "sd", path, flags);
356494a8ebeSWei Liu         if (retval > 0) {
357494a8ebeSWei Liu             header.size = retval;
358494a8ebeSWei Liu             header.type = T_OPEN;
359494a8ebeSWei Liu         }
360494a8ebeSWei Liu         break;
361494a8ebeSWei Liu     case T_CREATE:
362494a8ebeSWei Liu         path = va_arg(ap, V9fsString *);
363494a8ebeSWei Liu         flags = va_arg(ap, int);
364494a8ebeSWei Liu         mode = va_arg(ap, int);
365494a8ebeSWei Liu         uid = va_arg(ap, int);
366494a8ebeSWei Liu         gid = va_arg(ap, int);
367494a8ebeSWei Liu         retval = proxy_marshal(iovec, PROXY_HDR_SZ, "sdddd", path,
368494a8ebeSWei Liu                                     flags, mode, uid, gid);
369494a8ebeSWei Liu         if (retval > 0) {
370494a8ebeSWei Liu             header.size = retval;
371494a8ebeSWei Liu             header.type = T_CREATE;
372494a8ebeSWei Liu         }
373494a8ebeSWei Liu         break;
374494a8ebeSWei Liu     case T_MKNOD:
375494a8ebeSWei Liu         path = va_arg(ap, V9fsString *);
376494a8ebeSWei Liu         mode = va_arg(ap, int);
377494a8ebeSWei Liu         rdev = va_arg(ap, long int);
378494a8ebeSWei Liu         uid = va_arg(ap, int);
379494a8ebeSWei Liu         gid = va_arg(ap, int);
380494a8ebeSWei Liu         retval = proxy_marshal(iovec, PROXY_HDR_SZ, "ddsdq",
381494a8ebeSWei Liu                                     uid, gid, path, mode, rdev);
382494a8ebeSWei Liu         if (retval > 0) {
383494a8ebeSWei Liu             header.size = retval;
384494a8ebeSWei Liu             header.type = T_MKNOD;
385494a8ebeSWei Liu         }
386494a8ebeSWei Liu         break;
387494a8ebeSWei Liu     case T_MKDIR:
388494a8ebeSWei Liu         path = va_arg(ap, V9fsString *);
389494a8ebeSWei Liu         mode = va_arg(ap, int);
390494a8ebeSWei Liu         uid = va_arg(ap, int);
391494a8ebeSWei Liu         gid = va_arg(ap, int);
392494a8ebeSWei Liu         retval = proxy_marshal(iovec, PROXY_HDR_SZ, "ddsd",
393494a8ebeSWei Liu                                     uid, gid, path, mode);
394494a8ebeSWei Liu         if (retval > 0) {
395494a8ebeSWei Liu             header.size = retval;
396494a8ebeSWei Liu             header.type = T_MKDIR;
397494a8ebeSWei Liu         }
398494a8ebeSWei Liu         break;
399494a8ebeSWei Liu     case T_SYMLINK:
400494a8ebeSWei Liu         oldpath = va_arg(ap, V9fsString *);
401494a8ebeSWei Liu         path = va_arg(ap, V9fsString *);
402494a8ebeSWei Liu         uid = va_arg(ap, int);
403494a8ebeSWei Liu         gid = va_arg(ap, int);
404494a8ebeSWei Liu         retval = proxy_marshal(iovec, PROXY_HDR_SZ, "ddss",
405494a8ebeSWei Liu                                     uid, gid, oldpath, path);
406494a8ebeSWei Liu         if (retval > 0) {
407494a8ebeSWei Liu             header.size = retval;
408494a8ebeSWei Liu             header.type = T_SYMLINK;
409494a8ebeSWei Liu         }
410494a8ebeSWei Liu         break;
411494a8ebeSWei Liu     case T_LINK:
412494a8ebeSWei Liu         oldpath = va_arg(ap, V9fsString *);
413494a8ebeSWei Liu         path = va_arg(ap, V9fsString *);
414494a8ebeSWei Liu         retval = proxy_marshal(iovec, PROXY_HDR_SZ, "ss",
415494a8ebeSWei Liu                                     oldpath, path);
416494a8ebeSWei Liu         if (retval > 0) {
417494a8ebeSWei Liu             header.size = retval;
418494a8ebeSWei Liu             header.type = T_LINK;
419494a8ebeSWei Liu         }
420494a8ebeSWei Liu         break;
421494a8ebeSWei Liu     case T_LSTAT:
422494a8ebeSWei Liu         path = va_arg(ap, V9fsString *);
423494a8ebeSWei Liu         retval = proxy_marshal(iovec, PROXY_HDR_SZ, "s", path);
424494a8ebeSWei Liu         if (retval > 0) {
425494a8ebeSWei Liu             header.size = retval;
426494a8ebeSWei Liu             header.type = T_LSTAT;
427494a8ebeSWei Liu         }
428494a8ebeSWei Liu         break;
429494a8ebeSWei Liu     case T_READLINK:
430494a8ebeSWei Liu         path = va_arg(ap, V9fsString *);
431494a8ebeSWei Liu         size = va_arg(ap, int);
432494a8ebeSWei Liu         retval = proxy_marshal(iovec, PROXY_HDR_SZ, "sd", path, size);
433494a8ebeSWei Liu         if (retval > 0) {
434494a8ebeSWei Liu             header.size = retval;
435494a8ebeSWei Liu             header.type = T_READLINK;
436494a8ebeSWei Liu         }
437494a8ebeSWei Liu         break;
438494a8ebeSWei Liu     case T_STATFS:
439494a8ebeSWei Liu         path = va_arg(ap, V9fsString *);
440494a8ebeSWei Liu         retval = proxy_marshal(iovec, PROXY_HDR_SZ, "s", path);
441494a8ebeSWei Liu         if (retval > 0) {
442494a8ebeSWei Liu             header.size = retval;
443494a8ebeSWei Liu             header.type = T_STATFS;
444494a8ebeSWei Liu         }
445494a8ebeSWei Liu         break;
446494a8ebeSWei Liu     case T_CHMOD:
447494a8ebeSWei Liu         path = va_arg(ap, V9fsString *);
448494a8ebeSWei Liu         mode = va_arg(ap, int);
449494a8ebeSWei Liu         retval = proxy_marshal(iovec, PROXY_HDR_SZ, "sd", path, mode);
450494a8ebeSWei Liu         if (retval > 0) {
451494a8ebeSWei Liu             header.size = retval;
452494a8ebeSWei Liu             header.type = T_CHMOD;
453494a8ebeSWei Liu         }
454494a8ebeSWei Liu         break;
455494a8ebeSWei Liu     case T_CHOWN:
456494a8ebeSWei Liu         path = va_arg(ap, V9fsString *);
457494a8ebeSWei Liu         uid = va_arg(ap, int);
458494a8ebeSWei Liu         gid = va_arg(ap, int);
459494a8ebeSWei Liu         retval = proxy_marshal(iovec, PROXY_HDR_SZ, "sdd", path, uid, gid);
460494a8ebeSWei Liu         if (retval > 0) {
461494a8ebeSWei Liu             header.size = retval;
462494a8ebeSWei Liu             header.type = T_CHOWN;
463494a8ebeSWei Liu         }
464494a8ebeSWei Liu         break;
465494a8ebeSWei Liu     case T_TRUNCATE:
466494a8ebeSWei Liu         path = va_arg(ap, V9fsString *);
467494a8ebeSWei Liu         offset = va_arg(ap, uint64_t);
468494a8ebeSWei Liu         retval = proxy_marshal(iovec, PROXY_HDR_SZ, "sq", path, offset);
469494a8ebeSWei Liu         if (retval > 0) {
470494a8ebeSWei Liu             header.size = retval;
471494a8ebeSWei Liu             header.type = T_TRUNCATE;
472494a8ebeSWei Liu         }
473494a8ebeSWei Liu         break;
474494a8ebeSWei Liu     case T_UTIME:
475494a8ebeSWei Liu         path = va_arg(ap, V9fsString *);
476494a8ebeSWei Liu         spec[0].tv_sec = va_arg(ap, long);
477494a8ebeSWei Liu         spec[0].tv_nsec = va_arg(ap, long);
478494a8ebeSWei Liu         spec[1].tv_sec = va_arg(ap, long);
479494a8ebeSWei Liu         spec[1].tv_nsec = va_arg(ap, long);
480494a8ebeSWei Liu         retval = proxy_marshal(iovec, PROXY_HDR_SZ, "sqqqq", path,
481494a8ebeSWei Liu                                     spec[0].tv_sec, spec[1].tv_nsec,
482494a8ebeSWei Liu                                     spec[1].tv_sec, spec[1].tv_nsec);
483494a8ebeSWei Liu         if (retval > 0) {
484494a8ebeSWei Liu             header.size = retval;
485494a8ebeSWei Liu             header.type = T_UTIME;
486494a8ebeSWei Liu         }
487494a8ebeSWei Liu         break;
488494a8ebeSWei Liu     case T_RENAME:
489494a8ebeSWei Liu         oldpath = va_arg(ap, V9fsString *);
490494a8ebeSWei Liu         path = va_arg(ap, V9fsString *);
491494a8ebeSWei Liu         retval = proxy_marshal(iovec, PROXY_HDR_SZ, "ss", oldpath, path);
492494a8ebeSWei Liu         if (retval > 0) {
493494a8ebeSWei Liu             header.size = retval;
494494a8ebeSWei Liu             header.type = T_RENAME;
495494a8ebeSWei Liu         }
496494a8ebeSWei Liu         break;
497494a8ebeSWei Liu     case T_REMOVE:
498494a8ebeSWei Liu         path = va_arg(ap, V9fsString *);
499494a8ebeSWei Liu         retval = proxy_marshal(iovec, PROXY_HDR_SZ, "s", path);
500494a8ebeSWei Liu         if (retval > 0) {
501494a8ebeSWei Liu             header.size = retval;
502494a8ebeSWei Liu             header.type = T_REMOVE;
503494a8ebeSWei Liu         }
504494a8ebeSWei Liu         break;
505494a8ebeSWei Liu     case T_LGETXATTR:
506494a8ebeSWei Liu         size = va_arg(ap, int);
507494a8ebeSWei Liu         path = va_arg(ap, V9fsString *);
508494a8ebeSWei Liu         name = va_arg(ap, V9fsString *);
509494a8ebeSWei Liu         retval = proxy_marshal(iovec, PROXY_HDR_SZ,
510494a8ebeSWei Liu                                     "dss", size, path, name);
511494a8ebeSWei Liu         if (retval > 0) {
512494a8ebeSWei Liu             header.size = retval;
513494a8ebeSWei Liu             header.type = T_LGETXATTR;
514494a8ebeSWei Liu         }
515494a8ebeSWei Liu         break;
516494a8ebeSWei Liu     case T_LLISTXATTR:
517494a8ebeSWei Liu         size = va_arg(ap, int);
518494a8ebeSWei Liu         path = va_arg(ap, V9fsString *);
519494a8ebeSWei Liu         retval = proxy_marshal(iovec, PROXY_HDR_SZ, "ds", size, path);
520494a8ebeSWei Liu         if (retval > 0) {
521494a8ebeSWei Liu             header.size = retval;
522494a8ebeSWei Liu             header.type = T_LLISTXATTR;
523494a8ebeSWei Liu         }
524494a8ebeSWei Liu         break;
525494a8ebeSWei Liu     case T_LSETXATTR:
526494a8ebeSWei Liu         path = va_arg(ap, V9fsString *);
527494a8ebeSWei Liu         name = va_arg(ap, V9fsString *);
528494a8ebeSWei Liu         value = va_arg(ap, V9fsString *);
529494a8ebeSWei Liu         size = va_arg(ap, int);
530494a8ebeSWei Liu         flags = va_arg(ap, int);
531494a8ebeSWei Liu         retval = proxy_marshal(iovec, PROXY_HDR_SZ, "sssdd",
532494a8ebeSWei Liu                                     path, name, value, size, flags);
533494a8ebeSWei Liu         if (retval > 0) {
534494a8ebeSWei Liu             header.size = retval;
535494a8ebeSWei Liu             header.type = T_LSETXATTR;
536494a8ebeSWei Liu         }
537494a8ebeSWei Liu         break;
538494a8ebeSWei Liu     case T_LREMOVEXATTR:
539494a8ebeSWei Liu         path = va_arg(ap, V9fsString *);
540494a8ebeSWei Liu         name = va_arg(ap, V9fsString *);
541494a8ebeSWei Liu         retval = proxy_marshal(iovec, PROXY_HDR_SZ, "ss", path, name);
542494a8ebeSWei Liu         if (retval > 0) {
543494a8ebeSWei Liu             header.size = retval;
544494a8ebeSWei Liu             header.type = T_LREMOVEXATTR;
545494a8ebeSWei Liu         }
546494a8ebeSWei Liu         break;
547494a8ebeSWei Liu     case T_GETVERSION:
548494a8ebeSWei Liu         path = va_arg(ap, V9fsString *);
549494a8ebeSWei Liu         retval = proxy_marshal(iovec, PROXY_HDR_SZ, "s", path);
550494a8ebeSWei Liu         if (retval > 0) {
551494a8ebeSWei Liu             header.size = retval;
552494a8ebeSWei Liu             header.type = T_GETVERSION;
553494a8ebeSWei Liu         }
554494a8ebeSWei Liu         break;
555494a8ebeSWei Liu     default:
556494a8ebeSWei Liu         error_report("Invalid type %d", type);
557494a8ebeSWei Liu         retval = -EINVAL;
558494a8ebeSWei Liu         break;
559494a8ebeSWei Liu     }
560494a8ebeSWei Liu     va_end(ap);
561494a8ebeSWei Liu 
562494a8ebeSWei Liu     if (retval < 0) {
563494a8ebeSWei Liu         goto err_out;
564494a8ebeSWei Liu     }
565494a8ebeSWei Liu 
566494a8ebeSWei Liu     /* marshal the header details */
567acef3f8bSGreg Kurz     retval = proxy_marshal(iovec, 0, "dd", header.type, header.size);
568acef3f8bSGreg Kurz     assert(retval == 4 * 2);
569494a8ebeSWei Liu     header.size += PROXY_HDR_SZ;
570494a8ebeSWei Liu 
571494a8ebeSWei Liu     retval = qemu_write_full(proxy->sockfd, iovec->iov_base, header.size);
572494a8ebeSWei Liu     if (retval != header.size) {
573494a8ebeSWei Liu         goto close_error;
574494a8ebeSWei Liu     }
575494a8ebeSWei Liu 
576494a8ebeSWei Liu     switch (type) {
577494a8ebeSWei Liu     case T_OPEN:
578494a8ebeSWei Liu     case T_CREATE:
579494a8ebeSWei Liu         /*
580494a8ebeSWei Liu          * A file descriptor is returned as response for
581494a8ebeSWei Liu          * T_OPEN,T_CREATE on success
582494a8ebeSWei Liu          */
583494a8ebeSWei Liu         if (v9fs_receivefd(proxy->sockfd, &retval) < 0) {
584494a8ebeSWei Liu             goto close_error;
585494a8ebeSWei Liu         }
586494a8ebeSWei Liu         break;
587494a8ebeSWei Liu     case T_MKNOD:
588494a8ebeSWei Liu     case T_MKDIR:
589494a8ebeSWei Liu     case T_SYMLINK:
590494a8ebeSWei Liu     case T_LINK:
591494a8ebeSWei Liu     case T_CHMOD:
592494a8ebeSWei Liu     case T_CHOWN:
593494a8ebeSWei Liu     case T_RENAME:
594494a8ebeSWei Liu     case T_TRUNCATE:
595494a8ebeSWei Liu     case T_UTIME:
596494a8ebeSWei Liu     case T_REMOVE:
597494a8ebeSWei Liu     case T_LSETXATTR:
598494a8ebeSWei Liu     case T_LREMOVEXATTR:
599494a8ebeSWei Liu         if (v9fs_receive_status(proxy, reply, &retval) < 0) {
600494a8ebeSWei Liu             goto close_error;
601494a8ebeSWei Liu         }
602494a8ebeSWei Liu         break;
603494a8ebeSWei Liu     case T_LSTAT:
604494a8ebeSWei Liu     case T_READLINK:
605494a8ebeSWei Liu     case T_STATFS:
606494a8ebeSWei Liu     case T_GETVERSION:
607494a8ebeSWei Liu         if (v9fs_receive_response(proxy, type, &retval, response) < 0) {
608494a8ebeSWei Liu             goto close_error;
609494a8ebeSWei Liu         }
610494a8ebeSWei Liu         break;
611494a8ebeSWei Liu     case T_LGETXATTR:
612494a8ebeSWei Liu     case T_LLISTXATTR:
613494a8ebeSWei Liu         if (!size) {
614494a8ebeSWei Liu             if (v9fs_receive_status(proxy, reply, &retval) < 0) {
615494a8ebeSWei Liu                 goto close_error;
616494a8ebeSWei Liu             }
617494a8ebeSWei Liu         } else {
618494a8ebeSWei Liu             if (v9fs_receive_response(proxy, type, &retval, response) < 0) {
619494a8ebeSWei Liu                 goto close_error;
620494a8ebeSWei Liu             }
621494a8ebeSWei Liu         }
622494a8ebeSWei Liu         break;
623494a8ebeSWei Liu     }
624494a8ebeSWei Liu 
625494a8ebeSWei Liu err_out:
626494a8ebeSWei Liu     qemu_mutex_unlock(&proxy->mutex);
627494a8ebeSWei Liu     return retval;
628494a8ebeSWei Liu 
629494a8ebeSWei Liu close_error:
630494a8ebeSWei Liu     close(proxy->sockfd);
631494a8ebeSWei Liu     proxy->sockfd = -1;
632494a8ebeSWei Liu     qemu_mutex_unlock(&proxy->mutex);
633494a8ebeSWei Liu     return -EIO;
634494a8ebeSWei Liu }
635494a8ebeSWei Liu 
proxy_lstat(FsContext * fs_ctx,V9fsPath * fs_path,struct stat * stbuf)636494a8ebeSWei Liu static int proxy_lstat(FsContext *fs_ctx, V9fsPath *fs_path, struct stat *stbuf)
637494a8ebeSWei Liu {
638494a8ebeSWei Liu     int retval;
639799fe087SGreg Kurz     retval = v9fs_request(fs_ctx->private, T_LSTAT, stbuf, fs_path);
640494a8ebeSWei Liu     if (retval < 0) {
641494a8ebeSWei Liu         errno = -retval;
642494a8ebeSWei Liu         return -1;
643494a8ebeSWei Liu     }
644494a8ebeSWei Liu     return retval;
645494a8ebeSWei Liu }
646494a8ebeSWei Liu 
proxy_readlink(FsContext * fs_ctx,V9fsPath * fs_path,char * buf,size_t bufsz)647494a8ebeSWei Liu static ssize_t proxy_readlink(FsContext *fs_ctx, V9fsPath *fs_path,
648494a8ebeSWei Liu                               char *buf, size_t bufsz)
649494a8ebeSWei Liu {
650494a8ebeSWei Liu     int retval;
651799fe087SGreg Kurz     retval = v9fs_request(fs_ctx->private, T_READLINK, buf, fs_path, bufsz);
652494a8ebeSWei Liu     if (retval < 0) {
653494a8ebeSWei Liu         errno = -retval;
654494a8ebeSWei Liu         return -1;
655494a8ebeSWei Liu     }
656494a8ebeSWei Liu     return strlen(buf);
657494a8ebeSWei Liu }
658494a8ebeSWei Liu 
proxy_close(FsContext * ctx,V9fsFidOpenState * fs)659494a8ebeSWei Liu static int proxy_close(FsContext *ctx, V9fsFidOpenState *fs)
660494a8ebeSWei Liu {
661494a8ebeSWei Liu     return close(fs->fd);
662494a8ebeSWei Liu }
663494a8ebeSWei Liu 
proxy_closedir(FsContext * ctx,V9fsFidOpenState * fs)664494a8ebeSWei Liu static int proxy_closedir(FsContext *ctx, V9fsFidOpenState *fs)
665494a8ebeSWei Liu {
666f314ea4eSGreg Kurz     return closedir(fs->dir.stream);
667494a8ebeSWei Liu }
668494a8ebeSWei Liu 
proxy_open(FsContext * ctx,V9fsPath * fs_path,int flags,V9fsFidOpenState * fs)669494a8ebeSWei Liu static int proxy_open(FsContext *ctx, V9fsPath *fs_path,
670494a8ebeSWei Liu                       int flags, V9fsFidOpenState *fs)
671494a8ebeSWei Liu {
672799fe087SGreg Kurz     fs->fd = v9fs_request(ctx->private, T_OPEN, NULL, fs_path, flags);
673494a8ebeSWei Liu     if (fs->fd < 0) {
674494a8ebeSWei Liu         errno = -fs->fd;
675494a8ebeSWei Liu         fs->fd = -1;
676494a8ebeSWei Liu     }
677494a8ebeSWei Liu     return fs->fd;
678494a8ebeSWei Liu }
679494a8ebeSWei Liu 
proxy_opendir(FsContext * ctx,V9fsPath * fs_path,V9fsFidOpenState * fs)680494a8ebeSWei Liu static int proxy_opendir(FsContext *ctx,
681494a8ebeSWei Liu                          V9fsPath *fs_path, V9fsFidOpenState *fs)
682494a8ebeSWei Liu {
683494a8ebeSWei Liu     int serrno, fd;
684494a8ebeSWei Liu 
685f314ea4eSGreg Kurz     fs->dir.stream = NULL;
686799fe087SGreg Kurz     fd = v9fs_request(ctx->private, T_OPEN, NULL, fs_path, O_DIRECTORY);
687494a8ebeSWei Liu     if (fd < 0) {
688494a8ebeSWei Liu         errno = -fd;
689494a8ebeSWei Liu         return -1;
690494a8ebeSWei Liu     }
691f314ea4eSGreg Kurz     fs->dir.stream = fdopendir(fd);
692f314ea4eSGreg Kurz     if (!fs->dir.stream) {
693494a8ebeSWei Liu         serrno = errno;
694494a8ebeSWei Liu         close(fd);
695494a8ebeSWei Liu         errno = serrno;
696494a8ebeSWei Liu         return -1;
697494a8ebeSWei Liu     }
698494a8ebeSWei Liu     return 0;
699494a8ebeSWei Liu }
700494a8ebeSWei Liu 
proxy_rewinddir(FsContext * ctx,V9fsFidOpenState * fs)701494a8ebeSWei Liu static void proxy_rewinddir(FsContext *ctx, V9fsFidOpenState *fs)
702494a8ebeSWei Liu {
703f314ea4eSGreg Kurz     rewinddir(fs->dir.stream);
704494a8ebeSWei Liu }
705494a8ebeSWei Liu 
proxy_telldir(FsContext * ctx,V9fsFidOpenState * fs)706494a8ebeSWei Liu static off_t proxy_telldir(FsContext *ctx, V9fsFidOpenState *fs)
707494a8ebeSWei Liu {
708f314ea4eSGreg Kurz     return telldir(fs->dir.stream);
709494a8ebeSWei Liu }
710494a8ebeSWei Liu 
proxy_readdir(FsContext * ctx,V9fsFidOpenState * fs)711635324e8SGreg Kurz static struct dirent *proxy_readdir(FsContext *ctx, V9fsFidOpenState *fs)
712494a8ebeSWei Liu {
7136b3b279bSKeno Fischer     struct dirent *entry;
7146b3b279bSKeno Fischer     entry = readdir(fs->dir.stream);
7156b3b279bSKeno Fischer #ifdef CONFIG_DARWIN
7166b3b279bSKeno Fischer     if (!entry) {
7176b3b279bSKeno Fischer         return NULL;
7186b3b279bSKeno Fischer     }
7196b3b279bSKeno Fischer     int td;
7206b3b279bSKeno Fischer     td = telldir(fs->dir.stream);
7216b3b279bSKeno Fischer     /* If telldir fails, fail the entire readdir call */
7226b3b279bSKeno Fischer     if (td < 0) {
7236b3b279bSKeno Fischer         return NULL;
7246b3b279bSKeno Fischer     }
7256b3b279bSKeno Fischer     entry->d_seekoff = td;
7266b3b279bSKeno Fischer #endif
7276b3b279bSKeno Fischer     return entry;
728494a8ebeSWei Liu }
729494a8ebeSWei Liu 
proxy_seekdir(FsContext * ctx,V9fsFidOpenState * fs,off_t off)730494a8ebeSWei Liu static void proxy_seekdir(FsContext *ctx, V9fsFidOpenState *fs, off_t off)
731494a8ebeSWei Liu {
732f314ea4eSGreg Kurz     seekdir(fs->dir.stream, off);
733494a8ebeSWei Liu }
734494a8ebeSWei Liu 
proxy_preadv(FsContext * ctx,V9fsFidOpenState * fs,const struct iovec * iov,int iovcnt,off_t offset)735494a8ebeSWei Liu static ssize_t proxy_preadv(FsContext *ctx, V9fsFidOpenState *fs,
736494a8ebeSWei Liu                             const struct iovec *iov,
737494a8ebeSWei Liu                             int iovcnt, off_t offset)
738494a8ebeSWei Liu {
739494a8ebeSWei Liu     ssize_t ret;
740494a8ebeSWei Liu #ifdef CONFIG_PREADV
741494a8ebeSWei Liu     ret = preadv(fs->fd, iov, iovcnt, offset);
742494a8ebeSWei Liu #else
743494a8ebeSWei Liu     ret = lseek(fs->fd, offset, SEEK_SET);
744494a8ebeSWei Liu     if (ret >= 0) {
745494a8ebeSWei Liu         ret = readv(fs->fd, iov, iovcnt);
746494a8ebeSWei Liu     }
747494a8ebeSWei Liu #endif
748494a8ebeSWei Liu     return ret;
749494a8ebeSWei Liu }
750494a8ebeSWei Liu 
proxy_pwritev(FsContext * ctx,V9fsFidOpenState * fs,const struct iovec * iov,int iovcnt,off_t offset)751494a8ebeSWei Liu static ssize_t proxy_pwritev(FsContext *ctx, V9fsFidOpenState *fs,
752494a8ebeSWei Liu                              const struct iovec *iov,
753494a8ebeSWei Liu                              int iovcnt, off_t offset)
754494a8ebeSWei Liu {
755494a8ebeSWei Liu     ssize_t ret;
756494a8ebeSWei Liu 
757494a8ebeSWei Liu #ifdef CONFIG_PREADV
758494a8ebeSWei Liu     ret = pwritev(fs->fd, iov, iovcnt, offset);
759494a8ebeSWei Liu #else
760494a8ebeSWei Liu     ret = lseek(fs->fd, offset, SEEK_SET);
761494a8ebeSWei Liu     if (ret >= 0) {
762494a8ebeSWei Liu         ret = writev(fs->fd, iov, iovcnt);
763494a8ebeSWei Liu     }
764494a8ebeSWei Liu #endif
765494a8ebeSWei Liu #ifdef CONFIG_SYNC_FILE_RANGE
766494a8ebeSWei Liu     if (ret > 0 && ctx->export_flags & V9FS_IMMEDIATE_WRITEOUT) {
767494a8ebeSWei Liu         /*
768494a8ebeSWei Liu          * Initiate a writeback. This is not a data integrity sync.
769494a8ebeSWei Liu          * We want to ensure that we don't leave dirty pages in the cache
770*28cbbdd2SMichael Tokarev          * after write when writeout=immediate is specified.
771494a8ebeSWei Liu          */
772494a8ebeSWei Liu         sync_file_range(fs->fd, offset, ret,
773494a8ebeSWei Liu                         SYNC_FILE_RANGE_WAIT_BEFORE | SYNC_FILE_RANGE_WRITE);
774494a8ebeSWei Liu     }
775494a8ebeSWei Liu #endif
776494a8ebeSWei Liu     return ret;
777494a8ebeSWei Liu }
778494a8ebeSWei Liu 
proxy_chmod(FsContext * fs_ctx,V9fsPath * fs_path,FsCred * credp)779494a8ebeSWei Liu static int proxy_chmod(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp)
780494a8ebeSWei Liu {
781494a8ebeSWei Liu     int retval;
782799fe087SGreg Kurz     retval = v9fs_request(fs_ctx->private, T_CHMOD, NULL, fs_path,
783799fe087SGreg Kurz                           credp->fc_mode);
784494a8ebeSWei Liu     if (retval < 0) {
785494a8ebeSWei Liu         errno = -retval;
786494a8ebeSWei Liu     }
787494a8ebeSWei Liu     return retval;
788494a8ebeSWei Liu }
789494a8ebeSWei Liu 
proxy_mknod(FsContext * fs_ctx,V9fsPath * dir_path,const char * name,FsCred * credp)790494a8ebeSWei Liu static int proxy_mknod(FsContext *fs_ctx, V9fsPath *dir_path,
791494a8ebeSWei Liu                        const char *name, FsCred *credp)
792494a8ebeSWei Liu {
793494a8ebeSWei Liu     int retval;
794494a8ebeSWei Liu     V9fsString fullname;
795494a8ebeSWei Liu 
796494a8ebeSWei Liu     v9fs_string_init(&fullname);
797494a8ebeSWei Liu     v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name);
798494a8ebeSWei Liu 
799799fe087SGreg Kurz     retval = v9fs_request(fs_ctx->private, T_MKNOD, NULL, &fullname,
800799fe087SGreg Kurz                           credp->fc_mode, credp->fc_rdev,
801494a8ebeSWei Liu                           credp->fc_uid, credp->fc_gid);
802494a8ebeSWei Liu     v9fs_string_free(&fullname);
803494a8ebeSWei Liu     if (retval < 0) {
804494a8ebeSWei Liu         errno = -retval;
805494a8ebeSWei Liu         retval = -1;
806494a8ebeSWei Liu     }
807494a8ebeSWei Liu     return retval;
808494a8ebeSWei Liu }
809494a8ebeSWei Liu 
proxy_mkdir(FsContext * fs_ctx,V9fsPath * dir_path,const char * name,FsCred * credp)810494a8ebeSWei Liu static int proxy_mkdir(FsContext *fs_ctx, V9fsPath *dir_path,
811494a8ebeSWei Liu                        const char *name, FsCred *credp)
812494a8ebeSWei Liu {
813494a8ebeSWei Liu     int retval;
814494a8ebeSWei Liu     V9fsString fullname;
815494a8ebeSWei Liu 
816494a8ebeSWei Liu     v9fs_string_init(&fullname);
817494a8ebeSWei Liu     v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name);
818494a8ebeSWei Liu 
819799fe087SGreg Kurz     retval = v9fs_request(fs_ctx->private, T_MKDIR, NULL, &fullname,
820494a8ebeSWei Liu                           credp->fc_mode, credp->fc_uid, credp->fc_gid);
821494a8ebeSWei Liu     v9fs_string_free(&fullname);
822494a8ebeSWei Liu     if (retval < 0) {
823494a8ebeSWei Liu         errno = -retval;
824494a8ebeSWei Liu         retval = -1;
825494a8ebeSWei Liu     }
826494a8ebeSWei Liu     return retval;
827494a8ebeSWei Liu }
828494a8ebeSWei Liu 
proxy_fstat(FsContext * fs_ctx,int fid_type,V9fsFidOpenState * fs,struct stat * stbuf)829494a8ebeSWei Liu static int proxy_fstat(FsContext *fs_ctx, int fid_type,
830494a8ebeSWei Liu                        V9fsFidOpenState *fs, struct stat *stbuf)
831494a8ebeSWei Liu {
832494a8ebeSWei Liu     int fd;
833494a8ebeSWei Liu 
834494a8ebeSWei Liu     if (fid_type == P9_FID_DIR) {
835f314ea4eSGreg Kurz         fd = dirfd(fs->dir.stream);
836494a8ebeSWei Liu     } else {
837494a8ebeSWei Liu         fd = fs->fd;
838494a8ebeSWei Liu     }
839494a8ebeSWei Liu     return fstat(fd, stbuf);
840494a8ebeSWei Liu }
841494a8ebeSWei Liu 
proxy_open2(FsContext * fs_ctx,V9fsPath * dir_path,const char * name,int flags,FsCred * credp,V9fsFidOpenState * fs)842494a8ebeSWei Liu static int proxy_open2(FsContext *fs_ctx, V9fsPath *dir_path, const char *name,
843494a8ebeSWei Liu                        int flags, FsCred *credp, V9fsFidOpenState *fs)
844494a8ebeSWei Liu {
845494a8ebeSWei Liu     V9fsString fullname;
846494a8ebeSWei Liu 
847494a8ebeSWei Liu     v9fs_string_init(&fullname);
848494a8ebeSWei Liu     v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name);
849494a8ebeSWei Liu 
850799fe087SGreg Kurz     fs->fd = v9fs_request(fs_ctx->private, T_CREATE, NULL, &fullname, flags,
851799fe087SGreg Kurz                           credp->fc_mode, credp->fc_uid, credp->fc_gid);
852494a8ebeSWei Liu     v9fs_string_free(&fullname);
853494a8ebeSWei Liu     if (fs->fd < 0) {
854494a8ebeSWei Liu         errno = -fs->fd;
855494a8ebeSWei Liu         fs->fd = -1;
856494a8ebeSWei Liu     }
857494a8ebeSWei Liu     return fs->fd;
858494a8ebeSWei Liu }
859494a8ebeSWei Liu 
proxy_symlink(FsContext * fs_ctx,const char * oldpath,V9fsPath * dir_path,const char * name,FsCred * credp)860494a8ebeSWei Liu static int proxy_symlink(FsContext *fs_ctx, const char *oldpath,
861494a8ebeSWei Liu                          V9fsPath *dir_path, const char *name, FsCred *credp)
862494a8ebeSWei Liu {
863494a8ebeSWei Liu     int retval;
864494a8ebeSWei Liu     V9fsString fullname, target;
865494a8ebeSWei Liu 
866494a8ebeSWei Liu     v9fs_string_init(&fullname);
867494a8ebeSWei Liu     v9fs_string_init(&target);
868494a8ebeSWei Liu 
869494a8ebeSWei Liu     v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name);
870494a8ebeSWei Liu     v9fs_string_sprintf(&target, "%s", oldpath);
871494a8ebeSWei Liu 
872799fe087SGreg Kurz     retval = v9fs_request(fs_ctx->private, T_SYMLINK, NULL, &target, &fullname,
873799fe087SGreg Kurz                           credp->fc_uid, credp->fc_gid);
874494a8ebeSWei Liu     v9fs_string_free(&fullname);
875494a8ebeSWei Liu     v9fs_string_free(&target);
876494a8ebeSWei Liu     if (retval < 0) {
877494a8ebeSWei Liu         errno = -retval;
878494a8ebeSWei Liu         retval = -1;
879494a8ebeSWei Liu     }
880494a8ebeSWei Liu     return retval;
881494a8ebeSWei Liu }
882494a8ebeSWei Liu 
proxy_link(FsContext * ctx,V9fsPath * oldpath,V9fsPath * dirpath,const char * name)883494a8ebeSWei Liu static int proxy_link(FsContext *ctx, V9fsPath *oldpath,
884494a8ebeSWei Liu                       V9fsPath *dirpath, const char *name)
885494a8ebeSWei Liu {
886494a8ebeSWei Liu     int retval;
887494a8ebeSWei Liu     V9fsString newpath;
888494a8ebeSWei Liu 
889494a8ebeSWei Liu     v9fs_string_init(&newpath);
890494a8ebeSWei Liu     v9fs_string_sprintf(&newpath, "%s/%s", dirpath->data, name);
891494a8ebeSWei Liu 
892799fe087SGreg Kurz     retval = v9fs_request(ctx->private, T_LINK, NULL, oldpath, &newpath);
893494a8ebeSWei Liu     v9fs_string_free(&newpath);
894494a8ebeSWei Liu     if (retval < 0) {
895494a8ebeSWei Liu         errno = -retval;
896494a8ebeSWei Liu         retval = -1;
897494a8ebeSWei Liu     }
898494a8ebeSWei Liu     return retval;
899494a8ebeSWei Liu }
900494a8ebeSWei Liu 
proxy_truncate(FsContext * ctx,V9fsPath * fs_path,off_t size)901494a8ebeSWei Liu static int proxy_truncate(FsContext *ctx, V9fsPath *fs_path, off_t size)
902494a8ebeSWei Liu {
903494a8ebeSWei Liu     int retval;
904494a8ebeSWei Liu 
905799fe087SGreg Kurz     retval = v9fs_request(ctx->private, T_TRUNCATE, NULL, fs_path, size);
906494a8ebeSWei Liu     if (retval < 0) {
907494a8ebeSWei Liu         errno = -retval;
908494a8ebeSWei Liu         return -1;
909494a8ebeSWei Liu     }
910494a8ebeSWei Liu     return 0;
911494a8ebeSWei Liu }
912494a8ebeSWei Liu 
proxy_rename(FsContext * ctx,const char * oldpath,const char * newpath)913494a8ebeSWei Liu static int proxy_rename(FsContext *ctx, const char *oldpath,
914494a8ebeSWei Liu                         const char *newpath)
915494a8ebeSWei Liu {
916494a8ebeSWei Liu     int retval;
917494a8ebeSWei Liu     V9fsString oldname, newname;
918494a8ebeSWei Liu 
919494a8ebeSWei Liu     v9fs_string_init(&oldname);
920494a8ebeSWei Liu     v9fs_string_init(&newname);
921494a8ebeSWei Liu 
922494a8ebeSWei Liu     v9fs_string_sprintf(&oldname, "%s", oldpath);
923494a8ebeSWei Liu     v9fs_string_sprintf(&newname, "%s", newpath);
924799fe087SGreg Kurz     retval = v9fs_request(ctx->private, T_RENAME, NULL, &oldname, &newname);
925494a8ebeSWei Liu     v9fs_string_free(&oldname);
926494a8ebeSWei Liu     v9fs_string_free(&newname);
927494a8ebeSWei Liu     if (retval < 0) {
928494a8ebeSWei Liu         errno = -retval;
929494a8ebeSWei Liu     }
930494a8ebeSWei Liu     return retval;
931494a8ebeSWei Liu }
932494a8ebeSWei Liu 
proxy_chown(FsContext * fs_ctx,V9fsPath * fs_path,FsCred * credp)933494a8ebeSWei Liu static int proxy_chown(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp)
934494a8ebeSWei Liu {
935494a8ebeSWei Liu     int retval;
936799fe087SGreg Kurz     retval = v9fs_request(fs_ctx->private, T_CHOWN, NULL, fs_path,
937799fe087SGreg Kurz                           credp->fc_uid, credp->fc_gid);
938494a8ebeSWei Liu     if (retval < 0) {
939494a8ebeSWei Liu         errno = -retval;
940494a8ebeSWei Liu     }
941494a8ebeSWei Liu     return retval;
942494a8ebeSWei Liu }
943494a8ebeSWei Liu 
proxy_utimensat(FsContext * s,V9fsPath * fs_path,const struct timespec * buf)944494a8ebeSWei Liu static int proxy_utimensat(FsContext *s, V9fsPath *fs_path,
945494a8ebeSWei Liu                            const struct timespec *buf)
946494a8ebeSWei Liu {
947494a8ebeSWei Liu     int retval;
948799fe087SGreg Kurz     retval = v9fs_request(s->private, T_UTIME, NULL, fs_path,
949494a8ebeSWei Liu                           buf[0].tv_sec, buf[0].tv_nsec,
950494a8ebeSWei Liu                           buf[1].tv_sec, buf[1].tv_nsec);
951494a8ebeSWei Liu     if (retval < 0) {
952494a8ebeSWei Liu         errno = -retval;
953494a8ebeSWei Liu     }
954494a8ebeSWei Liu     return retval;
955494a8ebeSWei Liu }
956494a8ebeSWei Liu 
proxy_remove(FsContext * ctx,const char * path)957494a8ebeSWei Liu static int proxy_remove(FsContext *ctx, const char *path)
958494a8ebeSWei Liu {
959494a8ebeSWei Liu     int retval;
960494a8ebeSWei Liu     V9fsString name;
961494a8ebeSWei Liu     v9fs_string_init(&name);
962494a8ebeSWei Liu     v9fs_string_sprintf(&name, "%s", path);
963799fe087SGreg Kurz     retval = v9fs_request(ctx->private, T_REMOVE, NULL, &name);
964494a8ebeSWei Liu     v9fs_string_free(&name);
965494a8ebeSWei Liu     if (retval < 0) {
966494a8ebeSWei Liu         errno = -retval;
967494a8ebeSWei Liu     }
968494a8ebeSWei Liu     return retval;
969494a8ebeSWei Liu }
970494a8ebeSWei Liu 
proxy_fsync(FsContext * ctx,int fid_type,V9fsFidOpenState * fs,int datasync)971494a8ebeSWei Liu static int proxy_fsync(FsContext *ctx, int fid_type,
972494a8ebeSWei Liu                        V9fsFidOpenState *fs, int datasync)
973494a8ebeSWei Liu {
974494a8ebeSWei Liu     int fd;
975494a8ebeSWei Liu 
976494a8ebeSWei Liu     if (fid_type == P9_FID_DIR) {
977f314ea4eSGreg Kurz         fd = dirfd(fs->dir.stream);
978494a8ebeSWei Liu     } else {
979494a8ebeSWei Liu         fd = fs->fd;
980494a8ebeSWei Liu     }
981494a8ebeSWei Liu 
982494a8ebeSWei Liu     if (datasync) {
983494a8ebeSWei Liu         return qemu_fdatasync(fd);
984494a8ebeSWei Liu     } else {
985494a8ebeSWei Liu         return fsync(fd);
986494a8ebeSWei Liu     }
987494a8ebeSWei Liu }
988494a8ebeSWei Liu 
proxy_statfs(FsContext * s,V9fsPath * fs_path,struct statfs * stbuf)989494a8ebeSWei Liu static int proxy_statfs(FsContext *s, V9fsPath *fs_path, struct statfs *stbuf)
990494a8ebeSWei Liu {
991494a8ebeSWei Liu     int retval;
992799fe087SGreg Kurz     retval = v9fs_request(s->private, T_STATFS, stbuf, fs_path);
993494a8ebeSWei Liu     if (retval < 0) {
994494a8ebeSWei Liu         errno = -retval;
995494a8ebeSWei Liu         return -1;
996494a8ebeSWei Liu     }
997494a8ebeSWei Liu     return retval;
998494a8ebeSWei Liu }
999494a8ebeSWei Liu 
proxy_lgetxattr(FsContext * ctx,V9fsPath * fs_path,const char * name,void * value,size_t size)1000494a8ebeSWei Liu static ssize_t proxy_lgetxattr(FsContext *ctx, V9fsPath *fs_path,
1001494a8ebeSWei Liu                                const char *name, void *value, size_t size)
1002494a8ebeSWei Liu {
1003494a8ebeSWei Liu     int retval;
1004494a8ebeSWei Liu     V9fsString xname;
1005494a8ebeSWei Liu 
1006494a8ebeSWei Liu     v9fs_string_init(&xname);
1007494a8ebeSWei Liu     v9fs_string_sprintf(&xname, "%s", name);
1008799fe087SGreg Kurz     retval = v9fs_request(ctx->private, T_LGETXATTR, value, size, fs_path,
1009799fe087SGreg Kurz                           &xname);
1010494a8ebeSWei Liu     v9fs_string_free(&xname);
1011494a8ebeSWei Liu     if (retval < 0) {
1012494a8ebeSWei Liu         errno = -retval;
1013494a8ebeSWei Liu     }
1014494a8ebeSWei Liu     return retval;
1015494a8ebeSWei Liu }
1016494a8ebeSWei Liu 
proxy_llistxattr(FsContext * ctx,V9fsPath * fs_path,void * value,size_t size)1017494a8ebeSWei Liu static ssize_t proxy_llistxattr(FsContext *ctx, V9fsPath *fs_path,
1018494a8ebeSWei Liu                                 void *value, size_t size)
1019494a8ebeSWei Liu {
1020494a8ebeSWei Liu     int retval;
1021799fe087SGreg Kurz     retval = v9fs_request(ctx->private, T_LLISTXATTR, value, size, fs_path);
1022494a8ebeSWei Liu     if (retval < 0) {
1023494a8ebeSWei Liu         errno = -retval;
1024494a8ebeSWei Liu     }
1025494a8ebeSWei Liu     return retval;
1026494a8ebeSWei Liu }
1027494a8ebeSWei Liu 
proxy_lsetxattr(FsContext * ctx,V9fsPath * fs_path,const char * name,void * value,size_t size,int flags)1028494a8ebeSWei Liu static int proxy_lsetxattr(FsContext *ctx, V9fsPath *fs_path, const char *name,
1029494a8ebeSWei Liu                            void *value, size_t size, int flags)
1030494a8ebeSWei Liu {
1031494a8ebeSWei Liu     int retval;
1032494a8ebeSWei Liu     V9fsString xname, xvalue;
1033494a8ebeSWei Liu 
1034494a8ebeSWei Liu     v9fs_string_init(&xname);
1035494a8ebeSWei Liu     v9fs_string_sprintf(&xname, "%s", name);
1036494a8ebeSWei Liu 
1037494a8ebeSWei Liu     v9fs_string_init(&xvalue);
1038494a8ebeSWei Liu     xvalue.size = size;
1039494a8ebeSWei Liu     xvalue.data = g_malloc(size);
1040494a8ebeSWei Liu     memcpy(xvalue.data, value, size);
1041494a8ebeSWei Liu 
1042799fe087SGreg Kurz     retval = v9fs_request(ctx->private, T_LSETXATTR, value, fs_path, &xname,
1043799fe087SGreg Kurz                           &xvalue, size, flags);
1044494a8ebeSWei Liu     v9fs_string_free(&xname);
1045494a8ebeSWei Liu     v9fs_string_free(&xvalue);
1046494a8ebeSWei Liu     if (retval < 0) {
1047494a8ebeSWei Liu         errno = -retval;
1048494a8ebeSWei Liu     }
1049494a8ebeSWei Liu     return retval;
1050494a8ebeSWei Liu }
1051494a8ebeSWei Liu 
proxy_lremovexattr(FsContext * ctx,V9fsPath * fs_path,const char * name)1052494a8ebeSWei Liu static int proxy_lremovexattr(FsContext *ctx, V9fsPath *fs_path,
1053494a8ebeSWei Liu                               const char *name)
1054494a8ebeSWei Liu {
1055494a8ebeSWei Liu     int retval;
1056494a8ebeSWei Liu     V9fsString xname;
1057494a8ebeSWei Liu 
1058494a8ebeSWei Liu     v9fs_string_init(&xname);
1059494a8ebeSWei Liu     v9fs_string_sprintf(&xname, "%s", name);
1060799fe087SGreg Kurz     retval = v9fs_request(ctx->private, T_LREMOVEXATTR, NULL, fs_path, &xname);
1061494a8ebeSWei Liu     v9fs_string_free(&xname);
1062494a8ebeSWei Liu     if (retval < 0) {
1063494a8ebeSWei Liu         errno = -retval;
1064494a8ebeSWei Liu     }
1065494a8ebeSWei Liu     return retval;
1066494a8ebeSWei Liu }
1067494a8ebeSWei Liu 
proxy_name_to_path(FsContext * ctx,V9fsPath * dir_path,const char * name,V9fsPath * target)1068494a8ebeSWei Liu static int proxy_name_to_path(FsContext *ctx, V9fsPath *dir_path,
1069494a8ebeSWei Liu                               const char *name, V9fsPath *target)
1070494a8ebeSWei Liu {
1071494a8ebeSWei Liu     if (dir_path) {
1072e3e83f2eSGreg Kurz         v9fs_path_sprintf(target, "%s/%s", dir_path->data, name);
1073494a8ebeSWei Liu     } else {
1074e3e83f2eSGreg Kurz         v9fs_path_sprintf(target, "%s", name);
1075494a8ebeSWei Liu     }
1076494a8ebeSWei Liu     return 0;
1077494a8ebeSWei Liu }
1078494a8ebeSWei Liu 
proxy_renameat(FsContext * ctx,V9fsPath * olddir,const char * old_name,V9fsPath * newdir,const char * new_name)1079494a8ebeSWei Liu static int proxy_renameat(FsContext *ctx, V9fsPath *olddir,
1080494a8ebeSWei Liu                           const char *old_name, V9fsPath *newdir,
1081494a8ebeSWei Liu                           const char *new_name)
1082494a8ebeSWei Liu {
1083494a8ebeSWei Liu     int ret;
1084494a8ebeSWei Liu     V9fsString old_full_name, new_full_name;
1085494a8ebeSWei Liu 
1086494a8ebeSWei Liu     v9fs_string_init(&old_full_name);
1087494a8ebeSWei Liu     v9fs_string_init(&new_full_name);
1088494a8ebeSWei Liu 
1089494a8ebeSWei Liu     v9fs_string_sprintf(&old_full_name, "%s/%s", olddir->data, old_name);
1090494a8ebeSWei Liu     v9fs_string_sprintf(&new_full_name, "%s/%s", newdir->data, new_name);
1091494a8ebeSWei Liu 
1092494a8ebeSWei Liu     ret = proxy_rename(ctx, old_full_name.data, new_full_name.data);
1093494a8ebeSWei Liu     v9fs_string_free(&old_full_name);
1094494a8ebeSWei Liu     v9fs_string_free(&new_full_name);
1095494a8ebeSWei Liu     return ret;
1096494a8ebeSWei Liu }
1097494a8ebeSWei Liu 
proxy_unlinkat(FsContext * ctx,V9fsPath * dir,const char * name,int flags)1098494a8ebeSWei Liu static int proxy_unlinkat(FsContext *ctx, V9fsPath *dir,
1099494a8ebeSWei Liu                           const char *name, int flags)
1100494a8ebeSWei Liu {
1101494a8ebeSWei Liu     int ret;
1102494a8ebeSWei Liu     V9fsString fullname;
1103494a8ebeSWei Liu     v9fs_string_init(&fullname);
1104494a8ebeSWei Liu 
1105494a8ebeSWei Liu     v9fs_string_sprintf(&fullname, "%s/%s", dir->data, name);
1106494a8ebeSWei Liu     ret = proxy_remove(ctx, fullname.data);
1107494a8ebeSWei Liu     v9fs_string_free(&fullname);
1108494a8ebeSWei Liu 
1109494a8ebeSWei Liu     return ret;
1110494a8ebeSWei Liu }
1111494a8ebeSWei Liu 
proxy_ioc_getversion(FsContext * fs_ctx,V9fsPath * path,mode_t st_mode,uint64_t * st_gen)1112494a8ebeSWei Liu static int proxy_ioc_getversion(FsContext *fs_ctx, V9fsPath *path,
1113494a8ebeSWei Liu                                 mode_t st_mode, uint64_t *st_gen)
1114494a8ebeSWei Liu {
1115494a8ebeSWei Liu     int err;
1116494a8ebeSWei Liu 
1117494a8ebeSWei Liu     /* Do not try to open special files like device nodes, fifos etc
1118494a8ebeSWei Liu      * we can get fd for regular files and directories only
1119494a8ebeSWei Liu      */
1120494a8ebeSWei Liu     if (!S_ISREG(st_mode) && !S_ISDIR(st_mode)) {
1121494a8ebeSWei Liu         errno = ENOTTY;
1122494a8ebeSWei Liu         return -1;
1123494a8ebeSWei Liu     }
1124799fe087SGreg Kurz     err = v9fs_request(fs_ctx->private, T_GETVERSION, st_gen, path);
1125494a8ebeSWei Liu     if (err < 0) {
1126494a8ebeSWei Liu         errno = -err;
1127494a8ebeSWei Liu         err = -1;
1128494a8ebeSWei Liu     }
1129494a8ebeSWei Liu     return err;
1130494a8ebeSWei Liu }
1131494a8ebeSWei Liu 
connect_namedsocket(const char * path,Error ** errp)113265603a80SGreg Kurz static int connect_namedsocket(const char *path, Error **errp)
1133494a8ebeSWei Liu {
1134fde1f3e4SKeno Fischer     int sockfd;
1135494a8ebeSWei Liu     struct sockaddr_un helper;
1136494a8ebeSWei Liu 
1137494a8ebeSWei Liu     if (strlen(path) >= sizeof(helper.sun_path)) {
113865603a80SGreg Kurz         error_setg(errp, "socket name too long");
1139494a8ebeSWei Liu         return -1;
1140494a8ebeSWei Liu     }
1141494a8ebeSWei Liu     sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
1142494a8ebeSWei Liu     if (sockfd < 0) {
114365603a80SGreg Kurz         error_setg_errno(errp, errno, "failed to create client socket");
1144494a8ebeSWei Liu         return -1;
1145494a8ebeSWei Liu     }
1146494a8ebeSWei Liu     strcpy(helper.sun_path, path);
1147494a8ebeSWei Liu     helper.sun_family = AF_UNIX;
1148fde1f3e4SKeno Fischer     if (connect(sockfd, (struct sockaddr *)&helper, sizeof(helper)) < 0) {
114965603a80SGreg Kurz         error_setg_errno(errp, errno, "failed to connect to '%s'", path);
1150494a8ebeSWei Liu         close(sockfd);
1151494a8ebeSWei Liu         return -1;
1152494a8ebeSWei Liu     }
1153494a8ebeSWei Liu 
1154494a8ebeSWei Liu     /* remove the socket for security reasons */
1155494a8ebeSWei Liu     unlink(path);
1156494a8ebeSWei Liu     return sockfd;
1157494a8ebeSWei Liu }
1158494a8ebeSWei Liu 
error_append_socket_sockfd_hint(Error * const * errp)11594c5ec47eSVladimir Sementsov-Ogievskiy static void error_append_socket_sockfd_hint(Error *const *errp)
116091cda4e8SGreg Kurz {
116191cda4e8SGreg Kurz     error_append_hint(errp, "Either specify socket=/some/path where /some/path"
116291cda4e8SGreg Kurz                       " points to a listening AF_UNIX socket or sock_fd=fd"
116391cda4e8SGreg Kurz                       " where fd is a file descriptor to a connected AF_UNIX"
116491cda4e8SGreg Kurz                       " socket\n");
116591cda4e8SGreg Kurz }
116691cda4e8SGreg Kurz 
proxy_parse_opts(QemuOpts * opts,FsDriverEntry * fs,Error ** errp)116791cda4e8SGreg Kurz static int proxy_parse_opts(QemuOpts *opts, FsDriverEntry *fs, Error **errp)
1168494a8ebeSWei Liu {
1169494a8ebeSWei Liu     const char *socket = qemu_opt_get(opts, "socket");
1170494a8ebeSWei Liu     const char *sock_fd = qemu_opt_get(opts, "sock_fd");
1171494a8ebeSWei Liu 
1172494a8ebeSWei Liu     if (!socket && !sock_fd) {
117391cda4e8SGreg Kurz         error_setg(errp, "both socket and sock_fd properties are missing");
117491cda4e8SGreg Kurz         error_append_socket_sockfd_hint(errp);
1175494a8ebeSWei Liu         return -1;
1176494a8ebeSWei Liu     }
1177494a8ebeSWei Liu     if (socket && sock_fd) {
117891cda4e8SGreg Kurz         error_setg(errp, "both socket and sock_fd properties are set");
117991cda4e8SGreg Kurz         error_append_socket_sockfd_hint(errp);
1180494a8ebeSWei Liu         return -1;
1181494a8ebeSWei Liu     }
1182494a8ebeSWei Liu     if (socket) {
1183494a8ebeSWei Liu         fs->path = g_strdup(socket);
1184659f1953SGreg Kurz         fs->export_flags |= V9FS_PROXY_SOCK_NAME;
1185494a8ebeSWei Liu     } else {
1186494a8ebeSWei Liu         fs->path = g_strdup(sock_fd);
1187659f1953SGreg Kurz         fs->export_flags |= V9FS_PROXY_SOCK_FD;
1188494a8ebeSWei Liu     }
1189494a8ebeSWei Liu     return 0;
1190494a8ebeSWei Liu }
1191494a8ebeSWei Liu 
proxy_init(FsContext * ctx,Error ** errp)119265603a80SGreg Kurz static int proxy_init(FsContext *ctx, Error **errp)
1193494a8ebeSWei Liu {
11941366244aSMarkus Armbruster     V9fsProxy *proxy = g_new(V9fsProxy, 1);
1195494a8ebeSWei Liu     int sock_id;
1196494a8ebeSWei Liu 
1197494a8ebeSWei Liu     if (ctx->export_flags & V9FS_PROXY_SOCK_NAME) {
119865603a80SGreg Kurz         sock_id = connect_namedsocket(ctx->fs_root, errp);
1199494a8ebeSWei Liu     } else {
1200494a8ebeSWei Liu         sock_id = atoi(ctx->fs_root);
1201494a8ebeSWei Liu         if (sock_id < 0) {
120265603a80SGreg Kurz             error_setg(errp, "socket descriptor not initialized");
1203494a8ebeSWei Liu         }
1204494a8ebeSWei Liu     }
1205494a8ebeSWei Liu     if (sock_id < 0) {
1206494a8ebeSWei Liu         g_free(proxy);
1207494a8ebeSWei Liu         return -1;
1208494a8ebeSWei Liu     }
1209494a8ebeSWei Liu     g_free(ctx->fs_root);
1210494a8ebeSWei Liu     ctx->fs_root = NULL;
1211494a8ebeSWei Liu 
1212494a8ebeSWei Liu     proxy->in_iovec.iov_base  = g_malloc(PROXY_MAX_IO_SZ + PROXY_HDR_SZ);
1213494a8ebeSWei Liu     proxy->in_iovec.iov_len   = PROXY_MAX_IO_SZ + PROXY_HDR_SZ;
1214494a8ebeSWei Liu     proxy->out_iovec.iov_base = g_malloc(PROXY_MAX_IO_SZ + PROXY_HDR_SZ);
1215494a8ebeSWei Liu     proxy->out_iovec.iov_len  = PROXY_MAX_IO_SZ + PROXY_HDR_SZ;
1216494a8ebeSWei Liu 
1217494a8ebeSWei Liu     ctx->private = proxy;
1218494a8ebeSWei Liu     proxy->sockfd = sock_id;
1219494a8ebeSWei Liu     qemu_mutex_init(&proxy->mutex);
1220494a8ebeSWei Liu 
1221494a8ebeSWei Liu     ctx->export_flags |= V9FS_PATHNAME_FSCONTEXT;
1222494a8ebeSWei Liu     ctx->exops.get_st_gen = proxy_ioc_getversion;
1223494a8ebeSWei Liu     return 0;
1224494a8ebeSWei Liu }
1225494a8ebeSWei Liu 
proxy_cleanup(FsContext * ctx)1226898ae90aSLi Qiang static void proxy_cleanup(FsContext *ctx)
1227898ae90aSLi Qiang {
1228898ae90aSLi Qiang     V9fsProxy *proxy = ctx->private;
1229898ae90aSLi Qiang 
1230c0da0cb7SGreg Kurz     if (!proxy) {
1231c0da0cb7SGreg Kurz         return;
1232c0da0cb7SGreg Kurz     }
1233c0da0cb7SGreg Kurz 
1234898ae90aSLi Qiang     g_free(proxy->out_iovec.iov_base);
1235898ae90aSLi Qiang     g_free(proxy->in_iovec.iov_base);
1236898ae90aSLi Qiang     if (ctx->export_flags & V9FS_PROXY_SOCK_NAME) {
1237898ae90aSLi Qiang         close(proxy->sockfd);
1238898ae90aSLi Qiang     }
1239898ae90aSLi Qiang     g_free(proxy);
1240898ae90aSLi Qiang }
1241898ae90aSLi Qiang 
1242494a8ebeSWei Liu FileOperations proxy_ops = {
1243494a8ebeSWei Liu     .parse_opts   = proxy_parse_opts,
1244494a8ebeSWei Liu     .init         = proxy_init,
1245898ae90aSLi Qiang     .cleanup      = proxy_cleanup,
1246494a8ebeSWei Liu     .lstat        = proxy_lstat,
1247494a8ebeSWei Liu     .readlink     = proxy_readlink,
1248494a8ebeSWei Liu     .close        = proxy_close,
1249494a8ebeSWei Liu     .closedir     = proxy_closedir,
1250494a8ebeSWei Liu     .open         = proxy_open,
1251494a8ebeSWei Liu     .opendir      = proxy_opendir,
1252494a8ebeSWei Liu     .rewinddir    = proxy_rewinddir,
1253494a8ebeSWei Liu     .telldir      = proxy_telldir,
1254635324e8SGreg Kurz     .readdir      = proxy_readdir,
1255494a8ebeSWei Liu     .seekdir      = proxy_seekdir,
1256494a8ebeSWei Liu     .preadv       = proxy_preadv,
1257494a8ebeSWei Liu     .pwritev      = proxy_pwritev,
1258494a8ebeSWei Liu     .chmod        = proxy_chmod,
1259494a8ebeSWei Liu     .mknod        = proxy_mknod,
1260494a8ebeSWei Liu     .mkdir        = proxy_mkdir,
1261494a8ebeSWei Liu     .fstat        = proxy_fstat,
1262494a8ebeSWei Liu     .open2        = proxy_open2,
1263494a8ebeSWei Liu     .symlink      = proxy_symlink,
1264494a8ebeSWei Liu     .link         = proxy_link,
1265494a8ebeSWei Liu     .truncate     = proxy_truncate,
1266494a8ebeSWei Liu     .rename       = proxy_rename,
1267494a8ebeSWei Liu     .chown        = proxy_chown,
1268494a8ebeSWei Liu     .utimensat    = proxy_utimensat,
1269494a8ebeSWei Liu     .remove       = proxy_remove,
1270494a8ebeSWei Liu     .fsync        = proxy_fsync,
1271494a8ebeSWei Liu     .statfs       = proxy_statfs,
1272494a8ebeSWei Liu     .lgetxattr    = proxy_lgetxattr,
1273494a8ebeSWei Liu     .llistxattr   = proxy_llistxattr,
1274494a8ebeSWei Liu     .lsetxattr    = proxy_lsetxattr,
1275494a8ebeSWei Liu     .lremovexattr = proxy_lremovexattr,
1276494a8ebeSWei Liu     .name_to_path = proxy_name_to_path,
1277494a8ebeSWei Liu     .renameat     = proxy_renameat,
1278494a8ebeSWei Liu     .unlinkat     = proxy_unlinkat,
1279494a8ebeSWei Liu };
1280