From 17bff52b6225419931703aa04cc454c16cf5adad Mon Sep 17 00:00:00 2001 From: "M. Mohan Kumar" Date: Wed, 14 Dec 2011 13:58:42 +0530 Subject: hw/9pfs: File system helper process for qemu 9p proxy FS Provide root privilege access to QEMU 9p proxy filesystem using socket communication. Proxy helper is started by root user as: ~ # virtfs-proxy-helper -f|--fd -p|--path Signed-off-by: M. Mohan Kumar Signed-off-by: Aneesh Kumar K.V --- Makefile | 3 + configure | 19 +++ fsdev/virtfs-proxy-helper.c | 300 ++++++++++++++++++++++++++++++++++++++++++++ hw/9pfs/virtio-9p-proxy.h | 9 ++ 4 files changed, 331 insertions(+) create mode 100644 fsdev/virtfs-proxy-helper.c diff --git a/Makefile b/Makefile index 0838bc4..51591c3 100644 --- a/Makefile +++ b/Makefile @@ -155,6 +155,9 @@ qemu-img$(EXESUF): qemu-img.o $(tools-obj-y) $(block-obj-y) qemu-nbd$(EXESUF): qemu-nbd.o $(tools-obj-y) $(block-obj-y) qemu-io$(EXESUF): qemu-io.o cmd.o $(tools-obj-y) $(block-obj-y) +fsdev/virtfs-proxy-helper$(EXESUF): fsdev/virtfs-proxy-helper.o fsdev/virtio-9p-marshal.o +fsdev/virtfs-proxy-helper$(EXESUF): LIBS += -lcap + qemu-img-cmds.h: $(SRC_PATH)/qemu-img-cmds.hx $(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -h < $< > $@," GEN $@") diff --git a/configure b/configure index 640e815..c136f12 100755 --- a/configure +++ b/configure @@ -1967,6 +1967,22 @@ else fi ########################################## +# libcap probe + +if test "$cap" != "no" ; then + cat > $TMPC < +#include +int main(void) { cap_t caps; caps = cap_init(); } +EOF + if compile_prog "" "-lcap" ; then + cap=yes + else + cap=no + fi +fi + +########################################## # pthread probe PTHREADLIBS_LIST="-pthread -lpthread -lpthreadGC2" @@ -2767,6 +2783,9 @@ confdir=$sysconfdir$confsuffix tools= if test "$softmmu" = yes ; then tools="qemu-img\$(EXESUF) qemu-io\$(EXESUF) $tools" + if [ "$cap" = "yes" -a "$linux" = "yes" ] ; then + tools="$tools fsdev/virtfs-proxy-helper\$(EXESUF)" + fi if [ "$linux" = "yes" -o "$bsd" = "yes" -o "$solaris" = "yes" ] ; then tools="qemu-nbd\$(EXESUF) $tools" if [ "$guest_agent" = "yes" ]; then diff --git a/fsdev/virtfs-proxy-helper.c b/fsdev/virtfs-proxy-helper.c new file mode 100644 index 0000000..5fc2fc4 --- /dev/null +++ b/fsdev/virtfs-proxy-helper.c @@ -0,0 +1,300 @@ +/* + * Helper for QEMU Proxy FS Driver + * Copyright IBM, Corp. 2011 + * + * Authors: + * M. Mohan Kumar + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "qemu-common.h" +#include "virtio-9p-marshal.h" +#include "hw/9pfs/virtio-9p-proxy.h" + +#define PROGNAME "virtfs-proxy-helper" + +static struct option helper_opts[] = { + {"fd", required_argument, NULL, 'f'}, + {"path", required_argument, NULL, 'p'}, + {"nodaemon", no_argument, NULL, 'n'}, +}; + +static bool is_daemon; + +static void do_log(int loglevel, const char *format, ...) +{ + va_list ap; + + va_start(ap, format); + if (is_daemon) { + vsyslog(LOG_CRIT, format, ap); + } else { + vfprintf(stderr, format, ap); + } + va_end(ap); +} + +static void do_perror(const char *string) +{ + if (is_daemon) { + syslog(LOG_CRIT, "%s:%s", string, strerror(errno)); + } else { + fprintf(stderr, "%s:%s\n", string, strerror(errno)); + } +} + +static int do_cap_set(cap_value_t *cap_value, int size, int reset) +{ + cap_t caps; + if (reset) { + /* + * Start with an empty set and set permitted and effective + */ + caps = cap_init(); + if (caps == NULL) { + do_perror("cap_init"); + return -1; + } + if (cap_set_flag(caps, CAP_PERMITTED, size, cap_value, CAP_SET) < 0) { + do_perror("cap_set_flag"); + goto error; + } + } else { + caps = cap_get_proc(); + if (!caps) { + do_perror("cap_get_proc"); + return -1; + } + } + if (cap_set_flag(caps, CAP_EFFECTIVE, size, cap_value, CAP_SET) < 0) { + do_perror("cap_set_flag"); + goto error; + } + if (cap_set_proc(caps) < 0) { + do_perror("cap_set_proc"); + goto error; + } + cap_free(caps); + return 0; + +error: + cap_free(caps); + return -1; +} + +static int init_capabilities(void) +{ + /* helper needs following capbabilities only */ + cap_value_t cap_list[] = { + CAP_CHOWN, + CAP_DAC_OVERRIDE, + CAP_FOWNER, + CAP_FSETID, + CAP_SETGID, + CAP_MKNOD, + CAP_SETUID, + }; + return do_cap_set(cap_list, ARRAY_SIZE(cap_list), 1); +} + +static int socket_read(int sockfd, void *buff, ssize_t size) +{ + ssize_t retval, total = 0; + + while (size) { + retval = read(sockfd, buff, size); + if (retval == 0) { + return -EIO; + } + if (retval < 0) { + if (errno == EINTR) { + continue; + } + return -errno; + } + size -= retval; + buff += retval; + total += retval; + } + return total; +} + +static int socket_write(int sockfd, void *buff, ssize_t size) +{ + ssize_t retval, total = 0; + + while (size) { + retval = write(sockfd, buff, size); + if (retval < 0) { + if (errno == EINTR) { + continue; + } + return -errno; + } + size -= retval; + buff += retval; + total += retval; + } + return total; +} + +static int read_request(int sockfd, struct iovec *iovec, ProxyHeader *header) +{ + int retval; + + /* + * read the request header. + */ + iovec->iov_len = 0; + retval = socket_read(sockfd, iovec->iov_base, PROXY_HDR_SZ); + if (retval < 0) { + return retval; + } + iovec->iov_len = PROXY_HDR_SZ; + retval = proxy_unmarshal(iovec, 0, "dd", &header->type, &header->size); + if (retval < 0) { + return retval; + } + /* + * We can't process message.size > PROXY_MAX_IO_SZ. + * Treat it as fatal error + */ + if (header->size > PROXY_MAX_IO_SZ) { + return -ENOBUFS; + } + retval = socket_read(sockfd, iovec->iov_base + PROXY_HDR_SZ, header->size); + if (retval < 0) { + return retval; + } + iovec->iov_len += header->size; + return 0; +} + +static void usage(char *prog) +{ + fprintf(stderr, "usage: %s\n" + " -p|--path 9p path to export\n" + " {-f|--fd } socket file descriptor to be used\n" + " [-n|--nodaemon] Run as a normal program\n", + basename(prog)); +} + +static int process_requests(int sock) +{ + int retval; + ProxyHeader header; + struct iovec in_iovec; + + in_iovec.iov_base = g_malloc(PROXY_MAX_IO_SZ + PROXY_HDR_SZ); + in_iovec.iov_len = PROXY_MAX_IO_SZ + PROXY_HDR_SZ; + while (1) { + retval = read_request(sock, &in_iovec, &header); + if (retval < 0) { + goto error; + } + } + (void)socket_write; +error: + g_free(in_iovec.iov_base); + return -1; +} + +int main(int argc, char **argv) +{ + int sock; + char *rpath = NULL; + struct stat stbuf; + int c, option_index; + + is_daemon = true; + sock = -1; + while (1) { + option_index = 0; + c = getopt_long(argc, argv, "p:nh?f:", helper_opts, + &option_index); + if (c == -1) { + break; + } + switch (c) { + case 'p': + rpath = strdup(optarg); + break; + case 'n': + is_daemon = false; + break; + case 'f': + sock = atoi(optarg); + break; + case '?': + case 'h': + default: + usage(argv[0]); + exit(EXIT_FAILURE); + } + } + + /* Parameter validation */ + if (sock == -1 || rpath == NULL) { + fprintf(stderr, "socket descriptor or path not specified\n"); + usage(argv[0]); + exit(EXIT_FAILURE); + } + + if (lstat(rpath, &stbuf) < 0) { + fprintf(stderr, "invalid path \"%s\" specified, %s\n", + rpath, strerror(errno)); + exit(EXIT_FAILURE); + } + + if (!S_ISDIR(stbuf.st_mode)) { + fprintf(stderr, "specified path \"%s\" is not directory\n", rpath); + exit(EXIT_FAILURE); + } + + if (is_daemon) { + if (daemon(0, 0) < 0) { + fprintf(stderr, "daemon call failed\n"); + exit(EXIT_FAILURE); + } + openlog(PROGNAME, LOG_PID, LOG_DAEMON); + } + + do_log(LOG_INFO, "Started\n"); + + if (chdir("/") < 0) { + do_perror("chdir"); + goto error; + } + if (chroot(rpath) < 0) { + do_perror("chroot"); + goto error; + } + umask(0); + + if (init_capabilities() < 0) { + goto error; + } + + process_requests(sock); +error: + do_log(LOG_INFO, "Done\n"); + closelog(); + return 0; +} diff --git a/hw/9pfs/virtio-9p-proxy.h b/hw/9pfs/virtio-9p-proxy.h index 2d60b0e..97c5574 100644 --- a/hw/9pfs/virtio-9p-proxy.h +++ b/hw/9pfs/virtio-9p-proxy.h @@ -14,6 +14,15 @@ #define PROXY_MAX_IO_SZ (64 * 1024) +/* + * proxy iovec only support one element and + * marsha/unmarshal doesn't do little endian conversion. + */ +#define proxy_unmarshal(in_sg, offset, fmt, args...) \ + v9fs_unmarshal(in_sg, 1, offset, 0, fmt, ##args) +#define proxy_marshal(out_sg, offset, fmt, args...) \ + v9fs_marshal(out_sg, 1, offset, 0, fmt, ##args) + typedef struct { uint32_t type; uint32_t size; -- cgit v1.1