From: "M. Mohan Kumar" <mo...@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 <mo...@in.ibm.com> [mala...@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 --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 <mo...@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; } -- 1.7.5.4