Module Name: src Committed By: manu Date: Wed Aug 25 07:18:01 UTC 2010
Added Files: src/usr.sbin/perfused: Makefile debug.c msg.c perfused.8 perfused.c perfused.h Log Message: perfused(8) creates a /dev/fuse socket and performs PUFFS to FUSE relaying. This is still a work in progress. To generate a diff of this commit: cvs rdiff -u -r0 -r1.1 src/usr.sbin/perfused/Makefile \ src/usr.sbin/perfused/debug.c src/usr.sbin/perfused/msg.c \ src/usr.sbin/perfused/perfused.8 src/usr.sbin/perfused/perfused.c \ src/usr.sbin/perfused/perfused.h Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Added files: Index: src/usr.sbin/perfused/Makefile diff -u /dev/null src/usr.sbin/perfused/Makefile:1.1 --- /dev/null Wed Aug 25 07:18:01 2010 +++ src/usr.sbin/perfused/Makefile Wed Aug 25 07:18:01 2010 @@ -0,0 +1,13 @@ +PROG= perfused + +PERFUSE_OPT_DEBUG_FLAGS= -g -DPERFUSE_DEBUG + +CFLAGS= ${PERFUSE_OPT_DEBUG_FLAGS} +SRCS= perfused.c msg.c debug.c +MAN= perfused.8 +WARNS= 4 + +LDADD+= -lperfuse -lpuffs #-L/usr/pkg/lib -lefence +DPADD+= ${LIBPUFFS} + +.include <bsd.prog.mk> Index: src/usr.sbin/perfused/debug.c diff -u /dev/null src/usr.sbin/perfused/debug.c:1.1 --- /dev/null Wed Aug 25 07:18:01 2010 +++ src/usr.sbin/perfused/debug.c Wed Aug 25 07:18:01 2010 @@ -0,0 +1,74 @@ +/* $NetBSD: debug.c,v 1.1 2010/08/25 07:18:01 manu Exp $ */ + +/*- + * Copyright (c) 2010 Emmanuel Dreyfus. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#include <stdio.h> +#include <errno.h> +#include <string.h> +#include <ctype.h> + +#include "perfused.h" + +#ifdef PERFUSE_DEBUG +void +perfuse_hexdump(addr, len) + char *addr; + size_t len; +{ + unsigned int i, j, k; + + for (i = 0; i < len; i += 16) { + DPRINTF("%p ", &addr[i]); + for (j = 0; j < 16; j += 4) { + for (k = 0; k < 4; k++) { + if (i + j + k < len) { + DPRINTF("%02x ", + *(addr + i + j + k) & 0xff); + } else { + DPRINTF(" "); + } + } + } + + DPRINTF(" "); + for (j = 0; j < 16; j++) { + char c; + + if (i + j < len) { + c = *(addr + i + j); + DPRINTF("%c", isprint((int)c) ? c : '.'); + } else { + DPRINTF(" "); + } + } + DPRINTF("\n"); + } + + return; +} + + + +#endif /* PERFUSE_DEBUG */ Index: src/usr.sbin/perfused/msg.c diff -u /dev/null src/usr.sbin/perfused/msg.c:1.1 --- /dev/null Wed Aug 25 07:18:01 2010 +++ src/usr.sbin/perfused/msg.c Wed Aug 25 07:18:01 2010 @@ -0,0 +1,599 @@ +/* $NetBSD: msg.c,v 1.1 2010/08/25 07:18:01 manu Exp $ */ + +/*- + * Copyright (c) 2010 Emmanuel Dreyfus. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <err.h> +#include <errno.h> +#include <string.h> +#include <sysexits.h> +#include <syslog.h> +#include <paths.h> +#include <puffs.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <machine/vmparam.h> + +#include "../../lib/libperfuse/perfuse_if.h" +#include "perfused.h" + +static int xchg_pb_inloop(struct puffs_usermount *a, struct puffs_framebuf *, + int, enum perfuse_xchg_pb_reply); +static int xchg_pb_early(struct puffs_usermount *a, struct puffs_framebuf *, + int, enum perfuse_xchg_pb_reply); + +int +perfuse_open_sock(void) +{ + int s; + struct sockaddr_un sun; + const struct sockaddr *sa; + + (void)unlink(_PATH_FUSE); + + if ((s = socket(AF_LOCAL, SOCK_STREAM, 0)) == -1) + err(EX_OSERR, "socket failed"); + + sa = (const struct sockaddr *)(void *)&sun; + sun.sun_len = sizeof(sun); + sun.sun_family = AF_LOCAL; + (void)strcpy(sun.sun_path, _PATH_FUSE); + + if (bind(s, sa, (socklen_t )sun.sun_len) == -1) + err(EX_OSERR, "cannot open \"%s\" socket", _PATH_FUSE); + + if (listen(s, 1) == -1) + err(EX_OSERR, "listen failed"); + + return s; +} + + +void * +perfuse_recv_early(fd, len) + int fd; + size_t len; +{ + char *buf; + + if (len == 0) + return NULL; + + if ((buf = malloc(len + 1)) == NULL) + err(EX_OSERR, "malloc(%d) failed", len); + + if (read(fd, buf, len) != len) { + DWARN("short read"); + return NULL; + } + + buf[len] = '\0'; + return buf; +} + + +perfuse_msg_t * +perfuse_new_pb (pu, opc, opcode, payload_len, cred) + struct puffs_usermount *pu; + puffs_cookie_t opc; + int opcode; + size_t payload_len; + const struct puffs_cred *cred; +{ + struct puffs_framebuf *pb; + struct fuse_in_header *fih; + struct puffs_cc *pcc; + uint64_t nodeid; + void *data; + size_t len; + + if ((pb = puffs_framebuf_make()) == NULL) + DERR(EX_OSERR, "puffs_framebuf_make failed"); + + len = payload_len + sizeof(*fih); + nodeid = (opc != 0) ? perfuse_get_ino(pu, opc) : PERFUSE_UNKNOWN_INO; + + if (puffs_framebuf_reserve_space(pb, len) != 0) + DERR(EX_OSERR, "puffs_framebuf_reserve_space failed"); + + if (puffs_framebuf_getwindow(pb, 0, &data, &len) != 0) + DERR(EX_SOFTWARE, "puffs_framebuf_getwindow failed"); + if (len != payload_len + sizeof(*fih)) + DERR(EX_SOFTWARE, "puffs_framebuf_getwindow short len"); + + (void)memset(data, 0, len); + fih = (struct fuse_in_header *)data; + fih->len = len; + fih->opcode = opcode; + fih->unique = perfuse_next_unique(pu); + fih->nodeid = nodeid; + fih->uid = (uid_t)-1; + fih->gid = (gid_t)-1; + fih->pid = 0; + if (cred != NULL) { + (void)puffs_cred_getuid(cred, &fih->uid); + (void)puffs_cred_getuid(cred, &fih->uid); + } + if ((pcc = puffs_cc_getcc(pu)) != NULL) + (void)puffs_cc_getcaller(pcc, (pid_t *)&fih->pid, NULL); + + return (perfuse_msg_t *)(void *)pb; +} + +/* + * framebuf send/receive primitives based on pcc are + * not available until puffs mainloop is entered. + * This xchg_pb_inloop() variant allow early communication. + */ +static int +xchg_pb_early(pu, pb, fd, reply) + struct puffs_usermount *pu; + struct puffs_framebuf *pb; + int fd; + enum perfuse_xchg_pb_reply reply; +{ + int done; + int error; + + done = 0; + while (done == 0) { + if ((error = perfuse_writeframe(pu, pb, fd, &done)) != 0) + return error; + } + + if (reply == no_reply) { + puffs_framebuf_destroy(pb); + return 0; + } else { + puffs_framebuf_recycle(pb); + } + + done = 0; + while (done == 0) { + if ((error = perfuse_readframe(pu, pb, fd, &done)) != 0) + return error; + } + + return 0; +} + +static int +xchg_pb_inloop(pu, pb, fd, reply) + struct puffs_usermount *pu; + struct puffs_framebuf *pb; + int fd; + enum perfuse_xchg_pb_reply reply; +{ + struct puffs_cc *pcc; + int error; + + if (reply == no_reply) { + error = puffs_framev_enqueue_justsend(pu, fd, pb, 0, 0); + } else { + pcc = puffs_cc_getcc(pu); + error = puffs_framev_enqueue_cc(pcc, fd, pb, 0); + } + + return error; +} + +int +perfuse_xchg_pb(pu, pm, expected_len, reply) + struct puffs_usermount *pu; + perfuse_msg_t *pm; + size_t expected_len; + enum perfuse_xchg_pb_reply reply; +{ + struct puffs_framebuf *pb = (struct puffs_framebuf *)(void *)pm; + int fd; + int error; + struct fuse_out_header *foh; +#ifdef PERFUSE_DEBUG + struct fuse_in_header *fih; + uint64_t nodeid; + int opcode; + uint64_t unique_in; + uint64_t unique_out; + + fih = perfuse_get_inhdr(pm); + unique_in = fih->unique; + nodeid = fih->nodeid; + opcode = fih->opcode; + + if (perfuse_diagflags & PDF_FUSE) + DPRINTF("> unique = %lld, nodeid = %lld, opcode = %s (%d)\n", + unique_in, nodeid, perfuse_opname(opcode), opcode); + + if (perfuse_diagflags & PDF_DUMP) + perfuse_hexdump((char *)fih, fih->len); + +#endif /* PERFUSE_DEBUG */ + + fd = (int)perfuse_getspecific(pu); + + if (perfuse_inloop(pu)) + error = xchg_pb_inloop(pu, pb, fd, reply); + else + error = xchg_pb_early(pu, pb, fd, reply); + + if (error) + DERR(EX_SOFTWARE, "xchg_pb failed"); + + if (reply == no_reply) + return 0; + + foh = perfuse_get_outhdr((perfuse_msg_t *)(void *)pb); +#ifdef PERFUSE_DEBUG + unique_out = foh->unique; + + if (perfuse_diagflags & PDF_FUSE) + DPRINTF("< unique = %lld, nodeid = %lld, opcode = %s (%d), " + "error = %d\n", unique_out, nodeid, + perfuse_opname(opcode), opcode, error); + + if (perfuse_diagflags & PDF_DUMP) + perfuse_hexdump((char *)foh, foh->len); + + if (unique_in != unique_out) { + printf("%s: packet mismatch unique %lld vs %lld\n", + __func__, unique_in, unique_out); + abort(); + errx(EX_SOFTWARE, "%s: packet mismatch unique %lld vs %lld\n", + __func__, unique_in, unique_out); + } +#endif /* PERFUSE_DEBUG */ + + if ((expected_len != PERFUSE_UNSPEC_REPLY_LEN) && + (foh->len - sizeof(*foh) < expected_len) && + (foh->error == 0)) { + DERRX(EX_PROTOCOL, + "Unexpected short reply: received %d bytes, expected %d", + foh->len - sizeof(*foh), expected_len); + } + + if ((expected_len != 0) && + (foh->len - sizeof(*foh) > expected_len)) + DWARNX("Unexpected long reply"); + + /* + * Negative Linux errno... + */ + foh->error = -foh->error; + + return foh->error; +} + + +struct fuse_in_header * +perfuse_get_inhdr(pm) + perfuse_msg_t *pm; +{ + struct puffs_framebuf *pb; + struct fuse_in_header *fih; + void *hdr; + size_t len; + + pb = (struct puffs_framebuf *)(void *)pm; + len = sizeof(*fih); + if (puffs_framebuf_getwindow(pb, 0, &hdr, &len) != 0) + DERR(EX_SOFTWARE, "puffs_framebuf_getwindow failed"); + if (len != sizeof(*fih)) + DERR(EX_SOFTWARE, "puffs_framebuf_getwindow short header"); + + fih = (struct fuse_in_header *)hdr; + + return fih; +} + +struct fuse_out_header * +perfuse_get_outhdr(pm) + perfuse_msg_t *pm; +{ + struct puffs_framebuf *pb; + struct fuse_out_header *foh; + void *hdr; + size_t len; + + pb = (struct puffs_framebuf *)(void *)pm; + len = sizeof(*foh); + if (puffs_framebuf_getwindow(pb, 0, &hdr, &len) != 0) + DERR(EX_SOFTWARE, "puffs_framebuf_getwindow failed"); + if (len != sizeof(*foh)) + DERR(EX_SOFTWARE, "puffs_framebuf_getwindow short header"); + + foh = (struct fuse_out_header *)hdr; + + return foh; +} + +char * +perfuse_get_inpayload(pm) + perfuse_msg_t *pm; +{ + struct puffs_framebuf *pb; + struct fuse_in_header *fih; + void *hdr; + void *payload; + size_t len; + + pb = (struct puffs_framebuf *)(void *)pm; + len = sizeof(*fih); + if (puffs_framebuf_getwindow(pb, 0, &hdr, &len) != 0) + DERR(EX_SOFTWARE, "puffs_framebuf_getwindow failed"); + if (len != sizeof(*fih)) + DERR(EX_SOFTWARE, "puffs_framebuf_getwindow short header"); + + fih = (struct fuse_in_header *)hdr; + + len = fih->len - sizeof(*fih); + if (puffs_framebuf_getwindow(pb, sizeof(*fih), &payload, &len) != 0) + DERR(EX_SOFTWARE, "puffs_framebuf_getwindow failed"); + if (len != fih->len - sizeof(*fih)) + DERR(EX_SOFTWARE, "puffs_framebuf_getwindow short header"); + + return (char *)payload; +} + +char * +perfuse_get_outpayload(pm) + perfuse_msg_t *pm; +{ + struct puffs_framebuf *pb; + struct fuse_out_header *foh; + void *hdr; + void *payload; + size_t len; + + pb = (struct puffs_framebuf *)(void *)pm; + len = sizeof(*foh); + if (puffs_framebuf_getwindow(pb, 0, &hdr, &len) != 0) + DERR(EX_SOFTWARE, "puffs_framebuf_getwindow failed"); + if (len != sizeof(*foh)) + DERR(EX_SOFTWARE, "puffs_framebuf_getwindow short header"); + + foh = (struct fuse_out_header *)hdr; + + len = foh->len - sizeof(*foh); + if (puffs_framebuf_getwindow(pb, sizeof(*foh), &payload, &len) != 0) + DERR(EX_SOFTWARE, "puffs_framebuf_getwindow failed"); + if (len != foh->len - sizeof(*foh)) + DERR(EX_SOFTWARE, "puffs_framebuf_getwindow short header"); + + return (char *)payload; +} + +#define PUFFS_FRAMEBUF_GETWINDOW(pb, offset, data, len) \ + do { \ + int pfg_error; \ + size_t pfg_len = *(len); \ + \ + pfg_error = puffs_framebuf_getwindow(pb, offset, data, len); \ + if (pfg_error != 0) \ + DERR(EX_SOFTWARE, "puffs_framebuf_getwindow failed");\ + \ + if (*(len) != pfg_len) \ + DERRX(EX_SOFTWARE, "puffs_framebuf_getwindow size"); \ + } while (0 /* CONSTCOND */); + +/* ARGSUSED0 */ +int +perfuse_readframe(pu, pufbuf, fd, done) + struct puffs_usermount *pu; + struct puffs_framebuf *pufbuf; + int fd; + int *done; +{ + struct fuse_out_header foh; + size_t offset; + size_t remain; + ssize_t readen; + void *data; + + offset = puffs_framebuf_telloff(pufbuf); + + /* + * Read the header + */ + while (offset < sizeof(foh)) { + remain = sizeof(foh) - offset; + PUFFS_FRAMEBUF_GETWINDOW(pufbuf, offset, &data, &remain); + + switch (readen = recv(fd, data, remain, MSG_NOSIGNAL)) { + case 0: + DWARNX("%s: recv retunred 0", __func__); + return ECONNRESET; + /* NOTREACHED */ + break; + case -1: + if (errno == EAGAIN) + return 0; + DWARN("%s: recv retunred -1", __func__); + return errno; + /* NOTREACHED */ + break; + default: + break; + } + + offset += readen; + if (puffs_framebuf_seekset(pufbuf, offset) == -1) + DERR(EX_OSERR, "puffs_framebuf_seekset failed"); + } + + + /* + * We have a header, get remaing length to read + */ + if (puffs_framebuf_getdata_atoff(pufbuf, 0, &foh, sizeof(foh)) != 0) + DERR(EX_SOFTWARE, "puffs_framebuf_getdata_atoff failed"); +; +#ifdef PERFUSE_DEBUG + if (foh.len > FUSE_BUFSIZE) + DERRX(EX_SOFTWARE, "%s: foh.len = %d (this is huge!)", + __func__, foh.len); +#endif + + /* + * If we have only readen the header so far, + * this is time to reserve space. + */ + remain = foh.len - offset; + if (offset == sizeof(foh)) + if (puffs_framebuf_reserve_space(pufbuf, remain) == -1) + DERR(EX_OSERR, "puffs_framebuf_reserve_space failed"); + + + /* + * And read the remaining data + */ + while (remain != 0) { + PUFFS_FRAMEBUF_GETWINDOW(pufbuf, offset, &data, &remain); + + switch (readen = recv(fd, data, remain, MSG_NOSIGNAL)) { + case 0: + DWARNX("%s: recv retunred 0", __func__); + return ECONNRESET; + /* NOTREACHED */ + break; + case -1: + if (errno == EAGAIN) + return 0; + DWARN("%s: recv retunred -1", __func__); + return errno; + /* NOTREACHED */ + break; + default: + break; + } + + offset += readen; + remain -= readen; + + if (puffs_framebuf_seekset(pufbuf, offset) == -1) + DERR(EX_OSERR, "puffs_framebuf_seekset failed"); + } + + *done = 1; + return 0; +} + +/* ARGSUSED0 */ +int +perfuse_writeframe(pu, pufbuf, fd, done) + struct puffs_usermount *pu; + struct puffs_framebuf *pufbuf; + int fd; + int *done; +{ + size_t offset; + size_t len; + ssize_t written; + size_t remain; + void *data; + + offset = puffs_framebuf_telloff(pufbuf); + len = puffs_framebuf_tellsize(pufbuf) - offset; + remain = len; + + while (remain != 0) { + PUFFS_FRAMEBUF_GETWINDOW(pufbuf, offset, &data, &len); + + switch (written = send(fd, data, remain, MSG_NOSIGNAL)) { + case 0: + DWARNX("%s: send retunred 0", __func__); + return ECONNRESET; + /* NOTREACHED */ + break; + case -1: + if (errno == EAGAIN) + return 0; + DWARN("%s: send retunred -1", __func__); + return errno; + /* NOTREACHED */ + break; + default: + break; + } + + remain -= written; + offset += written; + + if (puffs_framebuf_seekset(pufbuf, offset) == -1) + DERR(EX_OSERR, "puffs_framebuf_seekset failed"); + } + + *done = 1; + return 0; +} + +/* ARGSUSED0 */ +int +perfuse_cmpframe(pu, pb1, pb2, match) + struct puffs_usermount *pu; + struct puffs_framebuf *pb1; + struct puffs_framebuf *pb2; + int *match; +{ + struct fuse_in_header *fih; + struct fuse_out_header *foh; + uint64_t unique_in; + uint64_t unique_out; + size_t len; + + len = sizeof(*fih); + PUFFS_FRAMEBUF_GETWINDOW(pb1, 0, (void **)&fih, &len); + unique_in = fih->unique; + + len = sizeof(*foh); + PUFFS_FRAMEBUF_GETWINDOW(pb2, 0, (void **)&foh, &len); + unique_out = foh->unique; + + return unique_in != unique_out; +} + +/* ARGSUSED0 */ +void +perfuse_gotframe(pu, pb) + struct puffs_usermount *pu; + struct puffs_framebuf *pb; +{ + struct fuse_out_header *foh; + size_t len; + + len = sizeof(*foh); + PUFFS_FRAMEBUF_GETWINDOW(pb, 0, (void **)&foh, &len); + + DWARNX("Unexpected frame: unique = %lld, error = %d", + foh->unique, foh->error); +#ifdef PERFUSE_DEBUG + perfuse_hexdump((char *)(void *)foh, foh->len); +#endif + + return; +} + Index: src/usr.sbin/perfused/perfused.8 diff -u /dev/null src/usr.sbin/perfused/perfused.8:1.1 --- /dev/null Wed Aug 25 07:18:01 2010 +++ src/usr.sbin/perfused/perfused.8 Wed Aug 25 07:18:01 2010 @@ -0,0 +1,121 @@ +.\" $NetBSD: perfused.8,v 1.1 2010/08/25 07:18:01 manu Exp $ +.\" +.\" Copyright (c) 2010 Emmanuel Dreyfus. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS +.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +.\" TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +.\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS +.\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +.\" POSSIBILITY OF SUCH DAMAGE. +.\" +.Dd August 12, 2010 +.Os +.Dt PERFUSED 8 +.Sh NAME +.Nm perfused +.Nd "PUFFS Enabled Relay to FUSE Daemon" +.Sh SYNOPSIS +.Nm +.Op Fl f +.Op Fl d Ar types +.Op Fl s +.Sh DESCRIPTION +.Nm +is a userland daemon mplementing the FUSE kernel level API. It creates a +.Pa /dev/fuse +socket for FUSE filesystem daemons to conenct to. +.Nm +takes care of mouting the filesystem using +.Xr puffs 3 . +.Pp +When the kernel sends a +.Xr puffs 3 +operation for the mounted filesystem, +.Nm +will translate it into a FUSE request, and will send it to the filesystem +daemon through +.Pa /dev/fuse . +The FUSE reply will be converted back into a +.Xr puffs 3 +reply and will be relayed to the kernel. +.Pp +FUSE filesystems daemons must be modified so that they request +.Nm +for performing the +.Xr mount 2 +system call instead of doing it on their own. This is done by +replacing in the sources +.Xr mount 2 +and the +.Xr open 2 +call for +.Pa /dev/fuse +by +.Fn perfuse_mount +and +.Fn perfuse_open . +.Xr libperfuse 3 +must be used at link time. +Most FUSE filesystem daemons use +.Nm libfuse +and will work unmodified, provided +the modification is done in +.Nm libfuse +itself . +.Pp +The following options are availlable: +.Bl -tag -width indent +.It Fl f +Do not fork and stay in the foreground. +.It Fl d Ar types +Print additionnal debug information. +.Ar types +is a comma-separated list of informaton type to print: +.Bl -tag -width indent +.It Ar puffs +Display PUFFS requests and replies +.It Ar fuse +Display FUSE requests and replies +.It Ar dump +Dump content of FUSE frames +.It Ar fh +Display filehandles activity +.It Ar reclaim +Display reclaim activity +.It Ar readdir +Display readdir activity +.It Ar requeue +Display requeue activity +.El +.It Fl s +Enable debug output only when receiving SIGINFO. +.El +.Sh ERRORS +The program logs to the syslog daemon as facility +.Dq daemon . +For detailed debugging use the +.Fl d +(debug) option. +.Sh SEE ALSO +.Xr puffs 4 , +.Xr mount 2 , +.Xr perfuse_mount 3 +.Sh AUTHORS +The program was written by +.An Emmanuel Dreyfus +.Aq m...@netbsd.org . Index: src/usr.sbin/perfused/perfused.c diff -u /dev/null src/usr.sbin/perfused/perfused.c:1.1 --- /dev/null Wed Aug 25 07:18:01 2010 +++ src/usr.sbin/perfused/perfused.c Wed Aug 25 07:18:01 2010 @@ -0,0 +1,384 @@ +/* $NetBSD: perfused.c,v 1.1 2010/08/25 07:18:01 manu Exp $ */ + +/*- + * Copyright (c) 2010 Emmanuel Dreyfus. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <fcntl.h> +#include <syslog.h> +#include <ctype.h> +#include <paths.h> +#include <stdarg.h> +#include <err.h> +#include <errno.h> +#include <string.h> +#include <sysexits.h> +#include <signal.h> +#include <puffs.h> +#include <sys/wait.h> +#include <sys/param.h> +#include <sys/queue.h> +#include <sys/uio.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <machine/vmparam.h> + +#include "../../lib/libperfuse/perfuse_if.h" +#include "perfused.h" + +static int getpeerid(int, pid_t *, uid_t *, gid_t *); +static int access_mount(const char *, uid_t, int); +static int accept_new_mount(int); +static int parse_debug(char *); +static void siginfo_handler(int); +static void parse_options(int, char **); +static void get_mount_info(int, struct perfuse_mount_info *); +int main(int, char **); + + +static int +getpeerid(s, pidp, uidp, gidp) + int s; + pid_t *pidp; + uid_t *uidp; + gid_t *gidp; +{ + struct unpcbid unp; + socklen_t len; + int error; + + len = sizeof(unp); + error = getsockopt(s, 0, LOCAL_PEEREID, &unp, &len); + if (error != 0) + return error; + + if (pidp != NULL) + *pidp = unp.unp_pid; + + if (uidp != NULL) + *uidp = unp.unp_euid; + + if (gidp != NULL) + *gidp = unp.unp_egid; + + return 0; +} + +static int +access_mount(mnt, uid, ro) + const char *mnt; + uid_t uid; + int ro; +{ + struct stat st; + mode_t mode; + + if (uid == 0) + return 0; + + if (stat(mnt, &st) == -1) + return -1; + + if (st.st_uid != uid) + return -1; + + mode = S_IRUSR; + if (!ro) + mode |= S_IWUSR; + + if ((st.st_mode & mode) == mode) + return 0; + + return -1; +} + +static void +get_mount_info(fd, pmi) + int fd; + struct perfuse_mount_info *pmi; +{ + struct perfuse_mount_out *pmo; + char *source = NULL; + char *target = NULL; + char *filesystemtype = NULL; + long mountflags = 0; + void *data; + size_t len; + + pmo = (struct perfuse_mount_out *)perfuse_recv_early(fd, sizeof(*pmo)); + if (pmo == NULL) { + if (shutdown(fd, SHUT_RDWR) != 0) + DERR(EX_OSERR, "shutdown failed"); + exit(EX_PROTOCOL); + } + +#ifdef PERFUSE_DEBUG + DPRINTF("perfuse lengths: source = %d, target = %d, " + "filesystemtype = %d, data = %d\n", + pmo->pmo_source_len, + pmo->pmo_target_len, + pmo->pmo_filesystemtype_len, + pmo->pmo_data_len); +#endif + len = pmo->pmo_source_len; + source = perfuse_recv_early(fd, len); + + len = pmo->pmo_target_len; + target = perfuse_recv_early(fd, len); + + len = pmo->pmo_filesystemtype_len; + filesystemtype = perfuse_recv_early(fd, len); + + mountflags = pmo->pmo_mountflags; + + len = pmo->pmo_data_len; + data = perfuse_recv_early(fd, len); + +#ifdef PERFUSE_DEBUG + DPRINTF("%s(\"%s\", \"%s\", \"%s\", 0x%lx, \"%s\")\n", __func__, + source, target, filesystemtype, mountflags, (const char *)data); +#endif + pmi->pmi_source = source; + pmi->pmi_target = target; + pmi->pmi_filesystemtype = filesystemtype; + pmi->pmi_mountflags = mountflags; + pmi->pmi_data = data; + + return; +} + +static int +accept_new_mount(s) + int s; +{ + struct puffs_usermount *pu; + struct sockaddr_storage ss; + socklen_t ss_len; + struct sockaddr *sa; + struct perfuse_mount_info pmi; + struct perfuse_callbacks pc; + int ro_flag; + pid_t pid; + int fd; + int flags; + +#ifdef PERFUSE_DEBUG + DPRINTF("waiting connexion\n"); +#endif + sa = (struct sockaddr *)(void *)&ss; + ss_len = sizeof(ss); + if ((fd = accept(s, sa, &ss_len)) == -1) + DERR(EX_OSERR, "accept failed"); + +#ifdef PERFUSE_DEBUG + DPRINTF("connexion accepted\n"); +#endif + + pid = (perfuse_diagflags & PDF_FOREGROUND) ? 0 : fork(); + switch(pid) { + case -1: + DERR(EX_OSERR, "cannot fork"); + break; + case 0: + break; + default: + return fd; + /* NOTREACHED */ + break; + } + + /* + * Mount information (source, target, mount flags...) + */ + get_mount_info(fd, &pmi); + + /* + * Get peer identity + */ + if (getpeerid(fd, NULL, &pmi.pmi_uid, NULL) != 0) + DWARNX("Unable to retreive peer identity"); + + /* + * Check that peer owns mountpoint and read (and write) on it? + */ + ro_flag = pmi.pmi_mountflags & MNT_RDONLY; + if (access_mount(pmi.pmi_target, pmi.pmi_uid, ro_flag) != 0) + DERRX(EX_NOPERM, "insuficient privvileges to mount %s", + pmi.pmi_target); + + + /* + * Initialize libperfuse, which will initialize libpuffs + */ + pc.pc_new_msg = perfuse_new_pb; + pc.pc_xchg_msg = perfuse_xchg_pb; + pc.pc_destroy_msg = (perfuse_destroy_msg_fn)puffs_framebuf_destroy; + pc.pc_get_inhdr = perfuse_get_inhdr; + pc.pc_get_inpayload = perfuse_get_inpayload; + pc.pc_get_outhdr = perfuse_get_outhdr; + pc.pc_get_outpayload = perfuse_get_outpayload; + + pu = perfuse_init(&pc, &pmi); + + puffs_framev_init(pu, perfuse_readframe, perfuse_writeframe, + perfuse_cmpframe, perfuse_gotframe, NULL); + + if (puffs_framev_addfd(pu, fd, PUFFS_FBIO_READ|PUFFS_FBIO_WRITE) == -1) + DERR(EX_SOFTWARE, "puffs_framev_addfd failed"); + + perfuse_setspecific(pu, (void *)fd); + + setproctitle("perfused %s", pmi.pmi_target); + (void)kill(getpid(), SIGINFO); /* This is for -s option */ + + perfuse_fs_init(pu); + + /* + * Non blocking I/O on /dev/fuse + * This must be done after perfuse_fs_init + */ + if ((flags = fcntl(fd, F_GETFL, 0)) == -1) + DERR(EX_OSERR, "fcntl failed"); + if (fcntl(fd, F_SETFL, flags|O_NONBLOCK) != 0) + DERR(EX_OSERR, "fcntl failed"); + + /* + * Hand over control to puffs main loop. + */ + return perfuse_mainloop(pu); +} + +static int +parse_debug(optstr) + char *optstr; +{ + int retval = PDF_SYSLOG; + char *opt; + char *lastp; + + for (opt = strtok_r(optstr, ",", &lastp); + opt; + opt = strtok_r(NULL, ",", &lastp)) { + if (strcmp(opt, "fuse") == 0) + retval |= PDF_FUSE; + else if (strcmp(opt, "puffs") == 0) + retval |= PDF_PUFFS; + else if (strcmp(opt, "dump") == 0) + retval |= PDF_DUMP; + else if (strcmp(opt, "fh") == 0) + retval |= PDF_FH; + else if (strcmp(opt, "readdir") == 0) + retval |= PDF_READDIR; + else if (strcmp(opt, "reclaim") == 0) + retval |= PDF_RECLAIM; + else if (strcmp(opt, "requeue") == 0) + retval |= PDF_REQUEUE; + else if (strcmp(opt, "misc") == 0) + retval |= PDF_MISC; + else + DERRX(EX_USAGE, "unknown debug flag \"%s\"", opt); + } + + return retval; +} + +/* ARGSUSED0 */ +static void +siginfo_handler(sig) + int sig; +{ + static int old_flags = 0; + int swap; + + swap = perfuse_diagflags; + perfuse_diagflags = old_flags; + old_flags = swap; + + DWARNX("debug %sabled", old_flags == 0 ? "en" : "dis"); + + return; +} + +static void +parse_options(argc, argv) + int argc; + char **argv; +{ + int ch; + int foreground = 0; + + perfuse_diagflags = PDF_FOREGROUND | PDF_SYSLOG; + + while ((ch = getopt(argc, argv, "d:fs")) != -1) { + switch (ch) { + case 'd': + perfuse_diagflags |= parse_debug(optarg); + break; + case 's': + if (signal(SIGINFO, siginfo_handler) != 0) + DERR(EX_OSERR, "signal failed"); + break; + case 'f': + foreground = 1; + break; + default: + DERR(EX_USAGE, "%s [-d level] [-s] [-f]", argv[0]); + break; + } + } + + if (!foreground) + perfuse_diagflags &= ~PDF_FOREGROUND; + + return; +} + +int +main(argc, argv) + int argc; + char **argv; +{ + int s; + + parse_options(argc, argv); + + if (perfuse_diagflags & PDF_SYSLOG) + openlog("perfused", 0, LOG_DAEMON); + + if (!(perfuse_diagflags & PDF_FOREGROUND)) + if (daemon(0, 0) != 0) + DERR(EX_OSERR, "daemon failed"); + + s = perfuse_open_sock(); + + do { + (void)accept_new_mount(s); + } while (1 /* CONSTCOND */); + + /* NOTREACHED */ + return 0; +} Index: src/usr.sbin/perfused/perfused.h diff -u /dev/null src/usr.sbin/perfused/perfused.h:1.1 --- /dev/null Wed Aug 25 07:18:01 2010 +++ src/usr.sbin/perfused/perfused.h Wed Aug 25 07:18:01 2010 @@ -0,0 +1,68 @@ +/* $NetBSD: perfused.h,v 1.1 2010/08/25 07:18:01 manu Exp $ */ + +/*- + * Copyright (c) 2010 Emmanuel Dreyfus. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _PERFUSED_H_ +#define _PERFUSED_H_ + +#include <puffs.h> +#include "../../lib/libperfuse/perfuse_if.h" +#include "fuse.h" + +#define PERFUSE_MSG_T struct puffs_framebuf + +__BEGIN_DECLS + +#ifdef PERFUSE_DEBUG +void perfuse_hexdump(char *, size_t); +const char *perfuse_opname(int); +extern int perfuse_diagflags; +#endif /* PERFUSE_DEBUG */ + +int perfuse_open_sock(void); +void *perfuse_recv_early(int, size_t); + +int perfuse_readframe(struct puffs_usermount *, + struct puffs_framebuf *, int, int *); +int perfuse_writeframe(struct puffs_usermount *, + struct puffs_framebuf *, int, int *); +int perfuse_cmpframe(struct puffs_usermount *, + struct puffs_framebuf *, struct puffs_framebuf *, int *); +void perfuse_gotframe(struct puffs_usermount *, struct puffs_framebuf *); + +struct fuse_out_header *perfuse_get_outhdr(perfuse_msg_t *); +struct fuse_in_header *perfuse_get_inhdr(perfuse_msg_t *); +char *perfuse_get_inpayload(perfuse_msg_t *); +char *perfuse_get_outpayload(perfuse_msg_t *); + +perfuse_msg_t *perfuse_new_pb(struct puffs_usermount *, + puffs_cookie_t, int, size_t, const struct puffs_cred *); +int perfuse_xchg_pb(struct puffs_usermount *, perfuse_msg_t *, size_t, + enum perfuse_xchg_pb_reply); + +__END_DECLS + +#endif /* _PERFUSED_H_ */