Implement chroot server 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.c | 183 ++++++++++++++++++++++++++++++++++++++++++++ hw/9pfs/virtio-9p-chroot.h | 39 +++++++++ hw/9pfs/virtio-9p.c | 23 ++++++ hw/file-op-9p.h | 2 + 5 files changed, 248 insertions(+), 0 deletions(-) create mode 100644 hw/9pfs/virtio-9p-chroot.c create mode 100644 hw/9pfs/virtio-9p-chroot.h diff --git a/Makefile.objs b/Makefile.objs index bc0344c..3007b6d 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -273,6 +273,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.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.c b/hw/9pfs/virtio-9p-chroot.c new file mode 100644 index 0000000..dcde2cc --- /dev/null +++ b/hw/9pfs/virtio-9p-chroot.c @@ -0,0 +1,183 @@ +/* + * Virtio 9p chroot environment for contained access to exported path + * + * 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" + +/* + * Structure used by chroot functions to transmit file descriptor and + * error info + */ +typedef struct { + int fi_fd; + int fi_error; +} FdInfo; + +union MsgControl { + struct cmsghdr cmsg; + char control[CMSG_SPACE(sizeof(int))]; +}; + +/* Send file descriptor and error status to qemu process */ +static int chroot_sendfd(int sockfd, FdInfo *fd_info) +{ + struct msghdr msg = { }; + struct iovec iov; + struct cmsghdr *cmsg; + int retval; + union MsgControl msg_control; + + iov.iov_base = fd_info; + iov.iov_len = sizeof(*fd_info); + + memset(&msg, 0, sizeof(msg)); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + /* No ancillary data on error */ + if (!fd_info->fi_error) { + msg.msg_control = &msg_control; + msg.msg_controllen = sizeof(msg_control); + + cmsg = &msg_control.cmsg; + cmsg->cmsg_len = CMSG_LEN(sizeof(fd_info->fi_fd)); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + memcpy(CMSG_DATA(cmsg), &fd_info->fi_fd, sizeof(fd_info->fi_fd)); + } + retval = sendmsg(sockfd, &msg, 0); + close(fd_info->fi_fd); + return retval; +} + +/* Read V9fsFileObjectRequest written by QEMU process */ +static void chroot_read_request(int sockfd, V9fsFileObjectRequest *request) +{ + int retval; + retval = qemu_read_full(sockfd, request, sizeof(request->data)); + if (retval <= 0 || request->data.path_len <= 0) { + _exit(-1); + } + request->path.path = qemu_mallocz(request->data.path_len + 1); + retval = qemu_read_full(sockfd, (void *)request->path.path, + request->data.path_len); + if (retval <= 0) { + _exit(-1); + } + if (request->data.oldpath_len > 0) { + request->path.old_path = + qemu_mallocz(request->data.oldpath_len + 1); + retval = qemu_read_full(sockfd, (void *)request->path.old_path, + request->data.oldpath_len); + if (retval <= 0) { + _exit(-1); + } + } +} + +static int chroot_daemonize(int chroot_sock) +{ + sigset_t sigset; + struct rlimit nr_fd; + int fd; + + /* Block all signals for this process */ + sigprocmask(SIG_SETMASK, &sigset, NULL); + + /* Daemonize */ + if (setsid() < 0) { + error_report("setsid %s", strerror(errno)); + return -1; + } + + /* Close other file descriptors */ + getrlimit(RLIMIT_NOFILE, &nr_fd); + for (fd = 0; fd < nr_fd.rlim_cur; fd++) { + if (fd != chroot_sock) { + close(fd); + } + } + + /* Create files with mode as requested by client */ + umask(0); + return 0; +} + +static void chroot_dummy(void) +{ + (void)chroot_sendfd; +} + +/* + * Fork a process and chroot into the share path. Communication + * between qemu process and chroot process happens via socket + */ +int v9fs_chroot(FsContext *fs_ctx) +{ + int fd_pair[2], pid, chroot_sock, error; + V9fsFileObjectRequest request; + + 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) { + error_report("chroot %s", strerror(errno)); + return -1; + } + + chroot_dummy(); + if (chroot_daemonize(chroot_sock) < 0) { + _exit(-1); + } + + /* + * Write 0 to chroot socket to indicate chroot process creation is + * successful + */ + error = 0; + if (qemu_write_full(chroot_sock, &error, sizeof(error)) != sizeof(error)) { + _exit(-1); + } + /* get the request from the socket */ + while (1) { + chroot_read_request(chroot_sock, &request); + qemu_free(request.path.path); + if (request.data.oldpath_len) { + qemu_free(request.path.old_path); + } + if (error) { + _exit(error); + } + } +} diff --git a/hw/9pfs/virtio-9p-chroot.h b/hw/9pfs/virtio-9p-chroot.h new file mode 100644 index 0000000..3bee5e3 --- /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 + +/* 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 + +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; + char *old_path; +}; + +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 3858e17..7f4c552 100644 --- a/hw/9pfs/virtio-9p.c +++ b/hw/9pfs/virtio-9p.c @@ -14,10 +14,13 @@ #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" +#include <pthread.h> int debug_9p_pdu; @@ -3743,5 +3746,25 @@ 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) { + pthread_mutex_init(&s->ctx.chroot_mutex, 0); + if (v9fs_chroot(&s->ctx) < 0) { + exit(1); + } + + /* + * Chroot process sends 0 to indicate chroot process creation is + * successful + */ + if (read(s->ctx.chroot_socket, &i, sizeof(i)) != sizeof(i)) { + error_report("chroot process creation failed"); + exit(1); + } + if (i != 0) { + error_report("chroot process creation failed"); + exit(1); + } + } + return &s->vdev; } diff --git a/hw/file-op-9p.h b/hw/file-op-9p.h index c7731c2..149a915 100644 --- a/hw/file-op-9p.h +++ b/hw/file-op-9p.h @@ -55,6 +55,8 @@ typedef struct FsContext SecModel fs_sm; uid_t uid; struct xattr_operations **xops; + pthread_mutex_t chroot_mutex; + int chroot_socket; } FsContext; extern void cred_init(FsCred *); -- 1.7.3.4