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