diff mbox

[V11,03/15] virtio-9p: Provide chroot worker side interfaces

Message ID 1308903744-2870-4-git-send-email-mohan@in.ibm.com
State New
Headers show

Commit Message

Mohan Kumar M June 24, 2011, 8:22 a.m. UTC
From: "M. Mohan Kumar" <mohan@in.ibm.com>

Implement chroot worker side interfaces like sending the file
descriptor to qemu process, reading the object request from socket etc.
Also add chroot main function and other helper routines.

Signed-off-by: M. Mohan Kumar <mohan@in.ibm.com>
[malahal@us.ibm.com: Do not send fd as part of data, instead a special
value is sent as part of data when fd is sent as part of ancillary data]
---
 Makefile.objs                     |    1 +
 fsdev/file-op-9p.h                |    3 +
 hw/9pfs/virtio-9p-chroot-worker.c |  193 +++++++++++++++++++++++++++++++++++++
 hw/9pfs/virtio-9p-chroot.h        |   39 ++++++++
 hw/9pfs/virtio-9p-device.c        |   24 +++++
 5 files changed, 260 insertions(+), 0 deletions(-)
 create mode 100644 hw/9pfs/virtio-9p-chroot-worker.c
 create mode 100644 hw/9pfs/virtio-9p-chroot.h
diff mbox

Patch

diff --git a/Makefile.objs b/Makefile.objs
index e38ca15..588eae2 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -304,6 +304,7 @@  hw-obj-$(CONFIG_SOUND) += $(sound-obj-y)
 9pfs-nested-$(CONFIG_VIRTFS) += virtio-9p-xattr-user.o virtio-9p-posix-acl.o
 9pfs-nested-$(CONFIG_VIRTFS) += virtio-9p-coth.o cofs.o codir.o cofile.o
 9pfs-nested-$(CONFIG_VIRTFS) += coxattr.o virtio-9p-handle.o
+9pfs-nested-$(CONFIG_VIRTFS) += virtio-9p-chroot-worker.o
 
 hw-obj-$(CONFIG_REALLY_VIRTFS) += $(addprefix 9pfs/, $(9pfs-nested-y))
 $(addprefix 9pfs/, $(9pfs-nested-y)): QEMU_CFLAGS+=$(GLIB_CFLAGS)
diff --git a/fsdev/file-op-9p.h b/fsdev/file-op-9p.h
index 5d088d4..66dac07 100644
--- a/fsdev/file-op-9p.h
+++ b/fsdev/file-op-9p.h
@@ -19,6 +19,7 @@ 
 #include <sys/stat.h>
 #include <sys/uio.h>
 #include <sys/vfs.h>
+#include "qemu-thread.h"
 
 #define SM_LOCAL_MODE_BITS    0600
 #define SM_LOCAL_DIR_MODE_BITS    0700
@@ -63,6 +64,8 @@  typedef struct FsContext
     struct xattr_operations **xops;
     /* fs driver specific data */
     void *private;
