From 9a9e35b4ba108b63e7371ea37686040769ed0555 Mon Sep 17 00:00:00 2001 From: David Sheets Date: Wed, 10 Feb 2016 09:59:45 +0000 Subject: [PATCH 01/17] Add a FUSE proxy written in C This should improve portability, be more palatable for future distribution, and enable lower-level control. Addresses #8. --- alpine/packages/transfused/.gitignore | 1 + alpine/packages/transfused/Dockerfile | 10 + alpine/packages/transfused/Makefile | 15 + .../packages/transfused/etc/init.d/transfused | 32 ++ alpine/packages/transfused/transfused.c | 491 ++++++++++++++++++ 5 files changed, 549 insertions(+) create mode 100644 alpine/packages/transfused/.gitignore create mode 100644 alpine/packages/transfused/Dockerfile create mode 100644 alpine/packages/transfused/Makefile create mode 100755 alpine/packages/transfused/etc/init.d/transfused create mode 100644 alpine/packages/transfused/transfused.c diff --git a/alpine/packages/transfused/.gitignore b/alpine/packages/transfused/.gitignore new file mode 100644 index 000000000..ea662ae3a --- /dev/null +++ b/alpine/packages/transfused/.gitignore @@ -0,0 +1 @@ +transfused diff --git a/alpine/packages/transfused/Dockerfile b/alpine/packages/transfused/Dockerfile new file mode 100644 index 000000000..d0c3197c0 --- /dev/null +++ b/alpine/packages/transfused/Dockerfile @@ -0,0 +1,10 @@ +FROM alpine:3.3 + +RUN apk update && apk upgrade && apk add alpine-sdk + +RUN mkdir -p /transfused +WORKDIR /transfused + +COPY . /transfused + +RUN make transfused diff --git a/alpine/packages/transfused/Makefile b/alpine/packages/transfused/Makefile new file mode 100644 index 000000000..d5739d5b7 --- /dev/null +++ b/alpine/packages/transfused/Makefile @@ -0,0 +1,15 @@ +.PHONY: all + +DEPS=transfused.c + +all: Dockerfile $(DEPS) + docker build -t transfused:build . + docker run transfused:build cat transfused > transfused + chmod 755 transfused + +transfused: $(DEPS) + gcc -g -static -Wall -Werror -o transfused transfused.c + +clean: + rm -f transfused + docker images -q transfused:build | xargs docker rmi -f diff --git a/alpine/packages/transfused/etc/init.d/transfused b/alpine/packages/transfused/etc/init.d/transfused new file mode 100755 index 000000000..06ee22a3b --- /dev/null +++ b/alpine/packages/transfused/etc/init.d/transfused @@ -0,0 +1,32 @@ +#!/sbin/openrc-run + +description="fuse proxy server" + +depend() +{ + need 9pinit +} + +start() +{ + [ -d /Transfuse ] || exit 0 + + ebegin "Starting FUSE socket passthrough" + + [ -n "${PIDFILE}" ] || PIDFILE=/var/run/transfused.pid + + /sbin/transfused & +} + +stop() +{ + [ -d /Transfuse ] || exit 0 + + ebegin "Stopping FUSE socket passthrough" + + [ -n "${PIDFILE}" ] || PIDFILE=/var/run/transfused.pid + + start-stop-daemon --stop --quiet \ + --pidfile "${PIDFILE}" + eend $? "Failed to stop transfused" +} diff --git a/alpine/packages/transfused/transfused.c b/alpine/packages/transfused/transfused.c new file mode 100644 index 000000000..097a88bcf --- /dev/null +++ b/alpine/packages/transfused/transfused.c @@ -0,0 +1,491 @@ +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#define COPY_BUFSZ 65536 + +int debug = 0; + +int save_trace; + +// execvpe is in unistd.h on Linux. +int execvpe(const char *path, char *const argv[], char *const envp[]); + +typedef struct { + char * socket9p_root; +} parameters; + +typedef struct { + parameters * params; + long id; +} connection_state; + +typedef struct { + char * descr; + long connection; + char * tag; + int from; + int to; +} copy_thread_state; + +char * fusermount = "fusermount"; + +void * must_malloc(char *const descr, size_t size) { + void * ptr; + + ptr = malloc(size); + if (size != 0 && ptr == NULL) { + perror(descr); + exit(1); + } + + return ptr; +} + +char ** read_opts(connection_state * connection, char * buf) { + int read_fd; + char * read_path; + int read_count; + int optc = 1; + char ** optv; + + if (asprintf(&read_path, "%s/connections/%ld/read", + connection->params->socket9p_root, connection->id) == -1) { + fprintf(stderr, "Couldn't allocate read path\n"); + exit(1); + } + + read_fd = open(read_path, O_RDONLY); + if (read_fd == -1) { + fprintf(stderr, "For connection %ld, ", connection->id); + perror("couldn't open read path"); + exit(1); + } + + read_count = read(read_fd, buf, COPY_BUFSZ - 1); + if (read_count == -1) { + perror("read_opts error reading"); + exit(1); + } + buf[read_count] = 0x0; + + for (int i = 0; i < read_count; i++) { + if (buf[i] == 0x0) optc++; + } + + optv = must_malloc("read_opts optv", (optc + 1) * sizeof(void *)); + optv[0] = buf; + optv[optc] = 0x0; + + int j = 1; + for (int i = 0; i < read_count && j < optc; i++) { + if (buf[i] == 0x0) { + optv[j] = buf + i + 1; + j++; + } + } + + free(read_path); + + return optv; +} + +uint64_t message_id(char * message) { + return *((uint64_t *) (message + 8)); +} + +int copy(copy_thread_state * copy_state) { + int from = copy_state->from; + int to = copy_state->to; + char * descr = copy_state->descr; + int read_count, write_count; + long connection = copy_state->connection; + char * tag = copy_state->tag; + char * buf; + + buf = must_malloc(descr, COPY_BUFSZ); + + while(1) { + read_count = read(from, buf, COPY_BUFSZ); + if (read_count == -1) { + fprintf(stderr, "copy %s: error reading ", descr); + perror(""); + exit(1); + } + + if (save_trace) { + int trace_fd; + char * trace_path; + + // TODO: check for errors + asprintf(&trace_path, "/tmp/transfused.%ld.%s.%llu", + connection, tag, message_id(buf)); + trace_fd = open(trace_path, O_WRONLY | O_CREAT, 0600); + write(trace_fd, buf, read_count); + close(trace_fd); + free(trace_path); + } + + write_count = write(to, buf, read_count); + if (write_count == -1) { + fprintf(stderr, "copy %s: error writing ", descr); + perror(""); + exit(1); + } + + if (write_count != read_count) { + fprintf(stderr, "copy %s: read %d but only wrote %d\n", + descr, read_count, write_count); + exit(1); + } + } + + free(buf); + return 0; +} + +int copy_clean_from(copy_thread_state * copy_state) { + int ret = copy(copy_state); + + if (close(copy_state->from)) { + fprintf(stderr, "For %s, ", copy_state->descr); + perror("couldn't close read fd"); + exit(1); + } + + free(copy_state->descr); + free(copy_state); + + return ret; +} + +int copy_clean_to(copy_thread_state * copy_state) { + int ret = copy(copy_state); + + if (close(copy_state->to)) { + fprintf(stderr, "For %s, ", copy_state->descr); + perror("couldn't close write fd"); + exit(1); + } + + free(copy_state->descr); + free(copy_state); + + return ret; +} + +int recv_fd(int sock) { + int ret; + int fd = -1; + char iochar; + char buf[CMSG_SPACE(sizeof(fd))]; + + struct msghdr msg; + struct iovec vec; + struct cmsghdr *cmsg; + + msg.msg_name=NULL; + msg.msg_namelen=0; + vec.iov_base=&iochar; + vec.iov_len=1; + msg.msg_iov=&vec; + + msg.msg_iovlen=1; + + msg.msg_control = buf; + msg.msg_controllen = sizeof(buf); + + ret = recvmsg(sock, &msg, 0); + + if (ret == -1) { + perror("recvmsg"); + exit(1); + } + + if (ret > 0 && msg.msg_controllen > 0) { + cmsg = CMSG_FIRSTHDR(&msg); + if (cmsg->cmsg_level == SOL_SOCKET && (cmsg->cmsg_type == SCM_RIGHTS)) { + fd = *(int*)CMSG_DATA(cmsg); + } else { + fprintf(stderr, "recvmsg: failed to receive an fd\n"); + } + } + + return fd; +} + +// optv must be null-terminated +int get_fuse_sock(char *const optv[]) { + int optc; + char ** argv; + char * envp[2]; + pid_t fusermount_pid; + int fuse_socks[2]; + int status; + int fd; + + // prepare argv from optv + for (optc = 0; optv[optc] != NULL; optc++) {} + + argv = must_malloc("fusermount argv",(optc + 2) * sizeof(char *)); + + argv[0] = fusermount; + memcpy(&argv[1], optv, (optc + 1) * sizeof(char *)); + + // make the socket over which we'll be sent the FUSE socket fd + if (socketpair(PF_UNIX, SOCK_STREAM, 0, fuse_socks)) { + perror("Couldn't create FUSE socketpair"); + exit(1); + } + + // prepare to exec the suid binary fusermount + if (asprintf(&envp[0], "_FUSE_COMMFD=%d", fuse_socks[0]) == -1) { + fprintf(stderr, "Couldn't allocate fusermount envp\n"); + exit(1); + } + envp[1] = 0x0; + + // fork and exec fusermount + fusermount_pid = fork(); + if (!fusermount_pid) { // child + if (execvpe(fusermount, argv, envp)) { + perror("Failed to execute fusermount"); + exit(1); + } + } + + // parent + free(argv); + free(envp[0]); + + // close the end of the socket that we gave away + if (close(fuse_socks[0])) { + perror("Couldn't close unneeded fusermount socket"); + exit(1); + } + + // wait for fusermount to return + waitpid(fusermount_pid, &status, 0); + if (!WIFEXITED(status)) { + fprintf(stderr, "fusermount terminated abnormally\n"); + exit(1); + } + if (WEXITSTATUS(status)) { + fprintf(stderr, "fusermount exited with code %d\n", WEXITSTATUS(status)); + exit(1); + } + + if (debug) fprintf(stderr, "about to recv_fd from fusermount\n"); + + fd = recv_fd(fuse_socks[1]); + if (fd == -1) { + fprintf(stderr, "Couldn't receive fd over FUSE socket\n"); + exit(1); + } + + // close the read end of the socket + if (close(fuse_socks[1])) { + perror("Couldn't close fusermount read socket"); + exit(1); + } + + return fd; +} + +int start_reader(connection_state * connection, int fuse) { + int read_fd; + char * read_path; + pthread_t child; + copy_thread_state * copy_state; + void *(*copy_clean)(void *) = (void *(*)(void *)) copy_clean_from; + + if (asprintf(&read_path, "%s/connections/%ld/read", + connection->params->socket9p_root, connection->id) == -1) { + fprintf(stderr, "Couldn't allocate read path\n"); + exit(1); + } + + read_fd = open(read_path, O_RDONLY); + if (read_fd == -1) { + fprintf(stderr, "For connection %ld, ", connection->id); + perror("couldn't open read path"); + exit(1); + } + + copy_state = must_malloc("start_reader copy_state", + sizeof(copy_thread_state)); + copy_state->descr = read_path; + copy_state->connection = connection->id; + copy_state->tag = "read"; + copy_state->from = read_fd; + copy_state->to = fuse; + if ((errno = pthread_create(&child, NULL, copy_clean, copy_state))) { + fprintf(stderr, "couldn't create read copy thread for connection %ld ", + connection->id); + perror(""); + exit(1); + } + + if ((errno = pthread_detach(child))) { + fprintf(stderr, "couldn't detach read copy thread for connection '%ld' ", + connection->id); + perror(""); + exit(1); + } + + return 0; +} + +int start_writer(connection_state * connection, int fuse) { + int write_fd; + char * write_path; + copy_thread_state * copy_state; + + if (asprintf(&write_path, "%s/connections/%ld/write", + connection->params->socket9p_root, connection->id) == -1) { + fprintf(stderr, "Couldn't allocate write path\n"); + exit(1); + } + + write_fd = open(write_path, O_WRONLY); + if (write_fd == -1) { + fprintf(stderr, "For connection %ld, ", connection->id); + perror("couldn't open write path"); + exit(1); + } + + copy_state = must_malloc("start_writer copy_state", + sizeof(copy_thread_state)); + copy_state->descr = write_path; + copy_state->connection = connection->id; + copy_state->tag = "write"; + copy_state->from = fuse; + copy_state->to = write_fd; + copy_clean_to(copy_state); + + return 0; +} + +int handle_connection(connection_state * connection) { + char ** optv; + int fuse; + char * buf; + int ret; + + buf = must_malloc("read_opts packet malloc", COPY_BUFSZ); + + optv = read_opts(connection, buf); + fuse = get_fuse_sock(optv); + free(optv); + free(buf); + + start_reader(connection, fuse); + ret = start_writer(connection, fuse); + free(connection); + + return ret; +} + +void toggle_save_trace(int sig) { + save_trace = !save_trace; +} + +void setup_save_trace() { + save_trace = 0; + + if (SIG_ERR == signal(SIGHUP, toggle_save_trace)) { + perror("Couldn't set SIGHUP behavior"); + exit(1); + } + + if (siginterrupt(SIGHUP, 1)) { + perror("Couldn't set siginterrupt for SIGHUP"); + exit(1); + } +} + +#define ID_LEN 512 + +int main(int argc, char * argv[]) { + int events, read_count; + char buf[ID_LEN]; + long conn; + pthread_t child; + void *(*handle)(void *) = (void *(*)(void *)) handle_connection; + parameters params; + connection_state * connection; + char * events_path; + + if (argc < 2) { + params.socket9p_root = "/Transfuse"; + } else { + params.socket9p_root = argv[1]; + } + + if (asprintf(&events_path, "%s/events", params.socket9p_root) == -1) { + fprintf(stderr, "Couldn't allocate events path\n"); + exit(1); + } + + setup_save_trace(); + + events = open(events_path, O_RDONLY | O_CLOEXEC); + while (events != -1) { + read_count = read(events, buf, ID_LEN - 1); + if (read_count == -1) { + perror("Error reading events path"); + exit(1); + } else if (read_count == 0) { + // TODO: this is probably the 9p server's fault due to + // not dropping the read 0 to force short read if + // the real read is flushed + fprintf(stderr, "read 0 from event stream\n"); + continue; + } + + buf[read_count] = 0x0; + + errno = 0; + conn = strtol(buf, NULL, 10); + if (errno) { + fprintf(stderr, "connection id of string '%s' ", buf); + perror("failed"); + exit(1); + } + + if (debug) fprintf(stderr, "handle connection %ld\n", conn); + + connection = must_malloc("connection state", sizeof(connection_state)); + connection->id = conn; + connection->params = ¶ms; + + if ((errno = pthread_create(&child, NULL, handle, connection))) { + fprintf(stderr, "couldn't create thread for connection '%ld' ", conn); + perror(""); + exit(1); + } + + if ((errno = pthread_detach(child))) { + fprintf(stderr, "couldn't detach thread for connection '%ld' ", conn); + perror(""); + exit(1); + } + + if (debug) fprintf(stderr, "thread spawned\n"); + } + + fprintf(stderr, "failed to open events path: %s\n", events_path); + + free(events_path); + return 1; +} From 192c90b0a7ddcae3a0551b35fb6c2d708553902d Mon Sep 17 00:00:00 2001 From: David Sheets Date: Wed, 10 Feb 2016 10:05:44 +0000 Subject: [PATCH 02/17] Replace 9pudfuse with transfused --- alpine/Dockerfile | 6 +++--- alpine/packages/Makefile | 6 +++--- alpine/packages/diagnostics/diagnostics | 4 ++-- alpine/packages/docker/etc/init.d/docker | 2 +- alpine/packages/hostlog/etc/init.d/hostlog | 2 +- xhyve/fuserun.sh | 2 +- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/alpine/Dockerfile b/alpine/Dockerfile index 285dc7ab8..d287ea157 100644 --- a/alpine/Dockerfile +++ b/alpine/Dockerfile @@ -23,8 +23,8 @@ COPY mkinitrd.sh /bin/ COPY packages/9pudc/9pudc /sbin/ COPY packages/9pudc/etc /etc/ -COPY packages/9pudfuse/9pudfuse /sbin/ -COPY packages/9pudfuse/etc /etc/ +COPY packages/transfused/transfused /sbin/ +COPY packages/transfused/etc /etc/ COPY packages/mdnstool/mdnstool /sbin/ COPY packages/mdnstool/etc /etc/ COPY packages/docker/docker /usr/bin/ @@ -78,7 +78,7 @@ RUN \ rc-update add docker default && \ rc-update add 9pinit boot && \ rc-update add 9pudc default && \ - rc-update add 9pudfuse default && \ + rc-update add transfused default && \ rc-update add mdnstool default && \ rc-update add automount boot && \ rc-update add diagnostics default && \ diff --git a/alpine/packages/Makefile b/alpine/packages/Makefile index 995805a3e..014f78c59 100644 --- a/alpine/packages/Makefile +++ b/alpine/packages/Makefile @@ -1,6 +1,6 @@ all: $(MAKE) -C 9pudc - $(MAKE) -C 9pudfuse + $(MAKE) -C transfused $(MAKE) -C mdnstool $(MAKE) -C docker $(MAKE) -C hupper @@ -8,14 +8,14 @@ all: arm: $(MAKE) -C 9pudc ARCH=arm - $(MAKE) -C 9pudfuse ARCH=arm + $(MAKE) -C transfused ARCH=arm $(MAKE) -C mdnstool ARCH=arm $(MAKE) -C docker ARCH=arm $(MAKE) -C hupper ARCH=arm clean: $(MAKE) -C 9pudc clean - $(MAKE) -C 9pudfuse clean + $(MAKE) -C transfused clean $(MAKE) -C mdnstool clean $(MAKE) -C docker clean $(MAKE) -C hupper clean diff --git a/alpine/packages/diagnostics/diagnostics b/alpine/packages/diagnostics/diagnostics index 5ed2299e6..b81f24733 100755 --- a/alpine/packages/diagnostics/diagnostics +++ b/alpine/packages/diagnostics/diagnostics @@ -11,8 +11,8 @@ UDC=$(ps -eo args | grep '^/sbin/9pudc') [ $? -eq 0 ] && printf "✓ Process 9pudc running: $UDC\n" || printf "✗ No 9pudc process\n" if [ -d /Transfuse ] then - FUSE=$(ps -eo args | grep '^/sbin/9pudfuse') - [ $? -eq 0 ] && printf "✓ Process 9pudfuse running\n" || printf "✗ No 9pudfuse process\n" + FUSE=$(ps -eo args | grep '^/sbin/transfused') + [ $? -eq 0 ] && printf "✓ Process transfused running\n" || printf "✗ No transfused process\n" fi MDNS=$(ps -eo args | grep '^/sbin/mdnstool') [ $? -eq 0 ] && printf "✓ Process mdnstool running: $MDNS\n" || printf "✗ No mdnstool process\n" diff --git a/alpine/packages/docker/etc/init.d/docker b/alpine/packages/docker/etc/init.d/docker index 19231e9c5..90e6e725f 100755 --- a/alpine/packages/docker/etc/init.d/docker +++ b/alpine/packages/docker/etc/init.d/docker @@ -27,6 +27,6 @@ start_stop_daemon_args="--background \ depend() { - after 9pudc 9pudfuse + after 9pudc transfused before chronyd } diff --git a/alpine/packages/hostlog/etc/init.d/hostlog b/alpine/packages/hostlog/etc/init.d/hostlog index b0e19cca5..f773ac8c0 100755 --- a/alpine/packages/hostlog/etc/init.d/hostlog +++ b/alpine/packages/hostlog/etc/init.d/hostlog @@ -3,7 +3,7 @@ depends() { before acpid docker syslog chronyd - after 9pinit 9pudfuse + after 9pinit transfused } start() diff --git a/xhyve/fuserun.sh b/xhyve/fuserun.sh index 3e57f2309..60742f031 100755 --- a/xhyve/fuserun.sh +++ b/xhyve/fuserun.sh @@ -17,7 +17,7 @@ fi #IMG_HDD="-s 4,virtio-blk,/somepath/somefile.img" PCI_DEV="-s 0:0,hostbridge -s 31,lpc" RND="-s 5,virtio-rnd" -FS="-s 6,virtio-9p,path=9pudfuse.sock,tag=fuse" +FS="-s 6,virtio-9p,path=transfused.sock,tag=fuse" LPC_DEV="-l com1,stdio" ACPI="-A" CLOCK="-u" From 6384fbe0dcce5d96caeac1663980835cf6f197fc Mon Sep 17 00:00:00 2001 From: David Sheets Date: Wed, 10 Feb 2016 10:07:26 +0000 Subject: [PATCH 03/17] transfused: fix .gitignore --- alpine/packages/transfused/.gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/alpine/packages/transfused/.gitignore b/alpine/packages/transfused/.gitignore index ea662ae3a..497a34c5f 100644 --- a/alpine/packages/transfused/.gitignore +++ b/alpine/packages/transfused/.gitignore @@ -1 +1 @@ -transfused +/transfused From 1efc36a38cefa8ebc18bca3d598931728fd960c3 Mon Sep 17 00:00:00 2001 From: David Sheets Date: Wed, 10 Feb 2016 10:09:09 +0000 Subject: [PATCH 04/17] 9pudfuse: remove in favor of transfused --- alpine/packages/9pudfuse/.gitignore | 1 - alpine/packages/9pudfuse/.merlin | 6 - alpine/packages/9pudfuse/Dockerfile | 13 -- alpine/packages/9pudfuse/Makefile | 11 -- alpine/packages/9pudfuse/etc/init.d/9pudfuse | 37 ---- alpine/packages/9pudfuse/fuse_socket_9p.ml | 170 ------------------- 6 files changed, 238 deletions(-) delete mode 100644 alpine/packages/9pudfuse/.gitignore delete mode 100644 alpine/packages/9pudfuse/.merlin delete mode 100644 alpine/packages/9pudfuse/Dockerfile delete mode 100644 alpine/packages/9pudfuse/Makefile delete mode 100755 alpine/packages/9pudfuse/etc/init.d/9pudfuse delete mode 100644 alpine/packages/9pudfuse/fuse_socket_9p.ml diff --git a/alpine/packages/9pudfuse/.gitignore b/alpine/packages/9pudfuse/.gitignore deleted file mode 100644 index a9a5bb2c6..000000000 --- a/alpine/packages/9pudfuse/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/9pudfuse diff --git a/alpine/packages/9pudfuse/.merlin b/alpine/packages/9pudfuse/.merlin deleted file mode 100644 index bde497c73..000000000 --- a/alpine/packages/9pudfuse/.merlin +++ /dev/null @@ -1,6 +0,0 @@ -PKG result -PKG stringext -PKG fd-send-recv - -S . -B . diff --git a/alpine/packages/9pudfuse/Dockerfile b/alpine/packages/9pudfuse/Dockerfile deleted file mode 100644 index 82b0f4c44..000000000 --- a/alpine/packages/9pudfuse/Dockerfile +++ /dev/null @@ -1,13 +0,0 @@ -FROM ocaml/opam:alpine-3.3_ocaml-4.02.3 - -ENV \ - OPAMYES=true \ - CAML_LD_LIBRARY_PATH="/root/.opam/4.02.3/lib/stublibs:/usr/pkg/lib/ocaml/stublibs" \ - MANPATH="/root/.opam/4.02.3/man:" \ - PERL5LIB="/root/.opam/4.02.3/lib/perl5" \ - OCAML_TOPLEVEL_PATH="/root/.opam/4.02.3/lib/toplevel" \ - PATH=/root/.opam/4.02.3/bin:$PATH - -COPY . . -RUN opam depext -i result stringext fd-send-recv -RUN opam config exec -- ocamlbuild -tag debug -use-ocamlfind -package stringext,fd-send-recv,result fuse_socket_9p.native diff --git a/alpine/packages/9pudfuse/Makefile b/alpine/packages/9pudfuse/Makefile deleted file mode 100644 index 78e5f6990..000000000 --- a/alpine/packages/9pudfuse/Makefile +++ /dev/null @@ -1,11 +0,0 @@ -all: 9pudfuse - -9pudfuse: Dockerfile fuse_socket_9p.ml - docker build -t 9pudfuse:build . - docker run --rm 9pudfuse:build \ - cat fuse_socket_9p.native > 9pudfuse - chmod 755 9pudfuse - -clean: - rm -f 9pudfuse - docker images -q 9pudfuse:build | xargs docker rmi -f diff --git a/alpine/packages/9pudfuse/etc/init.d/9pudfuse b/alpine/packages/9pudfuse/etc/init.d/9pudfuse deleted file mode 100755 index 68bb02a54..000000000 --- a/alpine/packages/9pudfuse/etc/init.d/9pudfuse +++ /dev/null @@ -1,37 +0,0 @@ -#!/sbin/openrc-run - -description="9p socket fuse proxy server" - -depend() -{ - need 9pinit -} - -start() -{ - [ -d /Transfuse ] || exit 0 - - ebegin "Starting FUSE socket passthrough" - - [ -n "${PIDFILE}" ] || PIDFILE=/var/run/9pudfuse.pid - -# start-stop-daemon --start --quiet \ -# --exec /sbin/9pudfuse \ -# --pidfile "${PIDFILE}" \ -# -- -path /Transfuse -# eend $? "Failed to start 9pudfuse" - OCAMLRUNPARAM=b /sbin/9pudfuse & -} - -stop() -{ - [ -d /Transfuse ] || exit 0 - - ebegin "Stopping FUSE socket passthrough" - - [ -n "${PIDFILE}" ] || PIDFILE=/var/run/9pudfuse.pid - - start-stop-daemon --stop --quiet \ - --pidfile "${PIDFILE}" - eend $? "Failed to stop 9pudfuse" -} diff --git a/alpine/packages/9pudfuse/fuse_socket_9p.ml b/alpine/packages/9pudfuse/fuse_socket_9p.ml deleted file mode 100644 index 104ce47f3..000000000 --- a/alpine/packages/9pudfuse/fuse_socket_9p.ml +++ /dev/null @@ -1,170 +0,0 @@ - -exception ReadClosed - -let fusermount = "fusermount" -let path = "/Transfuse" - -module Log = struct - let fatal fmt = - Printf.ksprintf (fun s -> prerr_endline s; exit 1) fmt - let error fmt = - Printf.ksprintf (fun s -> prerr_endline s) fmt - let info fmt = - Printf.ksprintf (fun s -> prerr_endline s) fmt -end - -let events_path = path ^ "/events" - -let try_fork caller_name = - try - Unix.fork () - with Unix.Unix_error (err,"fork",_) -> - Log.fatal "%s fork failed: %s" caller_name (Unix.error_message err) - -let check_status = function - | Unix.WEXITED 0 -> Result.Ok () - | Unix.WEXITED k -> - Result.Error ("exit code "^(string_of_int k)) - | Unix.WSIGNALED k -> - Result.Error ("ocaml kill signal "^(string_of_int k)) - | Unix.WSTOPPED k -> - Result.Error ("ocaml stop signal "^(string_of_int k)) - -let finally f at_end = - let r = try f () with e -> (at_end (); raise e) in - at_end (); - r - -let copy description dst src = - let sz = 1 lsl 16 in - let buf = Bytes.create sz in - (*let pnum = ref 0 in*) - let rec loop () = - let n = Unix.read src buf 0 sz in - (if n = 0 then raise ReadClosed); -(* - let fd = Unix.( - openfile ("/tmp/"^description^"_"^(string_of_int !pnum)) - [O_WRONLY; O_CREAT] 0o600) in - let k = Unix.write fd buf 0 n in - assert (k = n); - Unix.close fd; - incr pnum; -*) - let written = try - Unix.write dst buf 0 n - with - | Unix.Unix_error (Unix.EINVAL, "write", _) -> - failwith ("copy write for "^description^" failed with EINVAL") - | Unix.Unix_error (Unix.ENOENT, "write", _) -> - failwith ("copy write for "^description^" failed with ENOENT") - in - (if n <> written - then Log.error "copy of %s read %d but wrote %d" description n written); - loop () - in - try loop () - with - | ReadClosed -> raise ReadClosed - | e -> (Log.error "copy for %s failed" description; raise e) - -let with_reader id f = - let read_path = Printf.sprintf "%s/connections/%d/read" path id in - let read_fd = Unix.(openfile read_path [O_RDONLY] 0o000) in - try finally (fun () -> - f read_path read_fd - ) (fun () -> - Unix.close read_fd - ) with - | ReadClosed -> exit 0 - -let with_writer id f = - let write_path = Printf.sprintf "%s/connections/%d/write" path id in - let write_fd = Unix.(openfile write_path [O_WRONLY] 0o000) in - finally (fun () -> - f write_path write_fd - ) (fun () -> - Unix.close write_fd; - Unix.unlink write_path - ) - -let read_opts id = with_reader id (fun read_path read_fd -> - let sz = 512 in - let buf = Bytes.create sz in - let n = Unix.read read_fd buf 0 sz in - let opts = Stringext.split ~on:'\000' (Bytes.sub buf 0 n) in - Array.of_list opts -) - -let get_fuse_sock opts = - let wsock, rsock = Unix.(socketpair PF_UNIX SOCK_STREAM 0) in - let wfd = Fd_send_recv.int_of_fd wsock in - let opts = Array.append [|fusermount|] opts in - let pid = Unix.(create_process_env fusermount opts - [|"_FUSE_COMMFD="^(string_of_int wfd)|] - stdin stdout stderr) in - let _, status = Unix.waitpid [] pid in - let () = Unix.(shutdown wsock SHUTDOWN_ALL) in - match check_status status with - | Result.Error str -> - let opts = String.concat " " (Array.to_list opts) in - Log.fatal "%s: %s" opts str - | Result.Ok () -> - (* We must read at least 1 byte, by POSIX! *) - let _, _, fd = Fd_send_recv.recv_fd rsock "\000" 0 1 [] in - let () = Unix.(shutdown rsock SHUTDOWN_ALL) in - fd - -(* readers fork into a new process *) -let start_reader id fuse = - match try_fork "start_reader" with - | 0 -> (* child *) - with_reader id (fun _read_path read_fd -> - copy ("reader_"^string_of_int id) fuse read_fd - ) - | _child_pid -> (* parent *) - () - -(* writers stay in the calling process *) -let start_writer id fuse = with_writer id (fun write_path write_fd -> - copy ("writer_"^string_of_int id) write_fd fuse -) - -let handle_connection id = - Log.info "handle_connection %d" id; - match try_fork "handle_connection" with - | 0 -> (* child *) - let opts = read_opts id in - let fuse = get_fuse_sock opts in - start_reader id fuse; - begin try ignore (start_writer id fuse); exit 0 - with - | e -> prerr_endline (Printexc.to_string e); exit 1 - end - | _child_pid -> (* parent *) - () - -let connection_loop () = - let events = Unix.(openfile events_path [O_RDONLY] 0o000) in - (* 512 bytes is easily big enough to read a whole connection id *) - let sz = 512 in - let buf = Bytes.create sz in - let rec recv () = - begin try - let n = Unix.read events buf 0 sz in - let s = String.trim Bytes.(to_string (sub buf 0 n)) in - let id = int_of_string s in - handle_connection id; - with - | Unix.Unix_error (err,"read",path) -> - Log.fatal "Error reading events file %s: %s" - path (Unix.error_message err) - | Failure "int_of_string" -> - Log.fatal "Failed to parse integer connection id" - end; - recv () - in - recv () - -;; -connection_loop () From 7ab2f3816c1ead09683415dc89898eb50a39f641 Mon Sep 17 00:00:00 2001 From: David Sheets Date: Wed, 10 Feb 2016 17:09:11 +0000 Subject: [PATCH 05/17] transfused: address @yallop's comments --- alpine/packages/transfused/transfused.c | 336 ++++++++++-------------- 1 file changed, 136 insertions(+), 200 deletions(-) diff --git a/alpine/packages/transfused/transfused.c b/alpine/packages/transfused/transfused.c index 097a88bcf..7e398d69d 100644 --- a/alpine/packages/transfused/transfused.c +++ b/alpine/packages/transfused/transfused.c @@ -3,6 +3,7 @@ #include #include +#include #include #include @@ -40,14 +41,20 @@ typedef struct { char * fusermount = "fusermount"; +void die(int exit_code, const char * perror_arg, const char * fmt, ...) { + va_list argp; + va_start(argp, fmt); + vfprintf(stderr, fmt, argp); + va_end(argp); + if (perror_arg != NULL) perror(perror_arg); + exit(exit_code); +} + void * must_malloc(char *const descr, size_t size) { void * ptr; ptr = malloc(size); - if (size != 0 && ptr == NULL) { - perror(descr); - exit(1); - } + if (size != 0 && ptr == NULL) die(1,descr,""); return ptr; } @@ -60,30 +67,23 @@ char ** read_opts(connection_state * connection, char * buf) { char ** optv; if (asprintf(&read_path, "%s/connections/%ld/read", - connection->params->socket9p_root, connection->id) == -1) { - fprintf(stderr, "Couldn't allocate read path\n"); - exit(1); - } + connection->params->socket9p_root, connection->id) == -1) + die(1,"Couldn't allocate read path",""); read_fd = open(read_path, O_RDONLY); - if (read_fd == -1) { - fprintf(stderr, "For connection %ld, ", connection->id); - perror("couldn't open read path"); - exit(1); - } + if (read_fd == -1) + die(1,"couldn't open read path", "For connection %ld, ", connection->id); read_count = read(read_fd, buf, COPY_BUFSZ - 1); - if (read_count == -1) { - perror("read_opts error reading"); - exit(1); - } + if (read_count == -1) die(1,"read_opts error reading", ""); + buf[read_count] = 0x0; for (int i = 0; i < read_count; i++) { if (buf[i] == 0x0) optc++; } - optv = must_malloc("read_opts optv", (optc + 1) * sizeof(void *)); + optv = (char **) must_malloc("read_opts optv", (optc + 1) * sizeof(void *)); optv[0] = buf; optv[optc] = 0x0; @@ -100,28 +100,24 @@ char ** read_opts(connection_state * connection, char * buf) { return optv; } -uint64_t message_id(char * message) { - return *((uint64_t *) (message + 8)); +uint64_t message_id(uint64_t * message) { + return message[1]; } -int copy(copy_thread_state * copy_state) { +void copy(copy_thread_state * copy_state) { int from = copy_state->from; int to = copy_state->to; char * descr = copy_state->descr; int read_count, write_count; long connection = copy_state->connection; char * tag = copy_state->tag; - char * buf; + void * buf; buf = must_malloc(descr, COPY_BUFSZ); while(1) { read_count = read(from, buf, COPY_BUFSZ); - if (read_count == -1) { - fprintf(stderr, "copy %s: error reading ", descr); - perror(""); - exit(1); - } + if (read_count == -1) die(1, "", "copy %s: error reading: ", descr); if (save_trace) { int trace_fd; @@ -137,51 +133,42 @@ int copy(copy_thread_state * copy_state) { } write_count = write(to, buf, read_count); - if (write_count == -1) { - fprintf(stderr, "copy %s: error writing ", descr); - perror(""); - exit(1); - } + if (write_count == -1) die(1, "", "copy %s: error writing: ", descr); - if (write_count != read_count) { - fprintf(stderr, "copy %s: read %d but only wrote %d\n", - descr, read_count, write_count); - exit(1); - } + if (write_count != read_count) + die(1, NULL, "copy %s: read %d by only wrote %d\n", + descr, read_count, write_count); } free(buf); - return 0; } -int copy_clean_from(copy_thread_state * copy_state) { - int ret = copy(copy_state); +void * copy_clean_from(copy_thread_state * copy_state) { + copy(copy_state); - if (close(copy_state->from)) { - fprintf(stderr, "For %s, ", copy_state->descr); - perror("couldn't close read fd"); - exit(1); - } + if (close(copy_state->from)) + die(1, "couldn't close read fd", "For %s, ", copy_state->descr); free(copy_state->descr); free(copy_state); - return ret; + return NULL; } -int copy_clean_to(copy_thread_state * copy_state) { - int ret = copy(copy_state); +void * copy_clean_from_thread(void * copy_state) { + return (copy_clean_from((copy_thread_state *) copy_state)); +} - if (close(copy_state->to)) { - fprintf(stderr, "For %s, ", copy_state->descr); - perror("couldn't close write fd"); - exit(1); - } +void * copy_clean_to(copy_thread_state * copy_state) { + copy(copy_state); + + if (close(copy_state->to)) + die(1, "couldn't close write fd", "For %s, ", copy_state->descr); free(copy_state->descr); free(copy_state); - return ret; + return NULL; } int recv_fd(int sock) { @@ -207,17 +194,12 @@ int recv_fd(int sock) { ret = recvmsg(sock, &msg, 0); - if (ret == -1) { - perror("recvmsg"); - exit(1); - } + if (ret == -1) die(1,"recvmsg",""); if (ret > 0 && msg.msg_controllen > 0) { cmsg = CMSG_FIRSTHDR(&msg); if (cmsg->cmsg_level == SOL_SOCKET && (cmsg->cmsg_type == SCM_RIGHTS)) { fd = *(int*)CMSG_DATA(cmsg); - } else { - fprintf(stderr, "recvmsg: failed to receive an fd\n"); } } @@ -237,152 +219,116 @@ int get_fuse_sock(char *const optv[]) { // prepare argv from optv for (optc = 0; optv[optc] != NULL; optc++) {} - argv = must_malloc("fusermount argv",(optc + 2) * sizeof(char *)); + argv = (char **) must_malloc("fusermount argv",(optc + 2) * sizeof(char *)); argv[0] = fusermount; memcpy(&argv[1], optv, (optc + 1) * sizeof(char *)); // make the socket over which we'll be sent the FUSE socket fd - if (socketpair(PF_UNIX, SOCK_STREAM, 0, fuse_socks)) { - perror("Couldn't create FUSE socketpair"); - exit(1); - } + if (socketpair(PF_UNIX, SOCK_STREAM, 0, fuse_socks)) + die(1, "Couldn't create FUSE socketpair", ""); // prepare to exec the suid binary fusermount - if (asprintf(&envp[0], "_FUSE_COMMFD=%d", fuse_socks[0]) == -1) { - fprintf(stderr, "Couldn't allocate fusermount envp\n"); - exit(1); - } + if (asprintf(&envp[0], "_FUSE_COMMFD=%d", fuse_socks[0]) == -1) + die(1, "", "Couldn't allocate fusermount envp"); + envp[1] = 0x0; // fork and exec fusermount fusermount_pid = fork(); - if (!fusermount_pid) { // child - if (execvpe(fusermount, argv, envp)) { - perror("Failed to execute fusermount"); - exit(1); - } - } + if (!fusermount_pid) // child + if (execvpe(fusermount, argv, envp)) + die(1, "Failed to execute fusermount", ""); // parent free(argv); free(envp[0]); // close the end of the socket that we gave away - if (close(fuse_socks[0])) { - perror("Couldn't close unneeded fusermount socket"); - exit(1); - } - + if (close(fuse_socks[0])) + die(1, "Couldn't close unneeded fusermount socket", ""); + // wait for fusermount to return waitpid(fusermount_pid, &status, 0); - if (!WIFEXITED(status)) { - fprintf(stderr, "fusermount terminated abnormally\n"); - exit(1); - } - if (WEXITSTATUS(status)) { - fprintf(stderr, "fusermount exited with code %d\n", WEXITSTATUS(status)); - exit(1); - } + if (!WIFEXITED(status)) + die(1, NULL, "fusermount terminated abnormally\n"); + + if (WEXITSTATUS(status)) + die(1, NULL, "fusermount exited with code %d\n", WEXITSTATUS(status)); if (debug) fprintf(stderr, "about to recv_fd from fusermount\n"); fd = recv_fd(fuse_socks[1]); - if (fd == -1) { - fprintf(stderr, "Couldn't receive fd over FUSE socket\n"); - exit(1); - } + if (fd == -1) + die(1, NULL, "Couldn't receive fd over FUSE socket\n"); // close the read end of the socket - if (close(fuse_socks[1])) { - perror("Couldn't close fusermount read socket"); - exit(1); - } + if (close(fuse_socks[1])) + die(1, "Couldn't close fusermount read socket", ""); return fd; } -int start_reader(connection_state * connection, int fuse) { +void start_reader(connection_state * connection, int fuse) { int read_fd; char * read_path; pthread_t child; copy_thread_state * copy_state; - void *(*copy_clean)(void *) = (void *(*)(void *)) copy_clean_from; if (asprintf(&read_path, "%s/connections/%ld/read", - connection->params->socket9p_root, connection->id) == -1) { - fprintf(stderr, "Couldn't allocate read path\n"); - exit(1); - } + connection->params->socket9p_root, connection->id) == -1) + die(1, "", "Couldn't allocate read path: "); read_fd = open(read_path, O_RDONLY); - if (read_fd == -1) { - fprintf(stderr, "For connection %ld, ", connection->id); - perror("couldn't open read path"); - exit(1); - } + if (read_fd == -1) + die(1, "couldn't open read path", "For connection %ld, ", connection->id); - copy_state = must_malloc("start_reader copy_state", - sizeof(copy_thread_state)); + copy_state = (copy_thread_state *) must_malloc("start_reader copy_state", + sizeof(copy_thread_state)); copy_state->descr = read_path; copy_state->connection = connection->id; copy_state->tag = "read"; copy_state->from = read_fd; copy_state->to = fuse; - if ((errno = pthread_create(&child, NULL, copy_clean, copy_state))) { - fprintf(stderr, "couldn't create read copy thread for connection %ld ", - connection->id); - perror(""); - exit(1); - } + if ((errno = pthread_create(&child, NULL, + copy_clean_from_thread, copy_state))) + die(1, "", "couldn't create read copy thread for connection %ld: ", + connection->id); - if ((errno = pthread_detach(child))) { - fprintf(stderr, "couldn't detach read copy thread for connection '%ld' ", - connection->id); - perror(""); - exit(1); - } - - return 0; + if ((errno = pthread_detach(child))) + die (1, "", "couldn't detach read copy thread for connection '%ld': ", + connection->id); } -int start_writer(connection_state * connection, int fuse) { +void do_write(connection_state * connection, int fuse) { int write_fd; char * write_path; copy_thread_state * copy_state; if (asprintf(&write_path, "%s/connections/%ld/write", - connection->params->socket9p_root, connection->id) == -1) { - fprintf(stderr, "Couldn't allocate write path\n"); - exit(1); - } + connection->params->socket9p_root, connection->id) == -1) + die(1, "", "Couldn't allocate write path: "); write_fd = open(write_path, O_WRONLY); - if (write_fd == -1) { - fprintf(stderr, "For connection %ld, ", connection->id); - perror("couldn't open write path"); - exit(1); - } + if (write_fd == -1) + die(1, "couldn't open write path", "For connection %ld, ", connection->id); - copy_state = must_malloc("start_writer copy_state", - sizeof(copy_thread_state)); + copy_state = (copy_thread_state *) must_malloc("do_write copy_state", + sizeof(copy_thread_state)); copy_state->descr = write_path; copy_state->connection = connection->id; copy_state->tag = "write"; copy_state->from = fuse; copy_state->to = write_fd; copy_clean_to(copy_state); - - return 0; } -int handle_connection(connection_state * connection) { +void * handle_connection(connection_state * connection) { char ** optv; int fuse; char * buf; - int ret; - buf = must_malloc("read_opts packet malloc", COPY_BUFSZ); + buf = (char *) must_malloc("read_opts packet malloc", COPY_BUFSZ); optv = read_opts(connection, buf); fuse = get_fuse_sock(optv); @@ -390,10 +336,14 @@ int handle_connection(connection_state * connection) { free(buf); start_reader(connection, fuse); - ret = start_writer(connection, fuse); + do_write(connection, fuse); free(connection); - return ret; + return NULL; +} + +void * handle_connection_thread(void * connection) { + return handle_connection((connection_state *) connection); } void toggle_save_trace(int sig) { @@ -403,15 +353,11 @@ void toggle_save_trace(int sig) { void setup_save_trace() { save_trace = 0; - if (SIG_ERR == signal(SIGHUP, toggle_save_trace)) { - perror("Couldn't set SIGHUP behavior"); - exit(1); - } + if (SIG_ERR == signal(SIGHUP, toggle_save_trace)) + die(1, "Couldn't set SIGHUP behavior", ""); - if (siginterrupt(SIGHUP, 1)) { - perror("Couldn't set siginterrupt for SIGHUP"); - exit(1); - } + if (siginterrupt(SIGHUP, 1)) + die(1, "Couldn't set siginterrupt for SIGHUP", ""); } #define ID_LEN 512 @@ -419,11 +365,10 @@ void setup_save_trace() { int main(int argc, char * argv[]) { int events, read_count; char buf[ID_LEN]; - long conn; + long conn_id; pthread_t child; - void *(*handle)(void *) = (void *(*)(void *)) handle_connection; parameters params; - connection_state * connection; + connection_state * conn; char * events_path; if (argc < 2) { @@ -432,60 +377,51 @@ int main(int argc, char * argv[]) { params.socket9p_root = argv[1]; } - if (asprintf(&events_path, "%s/events", params.socket9p_root) == -1) { - fprintf(stderr, "Couldn't allocate events path\n"); - exit(1); - } + if (asprintf(&events_path, "%s/events", params.socket9p_root) == -1) + die(1, "", "Couldn't allocate events path: "); setup_save_trace(); events = open(events_path, O_RDONLY | O_CLOEXEC); - while (events != -1) { - read_count = read(events, buf, ID_LEN - 1); - if (read_count == -1) { - perror("Error reading events path"); - exit(1); - } else if (read_count == 0) { - // TODO: this is probably the 9p server's fault due to - // not dropping the read 0 to force short read if - // the real read is flushed - fprintf(stderr, "read 0 from event stream\n"); - continue; + if (events != -1) { + while (1) { + read_count = read(events, buf, ID_LEN - 1); + if (read_count == -1) { + die(1, "Error reading events path", ""); + } else if (read_count == 0) { + // TODO: this is probably the 9p server's fault due to + // not dropping the read 0 to force short read if + // the real read is flushed + fprintf(stderr, "read 0 from event stream\n"); + continue; + } + + buf[read_count] = 0x0; + + errno = 0; + conn_id = strtol(buf, NULL, 10); + if (errno) die(1, "failed", "Connection id of string '%s'", buf); + + if (debug) fprintf(stderr, "handle connection %ld\n", conn_id); + + conn = (connection_state *) must_malloc("connection state", + sizeof(connection_state)); + conn->id = conn_id; + conn->params = ¶ms; + + if ((errno = pthread_create(&child, NULL, + handle_connection_thread, conn))) + die(1, "", "Couldn't create thread for connection '%ld': ", conn_id); + + if ((errno = pthread_detach(child))) + die(1, "", "Couldn't detach thread for connection '%ld': ", conn_id); + + if (debug) fprintf(stderr, "thread spawned\n"); } - - buf[read_count] = 0x0; - - errno = 0; - conn = strtol(buf, NULL, 10); - if (errno) { - fprintf(stderr, "connection id of string '%s' ", buf); - perror("failed"); - exit(1); - } - - if (debug) fprintf(stderr, "handle connection %ld\n", conn); - - connection = must_malloc("connection state", sizeof(connection_state)); - connection->id = conn; - connection->params = ¶ms; - - if ((errno = pthread_create(&child, NULL, handle, connection))) { - fprintf(stderr, "couldn't create thread for connection '%ld' ", conn); - perror(""); - exit(1); - } - - if ((errno = pthread_detach(child))) { - fprintf(stderr, "couldn't detach thread for connection '%ld' ", conn); - perror(""); - exit(1); - } - - if (debug) fprintf(stderr, "thread spawned\n"); } - fprintf(stderr, "failed to open events path: %s\n", events_path); - + fprintf(stderr, "Failed to open events path %s: ", events_path); + perror(""); free(events_path); return 1; } From c8b9013ed3849859b71aae2d44a5a57fe3ad5858 Mon Sep 17 00:00:00 2001 From: David Sheets Date: Thu, 11 Feb 2016 12:03:46 +0000 Subject: [PATCH 06/17] transfused: use an absolute path for fusermount Gets rid of non-portable execvpe for execve and prevents diversion. Explicit diversion will be introduced as a command-line option later. --- alpine/packages/transfused/transfused.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/alpine/packages/transfused/transfused.c b/alpine/packages/transfused/transfused.c index 7e398d69d..30699edaf 100644 --- a/alpine/packages/transfused/transfused.c +++ b/alpine/packages/transfused/transfused.c @@ -19,9 +19,6 @@ int debug = 0; int save_trace; -// execvpe is in unistd.h on Linux. -int execvpe(const char *path, char *const argv[], char *const envp[]); - typedef struct { char * socket9p_root; } parameters; @@ -39,7 +36,7 @@ typedef struct { int to; } copy_thread_state; -char * fusermount = "fusermount"; +char * fusermount = "/bin/fusermount"; void die(int exit_code, const char * perror_arg, const char * fmt, ...) { va_list argp; @@ -237,7 +234,7 @@ int get_fuse_sock(char *const optv[]) { // fork and exec fusermount fusermount_pid = fork(); if (!fusermount_pid) // child - if (execvpe(fusermount, argv, envp)) + if (execve(fusermount, argv, envp)) die(1, "Failed to execute fusermount", ""); // parent From f949e737e82bd10d70a658457dfcfcdacbb4bfa8 Mon Sep 17 00:00:00 2001 From: David Sheets Date: Thu, 11 Feb 2016 12:04:24 +0000 Subject: [PATCH 07/17] transfused: clean up comma spacing --- alpine/packages/transfused/transfused.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/alpine/packages/transfused/transfused.c b/alpine/packages/transfused/transfused.c index 30699edaf..71578e520 100644 --- a/alpine/packages/transfused/transfused.c +++ b/alpine/packages/transfused/transfused.c @@ -51,7 +51,7 @@ void * must_malloc(char *const descr, size_t size) { void * ptr; ptr = malloc(size); - if (size != 0 && ptr == NULL) die(1,descr,""); + if (size != 0 && ptr == NULL) die(1, descr, ""); return ptr; } @@ -65,14 +65,14 @@ char ** read_opts(connection_state * connection, char * buf) { if (asprintf(&read_path, "%s/connections/%ld/read", connection->params->socket9p_root, connection->id) == -1) - die(1,"Couldn't allocate read path",""); + die(1, "Couldn't allocate read path", ""); read_fd = open(read_path, O_RDONLY); if (read_fd == -1) - die(1,"couldn't open read path", "For connection %ld, ", connection->id); + die(1, "couldn't open read path", "For connection %ld, ", connection->id); read_count = read(read_fd, buf, COPY_BUFSZ - 1); - if (read_count == -1) die(1,"read_opts error reading", ""); + if (read_count == -1) die(1, "read_opts error reading", ""); buf[read_count] = 0x0; @@ -191,7 +191,7 @@ int recv_fd(int sock) { ret = recvmsg(sock, &msg, 0); - if (ret == -1) die(1,"recvmsg",""); + if (ret == -1) die(1, "recvmsg", ""); if (ret > 0 && msg.msg_controllen > 0) { cmsg = CMSG_FIRSTHDR(&msg); @@ -216,7 +216,7 @@ int get_fuse_sock(char *const optv[]) { // prepare argv from optv for (optc = 0; optv[optc] != NULL; optc++) {} - argv = (char **) must_malloc("fusermount argv",(optc + 2) * sizeof(char *)); + argv = (char **) must_malloc("fusermount argv", (optc + 2) * sizeof(char *)); argv[0] = fusermount; memcpy(&argv[1], optv, (optc + 1) * sizeof(char *)); From 2fec071a41da4425ae04b1832dc009f1ead5e12e Mon Sep 17 00:00:00 2001 From: David Sheets Date: Thu, 11 Feb 2016 12:06:32 +0000 Subject: [PATCH 08/17] transfused: clean up equal sign spacing --- alpine/packages/transfused/transfused.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/alpine/packages/transfused/transfused.c b/alpine/packages/transfused/transfused.c index 71578e520..cb94efac3 100644 --- a/alpine/packages/transfused/transfused.c +++ b/alpine/packages/transfused/transfused.c @@ -178,13 +178,13 @@ int recv_fd(int sock) { struct iovec vec; struct cmsghdr *cmsg; - msg.msg_name=NULL; - msg.msg_namelen=0; - vec.iov_base=&iochar; - vec.iov_len=1; - msg.msg_iov=&vec; + msg.msg_name = NULL; + msg.msg_namelen = 0; + vec.iov_base = &iochar; + vec.iov_len = 1; + msg.msg_iov = &vec; - msg.msg_iovlen=1; + msg.msg_iovlen = 1; msg.msg_control = buf; msg.msg_controllen = sizeof(buf); From fee789baf1f5ae47701bde5330cc2acfe2de702c Mon Sep 17 00:00:00 2001 From: David Sheets Date: Thu, 11 Feb 2016 12:09:30 +0000 Subject: [PATCH 09/17] transfused: hook debug into HUP-toggled run-time debug system --- alpine/packages/transfused/transfused.c | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/alpine/packages/transfused/transfused.c b/alpine/packages/transfused/transfused.c index cb94efac3..fe1d20c05 100644 --- a/alpine/packages/transfused/transfused.c +++ b/alpine/packages/transfused/transfused.c @@ -17,8 +17,6 @@ int debug = 0; -int save_trace; - typedef struct { char * socket9p_root; } parameters; @@ -116,7 +114,7 @@ void copy(copy_thread_state * copy_state) { read_count = read(from, buf, COPY_BUFSZ); if (read_count == -1) die(1, "", "copy %s: error reading: ", descr); - if (save_trace) { + if (debug) { int trace_fd; char * trace_path; @@ -343,14 +341,12 @@ void * handle_connection_thread(void * connection) { return handle_connection((connection_state *) connection); } -void toggle_save_trace(int sig) { - save_trace = !save_trace; +void toggle_debug(int sig) { + debug = !debug; } -void setup_save_trace() { - save_trace = 0; - - if (SIG_ERR == signal(SIGHUP, toggle_save_trace)) +void setup_debug() { + if (SIG_ERR == signal(SIGHUP, toggle_debug)) die(1, "Couldn't set SIGHUP behavior", ""); if (siginterrupt(SIGHUP, 1)) @@ -377,7 +373,7 @@ int main(int argc, char * argv[]) { if (asprintf(&events_path, "%s/events", params.socket9p_root) == -1) die(1, "", "Couldn't allocate events path: "); - setup_save_trace(); + setup_debug(); events = open(events_path, O_RDONLY | O_CLOEXEC); if (events != -1) { From 71fa8711ec170992e655a5ede97a63983ecdf769 Mon Sep 17 00:00:00 2001 From: David Sheets Date: Thu, 11 Feb 2016 12:11:04 +0000 Subject: [PATCH 10/17] transfused: do not check return of close on advice from @justincormack --- alpine/packages/transfused/transfused.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/alpine/packages/transfused/transfused.c b/alpine/packages/transfused/transfused.c index fe1d20c05..90b7df2ff 100644 --- a/alpine/packages/transfused/transfused.c +++ b/alpine/packages/transfused/transfused.c @@ -141,8 +141,7 @@ void copy(copy_thread_state * copy_state) { void * copy_clean_from(copy_thread_state * copy_state) { copy(copy_state); - if (close(copy_state->from)) - die(1, "couldn't close read fd", "For %s, ", copy_state->descr); + close(copy_state->from); free(copy_state->descr); free(copy_state); @@ -157,8 +156,7 @@ void * copy_clean_from_thread(void * copy_state) { void * copy_clean_to(copy_thread_state * copy_state) { copy(copy_state); - if (close(copy_state->to)) - die(1, "couldn't close write fd", "For %s, ", copy_state->descr); + close(copy_state->to); free(copy_state->descr); free(copy_state); @@ -240,8 +238,7 @@ int get_fuse_sock(char *const optv[]) { free(envp[0]); // close the end of the socket that we gave away - if (close(fuse_socks[0])) - die(1, "Couldn't close unneeded fusermount socket", ""); + close(fuse_socks[0]); // wait for fusermount to return waitpid(fusermount_pid, &status, 0); @@ -258,8 +255,7 @@ int get_fuse_sock(char *const optv[]) { die(1, NULL, "Couldn't receive fd over FUSE socket\n"); // close the read end of the socket - if (close(fuse_socks[1])) - die(1, "Couldn't close fusermount read socket", ""); + close(fuse_socks[1]); return fd; } From c830c223355299aa2a4744b686875806d477b4d7 Mon Sep 17 00:00:00 2001 From: David Sheets Date: Thu, 11 Feb 2016 15:14:54 +0000 Subject: [PATCH 11/17] transfused: add trace logging error checking --- alpine/packages/transfused/transfused.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/alpine/packages/transfused/transfused.c b/alpine/packages/transfused/transfused.c index 90b7df2ff..e2da46623 100644 --- a/alpine/packages/transfused/transfused.c +++ b/alpine/packages/transfused/transfused.c @@ -118,11 +118,22 @@ void copy(copy_thread_state * copy_state) { int trace_fd; char * trace_path; - // TODO: check for errors - asprintf(&trace_path, "/tmp/transfused.%ld.%s.%llu", - connection, tag, message_id(buf)); + if (asprintf(&trace_path, "/tmp/transfused.%ld.%s.%llu", + connection, tag, message_id(buf)) == -1) + die(1, "", "Couldn't allocate trace packet path"); + trace_fd = open(trace_path, O_WRONLY | O_CREAT, 0600); - write(trace_fd, buf, read_count); + if (read_fd == -1) + die(1, "couldn't open trace packet path", "For %s, ", descr); + + write_count = write(trace_fd, buf, read_count); + if (write_count == -1) + die(1, "", "copy %s trace: error writing %s: ", descr, trace_path); + + if (write_count != read_count) + die(1, NULL, "copy %s trace: read %d but only write %d\n", + descr, read_count, write_count); + close(trace_fd); free(trace_path); } From d69b402584296529aba2cb436805dcbf69cb2fa0 Mon Sep 17 00:00:00 2001 From: David Sheets Date: Fri, 12 Feb 2016 11:00:09 +0000 Subject: [PATCH 12/17] transfused: improve some error message output and fix an fd mixup --- alpine/packages/transfused/transfused.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/alpine/packages/transfused/transfused.c b/alpine/packages/transfused/transfused.c index e2da46623..84b9cab20 100644 --- a/alpine/packages/transfused/transfused.c +++ b/alpine/packages/transfused/transfused.c @@ -120,10 +120,10 @@ void copy(copy_thread_state * copy_state) { if (asprintf(&trace_path, "/tmp/transfused.%ld.%s.%llu", connection, tag, message_id(buf)) == -1) - die(1, "", "Couldn't allocate trace packet path"); + die(1, "Couldn't allocate trace packet path", ""); trace_fd = open(trace_path, O_WRONLY | O_CREAT, 0600); - if (read_fd == -1) + if (trace_fd == -1) die(1, "couldn't open trace packet path", "For %s, ", descr); write_count = write(trace_fd, buf, read_count); @@ -234,7 +234,7 @@ int get_fuse_sock(char *const optv[]) { // prepare to exec the suid binary fusermount if (asprintf(&envp[0], "_FUSE_COMMFD=%d", fuse_socks[0]) == -1) - die(1, "", "Couldn't allocate fusermount envp"); + die(1, "Couldn't allocate fusermount envp", ""); envp[1] = 0x0; @@ -279,7 +279,7 @@ void start_reader(connection_state * connection, int fuse) { if (asprintf(&read_path, "%s/connections/%ld/read", connection->params->socket9p_root, connection->id) == -1) - die(1, "", "Couldn't allocate read path: "); + die(1, "Couldn't allocate read path", ""); read_fd = open(read_path, O_RDONLY); if (read_fd == -1) @@ -309,7 +309,7 @@ void do_write(connection_state * connection, int fuse) { if (asprintf(&write_path, "%s/connections/%ld/write", connection->params->socket9p_root, connection->id) == -1) - die(1, "", "Couldn't allocate write path: "); + die(1, "Couldn't allocate write path", ""); write_fd = open(write_path, O_WRONLY); if (write_fd == -1) @@ -387,12 +387,12 @@ int main(int argc, char * argv[]) { while (1) { read_count = read(events, buf, ID_LEN - 1); if (read_count == -1) { - die(1, "Error reading events path", ""); + die(1, "", "Error reading events path %s: ", events_path); } else if (read_count == 0) { // TODO: this is probably the 9p server's fault due to // not dropping the read 0 to force short read if // the real read is flushed - fprintf(stderr, "read 0 from event stream\n"); + fprintf(stderr, "read 0 from event stream %s\n", events_path); continue; } From 94c3143c76de437262a2a4488b6d70a02d6b1640 Mon Sep 17 00:00:00 2001 From: David Sheets Date: Fri, 12 Feb 2016 11:03:47 +0000 Subject: [PATCH 13/17] transfused: introduce command line arguments for pidfile, fusermount, 9p --- alpine/packages/transfused/transfused.c | 144 ++++++++++++++++++++---- 1 file changed, 120 insertions(+), 24 deletions(-) diff --git a/alpine/packages/transfused/transfused.c b/alpine/packages/transfused/transfused.c index 84b9cab20..412d71b00 100644 --- a/alpine/packages/transfused/transfused.c +++ b/alpine/packages/transfused/transfused.c @@ -15,10 +15,23 @@ #define COPY_BUFSZ 65536 +#define DEFAULT_FUSERMOUNT "/bin/fusermount" +#define DEFAULT_SOCKET9P_ROOT "/Transfused" + +char * default_fusermount = DEFAULT_FUSERMOUNT; +char * default_socket9p_root = DEFAULT_SOCKET9P_ROOT; +char * usage = + "usage: transfused [-p pidfile] [-9 socket9p_root] [-f fusermount]\n" + " -p pidfile\tthe path at which to write the pid of the process\n" + " -9 " DEFAULT_SOCKET9P_ROOT "\tthe root of the 9p socket file system\n" + " -f " DEFAULT_FUSERMOUNT "\tthe fusermount executable to use\n"; + int debug = 0; typedef struct { char * socket9p_root; + char * fusermount; + char * pidfile; } parameters; typedef struct { @@ -34,8 +47,6 @@ typedef struct { int to; } copy_thread_state; -char * fusermount = "/bin/fusermount"; - void die(int exit_code, const char * perror_arg, const char * fmt, ...) { va_list argp; va_start(argp, fmt); @@ -211,7 +222,7 @@ int recv_fd(int sock) { } // optv must be null-terminated -int get_fuse_sock(char *const optv[]) { +int get_fuse_sock(char * fusermount, char *const optv[]) { int optc; char ** argv; char * envp[2]; @@ -333,7 +344,7 @@ void * handle_connection(connection_state * connection) { buf = (char *) must_malloc("read_opts packet malloc", COPY_BUFSZ); optv = read_opts(connection, buf); - fuse = get_fuse_sock(optv); + fuse = get_fuse_sock(connection->params->fusermount, optv); free(optv); free(buf); @@ -360,30 +371,99 @@ void setup_debug() { die(1, "Couldn't set siginterrupt for SIGHUP", ""); } -#define ID_LEN 512 +void parse_parameters(int argc, char * argv[], parameters * params) { + int c; + int errflg = 0; -int main(int argc, char * argv[]) { - int events, read_count; - char buf[ID_LEN]; - long conn_id; - pthread_t child; - parameters params; - connection_state * conn; - char * events_path; + params->pidfile = NULL; + params->socket9p_root = NULL; + params->fusermount = NULL; - if (argc < 2) { - params.socket9p_root = "/Transfuse"; - } else { - params.socket9p_root = argv[1]; + while ((c = getopt(argc, argv, ":p:9:f:")) != -1) { + switch(c) { + + case 'p': + params->pidfile = optarg; + break; + + case '9': + params->socket9p_root = optarg; + break; + + case 'f': + params->fusermount = optarg; + break; + + case ':': + fprintf(stderr, "Option -%c requires a path argument\n", optopt); + errflg++; + break; + + case '?': + fprintf(stderr, "Unrecognized option: '-%c'\n", optopt); + errflg++; + break; + + default: + fprintf(stderr, "Internal error parsing -%c\n", c); + errflg++; + } } - if (asprintf(&events_path, "%s/events", params.socket9p_root) == -1) - die(1, "", "Couldn't allocate events path: "); + if (errflg) die(2, NULL, usage); - setup_debug(); + if (params->pidfile != NULL && access(params->pidfile, W_OK)) + die(2, "", "-p %s path to pidfile must be writable: ", params->pidfile); + + if (params->fusermount == NULL) + params->fusermount = default_fusermount; + if (access(params->fusermount, X_OK)) + die(2, "", "-f %s path to fusermount must be executable: ", + params->fusermount); + + if (params->socket9p_root == NULL) + params->socket9p_root = default_socket9p_root; + if (access(params->socket9p_root, X_OK)) + die(2, "", "-9 %s path to socket 9p root directory must be executable: ", + params->socket9p_root); +} + +void write_pidfile(char * pidfile) { + int fd; + pid_t pid = getpid(); + char * pid_s; + int pid_s_len, write_count; + + if (asprintf(&pid_s, "%lld", (long long) pid) == -1) + die(1, "Couldn't allocate pidfile string", ""); + + pid_s_len = strlen(pid_s); + + fd = open(pidfile, O_WRONLY | O_CREAT, 0644); + if (fd == -1) + die(1, "", "Couldn't open pidfile path %s: ", pidfile); + + write_count = write(fd, pid_s, pid_s_len); + if (write_count == -1) + die(1, "", "Error writing pidfile %s: ", pidfile); + + if (write_count != pid_s_len) + die(1, NULL, "Error writing %s to pidfile %s: only wrote %d bytes\n", + pid_s, pidfile, write_count); + + close(fd); + free(pid_s); +} + +#define ID_LEN 512 + +void process_events(char * events_path, int events, parameters * params) { + char buf[ID_LEN]; + int read_count; + long conn_id; + pthread_t child; + connection_state * conn; - events = open(events_path, O_RDONLY | O_CLOEXEC); - if (events != -1) { while (1) { read_count = read(events, buf, ID_LEN - 1); if (read_count == -1) { @@ -407,7 +487,7 @@ int main(int argc, char * argv[]) { conn = (connection_state *) must_malloc("connection state", sizeof(connection_state)); conn->id = conn_id; - conn->params = ¶ms; + conn->params = params; if ((errno = pthread_create(&child, NULL, handle_connection_thread, conn))) @@ -418,7 +498,23 @@ int main(int argc, char * argv[]) { if (debug) fprintf(stderr, "thread spawned\n"); } - } +} + +int main(int argc, char * argv[]) { + int events; + parameters params; + char * events_path; + + parse_parameters(argc, argv, ¶ms); + setup_debug(); + + if (params.pidfile != NULL) write_pidfile(params.pidfile); + + if (asprintf(&events_path, "%s/events", params.socket9p_root) == -1) + die(1, "Couldn't allocate events path", ""); + + events = open(events_path, O_RDONLY | O_CLOEXEC); + if (events != -1) process_events(events_path, events, ¶ms); fprintf(stderr, "Failed to open events path %s: ", events_path); perror(""); From 7f088f90085590361afb5455bfdde693374e3cf1 Mon Sep 17 00:00:00 2001 From: David Sheets Date: Fri, 12 Feb 2016 11:35:39 +0000 Subject: [PATCH 14/17] transfused: fix the 9p socket root name --- alpine/packages/transfused/transfused.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/alpine/packages/transfused/transfused.c b/alpine/packages/transfused/transfused.c index 412d71b00..4361bffb4 100644 --- a/alpine/packages/transfused/transfused.c +++ b/alpine/packages/transfused/transfused.c @@ -16,7 +16,7 @@ #define COPY_BUFSZ 65536 #define DEFAULT_FUSERMOUNT "/bin/fusermount" -#define DEFAULT_SOCKET9P_ROOT "/Transfused" +#define DEFAULT_SOCKET9P_ROOT "/Transfuse" char * default_fusermount = DEFAULT_FUSERMOUNT; char * default_socket9p_root = DEFAULT_SOCKET9P_ROOT; From eb199ca9d74bad7862a90da97d9fb718cbc9d096 Mon Sep 17 00:00:00 2001 From: David Sheets Date: Fri, 12 Feb 2016 11:40:26 +0000 Subject: [PATCH 15/17] transfused: init with a pidfile --- alpine/packages/transfused/etc/init.d/transfused | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/alpine/packages/transfused/etc/init.d/transfused b/alpine/packages/transfused/etc/init.d/transfused index 06ee22a3b..dd8ff212f 100755 --- a/alpine/packages/transfused/etc/init.d/transfused +++ b/alpine/packages/transfused/etc/init.d/transfused @@ -15,7 +15,7 @@ start() [ -n "${PIDFILE}" ] || PIDFILE=/var/run/transfused.pid - /sbin/transfused & + /sbin/transfused -p "${PIDFILE}" & } stop() From cbd5ca599117997671b219a103aa24ce7e0a6537 Mon Sep 17 00:00:00 2001 From: David Sheets Date: Fri, 12 Feb 2016 11:46:18 +0000 Subject: [PATCH 16/17] transfused: it is ok if the pidfile does not yet exist --- alpine/packages/transfused/transfused.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/alpine/packages/transfused/transfused.c b/alpine/packages/transfused/transfused.c index 4361bffb4..54d1cee2f 100644 --- a/alpine/packages/transfused/transfused.c +++ b/alpine/packages/transfused/transfused.c @@ -413,7 +413,8 @@ void parse_parameters(int argc, char * argv[], parameters * params) { if (errflg) die(2, NULL, usage); if (params->pidfile != NULL && access(params->pidfile, W_OK)) - die(2, "", "-p %s path to pidfile must be writable: ", params->pidfile); + if (errno != ENOENT) + die(2, "", "-p %s path to pidfile must be writable: ", params->pidfile); if (params->fusermount == NULL) params->fusermount = default_fusermount; From b1d310184625756db663e8cf375d0a0b5b7ba839 Mon Sep 17 00:00:00 2001 From: David Sheets Date: Fri, 12 Feb 2016 11:46:44 +0000 Subject: [PATCH 17/17] transfused: if the pidfile exists, make sure to truncate it --- alpine/packages/transfused/transfused.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/alpine/packages/transfused/transfused.c b/alpine/packages/transfused/transfused.c index 54d1cee2f..5179d4af1 100644 --- a/alpine/packages/transfused/transfused.c +++ b/alpine/packages/transfused/transfused.c @@ -440,7 +440,7 @@ void write_pidfile(char * pidfile) { pid_s_len = strlen(pid_s); - fd = open(pidfile, O_WRONLY | O_CREAT, 0644); + fd = open(pidfile, O_WRONLY | O_CREAT | O_TRUNC, 0644); if (fd == -1) die(1, "", "Couldn't open pidfile path %s: ", pidfile);