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_ */

Reply via email to