From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from eggs.gnu.org ([140.186.70.92]:43427) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1RKyrn-0003Z3-Lq for qemu-devel@nongnu.org; Mon, 31 Oct 2011 16:54:01 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1RKyrl-0000BX-Ky for qemu-devel@nongnu.org; Mon, 31 Oct 2011 16:53:59 -0400 Received: from e23smtp05.au.ibm.com ([202.81.31.147]:38131) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1RKyrk-0000BI-Qd for qemu-devel@nongnu.org; Mon, 31 Oct 2011 16:53:57 -0400 Received: from /spool/local by e23smtp05.au.ibm.com with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted for from ; Mon, 31 Oct 2011 20:52:17 +1000 Received: from d23av03.au.ibm.com (d23av03.au.ibm.com [9.190.234.97]) by d23relay05.au.ibm.com (8.13.8/8.13.8/NCO v10.0) with ESMTP id p9VKp2Fq2404490 for ; Tue, 1 Nov 2011 07:51:02 +1100 Received: from d23av03.au.ibm.com (loopback [127.0.0.1]) by d23av03.au.ibm.com (8.14.4/8.13.1/NCO v10.0 AVout) with ESMTP id p9VKrpWU021119 for ; Tue, 1 Nov 2011 07:53:51 +1100 From: "M. Mohan Kumar" Date: Tue, 1 Nov 2011 02:23:23 +0530 Message-Id: <1320094412-19091-5-git-send-email-mohan@in.ibm.com> In-Reply-To: <1320094412-19091-1-git-send-email-mohan@in.ibm.com> References: <1320094412-19091-1-git-send-email-mohan@in.ibm.com> Subject: [Qemu-devel] [PATCH 04/13] hw/9pfs: File system helper process for qemu 9p proxy FS List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: qemu-devel@nongnu.org, aneesh.kumar@linux.vnet.ibm.com Cc: "M. Mohan Kumar" From: "M. Mohan Kumar" Provide root privilege access to QEMU 9p proxy filesystem using socket communication. Proxy helper is started by root user as: ~ # virtfs-proxy-helper {{-s|--socket -u|--uid -g|--gid}|{-f|--fd }} -p [-r -t ] Where uid:gid gives socket access to uid:gid, -r:t combination drops the privilege to given uid:gid Signed-off-by: M. Mohan Kumar --- Makefile | 2 + configure | 25 ++++ hw/9pfs/proxy.h | 6 + hw/9pfs/virtfs-proxy-helper.c | 262 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 295 insertions(+), 0 deletions(-) create mode 100644 hw/9pfs/virtfs-proxy-helper.c diff --git a/Makefile b/Makefile index f63fc02..1fd443d 100644 --- a/Makefile +++ b/Makefile @@ -153,6 +153,8 @@ qemu-img$(EXESUF): qemu-img.o $(tools-obj-y) qemu-nbd$(EXESUF): qemu-nbd.o $(tools-obj-y) qemu-io$(EXESUF): qemu-io.o cmd.o $(tools-obj-y) +hw/9pfs/virtfs-proxy-helper$(EXESUF): LIBS+=$(LIBS_PROXY) + 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 19e8394..8abd17c 100755 --- a/configure +++ b/configure @@ -1866,6 +1866,23 @@ else fi ########################################## +# libcap probe + +if test "$cap" != "no" ; then + cat > $TMPC < +#include +int main(void) { cap_t caps; caps = cap_init(); } +EOF + if compile_prog "" "-lcap" ; then + cap=yes + libs_proxy="-lcap" + else + cap=no + fi +fi + +########################################## # pthread probe PTHREADLIBS_LIST="-pthread -lpthread -lpthreadGC2" @@ -2636,6 +2653,9 @@ confdir=$sysconfdir$confsuffix tools= if test "$softmmu" = yes ; then tools="qemu-img\$(EXESUF) qemu-io\$(EXESUF) $tools" +if test "$cap" = yes; then + tools="$tools hw/9pfs/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 @@ -3068,6 +3088,10 @@ if test "$linux_magic_h" = "yes" ; then echo "CONFIG_LINUX_MAGIC_H=y" >> $config_host_mak fi +if test "$cap" = "yes" ; then + echo "CONFIG_CAPABILITY=y" >> $config_host_mak +fi + # USB host support case "$usb" in linux) @@ -3143,6 +3167,7 @@ echo "LIBS+=$LIBS" >> $config_host_mak echo "LIBS_TOOLS+=$libs_tools" >> $config_host_mak echo "EXESUF=$EXESUF" >> $config_host_mak echo "LIBS_QGA+=$libs_qga" >> $config_host_mak +echo "LIBS_PROXY+=$libs_proxy" >> $config_host_mak # generate list of library paths for linker script diff --git a/hw/9pfs/proxy.h b/hw/9pfs/proxy.h index 1a47509..205d7b7 100644 --- a/hw/9pfs/proxy.h +++ b/hw/9pfs/proxy.h @@ -2,6 +2,12 @@ #define __PROXY_HELP_H #define BUFF_SZ (4 * 1024) +#define V9FS_FD_VALID INT_MAX + +union MsgControl { + struct cmsghdr cmsg; + char control[CMSG_SPACE(sizeof(int))]; +}; typedef struct { int type; diff --git a/hw/9pfs/virtfs-proxy-helper.c b/hw/9pfs/virtfs-proxy-helper.c new file mode 100644 index 0000000..8e82ca7 --- /dev/null +++ b/hw/9pfs/virtfs-proxy-helper.c @@ -0,0 +1,262 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "bswap.h" +#include +#include "qemu-common.h" +#include "hw/9pfs/proxy.h" + +#define PROGNAME "virtfs-proxy-helper" + +static struct option helper_opts[] = { + {"fd", required_argument, NULL, 'f'}, + {"path", required_argument, NULL, 'p'}, + {"nodaemon", no_argument, NULL, 'n'}, +}; + +int is_daemon; + +static void do_perror(const char *string) +{ + if (is_daemon) { + syslog(LOG_CRIT, "%s:%s", string, strerror(errno)); + } else { + fprintf(stderr, "%s:%s\n", string, strerror(errno)); + } +} + +static void do_log(int level, const char *string) +{ + if (is_daemon) { + syslog(level, "%s", string); + } else { + fprintf(stderr, "%s\n", string); + } +} + +static int cap_set(void) +{ + int retval; + 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; + } + retval = cap_set_flag(caps, CAP_PERMITTED, 8, cap_list, CAP_SET); + if (retval < 0) { + do_perror("cap_set_flag"); + goto error; + } + retval = cap_set_proc(caps); + if (retval < 0) { + do_perror("cap_set_proc"); + } + retval = cap_set_flag(caps, CAP_EFFECTIVE, 8, cap_list, CAP_SET); + if (retval < 0) { + do_perror("cap_set_flag"); + goto error; + } + retval = cap_set_proc(caps); + if (retval < 0) { + do_perror("cap_set_proc"); + } + +error: + cap_free(caps); + return retval; +} + +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) +{ + int retval; + + do { + retval = read(sockfd, buff, size); + } while (retval < 0 && errno == EINTR); + if (retval != size) { + if (errno != ENOENT) { + do_perror("socket read"); + } + return -EIO; + } + return retval; +} + +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) { + do_perror("socket_write"); + return -EIO; + } + return retval; +} + +static int read_request(int sockfd, struct iovec *iovec) +{ + int retval; + ProxyHeader header; + + do { + retval = socket_read(sockfd, &header, sizeof(header)); + if (retval != sizeof(header)) { + return -EIO; + } + retval = socket_read(sockfd, iovec->iov_base, header.size); + if (retval != header.size) { + return -EIO; + } + return header.type; + } while (1); +} + +static void usage(char *prog) +{ + fprintf(stderr, "usage: %s\n" + " -p|--path 9p path to export\n" + " {-f|--fd } 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[PATH_MAX]; + struct stat stbuf; + int c, option_index; + + is_daemon = 1; + rpath[0] = '\0'; + 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': + strcpy(rpath, optarg); + break; + case 'n': + is_daemon = 0; + break; + case 'f': + sock = atoi(optarg); + break; + case '?': + case 'h': + default: + usage(argv[0]); + return -1; + break; + } + } + + /* Parameter validation */ + if (sock == -1 || rpath[0] == '\0') { + fprintf(stderr, "socket descriptor or path not specified\n"); + usage(argv[0]); + return -1; + } + + if (lstat(rpath, &stbuf) < 0) { + fprintf(stderr, "invalid path \"%s\" specified?\n", rpath); + return -1; + } + + if (!S_ISDIR(stbuf.st_mode)) { + fprintf(stderr, "specified path \"%s\" is not directory\n", rpath); + return -1; + } + + if (is_daemon) { + if (daemon(1, 0) < 0) { + fprintf(stderr, "deamon error\n"); + return -1; + } + openlog(PROGNAME, LOG_PID, LOG_DAEMON); + } + + do_log(LOG_INFO, "Started"); + + 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"); + closelog(); + return 0; +} -- 1.7.6