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