|
|
1.1 root 1: /*
2: * Helper for QEMU Proxy FS Driver
3: * Copyright IBM, Corp. 2011
4: *
5: * Authors:
6: * M. Mohan Kumar <[email protected]>
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 && sock != -1) {
1040: fprintf(stderr, "both named socket and socket descriptor specified\n");
1041: usage(argv[0]);
1042: exit(EXIT_FAILURE);
1043: }
1044:
1045: if (sock_name && (own_u == -1 || own_g == -1)) {
1046: fprintf(stderr, "owner uid:gid not specified, ");
1047: fprintf(stderr,
1048: "owner uid:gid specifies who can access the socket file\n");
1049: usage(argv[0]);
1050: exit(EXIT_FAILURE);
1051: }
1052:
1053: if (lstat(rpath, &stbuf) < 0) {
1054: fprintf(stderr, "invalid path \"%s\" specified, %s\n",
1055: rpath, strerror(errno));
1056: exit(EXIT_FAILURE);
1057: }
1058:
1059: if (!S_ISDIR(stbuf.st_mode)) {
1060: fprintf(stderr, "specified path \"%s\" is not directory\n", rpath);
1061: exit(EXIT_FAILURE);
1062: }
1063:
1064: if (is_daemon) {
1065: if (daemon(0, 0) < 0) {
1066: fprintf(stderr, "daemon call failed\n");
1067: exit(EXIT_FAILURE);
1068: }
1069: openlog(PROGNAME, LOG_PID, LOG_DAEMON);
1070: }
1071:
1072: do_log(LOG_INFO, "Started\n");
1073: if (sock_name) {
1074: sock = proxy_socket(sock_name, own_u, own_g);
1075: if (sock < 0) {
1076: goto error;
1077: }
1078: }
1079:
1080: get_version = false;
1081: #ifdef FS_IOC_GETVERSION
1082: /* check whether underlying FS support IOC_GETVERSION */
1083: retval = statfs(rpath, &st_fs);
1084: if (!retval) {
1085: switch (st_fs.f_type) {
1086: case EXT2_SUPER_MAGIC:
1087: case BTRFS_SUPER_MAGIC:
1088: case REISERFS_SUPER_MAGIC:
1089: case XFS_SUPER_MAGIC:
1090: get_version = true;
1091: break;
1092: }
1093: }
1094: #endif
1095:
1096: if (chdir("/") < 0) {
1097: do_perror("chdir");
1098: goto error;
1099: }
1100: if (chroot(rpath) < 0) {
1101: do_perror("chroot");
1102: goto error;
1103: }
1104: umask(0);
1105:
1106: if (init_capabilities() < 0) {
1107: goto error;
1108: }
1109:
1110: process_requests(sock);
1111: error:
1112: do_log(LOG_INFO, "Done\n");
1113: closelog();
1114: return 0;
1115: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.