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