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> --- Makefile.objs | 1 + hw/9pfs/virtio-9p-chroot-worker.c | 183 +++++++++++++++++++++++++++++++++++++ hw/9pfs/virtio-9p-chroot.h | 43 +++++++++ hw/9pfs/virtio-9p.c | 25 +++++ hw/file-op-9p.h | 4 + 5 files changed, 256 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 6a9f765..44891c1 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -283,6 +283,7 @@ hw-obj-$(CONFIG_SOUND) += $(sound-obj-y) 9pfs-nested-$(CONFIG_VIRTFS) = virtio-9p-debug.o 9pfs-nested-$(CONFIG_VIRTFS) += virtio-9p-local.o virtio-9p-xattr.o 9pfs-nested-$(CONFIG_VIRTFS) += virtio-9p-xattr-user.o virtio-9p-posix-acl.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)): CFLAGS += -I$(SRC_PATH)/hw/ diff --git a/hw/9pfs/virtio-9p-chroot-worker.c b/hw/9pfs/virtio-9p-chroot-worker.c new file mode 100644 index 0000000..e7bc6c2 --- /dev/null +++ b/hw/9pfs/virtio-9p-chroot-worker.c @@ -0,0 +1,183 @@ +/* + * 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 "virtio.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; + union MsgControl msg_control; + + iov.iov_base = &fd; + iov.iov_len = sizeof(fd); + + memset(&msg, 0, sizeof(msg)); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + /* No ancillary data on error */ + if (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, error; + V9fsFileObjectRequest request; + pid_t pid; + uint32_t code; + int fd, 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; + error = qemu_write_full(chroot_sock, &code, sizeof(code)); + _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: + fd = chroot_do_open(&request); + if (fd >= 0) { + valid_fd = 1; + } + break; + default: + fd = -1; + break; + } + chroot_sendfd(chroot_sock, fd, valid_fd); + } +} diff --git a/hw/9pfs/virtio-9p-chroot.h b/hw/9pfs/virtio-9p-chroot.h new file mode 100644 index 0000000..97f616c --- /dev/null +++ b/hw/9pfs/virtio-9p-chroot.h @@ -0,0 +1,43 @@ +#ifndef _QEMU_VIRTIO_9P_CHROOT_H +#define _QEMU_VIRTIO_9P_CHROOT_H + +/* types for V9fsFileObjectRequest */ +#define T_OPEN 1 +#define T_CREATE 2 +#define T_MKDIR 3 +#define T_MKNOD 4 +#define T_SYMLINK 5 +#define T_LINK 6 + +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 path_len; + int oldpath_len; + 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.c b/hw/9pfs/virtio-9p.c index 08c0399..4dea542 100644 --- a/hw/9pfs/virtio-9p.c +++ b/hw/9pfs/virtio-9p.c @@ -14,10 +14,12 @@ #include "virtio.h" #include "pc.h" #include "qemu_socket.h" +#include "qerror.h" #include "virtio-9p.h" #include "fsdev/qemu-fsdev.h" #include "virtio-9p-debug.h" #include "virtio-9p-xattr.h" +#include "virtio-9p-chroot.h" int debug_9p_pdu; static void v9fs_reclaim_fd(V9fsState *s); @@ -3925,5 +3927,28 @@ VirtIODevice *virtio_9p_init(DeviceState *dev, V9fsConf *conf) s->tag_len; s->vdev.get_config = virtio_9p_get_config; + if (s->ctx.fs_sm == SM_PASSTHROUGH) { + uint32_t code; + qemu_mutex_init(&s->ctx.chroot_mutex); + s->ctx.chroot_ioerror = 0; + 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; } diff --git a/hw/file-op-9p.h b/hw/file-op-9p.h index 126e60e..b52b432 100644 --- a/hw/file-op-9p.h +++ b/hw/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 @@ -55,6 +56,9 @@ typedef struct FsContext SecModel fs_sm; uid_t uid; struct xattr_operations **xops; + QemuMutex chroot_mutex; + int chroot_socket; + int chroot_ioerror; } FsContext; void cred_init(FsCred *); -- 1.7.3.4