xref: /qemu/fsdev/virtfs-proxy-helper.c (revision 72ac97cd)
1 /*
2  * Helper for QEMU Proxy FS Driver
3  * Copyright IBM, Corp. 2011
4  *
5  * Authors:
6  * M. Mohan Kumar <mohan@in.ibm.com>
7  *
8  * This work is licensed under the terms of the GNU GPL, version 2. See
9  * the COPYING file in the top-level directory.
10  */
11 
12 #include <sys/resource.h>
13 #include <getopt.h>
14 #include <syslog.h>
15 #include <sys/capability.h>
16 #include <sys/fsuid.h>
17 #include <sys/vfs.h>
18 #include <sys/ioctl.h>
19 #include <linux/fs.h>
20 #ifdef CONFIG_LINUX_MAGIC_H
21 #include <linux/magic.h>
22 #endif
23 #include "qemu-common.h"
24 #include "qemu/sockets.h"
25 #include "qemu/xattr.h"
26 #include "virtio-9p-marshal.h"
27 #include "hw/9pfs/virtio-9p-proxy.h"
28 #include "fsdev/virtio-9p-marshal.h"
29 
30 #define PROGNAME "virtfs-proxy-helper"
31 
32 #ifndef XFS_SUPER_MAGIC
33 #define XFS_SUPER_MAGIC  0x58465342
34 #endif
35 #ifndef EXT2_SUPER_MAGIC
36 #define EXT2_SUPER_MAGIC 0xEF53
37 #endif
38 #ifndef REISERFS_SUPER_MAGIC
39 #define REISERFS_SUPER_MAGIC 0x52654973
40 #endif
41 #ifndef BTRFS_SUPER_MAGIC
42 #define BTRFS_SUPER_MAGIC 0x9123683E
43 #endif
44 
45 static struct option helper_opts[] = {
46     {"fd", required_argument, NULL, 'f'},
47     {"path", required_argument, NULL, 'p'},
48     {"nodaemon", no_argument, NULL, 'n'},
49     {"socket", required_argument, NULL, 's'},
50     {"uid", required_argument, NULL, 'u'},
51     {"gid", required_argument, NULL, 'g'},
52 };
53 
54 static bool is_daemon;
55 static bool get_version; /* IOC getversion IOCTL supported */
56 
57 static void GCC_FMT_ATTR(2, 3) do_log(int loglevel, const char *format, ...)
58 {
59     va_list ap;
60 
61     va_start(ap, format);
62     if (is_daemon) {
63         vsyslog(LOG_CRIT, format, ap);
64     } else {
65         vfprintf(stderr, format, ap);
66     }
67     va_end(ap);
68 }
69 
70 static void do_perror(const char *string)
71 {
72     if (is_daemon) {
73         syslog(LOG_CRIT, "%s:%s", string, strerror(errno));
74     } else {
75         fprintf(stderr, "%s:%s\n", string, strerror(errno));
76     }
77 }
78 
79 static int do_cap_set(cap_value_t *cap_value, int size, int reset)
80 {
81     cap_t caps;
82     if (reset) {
83         /*
84          * Start with an empty set and set permitted and effective
85          */
86         caps = cap_init();
87         if (caps == NULL) {
88             do_perror("cap_init");
89             return -1;
90         }
91         if (cap_set_flag(caps, CAP_PERMITTED, size, cap_value, CAP_SET) < 0) {
92             do_perror("cap_set_flag");
93             goto error;
94         }
95     } else {
96         caps = cap_get_proc();
97         if (!caps) {
98             do_perror("cap_get_proc");
99             return -1;
100         }
101     }
102     if (cap_set_flag(caps, CAP_EFFECTIVE, size, cap_value, CAP_SET) < 0) {
103         do_perror("cap_set_flag");
104         goto error;
105     }
106     if (cap_set_proc(caps) < 0) {
107         do_perror("cap_set_proc");
108         goto error;
109     }
110     cap_free(caps);
111     return 0;
112 
113 error:
114     cap_free(caps);
115     return -1;
116 }
117 
118 static int init_capabilities(void)
119 {
120     /* helper needs following capbabilities only */
121     cap_value_t cap_list[] = {
122         CAP_CHOWN,
123         CAP_DAC_OVERRIDE,
124         CAP_FOWNER,
125         CAP_FSETID,
126         CAP_SETGID,
127         CAP_MKNOD,
128         CAP_SETUID,
129     };
130     return do_cap_set(cap_list, ARRAY_SIZE(cap_list), 1);
131 }
132 
133 static int socket_read(int sockfd, void *buff, ssize_t size)
134 {
135     ssize_t retval, total = 0;
136 
137     while (size) {
138         retval = read(sockfd, buff, size);
139         if (retval == 0) {
140             return -EIO;
141         }
142         if (retval < 0) {
143             if (errno == EINTR) {
144                 continue;
145             }
146             return -errno;
147         }
148         size -= retval;
149         buff += retval;
150         total += retval;
151     }
152     return total;
153 }
154 
155 static int socket_write(int sockfd, void *buff, ssize_t size)
156 {
157     ssize_t retval, total = 0;
158 
159     while (size) {
160         retval = write(sockfd, buff, size);
161         if (retval < 0) {
162             if (errno == EINTR) {
163                 continue;
164             }
165             return -errno;
166         }
167         size -= retval;
168         buff += retval;
169         total += retval;
170     }
171     return total;
172 }
173 
174 static int read_request(int sockfd, struct iovec *iovec, ProxyHeader *header)
175 {
176     int retval;
177 
178     /*
179      * read the request header.
180      */
181     iovec->iov_len = 0;
182     retval = socket_read(sockfd, iovec->iov_base, PROXY_HDR_SZ);
183     if (retval < 0) {
184         return retval;
185     }
186     iovec->iov_len = PROXY_HDR_SZ;
187     retval = proxy_unmarshal(iovec, 0, "dd", &header->type, &header->size);
188     if (retval < 0) {
189         return retval;
190     }
191     /*
192      * We can't process message.size > PROXY_MAX_IO_SZ.
193      * Treat it as fatal error
194      */
195     if (header->size > PROXY_MAX_IO_SZ) {
196         return -ENOBUFS;
197     }
198     retval = socket_read(sockfd, iovec->iov_base + PROXY_HDR_SZ, header->size);
199     if (retval < 0) {
200         return retval;
201     }
202     iovec->iov_len += header->size;
203     return 0;
204 }
205 
206 static int send_fd(int sockfd, int fd)
207 {
208     struct msghdr msg;
209     struct iovec iov;
210     int retval, data;
211     struct cmsghdr *cmsg;
212     union MsgControl msg_control;
213 
214     iov.iov_base = &data;
215     iov.iov_len = sizeof(data);
216 
217     memset(&msg, 0, sizeof(msg));
218     msg.msg_iov = &iov;
219     msg.msg_iovlen = 1;
220     /* No ancillary data on error */
221     if (fd < 0) {
222         /* fd is really negative errno if the request failed  */
223         data = fd;
224     } else {
225         data = V9FS_FD_VALID;
226         msg.msg_control = &msg_control;
227         msg.msg_controllen = sizeof(msg_control);
228 
229         cmsg = &msg_control.cmsg;
230         cmsg->cmsg_len = CMSG_LEN(sizeof(fd));
231         cmsg->cmsg_level = SOL_SOCKET;
232         cmsg->cmsg_type = SCM_RIGHTS;
233         memcpy(CMSG_DATA(cmsg), &fd, sizeof(fd));
234     }
235 
236     do {
237         retval = sendmsg(sockfd, &msg, 0);
238     } while (retval < 0 && errno == EINTR);
239     if (fd >= 0) {
240         close(fd);
241     }
242     if (retval < 0) {
243         return retval;
244     }
245     return 0;
246 }
247 
248 static int send_status(int sockfd, struct iovec *iovec, int status)
249 {
250     ProxyHeader header;
251     int retval, msg_size;
252 
253     if (status < 0) {
254         header.type = T_ERROR;
255     } else {
256         header.type = T_SUCCESS;
257     }
258     header.size = sizeof(status);
259     /*
260      * marshal the return status. We don't check error.
261      * because we are sure we have enough space for the status
262      */
263     msg_size = proxy_marshal(iovec, 0, "ddd", header.type,
264                              header.size, status);
265     retval = socket_write(sockfd, iovec->iov_base, msg_size);
266     if (retval < 0) {
267         return retval;
268     }
269     return 0;
270 }
271 
272 /*
273  * from man 7 capabilities, section
274  * Effect of User ID Changes on Capabilities:
275  * If the effective user ID is changed from nonzero to 0, then the permitted
276  * set is copied to the effective set.  If the effective user ID is changed
277  * from 0 to nonzero, then all capabilities are are cleared from the effective
278  * set.
279  *
280  * The setfsuid/setfsgid man pages warn that changing the effective user ID may
281  * expose the program to unwanted signals, but this is not true anymore: for an
282  * unprivileged (without CAP_KILL) program to send a signal, the real or
283  * effective user ID of the sending process must equal the real or saved user
284  * ID of the target process.  Even when dropping privileges, it is enough to
285  * keep the saved UID to a "privileged" value and virtfs-proxy-helper won't
286  * be exposed to signals.  So just use setresuid/setresgid.
287  */
288 static int setugid(int uid, int gid, int *suid, int *sgid)
289 {
290     int retval;
291 
292     /*
293      * We still need DAC_OVERRIDE because we don't change
294      * supplementary group ids, and hence may be subjected DAC rules
295      */
296     cap_value_t cap_list[] = {
297         CAP_DAC_OVERRIDE,
298     };
299 
300     *suid = geteuid();
301     *sgid = getegid();
302 
303     if (setresgid(-1, gid, *sgid) == -1) {
304         retval = -errno;
305         goto err_out;
306     }
307 
308     if (setresuid(-1, uid, *suid) == -1) {
309         retval = -errno;
310         goto err_sgid;
311     }
312 
313     if (uid != 0 || gid != 0) {
314         if (do_cap_set(cap_list, ARRAY_SIZE(cap_list), 0) < 0) {
315             retval = -errno;
316             goto err_suid;
317         }
318     }
319     return 0;
320 
321 err_suid:
322     if (setresuid(-1, *suid, *suid) == -1) {
323         abort();
324     }
325 err_sgid:
326     if (setresgid(-1, *sgid, *sgid) == -1) {
327         abort();
328     }
329 err_out:
330     return retval;
331 }
332 
333 /*
334  * This is used to reset the ugid back with the saved values
335  * There is nothing much we can do checking error values here.
336  */
337 static void resetugid(int suid, int sgid)
338 {
339     if (setresgid(-1, sgid, sgid) == -1) {
340         abort();
341     }
342     if (setresuid(-1, suid, suid) == -1) {
343         abort();
344     }
345 }
346 
347 /*
348  * send response in two parts
349  * 1) ProxyHeader
350  * 2) Response or error status
351  * This function should be called with marshaled response
352  * send_response constructs header part and error part only.
353  * send response sends {ProxyHeader,Response} if the request was success
354  * otherwise sends {ProxyHeader,error status}
355  */
356 static int send_response(int sock, struct iovec *iovec, int size)
357 {
358     int retval;
359     ProxyHeader header;
360 
361     /*
362      * If response size exceeds available iovec->iov_len,
363      * we return ENOBUFS
364      */
365     if (size > PROXY_MAX_IO_SZ) {
366         size = -ENOBUFS;
367     }
368 
369     if (size < 0) {
370         /*
371          * In case of error we would not have got the error encoded
372          * already so encode the error here.
373          */
374         header.type = T_ERROR;
375         header.size = sizeof(size);
376         proxy_marshal(iovec, PROXY_HDR_SZ, "d", size);
377     } else {
378         header.type = T_SUCCESS;
379         header.size = size;
380     }
381     proxy_marshal(iovec, 0, "dd", header.type, header.size);
382     retval = socket_write(sock, iovec->iov_base, header.size + PROXY_HDR_SZ);
383     if (retval < 0) {
384         return retval;
385     }
386     return 0;
387 }
388 
389 /*
390  * gets generation number
391  * returns -errno on failure and sizeof(generation number) on success
392  */
393 static int do_getversion(struct iovec *iovec, struct iovec *out_iovec)
394 {
395     uint64_t version;
396     int retval = -ENOTTY;
397 #ifdef FS_IOC_GETVERSION
398     int fd;
399     V9fsString path;
400 #endif
401 
402 
403     /* no need to issue ioctl */
404     if (!get_version) {
405         version = 0;
406         retval = proxy_marshal(out_iovec, PROXY_HDR_SZ, "q", version);
407         return retval;
408     }
409 #ifdef FS_IOC_GETVERSION
410     retval = proxy_unmarshal(iovec, PROXY_HDR_SZ, "s", &path);
411     if (retval < 0) {
412         return retval;
413     }
414 
415     fd = open(path.data, O_RDONLY);
416     if (fd < 0) {
417         retval = -errno;
418         goto err_out;
419     }
420     if (ioctl(fd, FS_IOC_GETVERSION, &version) < 0) {
421         retval = -errno;
422     } else {
423         retval = proxy_marshal(out_iovec, PROXY_HDR_SZ, "q", version);
424     }
425     close(fd);
426 err_out:
427     v9fs_string_free(&path);
428 #endif
429     return retval;
430 }
431 
432 static int do_getxattr(int type, struct iovec *iovec, struct iovec *out_iovec)
433 {
434     int size = 0, offset, retval;
435     V9fsString path, name, xattr;
436 
437     v9fs_string_init(&xattr);
438     v9fs_string_init(&path);
439     retval = proxy_unmarshal(iovec, PROXY_HDR_SZ, "ds", &size, &path);
440     if (retval < 0) {
441         return retval;
442     }
443     offset = PROXY_HDR_SZ + retval;
444 
445     if (size) {
446         xattr.data = g_malloc(size);
447         xattr.size = size;
448     }
449     switch (type) {
450     case T_LGETXATTR:
451         v9fs_string_init(&name);
452         retval = proxy_unmarshal(iovec, offset, "s", &name);
453         if (retval > 0) {
454             retval = lgetxattr(path.data, name.data, xattr.data, size);
455             if (retval < 0) {
456                 retval = -errno;
457             } else {
458                 xattr.size = retval;
459             }
460         }
461         v9fs_string_free(&name);
462         break;
463     case T_LLISTXATTR:
464         retval = llistxattr(path.data, xattr.data, size);
465         if (retval < 0) {
466             retval = -errno;
467         } else {
468             xattr.size = retval;
469         }
470         break;
471     }
472     if (retval < 0) {
473         goto err_out;
474     }
475 
476     if (!size) {
477         proxy_marshal(out_iovec, PROXY_HDR_SZ, "d", retval);
478         retval = sizeof(retval);
479     } else {
480         retval = proxy_marshal(out_iovec, PROXY_HDR_SZ, "s", &xattr);
481     }
482 err_out:
483     v9fs_string_free(&xattr);
484     v9fs_string_free(&path);
485     return retval;
486 }
487 
488 static void stat_to_prstat(ProxyStat *pr_stat, struct stat *stat)
489 {
490     memset(pr_stat, 0, sizeof(*pr_stat));
491     pr_stat->st_dev = stat->st_dev;
492     pr_stat->st_ino = stat->st_ino;
493     pr_stat->st_nlink = stat->st_nlink;
494     pr_stat->st_mode = stat->st_mode;
495     pr_stat->st_uid = stat->st_uid;
496     pr_stat->st_gid = stat->st_gid;
497     pr_stat->st_rdev = stat->st_rdev;
498     pr_stat->st_size = stat->st_size;
499     pr_stat->st_blksize = stat->st_blksize;
500     pr_stat->st_blocks = stat->st_blocks;
501     pr_stat->st_atim_sec = stat->st_atim.tv_sec;
502     pr_stat->st_atim_nsec = stat->st_atim.tv_nsec;
503     pr_stat->st_mtim_sec = stat->st_mtim.tv_sec;
504     pr_stat->st_mtim_nsec = stat->st_mtim.tv_nsec;
505     pr_stat->st_ctim_sec = stat->st_ctim.tv_sec;
506     pr_stat->st_ctim_nsec = stat->st_ctim.tv_nsec;
507 }
508 
509 static void statfs_to_prstatfs(ProxyStatFS *pr_stfs, struct statfs *stfs)
510 {
511     memset(pr_stfs, 0, sizeof(*pr_stfs));
512     pr_stfs->f_type = stfs->f_type;
513     pr_stfs->f_bsize = stfs->f_bsize;
514     pr_stfs->f_blocks = stfs->f_blocks;
515     pr_stfs->f_bfree = stfs->f_bfree;
516     pr_stfs->f_bavail = stfs->f_bavail;
517     pr_stfs->f_files = stfs->f_files;
518     pr_stfs->f_ffree = stfs->f_ffree;
519     pr_stfs->f_fsid[0] = stfs->f_fsid.__val[0];
520     pr_stfs->f_fsid[1] = stfs->f_fsid.__val[1];
521     pr_stfs->f_namelen = stfs->f_namelen;
522     pr_stfs->f_frsize = stfs->f_frsize;
523 }
524 
525 /*
526  * Gets stat/statfs information and packs in out_iovec structure
527  * on success returns number of bytes packed in out_iovec struture
528  * otherwise returns -errno
529  */
530 static int do_stat(int type, struct iovec *iovec, struct iovec *out_iovec)
531 {
532     int retval;
533     V9fsString path;
534     ProxyStat pr_stat;
535     ProxyStatFS pr_stfs;
536     struct stat st_buf;
537     struct statfs stfs_buf;
538 
539     v9fs_string_init(&path);
540     retval = proxy_unmarshal(iovec, PROXY_HDR_SZ, "s", &path);
541     if (retval < 0) {
542         return retval;
543     }
544 
545     switch (type) {
546     case T_LSTAT:
547         retval = lstat(path.data, &st_buf);
548         if (retval < 0) {
549             retval = -errno;
550         } else {
551             stat_to_prstat(&pr_stat, &st_buf);
552             retval = proxy_marshal(out_iovec, PROXY_HDR_SZ,
553                                    "qqqdddqqqqqqqqqq", pr_stat.st_dev,
554                                    pr_stat.st_ino, pr_stat.st_nlink,
555                                    pr_stat.st_mode, pr_stat.st_uid,
556                                    pr_stat.st_gid, pr_stat.st_rdev,
557                                    pr_stat.st_size, pr_stat.st_blksize,
558                                    pr_stat.st_blocks,
559                                    pr_stat.st_atim_sec, pr_stat.st_atim_nsec,
560                                    pr_stat.st_mtim_sec, pr_stat.st_mtim_nsec,
561                                    pr_stat.st_ctim_sec, pr_stat.st_ctim_nsec);
562         }
563         break;
564     case T_STATFS:
565         retval = statfs(path.data, &stfs_buf);
566         if (retval < 0) {
567             retval = -errno;
568         } else {
569             statfs_to_prstatfs(&pr_stfs, &stfs_buf);
570             retval = proxy_marshal(out_iovec, PROXY_HDR_SZ,
571                                    "qqqqqqqqqqq", pr_stfs.f_type,
572                                    pr_stfs.f_bsize, pr_stfs.f_blocks,
573                                    pr_stfs.f_bfree, pr_stfs.f_bavail,
574                                    pr_stfs.f_files, pr_stfs.f_ffree,
575                                    pr_stfs.f_fsid[0], pr_stfs.f_fsid[1],
576                                    pr_stfs.f_namelen, pr_stfs.f_frsize);
577         }
578         break;
579     }
580     v9fs_string_free(&path);
581     return retval;
582 }
583 
584 static int do_readlink(struct iovec *iovec, struct iovec *out_iovec)
585 {
586     char *buffer;
587     int size, retval;
588     V9fsString target, path;
589 
590     v9fs_string_init(&path);
591     retval = proxy_unmarshal(iovec, PROXY_HDR_SZ, "sd", &path, &size);
592     if (retval < 0) {
593         v9fs_string_free(&path);
594         return retval;
595     }
596     buffer = g_malloc(size);
597     v9fs_string_init(&target);
598     retval = readlink(path.data, buffer, size - 1);
599     if (retval > 0) {
600         buffer[retval] = '\0';
601         v9fs_string_sprintf(&target, "%s", buffer);
602         retval = proxy_marshal(out_iovec, PROXY_HDR_SZ, "s", &target);
603     } else {
604         retval = -errno;
605     }
606     g_free(buffer);
607     v9fs_string_free(&target);
608     v9fs_string_free(&path);
609     return retval;
610 }
611 
612 /*
613  * create other filesystem objects and send 0 on success
614  * return -errno on error
615  */
616 static int do_create_others(int type, struct iovec *iovec)
617 {
618     dev_t rdev;
619     int retval = 0;
620     int offset = PROXY_HDR_SZ;
621     V9fsString oldpath, path;
622     int mode, uid, gid, cur_uid, cur_gid;
623 
624     v9fs_string_init(&path);
625     v9fs_string_init(&oldpath);
626 
627     retval = proxy_unmarshal(iovec, offset, "dd", &uid, &gid);
628     if (retval < 0) {
629         return retval;
630     }
631     offset += retval;
632     retval = setugid(uid, gid, &cur_uid, &cur_gid);
633     if (retval < 0) {
634         goto unmarshal_err_out;
635     }
636     switch (type) {
637     case T_MKNOD:
638         retval = proxy_unmarshal(iovec, offset, "sdq", &path, &mode, &rdev);
639         if (retval < 0) {
640             goto err_out;
641         }
642         retval = mknod(path.data, mode, rdev);
643         break;
644     case T_MKDIR:
645         retval = proxy_unmarshal(iovec, offset, "sd", &path, &mode);
646         if (retval < 0) {
647             goto err_out;
648         }
649         retval = mkdir(path.data, mode);
650         break;
651     case T_SYMLINK:
652         retval = proxy_unmarshal(iovec, offset, "ss", &oldpath, &path);
653         if (retval < 0) {
654             goto err_out;
655         }
656         retval = symlink(oldpath.data, path.data);
657         break;
658     }
659     if (retval < 0) {
660         retval = -errno;
661     }
662 
663 err_out:
664     resetugid(cur_uid, cur_gid);
665 unmarshal_err_out:
666     v9fs_string_free(&path);
667     v9fs_string_free(&oldpath);
668     return retval;
669 }
670 
671 /*
672  * create a file and send fd on success
673  * return -errno on error
674  */
675 static int do_create(struct iovec *iovec)
676 {
677     int ret;
678     V9fsString path;
679     int flags, mode, uid, gid, cur_uid, cur_gid;
680 
681     v9fs_string_init(&path);
682     ret = proxy_unmarshal(iovec, PROXY_HDR_SZ, "sdddd",
683                           &path, &flags, &mode, &uid, &gid);
684     if (ret < 0) {
685         goto unmarshal_err_out;
686     }
687     ret = setugid(uid, gid, &cur_uid, &cur_gid);
688     if (ret < 0) {
689         goto unmarshal_err_out;
690     }
691     ret = open(path.data, flags, mode);
692     if (ret < 0) {
693         ret = -errno;
694     }
695 
696     resetugid(cur_uid, cur_gid);
697 unmarshal_err_out:
698     v9fs_string_free(&path);
699     return ret;
700 }
701 
702 /*
703  * open a file and send fd on success
704  * return -errno on error
705  */
706 static int do_open(struct iovec *iovec)
707 {
708     int flags, ret;
709     V9fsString path;
710 
711     v9fs_string_init(&path);
712     ret = proxy_unmarshal(iovec, PROXY_HDR_SZ, "sd", &path, &flags);
713     if (ret < 0) {
714         goto err_out;
715     }
716     ret = open(path.data, flags);
717     if (ret < 0) {
718         ret = -errno;
719     }
720 err_out:
721     v9fs_string_free(&path);
722     return ret;
723 }
724 
725 /* create unix domain socket and return the descriptor */
726 static int proxy_socket(const char *path, uid_t uid, gid_t gid)
727 {
728     int sock, client;
729     struct sockaddr_un proxy, qemu;
730     socklen_t size;
731 
732     /* requested socket already exists, refuse to start */
733     if (!access(path, F_OK)) {
734         do_log(LOG_CRIT, "socket already exists\n");
735         return -1;
736     }
737 
738     sock = socket(AF_UNIX, SOCK_STREAM, 0);
739     if (sock < 0) {
740         do_perror("socket");
741         return -1;
742     }
743 
744     /* mask other part of mode bits */
745     umask(7);
746 
747     proxy.sun_family = AF_UNIX;
748     strcpy(proxy.sun_path, path);
749     if (bind(sock, (struct sockaddr *)&proxy,
750             sizeof(struct sockaddr_un)) < 0) {
751         do_perror("bind");
752         return -1;
753     }
754     if (chown(proxy.sun_path, uid, gid) < 0) {
755         do_perror("chown");
756         return -1;
757     }
758     if (listen(sock, 1) < 0) {
759         do_perror("listen");
760         return -1;
761     }
762 
763     size = sizeof(qemu);
764     client = accept(sock, (struct sockaddr *)&qemu, &size);
765     if (client < 0) {
766         do_perror("accept");
767         return -1;
768     }
769     return client;
770 }
771 
772 static void usage(char *prog)
773 {
774     fprintf(stderr, "usage: %s\n"
775             " -p|--path <path> 9p path to export\n"
776             " {-f|--fd <socket-descriptor>} socket file descriptor to be used\n"
777             " {-s|--socket <socketname> socket file used for communication\n"
778             " \t-u|--uid <uid> -g|--gid <gid>} - uid:gid combination to give "
779             " access to this socket\n"
780             " \tNote: -s & -f can not be used together\n"
781             " [-n|--nodaemon] Run as a normal program\n",
782             basename(prog));
783 }
784 
785 static int process_reply(int sock, int type,
786                          struct iovec *out_iovec, int retval)
787 {
788     switch (type) {
789     case T_OPEN:
790     case T_CREATE:
791         if (send_fd(sock, retval) < 0) {
792             return -1;
793         }
794         break;
795     case T_MKNOD:
796     case T_MKDIR:
797     case T_SYMLINK:
798     case T_LINK:
799     case T_CHMOD:
800     case T_CHOWN:
801     case T_TRUNCATE:
802     case T_UTIME:
803     case T_RENAME:
804     case T_REMOVE:
805     case T_LSETXATTR:
806     case T_LREMOVEXATTR:
807         if (send_status(sock, out_iovec, retval) < 0) {
808             return -1;
809         }
810         break;
811     case T_LSTAT:
812     case T_STATFS:
813     case T_READLINK:
814     case T_LGETXATTR:
815     case T_LLISTXATTR:
816     case T_GETVERSION:
817         if (send_response(sock, out_iovec, retval) < 0) {
818             return -1;
819         }
820         break;
821     default:
822         return -1;
823         break;
824     }
825     return 0;
826 }
827 
828 static int process_requests(int sock)
829 {
830     int flags;
831     int size = 0;
832     int retval = 0;
833     uint64_t offset;
834     ProxyHeader header;
835     int mode, uid, gid;
836     V9fsString name, value;
837     struct timespec spec[2];
838     V9fsString oldpath, path;
839     struct iovec in_iovec, out_iovec;
840 
841     in_iovec.iov_base  = g_malloc(PROXY_MAX_IO_SZ + PROXY_HDR_SZ);
842     in_iovec.iov_len   = PROXY_MAX_IO_SZ + PROXY_HDR_SZ;
843     out_iovec.iov_base = g_malloc(PROXY_MAX_IO_SZ + PROXY_HDR_SZ);
844     out_iovec.iov_len  = PROXY_MAX_IO_SZ + PROXY_HDR_SZ;
845 
846     while (1) {
847         /*
848          * initialize the header type, so that we send
849          * response to proper request type.
850          */
851         header.type = 0;
852         retval = read_request(sock, &in_iovec, &header);
853         if (retval < 0) {
854             goto err_out;
855         }
856 
857         switch (header.type) {
858         case T_OPEN:
859             retval = do_open(&in_iovec);
860             break;
861         case T_CREATE:
862             retval = do_create(&in_iovec);
863             break;
864         case T_MKNOD:
865         case T_MKDIR:
866         case T_SYMLINK:
867             retval = do_create_others(header.type, &in_iovec);
868             break;
869         case T_LINK:
870             v9fs_string_init(&path);
871             v9fs_string_init(&oldpath);
872             retval = proxy_unmarshal(&in_iovec, PROXY_HDR_SZ,
873                                      "ss", &oldpath, &path);
874             if (retval > 0) {
875                 retval = link(oldpath.data, path.data);
876                 if (retval < 0) {
877                     retval = -errno;
878                 }
879             }
880             v9fs_string_free(&oldpath);
881             v9fs_string_free(&path);
882             break;
883         case T_LSTAT:
884         case T_STATFS:
885             retval = do_stat(header.type, &in_iovec, &out_iovec);
886             break;
887         case T_READLINK:
888             retval = do_readlink(&in_iovec, &out_iovec);
889             break;
890         case T_CHMOD:
891             v9fs_string_init(&path);
892             retval = proxy_unmarshal(&in_iovec, PROXY_HDR_SZ,
893                                      "sd", &path, &mode);
894             if (retval > 0) {
895                 retval = chmod(path.data, mode);
896                 if (retval < 0) {
897                     retval = -errno;
898                 }
899             }
900             v9fs_string_free(&path);
901             break;
902         case T_CHOWN:
903             v9fs_string_init(&path);
904             retval = proxy_unmarshal(&in_iovec, PROXY_HDR_SZ, "sdd", &path,
905                                      &uid, &gid);
906             if (retval > 0) {
907                 retval = lchown(path.data, uid, gid);
908                 if (retval < 0) {
909                     retval = -errno;
910                 }
911             }
912             v9fs_string_free(&path);
913             break;
914         case T_TRUNCATE:
915             v9fs_string_init(&path);
916             retval = proxy_unmarshal(&in_iovec, PROXY_HDR_SZ, "sq",
917                                      &path, &offset);
918             if (retval > 0) {
919                 retval = truncate(path.data, offset);
920                 if (retval < 0) {
921                     retval = -errno;
922                 }
923             }
924             v9fs_string_free(&path);
925             break;
926         case T_UTIME:
927             v9fs_string_init(&path);
928             retval = proxy_unmarshal(&in_iovec, PROXY_HDR_SZ, "sqqqq", &path,
929                                      &spec[0].tv_sec, &spec[0].tv_nsec,
930                                      &spec[1].tv_sec, &spec[1].tv_nsec);
931             if (retval > 0) {
932                 retval = qemu_utimens(path.data, spec);
933                 if (retval < 0) {
934                     retval = -errno;
935                 }
936             }
937             v9fs_string_free(&path);
938             break;
939         case T_RENAME:
940             v9fs_string_init(&path);
941             v9fs_string_init(&oldpath);
942             retval = proxy_unmarshal(&in_iovec, PROXY_HDR_SZ,
943                                      "ss", &oldpath, &path);
944             if (retval > 0) {
945                 retval = rename(oldpath.data, path.data);
946                 if (retval < 0) {
947                     retval = -errno;
948                 }
949             }
950             v9fs_string_free(&oldpath);
951             v9fs_string_free(&path);
952             break;
953         case T_REMOVE:
954             v9fs_string_init(&path);
955             retval = proxy_unmarshal(&in_iovec, PROXY_HDR_SZ, "s", &path);
956             if (retval > 0) {
957                 retval = remove(path.data);
958                 if (retval < 0) {
959                     retval = -errno;
960                 }
961             }
962             v9fs_string_free(&path);
963             break;
964         case T_LGETXATTR:
965         case T_LLISTXATTR:
966             retval = do_getxattr(header.type, &in_iovec, &out_iovec);
967             break;
968         case T_LSETXATTR:
969             v9fs_string_init(&path);
970             v9fs_string_init(&name);
971             v9fs_string_init(&value);
972             retval = proxy_unmarshal(&in_iovec, PROXY_HDR_SZ, "sssdd", &path,
973                                      &name, &value, &size, &flags);
974             if (retval > 0) {
975                 retval = lsetxattr(path.data,
976                                    name.data, value.data, size, flags);
977                 if (retval < 0) {
978                     retval = -errno;
979                 }
980             }
981             v9fs_string_free(&path);
982             v9fs_string_free(&name);
983             v9fs_string_free(&value);
984             break;
985         case T_LREMOVEXATTR:
986             v9fs_string_init(&path);
987             v9fs_string_init(&name);
988             retval = proxy_unmarshal(&in_iovec,
989                                      PROXY_HDR_SZ, "ss", &path, &name);
990             if (retval > 0) {
991                 retval = lremovexattr(path.data, name.data);
992                 if (retval < 0) {
993                     retval = -errno;
994                 }
995             }
996             v9fs_string_free(&path);
997             v9fs_string_free(&name);
998             break;
999         case T_GETVERSION:
1000             retval = do_getversion(&in_iovec, &out_iovec);
1001             break;
1002         default:
1003             goto err_out;
1004             break;
1005         }
1006 
1007         if (process_reply(sock, header.type, &out_iovec, retval) < 0) {
1008             goto err_out;
1009         }
1010     }
1011 err_out:
1012     g_free(in_iovec.iov_base);
1013     g_free(out_iovec.iov_base);
1014     return -1;
1015 }
1016 
1017 int main(int argc, char **argv)
1018 {
1019     int sock;
1020     uid_t own_u;
1021     gid_t own_g;
1022     char *rpath = NULL;
1023     char *sock_name = NULL;
1024     struct stat stbuf;
1025     int c, option_index;
1026 #ifdef FS_IOC_GETVERSION
1027     int retval;
1028     struct statfs st_fs;
1029 #endif
1030 
1031     is_daemon = true;
1032     sock = -1;
1033     own_u = own_g = -1;
1034     while (1) {
1035         option_index = 0;
1036         c = getopt_long(argc, argv, "p:nh?f:s:u:g:", helper_opts,
1037                         &option_index);
1038         if (c == -1) {
1039             break;
1040         }
1041         switch (c) {
1042         case 'p':
1043             rpath = g_strdup(optarg);
1044             break;
1045         case 'n':
1046             is_daemon = false;
1047             break;
1048         case 'f':
1049             sock = atoi(optarg);
1050             break;
1051         case 's':
1052             sock_name = g_strdup(optarg);
1053             break;
1054         case 'u':
1055             own_u = atoi(optarg);
1056             break;
1057         case 'g':
1058             own_g = atoi(optarg);
1059             break;
1060         case '?':
1061         case 'h':
1062         default:
1063             usage(argv[0]);
1064             exit(EXIT_FAILURE);
1065         }
1066     }
1067 
1068     /* Parameter validation */
1069     if ((sock_name == NULL && sock == -1) || rpath == NULL) {
1070         fprintf(stderr, "socket, socket descriptor or path not specified\n");
1071         usage(argv[0]);
1072         return -1;
1073     }
1074 
1075     if (sock_name && sock != -1) {
1076         fprintf(stderr, "both named socket and socket descriptor specified\n");
1077         usage(argv[0]);
1078         exit(EXIT_FAILURE);
1079     }
1080 
1081     if (sock_name && (own_u == -1 || own_g == -1)) {
1082         fprintf(stderr, "owner uid:gid not specified, ");
1083         fprintf(stderr,
1084                 "owner uid:gid specifies who can access the socket file\n");
1085         usage(argv[0]);
1086         exit(EXIT_FAILURE);
1087     }
1088 
1089     if (lstat(rpath, &stbuf) < 0) {
1090         fprintf(stderr, "invalid path \"%s\" specified, %s\n",
1091                 rpath, strerror(errno));
1092         exit(EXIT_FAILURE);
1093     }
1094 
1095     if (!S_ISDIR(stbuf.st_mode)) {
1096         fprintf(stderr, "specified path \"%s\" is not directory\n", rpath);
1097         exit(EXIT_FAILURE);
1098     }
1099 
1100     if (is_daemon) {
1101         if (daemon(0, 0) < 0) {
1102             fprintf(stderr, "daemon call failed\n");
1103             exit(EXIT_FAILURE);
1104         }
1105         openlog(PROGNAME, LOG_PID, LOG_DAEMON);
1106     }
1107 
1108     do_log(LOG_INFO, "Started\n");
1109     if (sock_name) {
1110         sock = proxy_socket(sock_name, own_u, own_g);
1111         if (sock < 0) {
1112             goto error;
1113         }
1114     }
1115 
1116     get_version = false;
1117 #ifdef FS_IOC_GETVERSION
1118     /* check whether underlying FS support IOC_GETVERSION */
1119     retval = statfs(rpath, &st_fs);
1120     if (!retval) {
1121         switch (st_fs.f_type) {
1122         case EXT2_SUPER_MAGIC:
1123         case BTRFS_SUPER_MAGIC:
1124         case REISERFS_SUPER_MAGIC:
1125         case XFS_SUPER_MAGIC:
1126             get_version = true;
1127             break;
1128         }
1129     }
1130 #endif
1131 
1132     if (chdir("/") < 0) {
1133         do_perror("chdir");
1134         goto error;
1135     }
1136     if (chroot(rpath) < 0) {
1137         do_perror("chroot");
1138         goto error;
1139     }
1140     umask(0);
1141 
1142     if (init_capabilities() < 0) {
1143         goto error;
1144     }
1145 
1146     process_requests(sock);
1147 error:
1148     do_log(LOG_INFO, "Done\n");
1149     closelog();
1150     return 0;
1151 }
1152