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