xref: /openbmc/qemu/fsdev/virtfs-proxy-helper.c (revision 1529ae1b)
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_socket.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  * 4. If the file system user ID is changed from 0 to nonzero (see setfsuid(2))
276  * then the following capabilities are cleared from the effective set:
277  * CAP_CHOWN, CAP_DAC_OVERRIDE, CAP_DAC_READ_SEARCH,  CAP_FOWNER, CAP_FSETID,
278  * CAP_LINUX_IMMUTABLE  (since  Linux 2.2.30), CAP_MAC_OVERRIDE, and CAP_MKNOD
279  * (since Linux 2.2.30). If the file system UID is changed from nonzero to 0,
280  * then any of these capabilities that are enabled in the permitted set
281  * are enabled in the effective set.
282  */
283 static int setfsugid(int uid, int gid)
284 {
285     /*
286      * We still need DAC_OVERRIDE because  we don't change
287      * supplementary group ids, and hence may be subjected DAC rules
288      */
289     cap_value_t cap_list[] = {
290         CAP_DAC_OVERRIDE,
291     };
292 
293     setfsgid(gid);
294     setfsuid(uid);
295 
296     if (uid != 0 || gid != 0) {
297         return do_cap_set(cap_list, ARRAY_SIZE(cap_list), 0);
298     }
299     return 0;
300 }
301 
302 /*
303  * send response in two parts
304  * 1) ProxyHeader
305  * 2) Response or error status
306  * This function should be called with marshaled response
307  * send_response constructs header part and error part only.
308  * send response sends {ProxyHeader,Response} if the request was success
309  * otherwise sends {ProxyHeader,error status}
310  */
311 static int send_response(int sock, struct iovec *iovec, int size)
312 {
313     int retval;
314     ProxyHeader header;
315 
316     /*
317      * If response size exceeds available iovec->iov_len,
318      * we return ENOBUFS
319      */
320     if (size > PROXY_MAX_IO_SZ) {
321         size = -ENOBUFS;
322     }
323 
324     if (size < 0) {
325         /*
326          * In case of error we would not have got the error encoded
327          * already so encode the error here.
328          */
329         header.type = T_ERROR;
330         header.size = sizeof(size);
331         proxy_marshal(iovec, PROXY_HDR_SZ, "d", size);
332     } else {
333         header.type = T_SUCCESS;
334         header.size = size;
335     }
336     proxy_marshal(iovec, 0, "dd", header.type, header.size);
337     retval = socket_write(sock, iovec->iov_base, header.size + PROXY_HDR_SZ);
338     if (retval < 0) {
339         return retval;;
340     }
341     return 0;
342 }
343 
344 /*
345  * gets generation number
346  * returns -errno on failure and sizeof(generation number) on success
347  */
348 static int do_getversion(struct iovec *iovec, struct iovec *out_iovec)
349 {
350     uint64_t version;
351     int retval = -ENOTTY;
352 #ifdef FS_IOC_GETVERSION
353     int fd;
354     V9fsString path;
355 #endif
356 
357 
358     /* no need to issue ioctl */
359     if (!get_version) {
360         version = 0;
361         retval = proxy_marshal(out_iovec, PROXY_HDR_SZ, "q", version);
362         return retval;
363     }
364 #ifdef FS_IOC_GETVERSION
365     retval = proxy_unmarshal(iovec, PROXY_HDR_SZ, "s", &path);
366     if (retval < 0) {
367         return retval;
368     }
369 
370     fd = open(path.data, O_RDONLY);
371     if (fd < 0) {
372         retval = -errno;
373         goto err_out;
374     }
375     if (ioctl(fd, FS_IOC_GETVERSION, &version) < 0) {
376         retval = -errno;
377     } else {
378         retval = proxy_marshal(out_iovec, PROXY_HDR_SZ, "q", version);
379     }
380     close(fd);
381 err_out:
382     v9fs_string_free(&path);
383 #endif
384     return retval;
385 }
386 
387 static int do_getxattr(int type, struct iovec *iovec, struct iovec *out_iovec)
388 {
389     int size = 0, offset, retval;
390     V9fsString path, name, xattr;
391 
392     v9fs_string_init(&xattr);
393     v9fs_string_init(&path);
394     retval = proxy_unmarshal(iovec, PROXY_HDR_SZ, "ds", &size, &path);
395     if (retval < 0) {
396         return retval;
397     }
398     offset = PROXY_HDR_SZ + retval;
399 
400     if (size) {
401         xattr.data = g_malloc(size);
402         xattr.size = size;
403     }
404     switch (type) {
405     case T_LGETXATTR:
406         v9fs_string_init(&name);
407         retval = proxy_unmarshal(iovec, offset, "s", &name);
408         if (retval > 0) {
409             retval = lgetxattr(path.data, name.data, xattr.data, size);
410             if (retval < 0) {
411                 retval = -errno;
412             } else {
413                 xattr.size = retval;
414             }
415         }
416         v9fs_string_free(&name);
417         break;
418     case T_LLISTXATTR:
419         retval = llistxattr(path.data, xattr.data, size);
420         if (retval < 0) {
421             retval = -errno;
422         } else {
423             xattr.size = retval;
424         }
425         break;
426     }
427     if (retval < 0) {
428         goto err_out;
429     }
430 
431     if (!size) {
432         proxy_marshal(out_iovec, PROXY_HDR_SZ, "d", retval);
433         retval = sizeof(retval);
434     } else {
435         retval = proxy_marshal(out_iovec, PROXY_HDR_SZ, "s", &xattr);
436     }
437 err_out:
438     v9fs_string_free(&xattr);
439     v9fs_string_free(&path);
440     return retval;
441 }
442 
443 static void stat_to_prstat(ProxyStat *pr_stat, struct stat *stat)
444 {
445     memset(pr_stat, 0, sizeof(*pr_stat));
446     pr_stat->st_dev = stat->st_dev;
447     pr_stat->st_ino = stat->st_ino;
448     pr_stat->st_nlink = stat->st_nlink;
449     pr_stat->st_mode = stat->st_mode;
450     pr_stat->st_uid = stat->st_uid;
451     pr_stat->st_gid = stat->st_gid;
452     pr_stat->st_rdev = stat->st_rdev;
453     pr_stat->st_size = stat->st_size;
454     pr_stat->st_blksize = stat->st_blksize;
455     pr_stat->st_blocks = stat->st_blocks;
456     pr_stat->st_atim_sec = stat->st_atim.tv_sec;
457     pr_stat->st_atim_nsec = stat->st_atim.tv_nsec;
458     pr_stat->st_mtim_sec = stat->st_mtim.tv_sec;
459     pr_stat->st_mtim_nsec = stat->st_mtim.tv_nsec;
460     pr_stat->st_ctim_sec = stat->st_ctim.tv_sec;
461     pr_stat->st_ctim_nsec = stat->st_ctim.tv_nsec;
462 }
463 
464 static void statfs_to_prstatfs(ProxyStatFS *pr_stfs, struct statfs *stfs)
465 {
466     memset(pr_stfs, 0, sizeof(*pr_stfs));
467     pr_stfs->f_type = stfs->f_type;
468     pr_stfs->f_bsize = stfs->f_bsize;
469     pr_stfs->f_blocks = stfs->f_blocks;
470     pr_stfs->f_bfree = stfs->f_bfree;
471     pr_stfs->f_bavail = stfs->f_bavail;
472     pr_stfs->f_files = stfs->f_files;
473     pr_stfs->f_ffree = stfs->f_ffree;
474     pr_stfs->f_fsid[0] = stfs->f_fsid.__val[0];
475     pr_stfs->f_fsid[1] = stfs->f_fsid.__val[1];
476     pr_stfs->f_namelen = stfs->f_namelen;
477     pr_stfs->f_frsize = stfs->f_frsize;
478 }
479 
480 /*
481  * Gets stat/statfs information and packs in out_iovec structure
482  * on success returns number of bytes packed in out_iovec struture
483  * otherwise returns -errno
484  */
485 static int do_stat(int type, struct iovec *iovec, struct iovec *out_iovec)
486 {
487     int retval;
488     V9fsString path;
489     ProxyStat pr_stat;
490     ProxyStatFS pr_stfs;
491     struct stat st_buf;
492     struct statfs stfs_buf;
493 
494     v9fs_string_init(&path);
495     retval = proxy_unmarshal(iovec, PROXY_HDR_SZ, "s", &path);
496     if (retval < 0) {
497         return retval;
498     }
499 
500     switch (type) {
501     case T_LSTAT:
502         retval = lstat(path.data, &st_buf);
503         if (retval < 0) {
504             retval = -errno;
505         } else {
506             stat_to_prstat(&pr_stat, &st_buf);
507             retval = proxy_marshal(out_iovec, PROXY_HDR_SZ,
508                                    "qqqdddqqqqqqqqqq", pr_stat.st_dev,
509                                    pr_stat.st_ino, pr_stat.st_nlink,
510                                    pr_stat.st_mode, pr_stat.st_uid,
511                                    pr_stat.st_gid, pr_stat.st_rdev,
512                                    pr_stat.st_size, pr_stat.st_blksize,
513                                    pr_stat.st_blocks,
514                                    pr_stat.st_atim_sec, pr_stat.st_atim_nsec,
515                                    pr_stat.st_mtim_sec, pr_stat.st_mtim_nsec,
516                                    pr_stat.st_ctim_sec, pr_stat.st_ctim_nsec);
517         }
518         break;
519     case T_STATFS:
520         retval = statfs(path.data, &stfs_buf);
521         if (retval < 0) {
522             retval = -errno;
523         } else {
524             statfs_to_prstatfs(&pr_stfs, &stfs_buf);
525             retval = proxy_marshal(out_iovec, PROXY_HDR_SZ,
526                                    "qqqqqqqqqqq", pr_stfs.f_type,
527                                    pr_stfs.f_bsize, pr_stfs.f_blocks,
528                                    pr_stfs.f_bfree, pr_stfs.f_bavail,
529                                    pr_stfs.f_files, pr_stfs.f_ffree,
530                                    pr_stfs.f_fsid[0], pr_stfs.f_fsid[1],
531                                    pr_stfs.f_namelen, pr_stfs.f_frsize);
532         }
533         break;
534     }
535     v9fs_string_free(&path);
536     return retval;
537 }
538 
539 static int do_readlink(struct iovec *iovec, struct iovec *out_iovec)
540 {
541     char *buffer;
542     int size, retval;
543     V9fsString target, path;
544 
545     v9fs_string_init(&path);
546     retval = proxy_unmarshal(iovec, PROXY_HDR_SZ, "sd", &path, &size);
547     if (retval < 0) {
548         v9fs_string_free(&path);
549         return retval;
550     }
551     buffer = g_malloc(size);
552     v9fs_string_init(&target);
553     retval = readlink(path.data, buffer, size);
554     if (retval > 0) {
555         buffer[retval] = '\0';
556         v9fs_string_sprintf(&target, "%s", buffer);
557         retval = proxy_marshal(out_iovec, PROXY_HDR_SZ, "s", &target);
558     } else {
559         retval = -errno;
560     }
561     g_free(buffer);
562     v9fs_string_free(&target);
563     v9fs_string_free(&path);
564     return retval;
565 }
566 
567 /*
568  * create other filesystem objects and send 0 on success
569  * return -errno on error
570  */
571 static int do_create_others(int type, struct iovec *iovec)
572 {
573     dev_t rdev;
574     int retval = 0;
575     int offset = PROXY_HDR_SZ;
576     V9fsString oldpath, path;
577     int mode, uid, gid, cur_uid, cur_gid;
578 
579     v9fs_string_init(&path);
580     v9fs_string_init(&oldpath);
581     cur_uid = geteuid();
582     cur_gid = getegid();
583 
584     retval = proxy_unmarshal(iovec, offset, "dd", &uid, &gid);
585     if (retval < 0) {
586         return retval;
587     }
588     offset += retval;
589     retval = setfsugid(uid, gid);
590     if (retval < 0) {
591         retval = -errno;
592         goto err_out;
593     }
594     switch (type) {
595     case T_MKNOD:
596         retval = proxy_unmarshal(iovec, offset, "sdq", &path, &mode, &rdev);
597         if (retval < 0) {
598             goto err_out;
599         }
600         retval = mknod(path.data, mode, rdev);
601         break;
602     case T_MKDIR:
603         retval = proxy_unmarshal(iovec, offset, "sd", &path, &mode);
604         if (retval < 0) {
605             goto err_out;
606         }
607         retval = mkdir(path.data, mode);
608         break;
609     case T_SYMLINK:
610         retval = proxy_unmarshal(iovec, offset, "ss", &oldpath, &path);
611         if (retval < 0) {
612             goto err_out;
613         }
614         retval = symlink(oldpath.data, path.data);
615         break;
616     }
617     if (retval < 0) {
618         retval = -errno;
619     }
620 
621 err_out:
622     v9fs_string_free(&path);
623     v9fs_string_free(&oldpath);
624     setfsugid(cur_uid, cur_gid);
625     return retval;
626 }
627 
628 /*
629  * create a file and send fd on success
630  * return -errno on error
631  */
632 static int do_create(struct iovec *iovec)
633 {
634     int ret;
635     V9fsString path;
636     int flags, mode, uid, gid, cur_uid, cur_gid;
637 
638     v9fs_string_init(&path);
639     ret = proxy_unmarshal(iovec, PROXY_HDR_SZ, "sdddd",
640                           &path, &flags, &mode, &uid, &gid);
641     if (ret < 0) {
642         goto unmarshal_err_out;
643     }
644     cur_uid = geteuid();
645     cur_gid = getegid();
646     ret = setfsugid(uid, gid);
647     if (ret < 0) {
648         /*
649          * On failure reset back to the
650          * old uid/gid
651          */
652         ret = -errno;
653         goto err_out;
654     }
655     ret = open(path.data, flags, mode);
656     if (ret < 0) {
657         ret = -errno;
658     }
659 
660 err_out:
661     setfsugid(cur_uid, cur_gid);
662 unmarshal_err_out:
663     v9fs_string_free(&path);
664     return ret;
665 }
666 
667 /*
668  * open a file and send fd on success
669  * return -errno on error
670  */
671 static int do_open(struct iovec *iovec)
672 {
673     int flags, ret;
674     V9fsString path;
675 
676     v9fs_string_init(&path);
677     ret = proxy_unmarshal(iovec, PROXY_HDR_SZ, "sd", &path, &flags);
678     if (ret < 0) {
679         goto err_out;
680     }
681     ret = open(path.data, flags);
682     if (ret < 0) {
683         ret = -errno;
684     }
685 err_out:
686     v9fs_string_free(&path);
687     return ret;
688 }
689 
690 /* create unix domain socket and return the descriptor */
691 static int proxy_socket(const char *path, uid_t uid, gid_t gid)
692 {
693     int sock, client;
694     struct sockaddr_un proxy, qemu;
695     socklen_t size;
696 
697     /* requested socket already exists, refuse to start */
698     if (!access(path, F_OK)) {
699         do_log(LOG_CRIT, "socket already exists\n");
700         return -1;
701     }
702 
703     sock = socket(AF_UNIX, SOCK_STREAM, 0);
704     if (sock < 0) {
705         do_perror("socket");
706         return -1;
707     }
708 
709     /* mask other part of mode bits */
710     umask(7);
711 
712     proxy.sun_family = AF_UNIX;
713     strcpy(proxy.sun_path, path);
714     if (bind(sock, (struct sockaddr *)&proxy,
715             sizeof(struct sockaddr_un)) < 0) {
716         do_perror("bind");
717         return -1;
718     }
719     if (chown(proxy.sun_path, uid, gid) < 0) {
720         do_perror("chown");
721         return -1;
722     }
723     if (listen(sock, 1) < 0) {
724         do_perror("listen");
725         return -1;
726     }
727 
728     client = accept(sock, (struct sockaddr *)&qemu, &size);
729     if (client < 0) {
730         do_perror("accept");
731         return -1;
732     }
733     return client;
734 }
735 
736 static void usage(char *prog)
737 {
738     fprintf(stderr, "usage: %s\n"
739             " -p|--path <path> 9p path to export\n"
740             " {-f|--fd <socket-descriptor>} socket file descriptor to be used\n"
741             " {-s|--socket <socketname> socket file used for communication\n"
742             " \t-u|--uid <uid> -g|--gid <gid>} - uid:gid combination to give "
743             " access to this socket\n"
744             " \tNote: -s & -f can not be used together\n"
745             " [-n|--nodaemon] Run as a normal program\n",
746             basename(prog));
747 }
748 
749 static int process_reply(int sock, int type,
750                          struct iovec *out_iovec, int retval)
751 {
752     switch (type) {
753     case T_OPEN:
754     case T_CREATE:
755         if (send_fd(sock, retval) < 0) {
756             return -1;
757         }
758         break;
759     case T_MKNOD:
760     case T_MKDIR:
761     case T_SYMLINK:
762     case T_LINK:
763     case T_CHMOD:
764     case T_CHOWN:
765     case T_TRUNCATE:
766     case T_UTIME:
767     case T_RENAME:
768     case T_REMOVE:
769     case T_LSETXATTR:
770     case T_LREMOVEXATTR:
771         if (send_status(sock, out_iovec, retval) < 0) {
772             return -1;
773         }
774         break;
775     case T_LSTAT:
776     case T_STATFS:
777     case T_READLINK:
778     case T_LGETXATTR:
779     case T_LLISTXATTR:
780     case T_GETVERSION:
781         if (send_response(sock, out_iovec, retval) < 0) {
782             return -1;
783         }
784         break;
785     default:
786         return -1;
787         break;
788     }
789     return 0;
790 }
791 
792 static int process_requests(int sock)
793 {
794     int flags;
795     int size = 0;
796     int retval = 0;
797     uint64_t offset;
798     ProxyHeader header;
799     int mode, uid, gid;
800     V9fsString name, value;
801     struct timespec spec[2];
802     V9fsString oldpath, path;
803     struct iovec in_iovec, out_iovec;
804 
805     in_iovec.iov_base  = g_malloc(PROXY_MAX_IO_SZ + PROXY_HDR_SZ);
806     in_iovec.iov_len   = PROXY_MAX_IO_SZ + PROXY_HDR_SZ;
807     out_iovec.iov_base = g_malloc(PROXY_MAX_IO_SZ + PROXY_HDR_SZ);
808     out_iovec.iov_len  = PROXY_MAX_IO_SZ + PROXY_HDR_SZ;
809 
810     while (1) {
811         /*
812          * initialize the header type, so that we send
813          * response to proper request type.
814          */
815         header.type = 0;
816         retval = read_request(sock, &in_iovec, &header);
817         if (retval < 0) {
818             goto err_out;
819         }
820 
821         switch (header.type) {
822         case T_OPEN:
823             retval = do_open(&in_iovec);
824             break;
825         case T_CREATE:
826             retval = do_create(&in_iovec);
827             break;
828         case T_MKNOD:
829         case T_MKDIR:
830         case T_SYMLINK:
831             retval = do_create_others(header.type, &in_iovec);
832             break;
833         case T_LINK:
834             v9fs_string_init(&path);
835             v9fs_string_init(&oldpath);
836             retval = proxy_unmarshal(&in_iovec, PROXY_HDR_SZ,
837                                      "ss", &oldpath, &path);
838             if (retval > 0) {
839                 retval = link(oldpath.data, path.data);
840                 if (retval < 0) {
841                     retval = -errno;
842                 }
843             }
844             v9fs_string_free(&oldpath);
845             v9fs_string_free(&path);
846             break;
847         case T_LSTAT:
848         case T_STATFS:
849             retval = do_stat(header.type, &in_iovec, &out_iovec);
850             break;
851         case T_READLINK:
852             retval = do_readlink(&in_iovec, &out_iovec);
853             break;
854         case T_CHMOD:
855             v9fs_string_init(&path);
856             retval = proxy_unmarshal(&in_iovec, PROXY_HDR_SZ,
857                                      "sd", &path, &mode);
858             if (retval > 0) {
859                 retval = chmod(path.data, mode);
860                 if (retval < 0) {
861                     retval = -errno;
862                 }
863             }
864             v9fs_string_free(&path);
865             break;
866         case T_CHOWN:
867             v9fs_string_init(&path);
868             retval = proxy_unmarshal(&in_iovec, PROXY_HDR_SZ, "sdd", &path,
869                                      &uid, &gid);
870             if (retval > 0) {
871                 retval = lchown(path.data, uid, gid);
872                 if (retval < 0) {
873                     retval = -errno;
874                 }
875             }
876             v9fs_string_free(&path);
877             break;
878         case T_TRUNCATE:
879             v9fs_string_init(&path);
880             retval = proxy_unmarshal(&in_iovec, PROXY_HDR_SZ, "sq",
881                                      &path, &offset);
882             if (retval > 0) {
883                 retval = truncate(path.data, offset);
884                 if (retval < 0) {
885                     retval = -errno;
886                 }
887             }
888             v9fs_string_free(&path);
889             break;
890         case T_UTIME:
891             v9fs_string_init(&path);
892             retval = proxy_unmarshal(&in_iovec, PROXY_HDR_SZ, "sqqqq", &path,
893                                      &spec[0].tv_sec, &spec[0].tv_nsec,
894                                      &spec[1].tv_sec, &spec[1].tv_nsec);
895             if (retval > 0) {
896                 retval = qemu_utimens(path.data, spec);
897                 if (retval < 0) {
898                     retval = -errno;
899                 }
900             }
901             v9fs_string_free(&path);
902             break;
903         case T_RENAME:
904             v9fs_string_init(&path);
905             v9fs_string_init(&oldpath);
906             retval = proxy_unmarshal(&in_iovec, PROXY_HDR_SZ,
907                                      "ss", &oldpath, &path);
908             if (retval > 0) {
909                 retval = rename(oldpath.data, path.data);
910                 if (retval < 0) {
911                     retval = -errno;
912                 }
913             }
914             v9fs_string_free(&oldpath);
915             v9fs_string_free(&path);
916             break;
917         case T_REMOVE:
918             v9fs_string_init(&path);
919             retval = proxy_unmarshal(&in_iovec, PROXY_HDR_SZ, "s", &path);
920             if (retval > 0) {
921                 retval = remove(path.data);
922                 if (retval < 0) {
923                     retval = -errno;
924                 }
925             }
926             v9fs_string_free(&path);
927             break;
928         case T_LGETXATTR:
929         case T_LLISTXATTR:
930             retval = do_getxattr(header.type, &in_iovec, &out_iovec);
931             break;
932         case T_LSETXATTR:
933             v9fs_string_init(&path);
934             v9fs_string_init(&name);
935             v9fs_string_init(&value);
936             retval = proxy_unmarshal(&in_iovec, PROXY_HDR_SZ, "sssdd", &path,
937                                      &name, &value, &size, &flags);
938             if (retval > 0) {
939                 retval = lsetxattr(path.data,
940                                    name.data, value.data, size, flags);
941                 if (retval < 0) {
942                     retval = -errno;
943                 }
944             }
945             v9fs_string_free(&path);
946             v9fs_string_free(&name);
947             v9fs_string_free(&value);
948             break;
949         case T_LREMOVEXATTR:
950             v9fs_string_init(&path);
951             v9fs_string_init(&name);
952             retval = proxy_unmarshal(&in_iovec,
953                                      PROXY_HDR_SZ, "ss", &path, &name);
954             if (retval > 0) {
955                 retval = lremovexattr(path.data, name.data);
956                 if (retval < 0) {
957                     retval = -errno;
958                 }
959             }
960             v9fs_string_free(&path);
961             v9fs_string_free(&name);
962             break;
963         case T_GETVERSION:
964             retval = do_getversion(&in_iovec, &out_iovec);
965             break;
966         default:
967             goto err_out;
968             break;
969         }
970 
971         if (process_reply(sock, header.type, &out_iovec, retval) < 0) {
972             goto err_out;
973         }
974     }
975 err_out:
976     g_free(in_iovec.iov_base);
977     g_free(out_iovec.iov_base);
978     return -1;
979 }
980 
981 int main(int argc, char **argv)
982 {
983     int sock;
984     uid_t own_u;
985     gid_t own_g;
986     char *rpath = NULL;
987     char *sock_name = NULL;
988     struct stat stbuf;
989     int c, option_index;
990 #ifdef FS_IOC_GETVERSION
991     int retval;
992     struct statfs st_fs;
993 #endif
994 
995     is_daemon = true;
996     sock = -1;
997     own_u = own_g = -1;
998     while (1) {
999         option_index = 0;
1000         c = getopt_long(argc, argv, "p:nh?f:s:u:g:", helper_opts,
1001                         &option_index);
1002         if (c == -1) {
1003             break;
1004         }
1005         switch (c) {
1006         case 'p':
1007             rpath = strdup(optarg);
1008             break;
1009         case 'n':
1010             is_daemon = false;
1011             break;
1012         case 'f':
1013             sock = atoi(optarg);
1014             break;
1015         case 's':
1016             sock_name = strdup(optarg);
1017             break;
1018         case 'u':
1019             own_u = atoi(optarg);
1020             break;
1021         case 'g':
1022             own_g = atoi(optarg);
1023             break;
1024         case '?':
1025         case 'h':
1026         default:
1027             usage(argv[0]);
1028             exit(EXIT_FAILURE);
1029         }
1030     }
1031 
1032     /* Parameter validation */
1033     if ((sock_name == NULL && sock == -1) || rpath == NULL) {
1034         fprintf(stderr, "socket, socket descriptor or path not specified\n");
1035         usage(argv[0]);
1036         return -1;
1037     }
1038 
1039     if (*sock_name && (own_u == -1 || own_g == -1)) {
1040         fprintf(stderr, "owner uid:gid not specified, ");
1041         fprintf(stderr,
1042                 "owner uid:gid specifies who can access the socket file\n");
1043         usage(argv[0]);
1044         exit(EXIT_FAILURE);
1045     }
1046 
1047     if (lstat(rpath, &stbuf) < 0) {
1048         fprintf(stderr, "invalid path \"%s\" specified, %s\n",
1049                 rpath, strerror(errno));
1050         exit(EXIT_FAILURE);
1051     }
1052 
1053     if (!S_ISDIR(stbuf.st_mode)) {
1054         fprintf(stderr, "specified path \"%s\" is not directory\n", rpath);
1055         exit(EXIT_FAILURE);
1056     }
1057 
1058     if (is_daemon) {
1059         if (daemon(0, 0) < 0) {
1060             fprintf(stderr, "daemon call failed\n");
1061             exit(EXIT_FAILURE);
1062         }
1063         openlog(PROGNAME, LOG_PID, LOG_DAEMON);
1064     }
1065 
1066     do_log(LOG_INFO, "Started\n");
1067     if (*sock_name) {
1068         sock = proxy_socket(sock_name, own_u, own_g);
1069         if (sock < 0) {
1070             goto error;
1071         }
1072     }
1073 
1074     get_version = false;
1075 #ifdef FS_IOC_GETVERSION
1076     /* check whether underlying FS support IOC_GETVERSION */
1077     retval = statfs(rpath, &st_fs);
1078     if (!retval) {
1079         switch (st_fs.f_type) {
1080         case EXT2_SUPER_MAGIC:
1081         case BTRFS_SUPER_MAGIC:
1082         case REISERFS_SUPER_MAGIC:
1083         case XFS_SUPER_MAGIC:
1084             get_version = true;
1085             break;
1086         }
1087     }
1088 #endif
1089 
1090     if (chdir("/") < 0) {
1091         do_perror("chdir");
1092         goto error;
1093     }
1094     if (chroot(rpath) < 0) {
1095         do_perror("chroot");
1096         goto error;
1097     }
1098     umask(0);
1099 
1100     if (init_capabilities() < 0) {
1101         goto error;
1102     }
1103 
1104     process_requests(sock);
1105 error:
1106     do_log(LOG_INFO, "Done\n");
1107     closelog();
1108     return 0;
1109 }
1110