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