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