From patchwork Mon Dec 5 16:18:41 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "M. Mohan Kumar" X-Patchwork-Id: 129352 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [140.186.70.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id 9E2551007D6 for ; Tue, 6 Dec 2011 03:22:30 +1100 (EST) Received: from localhost ([::1]:51931 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1RXbJ8-0000Od-HM for incoming@patchwork.ozlabs.org; Mon, 05 Dec 2011 11:22:22 -0500 Received: from eggs.gnu.org ([140.186.70.92]:35823) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1RXbIK-0006YL-Cr for qemu-devel@nongnu.org; Mon, 05 Dec 2011 11:21:34 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1RXbIF-0008Rj-GE for qemu-devel@nongnu.org; Mon, 05 Dec 2011 11:21:32 -0500 Received: from mail-iy0-f173.google.com ([209.85.210.173]:51365) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1RXbIF-0008QH-Cl for qemu-devel@nongnu.org; Mon, 05 Dec 2011 11:21:27 -0500 Received: by mail-iy0-f173.google.com with SMTP id k32so10138078iak.4 for ; Mon, 05 Dec 2011 08:21:27 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=gamma; h=from:to:cc:subject:date:message-id:x-mailer:in-reply-to:references; bh=U5Sgm3gwMoMjdCP/BStLZ/OmgTfKFsypjp2ctZvZWSQ=; b=KUvSrIVTp9zM+0zDLEhqpY+nhgMV9eBEb/hVNiU9Zh2WKCDLxJrlH//5Z7ZsEZ82iD kkOdbouEeqGqoRQt5mAcLq3YRugaqxtPX8yTq+bx1wQZignkOJh7XLEpte3XS1qVIlQu mdJ+5SMD3t2xf92tSVhmkk/FeYh9aW5J9FQ6g= Received: by 10.42.72.135 with SMTP id o7mr10898844icj.45.1323102087189; Mon, 05 Dec 2011 08:21:27 -0800 (PST) Received: from explorer.in.ibm.com ([117.192.11.142]) by mx.google.com with ESMTPS id el2sm78245721ibb.10.2011.12.05.08.21.23 (version=SSLv3 cipher=OTHER); Mon, 05 Dec 2011 08:21:26 -0800 (PST) From: "M. Mohan Kumar" To: qemu-devel@nongnu.org, aneesh.kumar@linux.vnet.ibm.com, stefanha@gmail.com Date: Mon, 5 Dec 2011 21:48:41 +0530 Message-Id: <1323101930-27163-5-git-send-email-mohan@in.ibm.com> X-Mailer: git-send-email 1.7.6 In-Reply-To: <1323101930-27163-1-git-send-email-mohan@in.ibm.com> References: <1323101930-27163-1-git-send-email-mohan@in.ibm.com> X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.6 (newer, 2) X-Received-From: 209.85.210.173 Cc: "M. Mohan Kumar" Subject: [Qemu-devel] [PATCH V4 04/13] hw/9pfs: File system helper process for qemu 9p proxy FS X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org From: "M. Mohan Kumar" 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 --- Makefile | 3 + configure | 19 +++ fsdev/virtfs-proxy-helper.c | 308 +++++++++++++++++++++++++++++++++++++++++++ hw/9pfs/virtio-9p-proxy.h | 9 ++ 4 files changed, 339 insertions(+), 0 deletions(-) create mode 100644 fsdev/virtfs-proxy-helper.c diff --git a/Makefile b/Makefile index 301c75e..1906c5e 100644 --- a/Makefile +++ b/Makefile @@ -154,6 +154,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 ac4840d..4ecdb1c 100755 --- a/configure +++ b/configure @@ -1938,6 +1938,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" @@ -2735,6 +2751,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..7670a0e --- /dev/null +++ b/fsdev/virtfs-proxy-helper.c @@ -0,0 +1,308 @@ +/* + * 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, read the complete + * message from the socket and ignore it. This ensures that + * we can correctly handle the next request. We also return + * ENOBUFS as error to indicate we ran out of buffer space. + */ + if (header->size > PROXY_MAX_IO_SZ) { + int count, size; + size = header->size; + while (size > 0) { + count = MIN(PROXY_MAX_IO_SZ, size); + count = socket_read(sockfd, iovec->iov_base + PROXY_HDR_SZ, count); + if (count < 0) { + return count; + } + size -= count; + } + 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 (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 b3c59c7..c33a84b 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 { int type; int size;