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