Patchwork [V3,04/13] hw/9pfs: File system helper process for qemu 9p proxy FS

login
register
mail settings
Submitter Mohan Kumar M
Date Nov. 21, 2011, 1:36 p.m.
Message ID <1321882578-7498-5-git-send-email-mohan@in.ibm.com>
Download mbox | patch
Permalink /patch/126803/
State New
Headers show

Comments

Mohan Kumar M - Nov. 21, 2011, 1:36 p.m.
From: "M. Mohan Kumar" <mohan@in.ibm.com>

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 <socket descriptor> -p|--path <path-to-share>

Signed-off-by: M. Mohan Kumar <mohan@in.ibm.com>
---
 Makefile                    |    3 +
 configure                   |   19 +++
 fsdev/virtfs-proxy-helper.c |  280 +++++++++++++++++++++++++++++++++++++++++++
 hw/9pfs/virtio-9p-proxy.h   |    7 +-
 4 files changed, 308 insertions(+), 1 deletions(-)
 create mode 100644 fsdev/virtfs-proxy-helper.c

Patch

diff --git a/Makefile b/Makefile
index ba8d738..19b481a 100644
--- a/Makefile
+++ b/Makefile
@@ -153,6 +153,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 a6cf6d6..a2b55e8 100755
--- a/configure
+++ b/configure
@@ -1873,6 +1873,22 @@  else
 fi
 
 ##########################################
+# libcap probe
+
+if test "$cap" != "no" ; then
+  cat > $TMPC <<EOF
+#include <stdio.h>
+#include <sys/capability.h>
+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"
 
@@ -2662,6 +2678,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..dc222d4
--- /dev/null
+++ b/fsdev/virtfs-proxy-helper.c
@@ -0,0 +1,280 @@ 
+/*
+ * Helper for QEMU Proxy FS Driver
+ * Copyright IBM, Corp. 2011
+ *
+ * Authors:
+ * M. Mohan Kumar <mohan@in.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ */
+#include <stdio.h>
+#include <string.h>
+#include <sys/un.h>
+#include <limits.h>
+#include <signal.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <getopt.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <sys/prctl.h>
+#include <sys/capability.h>
+#include <sys/fsuid.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include "qemu-common.h"
+#include "virtio-9p-marshal.h"
+#include "hw/9pfs/virtio-9p-proxy.h"
+
+#define PROGNAME "virtfs-proxy-helper"
+
+#define do_log(loglevel, fmt, args...) \
+    do { \
+        if (is_daemon) {\
+            syslog(loglevel, fmt, ##args); \
+        } else { \
+            fprintf(stderr, fmt, ##args); \
+        } \
+    } while (0)
+
+#define do_perror(string) \
+    do { \
+        if (is_daemon) {\
+            syslog(LOG_CRIT, string ":%s", strerror(errno)); \
+        } else { \
+            fprintf(stderr, string ":%s\n", strerror(errno)); \
+        } \
+    } while (0)
+
+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 int cap_set(void)
+{
+    cap_t caps;
+    cap_value_t cap_list[10];
+
+    /* helper needs following capbabilities only */
+    cap_list[0] = CAP_CHOWN;
+    cap_list[1] = CAP_DAC_OVERRIDE;
+    cap_list[2] = CAP_DAC_READ_SEARCH;
+    cap_list[3] = CAP_FOWNER;
+    cap_list[4] = CAP_FSETID;
+    cap_list[5] = CAP_SETGID;
+    cap_list[6] = CAP_MKNOD;
+    cap_list[7] = CAP_SETUID;
+
+    caps = cap_init();
+    if (caps == NULL) {
+        do_perror("cap_init");
+        return -1;
+    }
+    if (cap_set_flag(caps, CAP_PERMITTED, 8, cap_list, CAP_SET) < 0) {
+        do_perror("cap_set_flag");
+        goto error;
+    }
+    if (cap_set_proc(caps) < 0) {
+        do_perror("cap_set_proc");
+        goto error;
+    }
+    if (cap_set_flag(caps, CAP_EFFECTIVE, 8, cap_list, CAP_SET)) {
+        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)
+{
+    if (prctl(PR_SET_KEEPCAPS, 1) < 0) {
+        do_perror("prctl");
+        return -1;
+    }
+    if (cap_set() < 0) {
+        return -1;
+    }
+    return 0;
+}
+
+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) {
+            break;
+        }
+        if (retval < 0) {
+            if (errno == EINTR) {
+                continue;
+            }
+            break;
+        }
+        size -= retval;
+        buff += retval;
+        total += retval;
+    }
+    return total;
+}
+
+static int socket_write(int sockfd, void *buff, ssize_t size)
+{
+    int retval;
+
+    do {
+        retval = write(sockfd, buff, size);
+    } while (retval < 0 && errno == EINTR);
+    if (retval != size) {
+        return -errno;
+    }
+    return retval;
+}
+
+static int read_request(int sockfd, struct iovec *iovec)
+{
+    int retval;
+    ProxyHeader header;
+
+    /* read the header */
+    retval = socket_read(sockfd, iovec->iov_base, sizeof(header));
+    if (retval != sizeof(header)) {
+        return -EIO;
+    }
+    /* unmarshal header */
+    proxy_unmarshal(iovec, 1, 0, "dd", &header.type, &header.size);
+    if (header.size > BUFF_SZ) {
+        do_log(LOG_CRIT, "header.size exceeds maximum size\n");
+        return -E2BIG;
+    }
+    /* read the request */
+    retval = socket_read(sockfd, iovec->iov_base + sizeof(header), header.size);
+    if (retval != header.size) {
+        return -EIO;
+    }
+    return header.type;
+}
+
+static void usage(char *prog)
+{
+    fprintf(stderr, "usage: %s\n"
+            " -p|--path <path> 9p path to export\n"
+            " {-f|--fd <socket-descriptor>} socket file descriptor to be used\n"
+            " [-n|--nodaemon] Run as a normal program\n",
+            basename(prog));
+}
+
+static int process_requests(int sock)
+{
+    int type;
+    struct iovec iovec;
+
+    iovec.iov_base = g_malloc(BUFF_SZ);
+    iovec.iov_len = BUFF_SZ;
+    while (1) {
+        type = read_request(sock, &iovec);
+        if (type <= 0) {
+            goto error;
+        }
+    }
+    (void)socket_write;
+error:
+    g_free(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 = 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[0] == '\0') {
+        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 f5e1a02..7be44bd 100644
--- a/hw/9pfs/virtio-9p-proxy.h
+++ b/hw/9pfs/virtio-9p-proxy.h
@@ -1,7 +1,12 @@ 
 #ifndef _QEMU_VIRTIO_9P_PROXY_H
 #define _QEMU_VIRTIO_9P_PROXY_H
 
-#define BUFF_SZ (4 * 1024)
+#define BUFF_SZ (64 * 1024)
+
+#define proxy_unmarshal(in_sg, in_elem, offset, fmt, args...) \
+    v9fs_unmarshal(in_sg, in_elem, offset, 0, fmt, ##args)
+#define proxy_marshal(out_sg, out_elem, offset, fmt, args...) \
+    v9fs_marshal(out_sg, out_elem, offset, 0, fmt, ##args)
 
 typedef struct {
     int type;