+    QemuMutex chroot_mutex;
+    int chroot_socket;
 } FsContext;
 
 typedef struct V9fsPath {
diff --git a/hw/9pfs/virtio-9p-chroot-worker.c b/hw/9pfs/virtio-9p-chroot-worker.c
new file mode 100644
index 0000000..40a54b3
--- /dev/null
+++ b/hw/9pfs/virtio-9p-chroot-worker.c
@@ -0,0 +1,193 @@ 
+/*
+ * Virtio 9p chroot environment for contained access to the exported path
+ * Code path handles chroot worker side interfaces
+ * 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 <sys/fsuid.h>
+#include <sys/resource.h>
+#include <signal.h>
+#include "qemu_socket.h"
+#include "qemu-thread.h"
+#include "qerror.h"
+#include "virtio-9p.h"
+#include "virtio-9p-chroot.h"
+
+/* Send file descriptor and error status to qemu process */
+static void chroot_sendfd(int sockfd, int fd, int fd_valid)
+{
+    struct msghdr msg = { };
+    struct iovec iov;
+    struct cmsghdr *cmsg;
+    int retval, data;
+    union MsgControl msg_control;
+
+    iov.iov_base = &data;
+    iov.iov_len = sizeof(data);
+
+    memset(&msg, 0, sizeof(msg));
+    msg.msg_iov = &iov;
+    msg.msg_iovlen = 1;
+    /* No ancillary data on error */
+    if (!fd_valid) {
+        /*
+         * fd is really negative errno if the request failed. Or simply
+         * zero if the request is successful and it doesn't need a file
+         * descriptor.
+         */
+        data = fd;
+    } else {
+        data = V9FS_FD_VALID;
+        msg.msg_control = &msg_control;
+        msg.msg_controllen = sizeof(msg_control);
+
+        cmsg = &msg_control.cmsg;
+        cmsg->cmsg_len = CMSG_LEN(sizeof(fd));
+        cmsg->cmsg_level = SOL_SOCKET;
+        cmsg->cmsg_type = SCM_RIGHTS;
+        memcpy(CMSG_DATA(cmsg), &fd, sizeof(fd));
+    }
+
+    do {
+        retval = sendmsg(sockfd, &msg, 0);
+    } while (retval < 0 && errno == EINTR);
+    if (retval < 0) {
+        _exit(1);
+    }
+    if (fd_valid) {
+        close(fd);
+    }
+}
+
+/* Read V9fsFileObjectRequest sent by QEMU process */
+static int chroot_read_request(int sockfd, V9fsFileObjectRequest *request)
+{
+    int retval;
+    retval = qemu_read_full(sockfd, request, sizeof(*request));
+    if (retval != sizeof(*request)) {
+        if (errno == EBADF) {
+            _exit(1);
+        }
+        return -EIO;
+    }
+    return 0;
+}
+
+/*
+ * Helper routine to open a file
+ */
+static int chroot_do_open(V9fsFileObjectRequest *request)
+{
+    int fd;
+    fd = open(request->path.path, request->data.flags);
+    if (fd < 0) {
+        fd = -errno;
+    }
+    return fd;
+}
+
+static void chroot_daemonize(int chroot_sock)
+{
+    sigset_t sigset;
+    struct rlimit nr_fd;
+    int fd;
+
+    /* Block all signals for this process */
+    sigfillset(&sigset);
+    sigprocmask(SIG_SETMASK, &sigset, NULL);
+
+    /* Close other file descriptors */
+    getrlimit(RLIMIT_NOFILE, &nr_fd);
+    for (fd = 0; fd < nr_fd.rlim_cur; fd++) {
+        if (fd != chroot_sock) {
+            close(fd);
+        }
+    }
+    chdir("/");
+    /* Create files with mode as per request */
+    umask(0);
+}
+
+/*
+ * Fork a process and chroot into the share path. Communication
+ * between qemu process and chroot process happens via socket.
+ * All file descriptors (including stdout and stderr) are closed
+ * except one socket descriptor (which is used for communicating
+ * between qemu process and chroot process).
+ * Note: To avoid errors in forked process in multi threaded environment
+ * only async-signal safe functions used. For more information see
+ * man fork(3p), signal(7)
+ */
+int v9fs_chroot(FsContext *fs_ctx)
+{
+    int fd_pair[2], chroot_sock;
+    V9fsFileObjectRequest request;
+    pid_t pid;
+    uint32_t code;
+    int retval, valid_fd;
+
+    if (socketpair(PF_UNIX, SOCK_STREAM, 0, fd_pair) < 0) {
+        error_report("socketpair %s", strerror(errno));
+        return -1;
+    }
+
+    pid = fork();
+    if (pid < 0) {
+        error_report("fork %s", strerror(errno));
+        return -1;
+    }
+    if (pid != 0) {
+        fs_ctx->chroot_socket = fd_pair[0];
+        close(fd_pair[1]);
+        return 0;
+    }
+
+    close(fd_pair[0]);
+    chroot_sock = fd_pair[1];
+    if (chroot(fs_ctx->fs_root) < 0) {
+        code = errno;
+        if (qemu_write_full(chroot_sock, &code, sizeof(code)) < 0) {
+                /* No need to process error, we are about to exit */
+        }
+        _exit(1);
+    }
+
+    chroot_daemonize(chroot_sock);
+
+    /*
+     * Write 0 to chroot socket to indicate chroot process creation is
+     * successful
+     */
+    code = 0;
+    if (qemu_write_full(chroot_sock, &code, sizeof(code))
+                    != sizeof(code)) {
+        _exit(1);
+    }
+    /* get the request from the socket */
+    while (1) {
+        valid_fd = 0;
+        if (chroot_read_request(chroot_sock, &request) < 0) {
+            chroot_sendfd(chroot_sock, -1, valid_fd);
+            continue;
+        }
+        switch (request.data.type) {
+        case T_OPEN:
+            retval = chroot_do_open(&request);
+            if (retval >= 0) {
+                valid_fd = 1;
+            }
+            break;
+        default:
+            retval = -1;
+            break;
+        }
+        chroot_sendfd(chroot_sock, retval, valid_fd);
+    }
+}
diff --git a/hw/9pfs/virtio-9p-chroot.h b/hw/9pfs/virtio-9p-chroot.h
new file mode 100644
index 0000000..c2a4a6e
--- /dev/null
+++ b/hw/9pfs/virtio-9p-chroot.h
@@ -0,0 +1,39 @@ 
+#ifndef _QEMU_VIRTIO_9P_CHROOT_H
+#define _QEMU_VIRTIO_9P_CHROOT_H
+
+#include "qemu_socket.h"
+/* types for V9fsFileObjectRequest */
+#define T_OPEN      1
+
+#define V9FS_FD_VALID INT_MAX
+
+union MsgControl {
+    struct cmsghdr cmsg;
+    char control[CMSG_SPACE(sizeof(int))];
+};
+
+struct V9fsFileObjectData
+{
+    int flags;
+    int mode;
+    uid_t uid;
+    gid_t gid;
+    dev_t dev;
+    int type;
+};
+
+struct V9fsFileObjectPath
+{
+    char path[PATH_MAX];
+    char old_path[PATH_MAX];
+};
+
+typedef struct V9fsFileObjectRequest
+{
+    struct V9fsFileObjectData data;
+    struct V9fsFileObjectPath path;
+} V9fsFileObjectRequest;
+
+int v9fs_chroot(FsContext *fs_ctx);
+
+#endif /* _QEMU_VIRTIO_9P_CHROOT_H */
diff --git a/hw/9pfs/virtio-9p-device.c b/hw/9pfs/virtio-9p-device.c
index 0b40ba4..b302de7 100644
--- a/hw/9pfs/virtio-9p-device.c
+++ b/hw/9pfs/virtio-9p-device.c
@@ -19,6 +19,8 @@ 
 #include "fsdev/qemu-fsdev.h"
 #include "virtio-9p-xattr.h"
 #include "virtio-9p-coth.h"
+#include "qemu-error.h"
+#include "virtio-9p-chroot.h"
 
 static uint32_t virtio_9p_get_features(VirtIODevice *vdev, uint32_t features)
 {
@@ -150,6 +152,28 @@  VirtIODevice *virtio_9p_init(DeviceState *dev, V9fsConf *conf)
         fprintf(stderr, "worker thread initialization failed\n");
         exit(1);
     }
+    if (s->ctx.fs_sm == SM_PASSTHROUGH) {
+        uint32_t code;
+        qemu_mutex_init(&s->ctx.chroot_mutex);
+        if (v9fs_chroot(&s->ctx) < 0) {
+            exit(1);
+        }
+
+        /*
+         * Chroot process sends 0 to indicate chroot process creation is
+         * successful
+         */
+        if (qemu_read_full(s->ctx.chroot_socket, &code,
+                                sizeof(code)) != sizeof(code)) {
+            error_report("chroot process creation failed");
+            exit(1);
+        }
+        if (code != 0) {
+            error_report("chroot system call failed: %s", strerror(code));
+            exit(1);
+        }
+    }
+
     return &s->vdev;
 }