Bring in the "Port PUFFS from NetBSD/FreeBSD" GSoC 2011 project results.
authorSascha Wildner <saw@online.de>
Sun, 5 Feb 2012 01:29:51 +0000 (02:29 +0100)
committerSascha Wildner <saw@online.de>
Sun, 5 Feb 2012 01:29:51 +0000 (02:29 +0100)
With some additional stuff done by me (properly hooking into the build,
add some manpages, and other missing bits here and there, nothing
functional).

What we get:

* puffs(3) and refuse(3) libraries
* puffs(4) and putter(9) kernel parts
* mount_psshfs(8) utility

Note that it still might crash or stop working, so it's still
experimental. We're bringing it anyway because several people showed
interest in continuing to work on it and so it doesn't rot outside
our tree, gets at least built with world/kernel, etc.

Submitted-by: Nick Prokharau <nickprok@gmail.com>
76 files changed:
etc/mtree/BSD.usr.dist
gnu/usr.bin/groff/tmac/mdoc.local
lib/Makefile
lib/libpuffs/Makefile [new file with mode: 0644]
lib/libpuffs/TODO [new file with mode: 0644]
lib/libpuffs/callcontext.c [new file with mode: 0644]
lib/libpuffs/creds.c [new file with mode: 0644]
lib/libpuffs/dispatcher.c [new file with mode: 0644]
lib/libpuffs/flush.c [new file with mode: 0644]
lib/libpuffs/framebuf.c [new file with mode: 0644]
lib/libpuffs/hash.h [new file with mode: 0644]
lib/libpuffs/null.c [new file with mode: 0644]
lib/libpuffs/opdump.c [new file with mode: 0644]
lib/libpuffs/paths.c [new file with mode: 0644]
lib/libpuffs/pnode.c [new file with mode: 0644]
lib/libpuffs/puffs.3 [new file with mode: 0644]
lib/libpuffs/puffs.c [new file with mode: 0644]
lib/libpuffs/puffs.h [new file with mode: 0644]
lib/libpuffs/puffs_cc.3 [new file with mode: 0644]
lib/libpuffs/puffs_cred.3 [new file with mode: 0644]
lib/libpuffs/puffs_flush.3 [new file with mode: 0644]
lib/libpuffs/puffs_framebuf.3 [new file with mode: 0644]
lib/libpuffs/puffs_node.3 [new file with mode: 0644]
lib/libpuffs/puffs_ops.3 [new file with mode: 0644]
lib/libpuffs/puffs_path.3 [new file with mode: 0644]
lib/libpuffs/puffs_priv.h [new file with mode: 0644]
lib/libpuffs/puffsdump.h [new file with mode: 0644]
lib/libpuffs/requests.c [new file with mode: 0644]
lib/libpuffs/subr.c [new file with mode: 0644]
lib/libpuffs/suspend.c [new file with mode: 0644]
lib/librefuse/Makefile [new file with mode: 0644]
lib/librefuse/TODO [new file with mode: 0644]
lib/librefuse/fuse.h [new file with mode: 0644]
lib/librefuse/fuse_opt.h [new file with mode: 0644]
lib/librefuse/refuse.3 [new file with mode: 0644]
lib/librefuse/refuse.c [new file with mode: 0644]
lib/librefuse/refuse_opt.c [new file with mode: 0644]
share/examples/puffs/pnullfs/Makefile [new file with mode: 0644]
share/examples/puffs/pnullfs/pnullfs.c [new file with mode: 0644]
share/man/man4/Makefile
share/man/man4/puffs.4 [new file with mode: 0644]
share/man/man9/Makefile
share/man/man9/putter.9 [new file with mode: 0644]
share/mk/bsd.libnames.mk
sys/conf/files
sys/conf/options
sys/config/LINT
sys/config/LINT64
sys/dev/misc/Makefile
sys/dev/misc/putter/Makefile [new file with mode: 0644]
sys/dev/misc/putter/putter.c [new file with mode: 0644]
sys/dev/misc/putter/putter.h [new file with mode: 0644]
sys/dev/misc/putter/putter_sys.h [new file with mode: 0644]
sys/vfs/Makefile
sys/vfs/puffs/Makefile [new file with mode: 0644]
sys/vfs/puffs/puffs_io.c [new file with mode: 0644]
sys/vfs/puffs/puffs_msgif.c [new file with mode: 0644]
sys/vfs/puffs/puffs_msgif.h [new file with mode: 0644]
sys/vfs/puffs/puffs_node.c [new file with mode: 0644]
sys/vfs/puffs/puffs_subr.c [new file with mode: 0644]
sys/vfs/puffs/puffs_sys.h [new file with mode: 0644]
sys/vfs/puffs/puffs_vfsops.c [new file with mode: 0644]
sys/vfs/puffs/puffs_vnops.c [new file with mode: 0644]
usr.sbin/Makefile
usr.sbin/puffs/Makefile [new file with mode: 0644]
usr.sbin/puffs/Makefile.inc [new file with mode: 0644]
usr.sbin/puffs/mount_psshfs/Makefile [new file with mode: 0644]
usr.sbin/puffs/mount_psshfs/fs.c [new file with mode: 0644]
usr.sbin/puffs/mount_psshfs/mount_psshfs.8 [new file with mode: 0644]
usr.sbin/puffs/mount_psshfs/node.c [new file with mode: 0644]
usr.sbin/puffs/mount_psshfs/psbuf.c [new file with mode: 0644]
usr.sbin/puffs/mount_psshfs/psshfs.c [new file with mode: 0644]
usr.sbin/puffs/mount_psshfs/psshfs.h [new file with mode: 0644]
usr.sbin/puffs/mount_psshfs/sftp_proto.h [new file with mode: 0644]
usr.sbin/puffs/mount_psshfs/subr.c [new file with mode: 0644]
usr.sbin/puffs/mount_psshfs/util_compat.h [new file with mode: 0644]

index bd7cc5c..bc861b1 100644 (file)
             ..
             printing
             ..
+            puffs
+                pnullfs
+                ..
+            ..
             rconfig
             ..
             scsi_target
index f89816a..32439a0 100644 (file)
@@ -56,7 +56,9 @@
 .ds doc-str-Lb-libnetgraph      Netgraph User Library (libnetgraph, \-lnetgraph)
 .ds doc-str-Lb-libposix1e       POSIX.1e Security API Library (libposix1e, \-lposix1e)
 .ds doc-str-Lb-libprop          Property Container Object Library (libprop, \-lprop)
+.ds doc-str-Lb-libpuffs         puffs Convenience Library (libpuffs, \-lpuffs)
 .ds doc-str-Lb-libradius        RADIUS Client Library (libradius, \-lradius)
+.ds doc-str-Lb-librefuse        File System in Userspace Convenience Library (librefuse, \-lrefuse)
 .ds doc-str-Lb-librpcsvc        RPC Service Library (librpcsvc, \-lrpcsvc)
 .ds doc-str-Lb-libsdp           Bluetooth Service Discovery Protocol Library (libsdp, \-lsdp)
 .ds doc-str-Lb-libskey          S/Key Password Control Table Access Library (libskey, \-lskey)
index 56549e5..29f91af 100644 (file)
@@ -1,6 +1,5 @@
 #      @(#)Makefile    8.1 (Berkeley) 6/4/93
 # $FreeBSD: src/lib/Makefile,v 1.107.2.16 2002/10/10 19:24:35 kbyanc Exp $
-# $DragonFly: src/lib/Makefile,v 1.35 2008/09/30 12:20:29 hasso Exp $
 
 # To satisfy shared library or ELF linkage when only the libraries being
 # built are visible:
@@ -73,6 +72,8 @@ SUBDIR=       ${SUBDIR_ORDERED} \
        libpcap \
        libposix1e \
        libprop \
+       libpuffs \
+       librefuse \
        librpcsvc \
        librt \
        libsctp \
diff --git a/lib/libpuffs/Makefile b/lib/libpuffs/Makefile
new file mode 100644 (file)
index 0000000..ac5999d
--- /dev/null
@@ -0,0 +1,16 @@
+#      $NetBSD: Makefile,v 1.24 2010/12/06 14:50:34 pooka Exp $
+#
+
+WARNS=         2
+
+LIB=           puffs
+
+SRCS=          puffs.c callcontext.c creds.c dispatcher.c flush.c      \
+               framebuf.c null.c opdump.c paths.c pnode.c requests.c   \
+               subr.c suspend.c
+MAN=           puffs.3 puffs_cc.3 puffs_cred.3 puffs_flush.3           \
+               puffs_framebuf.3 puffs_node.3 puffs_ops.3 puffs_path.3
+INCS=          puffs.h puffsdump.h
+CFLAGS+=       -I${.CURDIR}/../../sys -I/usr/src/sbin/mount
+
+.include <bsd.lib.mk>
diff --git a/lib/libpuffs/TODO b/lib/libpuffs/TODO
new file mode 100644 (file)
index 0000000..0a5e3e2
--- /dev/null
@@ -0,0 +1,42 @@
+       $NetBSD: TODO,v 1.3 2007/07/19 07:48:10 pooka Exp $
+
+Document some possible user-visible changes that may take place.
+For a complete list, please dump my brain and excavate.
+
+  * figure out what do to with struct vattr, maybe introduce some
+    vattr-like puffs-specific structure (translation costs?) instead
+    of direct exposure
+
+  * make puffs_node opaque outside the library
+
+  * make it possible to cache symlink names and getattr results in
+    the kernel ... although I'm not as critically concerned with the
+    kernel caching as I am with userlevel caching
+
+  * try to implement a kernel policy for VOP_ACCESS, it's called
+    very very often - but this requires some kernel caching
+
+    + also try to figure out how to implement it in cases where it's
+      more difficult to do, e.g. how can we know readdir on sshfs will
+      fail without actually trying to read the directory?  If we fail
+      readdir itself, it's treated as success
+
+  * implement file system layering .. this will most likely bring
+    massive changes to how the ops vector is handled, for instance.
+    pcc/pu division should stay quite like the way it is now, but
+    maybe there will be layer specific ops.
+
+  * make puffs more like kernel vfs - or make kernel vfs more like puffs
+
+  * decide what to do about setback operations.  they kind of violate
+    the transparency of FAF for op handling
+
+  * remove flags parameter to puffs_init and replace with something
+    more generic
+
+  * fix incoming requests to not require memcpy with continuations
+    (not user-visible?)
+
+  * make continuations play with libpthread, lib/36011 (not user-visible?)
+
+  * clean up the request dispatching / continuation code (not user-visible?)
diff --git a/lib/libpuffs/callcontext.c b/lib/libpuffs/callcontext.c
new file mode 100644 (file)
index 0000000..2322a42
--- /dev/null
@@ -0,0 +1,362 @@
+/*     $NetBSD: callcontext.c,v 1.25 2011/03/04 09:47:47 yamt Exp $    */
+
+/*
+ * Copyright (c) 2006, 2007, 2008 Antti Kantee.  All Rights Reserved.
+ *
+ * Development of this software was supported by the
+ * Research Foundation of Helsinki University of Technology
+ *
+ * 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 AUTHOR ``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 AUTHOR 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 <sys/types.h>
+#include <sys/mman.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ucontext.h>
+#include <unistd.h>
+
+#include "puffs.h"
+#include "puffs_priv.h"
+
+#if 0
+#define DPRINTF(x) printf x
+#else
+#define DPRINTF(x)
+#endif
+
+/*
+ * Set the following to 1 to not handle each request on a separate
+ * stack.  This is highly volatile kludge, therefore no external
+ * interface.
+ */
+int puffs_fakecc;
+
+/*
+ * user stuff
+ */
+
+/*
+ * So, we need to get back to where we came from.  This can happen in two
+ * different ways:
+ *  1) PCC_MLCONT is set, in which case we need to go to the mainloop
+ *  2) It is not set, and we simply jump to pcc_uc_ret.
+ */
+void
+puffs_cc_yield(struct puffs_cc *pcc)
+{
+       struct puffs_cc *jumpcc;
+       int rv;
+
+       assert(puffs_fakecc == 0);
+
+       pcc->pcc_flags &= ~PCC_BORROWED;
+
+       /* romanes eunt domus */
+       DPRINTF(("puffs_cc_yield: "));
+       if ((pcc->pcc_flags & PCC_MLCONT) == 0) {
+               DPRINTF(("no mlcont, pcc %p\n", pcc));
+               swapcontext(&pcc->pcc_uc, &pcc->pcc_uc_ret);
+       } else {
+               DPRINTF(("mlcont, pcc %p\n", pcc));
+               pcc->pcc_flags &= ~PCC_MLCONT;
+               rv = puffs__cc_create(pcc->pcc_pu, puffs__theloop, &jumpcc);
+               if (rv)
+                       abort(); /* p-p-p-pa-pa-panic (XXX: fixme) */
+               swapcontext(&pcc->pcc_uc, &jumpcc->pcc_uc);
+               DPRINTF(("puffs_cc_yield: post swap pcc %p\n", pcc));
+       }
+}
+
+/*
+ * Internal continue routine.  This has slightly different semantics.
+ * We simply make our cc available in the freelist and jump to the
+ * indicated pcc.
+ */
+void
+puffs__cc_cont(struct puffs_cc *pcc)
+{
+       struct puffs_cc *mycc;
+
+       mycc = puffs_cc_getcc(pcc->pcc_pu);
+       DPRINTF(("puffs__cc_cont: pcc %p, mycc %p\n", pcc, mycc));
+
+       /*
+        * XXX: race between setcontext() and recycle if
+        * we go multithreaded
+        */
+       puffs__cc_destroy(mycc, 1);
+       pcc->pcc_flags |= PCC_MLCONT;
+       setcontext(&pcc->pcc_uc);
+}
+
+void
+puffs_cc_continue(struct puffs_cc *pcc)
+{
+
+       /* ramble on */
+       DPRINTF(("puffs_cc_continue: pcc %p\n", pcc));
+       if (puffs_fakecc) {
+               pcc->pcc_func(pcc->pcc_farg);
+       } else {
+               swapcontext(&pcc->pcc_uc_ret, &pcc->pcc_uc);
+       }
+}
+
+/*
+ * "Borrows" pcc, *NOT* called from pcc owner.  Acts like continue.
+ * So the idea is to use this, give something the context back to
+ * run to completion and then jump back to where ever this was called
+ * from after the op dispatching is complete (or if the pcc decides to
+ * yield again).
+ */
+void
+puffs__goto(struct puffs_cc *loanpcc)
+{
+
+       loanpcc->pcc_flags |= PCC_BORROWED;
+
+       swapcontext(&loanpcc->pcc_uc_ret, &loanpcc->pcc_uc);
+}
+
+void
+puffs_cc_schedule(struct puffs_cc *pcc)
+{
+       struct puffs_usermount *pu = pcc->pcc_pu;
+
+       assert(pu->pu_state & PU_INLOOP);
+       TAILQ_INSERT_TAIL(&pu->pu_sched, pcc, pcc_schedent);
+}
+
+int
+puffs_cc_getcaller(struct puffs_cc *pcc, pid_t *pid, lwpid_t *lid)
+{
+
+       if ((pcc->pcc_flags & PCC_HASCALLER) == 0) {
+               errno = ESRCH;
+               return -1;
+       }
+
+       if (pid)
+               *pid = pcc->pcc_pid;
+       if (lid)
+               *lid = pcc->pcc_lid;
+       return 0;
+}
+
+static struct puffs_cc fakecc;
+
+static struct puffs_cc *
+slowccalloc(struct puffs_usermount *pu)
+{
+       struct puffs_cc *volatile pcc;
+       void *sp;
+       size_t stacksize = 1<<pu->pu_cc_stackshift;
+       size_t stackalign;
+       long psize = sysconf(_SC_PAGESIZE);
+
+       if (puffs_fakecc)
+               return &fakecc;
+
+       /*
+        * Emulate MAP_ALIGNED(pu->pu_cc_stackshift) by allocating stacksize*2
+        * bytes and unmapping extra pages
+        */
+       sp = mmap(NULL, stacksize * 2, PROT_READ|PROT_WRITE,
+           MAP_ANON|MAP_PRIVATE, -1, 0);
+       if (sp == MAP_FAILED)
+               return NULL;
+       stackalign = ((uintptr_t)sp) & (stacksize-1);
+       if (stackalign != 0) {
+               munmap(sp, stacksize - stackalign);
+               sp = (uint8_t *)sp + stacksize - stackalign;
+               munmap((uint8_t *)sp + stacksize, stackalign);
+       } else {
+               munmap((uint8_t *)sp + stacksize, stacksize);
+       }
+
+       pcc = sp;
+       memset(pcc, 0, sizeof(struct puffs_cc));
+
+       mprotect((uint8_t *)sp + psize, (size_t)psize, PROT_NONE);
+
+       /* initialize both ucontext's */
+       if (getcontext(&pcc->pcc_uc) == -1) {
+               munmap(pcc, stacksize);
+               return NULL;
+       }
+       if (getcontext(&pcc->pcc_uc_ret) == -1) {
+               munmap(pcc, stacksize);
+               return NULL;
+       }
+
+       return pcc;
+}
+
+int
+puffs__cc_create(struct puffs_usermount *pu, puffs_ccfunc func,
+       struct puffs_cc **pccp)
+{
+       struct puffs_cc *pcc;
+       size_t stacksize = 1<<pu->pu_cc_stackshift;
+       stack_t *st;
+
+       /* Do we have a cached copy? */
+       if (pu->pu_cc_nstored == 0) {
+               pcc = slowccalloc(pu);
+               if (pcc == NULL)
+                       return -1;
+               pcc->pcc_pu = pu;
+               DPRINTF(("puffs__cc_create: allocated pcc %p\n", pcc));
+       } else {
+               pcc = LIST_FIRST(&pu->pu_ccmagazin);
+               assert(pcc != NULL);
+
+               LIST_REMOVE(pcc, pcc_rope);
+               pu->pu_cc_nstored--;
+               DPRINTF(("puffs__cc_create: magazin pcc %p\n", pcc));
+       }
+       assert(pcc->pcc_pu == pu);
+
+       if (puffs_fakecc) {
+               pcc->pcc_func = func;
+               pcc->pcc_farg = pcc;
+       } else {
+               /* link context */
+               pcc->pcc_uc.uc_link = &pcc->pcc_uc_ret;
+
+               /* setup stack
+                *
+                * XXX: I guess this should theoretically be preserved by
+                * swapcontext().  However, it gets lost.  So reinit it.
+                */
+               st = &pcc->pcc_uc.uc_stack;
+               st->ss_sp = (void *)pcc;
+               st->ss_size = stacksize;
+               st->ss_flags = 0;
+
+               /*
+                * Give us an initial context to jump to.
+                *
+                * Our manual page says that portable code shouldn't
+                * rely on being able to pass pointers through makecontext().
+                * kjk says that NetBSD code doesn't need to worry about this.
+                * uwe says it would be like putting a "keep away from
+                * children" sign on a box of toys.
+                */
+               makecontext(&pcc->pcc_uc, (void *)func, 1, (uintptr_t)pcc);
+       }
+
+       *pccp = pcc;
+       return 0;
+}
+
+void
+puffs__cc_setcaller(struct puffs_cc *pcc, pid_t pid, lwpid_t lid)
+{
+
+       pcc->pcc_pid = pid;
+       pcc->pcc_lid = lid;
+       pcc->pcc_flags |= PCC_HASCALLER;
+}
+
+static void
+cc_free(struct puffs_cc *pcc)
+{
+       struct puffs_usermount *pu = pcc->pcc_pu;
+       size_t stacksize = 1<<pu->pu_cc_stackshift;
+
+       DPRINTF(("invalidating pcc %p\n", pcc));
+       assert(!puffs_fakecc);
+       munmap(pcc, stacksize);
+}
+
+void
+puffs__cc_destroy(struct puffs_cc *pcc, int nonuke)
+{
+       struct puffs_usermount *pu = pcc->pcc_pu;
+
+       pcc->pcc_flags &= ~PCC_HASCALLER;
+       assert(pcc->pcc_flags == 0);
+       assert(!puffs_fakecc);
+
+       /* not over limit?  stuff away in the store, otherwise nuke */
+       if (nonuke || pu->pu_cc_nstored < PUFFS_CCMAXSTORE) {
+               pcc->pcc_pb = NULL;
+               DPRINTF(("puffs__cc_destroy: storing pcc %p\n", pcc));
+               LIST_INSERT_HEAD(&pu->pu_ccmagazin, pcc, pcc_rope);
+               pu->pu_cc_nstored++;
+       } else {
+               cc_free(pcc);
+       }
+}
+
+void
+puffs__cc_exit(struct puffs_usermount *pu)
+{
+       struct puffs_cc *pcc;
+
+       while ((pcc = LIST_FIRST(&pu->pu_ccmagazin)) != NULL) {
+               LIST_REMOVE(pcc, pcc_rope);
+               cc_free(pcc);
+       }
+}
+
+struct puffs_cc *
+puffs_cc_getcc(struct puffs_usermount *pu)
+{
+       size_t stacksize = 1<<pu->pu_cc_stackshift;
+       uintptr_t bottom;
+
+       if (puffs_fakecc)
+               return &fakecc;
+
+       bottom = ((uintptr_t)&bottom) & ~(stacksize-1);
+       return (struct puffs_cc *)bottom;
+}
+
+int
+puffs__cc_savemain(struct puffs_usermount *pu)
+{
+
+       if (puffs_fakecc)
+               return 0;
+
+       PU_CLRSFLAG(pu, PU_MAINRESTORE);
+       return getcontext(&pu->pu_mainctx);
+}
+
+int
+puffs__cc_restoremain(struct puffs_usermount *pu)
+{
+
+       if (puffs_fakecc)
+               return 0;
+
+       puffs__cc_destroy(puffs_cc_getcc(pu), 1);
+       PU_SETSFLAG(pu, PU_MAINRESTORE);
+       return setcontext(&pu->pu_mainctx);
+}
diff --git a/lib/libpuffs/creds.c b/lib/libpuffs/creds.c
new file mode 100644 (file)
index 0000000..8a31503
--- /dev/null
@@ -0,0 +1,254 @@
+/*     $NetBSD: creds.c,v 1.15 2009/11/20 14:23:54 pooka Exp $ */
+
+/*
+ * Copyright (c) 2006  Antti Kantee.  All Rights Reserved.
+ *
+ * Development of this software was supported by the Ulla Tuominen Foundation.
+ *
+ * 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 AUTHOR ``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 AUTHOR 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.
+ */
+
+/*
+ * Interface for dealing with credits.
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+
+#include <errno.h>
+#include <stdbool.h>
+#include <string.h>
+
+#include "puffs.h"
+#include "puffs_priv.h"
+
+#define UUCCRED(a) (a->pkcr_type == PUFFCRED_TYPE_UUC)
+#define INTCRED(a) (a->pkcr_type == PUFFCRED_TYPE_INTERNAL)
+
+int
+puffs_cred_getuid(const struct puffs_cred *pcr, uid_t *ruid)
+{
+       PUFFS_MAKEKCRED(pkcr, pcr);
+
+       if (!UUCCRED(pkcr)) {
+               errno = EOPNOTSUPP;
+               return -1;
+       }
+       *ruid = pkcr->pkcr_uuc.cr_uid;
+
+       return 0;
+}
+
+int
+puffs_cred_getgid(const struct puffs_cred *pcr, gid_t *rgid)
+{
+       PUFFS_MAKEKCRED(pkcr, pcr);
+
+       if (!UUCCRED(pkcr)) {
+               errno = EOPNOTSUPP;
+               return -1;
+       }
+       *rgid = pkcr->pkcr_uuc.cr_gid;
+
+       return 0;
+}
+
+int
+puffs_cred_getgroups(const struct puffs_cred *pcr, gid_t *rgids, short *ngids)
+{
+       PUFFS_MAKEKCRED(pkcr, pcr);
+       size_t ncopy;
+
+       if (!UUCCRED(pkcr)) {
+               errno = EOPNOTSUPP;
+               *ngids = 0;
+               return -1;
+       }
+
+       ncopy = MIN(*ngids, pkcr->pkcr_uuc.cr_ngroups);
+       (void)memcpy(rgids, pkcr->pkcr_uuc.cr_groups, sizeof(gid_t) * ncopy);
+       *ngids = (short)ncopy;
+
+       return 0;
+}
+
+bool
+puffs_cred_isuid(const struct puffs_cred *pcr, uid_t uid)
+{
+       PUFFS_MAKEKCRED(pkcr, pcr);
+
+       return UUCCRED(pkcr) && pkcr->pkcr_uuc.cr_uid == uid;
+}
+
+bool
+puffs_cred_hasgroup(const struct puffs_cred *pcr, gid_t gid)
+{
+       PUFFS_MAKEKCRED(pkcr, pcr);
+       short i;
+
+       if (!UUCCRED(pkcr))
+               return false;
+
+       if (pkcr->pkcr_uuc.cr_gid == gid)
+               return true;
+       for (i = 0; i < pkcr->pkcr_uuc.cr_ngroups; i++)
+               if (pkcr->pkcr_uuc.cr_groups[i] == gid)
+                       return true;
+
+       return false;
+}
+
+bool
+puffs_cred_isregular(const struct puffs_cred *pcr)
+{
+       PUFFS_MAKEKCRED(pkcr, pcr);
+
+       return UUCCRED(pkcr);
+}
+
+bool
+puffs_cred_iskernel(const struct puffs_cred *pcr)
+{
+       PUFFS_MAKEKCRED(pkcr, pcr);
+
+       return INTCRED(pkcr) && pkcr->pkcr_internal == PUFFCRED_CRED_NOCRED;
+}
+
+bool
+puffs_cred_isfs(const struct puffs_cred *pcr)
+{
+       PUFFS_MAKEKCRED(pkcr, pcr);
+
+       return INTCRED(pkcr) && pkcr->pkcr_internal == PUFFCRED_CRED_FSCRED;
+}
+
+bool
+puffs_cred_isjuggernaut(const struct puffs_cred *pcr)
+{
+
+       return puffs_cred_isuid(pcr, 0) || puffs_cred_iskernel(pcr)
+           || puffs_cred_isfs(pcr);
+}
+
+/*
+ * Generic routine for checking file access rights.  Modeled after
+ * vaccess() in the kernel.
+ */
+int
+puffs_access(enum vtype type, mode_t file_mode, uid_t uid, gid_t gid,
+       mode_t acc_mode, const struct puffs_cred *pcr)
+{
+       mode_t mask;
+
+       /* megapower */
+       if (puffs_cred_iskernel(pcr) || puffs_cred_isfs(pcr))
+               return 0;
+
+       /* superuser, allow all except exec if *ALL* exec bits are unset */
+       if (puffs_cred_isuid(pcr, 0)) {
+               if ((acc_mode & PUFFS_VEXEC) && type != VDIR &&
+                   (file_mode & (S_IXUSR|S_IXGRP|S_IXOTH)) == 0)
+                       return EACCES;
+               return 0;
+       }
+
+       mask = 0;
+       /* owner */
+       if (puffs_cred_isuid(pcr, uid)) {
+               if (acc_mode & PUFFS_VEXEC)
+                       mask |= S_IXUSR;
+               if (acc_mode & PUFFS_VREAD)
+                       mask |= S_IRUSR;
+               if (acc_mode & PUFFS_VWRITE)
+                       mask |= S_IWUSR;
+       /* group */
+       } else if (puffs_cred_hasgroup(pcr, gid)) {
+               if (acc_mode & PUFFS_VEXEC)
+                       mask |= S_IXGRP;
+               if (acc_mode & PUFFS_VREAD)
+                       mask |= S_IRGRP;
+               if (acc_mode & PUFFS_VWRITE)
+                       mask |= S_IWGRP;
+       /* other */
+       } else {
+               if (acc_mode & PUFFS_VEXEC)
+                       mask |= S_IXOTH;
+               if (acc_mode & PUFFS_VREAD)
+                       mask |= S_IROTH;
+               if (acc_mode & PUFFS_VWRITE)
+                       mask |= S_IWOTH;
+       }
+
+       if ((file_mode & mask) == mask)
+               return 0;
+       else
+               return EACCES;
+}
+
+int
+puffs_access_chown(uid_t owner, gid_t group, uid_t newowner, gid_t newgroup,
+       const struct puffs_cred *pcr)
+{
+
+       if (newowner == (uid_t)PUFFS_VNOVAL)
+               newowner = owner;
+       if (newgroup == (gid_t)PUFFS_VNOVAL)
+               newgroup = group;
+
+       if ((!puffs_cred_isuid(pcr, owner) || newowner != owner ||
+           ((newgroup != group && !puffs_cred_hasgroup(pcr, newgroup))))
+           && !puffs_cred_isjuggernaut(pcr))
+               return EPERM;
+
+       return 0;
+}
+
+int
+puffs_access_chmod(uid_t owner, gid_t group, enum vtype type, mode_t mode,
+       const struct puffs_cred *pcr)
+{
+
+       if (!puffs_cred_isuid(pcr, owner) && !puffs_cred_isjuggernaut(pcr))
+               return EPERM;
+
+       if (!puffs_cred_isjuggernaut(pcr)) {
+               if (type != VDIR && (mode & S_ISTXT))
+                       return EFTYPE;
+               if (!puffs_cred_hasgroup(pcr, group) && (mode & S_ISGID))
+                       return EPERM;
+       }
+
+       return 0;
+}
+
+int
+puffs_access_times(uid_t uid, gid_t gid, mode_t mode, int va_utimes_null,
+       const struct puffs_cred *pcr)
+{
+
+       if (!puffs_cred_isuid(pcr, uid) && !puffs_cred_isjuggernaut(pcr)
+           && (va_utimes_null == 0
+             || puffs_access(VNON, mode, uid, gid, PUFFS_VWRITE, pcr) != 0))
+               return EPERM;
+
+       return 0;
+}
diff --git a/lib/libpuffs/dispatcher.c b/lib/libpuffs/dispatcher.c
new file mode 100644 (file)
index 0000000..7242bd1
--- /dev/null
@@ -0,0 +1,1103 @@
+/*     $NetBSD: dispatcher.c,v 1.36 2011/07/04 08:07:30 manu Exp $     */
+
+/*
+ * Copyright (c) 2006, 2007, 2008 Antti Kantee.  All Rights Reserved.
+ *
+ * Development of this software was supported by the
+ * Ulla Tuominen Foundation, the Finnish Cultural Foundation and
+ * Research Foundation of Helsinki University of Technology.
+ *
+ * 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 AUTHOR ``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 AUTHOR 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 <sys/types.h>
+#include <sys/poll.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "puffs.h"
+#include "puffsdump.h"
+#include "puffs_priv.h"
+
+static void dispatch(struct puffs_cc *);
+
+/* for our eyes only */
+void
+puffs__ml_dispatch(struct puffs_usermount *pu, struct puffs_framebuf *pb)
+{
+       struct puffs_cc *pcc = puffs_cc_getcc(pu);
+       struct puffs_req *preq;
+
+       pcc->pcc_pb = pb;
+       pcc->pcc_flags |= PCC_MLCONT;
+       dispatch(pcc);
+
+       /* Put result to kernel sendqueue if necessary */
+       preq = puffs__framebuf_getdataptr(pcc->pcc_pb);
+       if (PUFFSOP_WANTREPLY(preq->preq_opclass)) {
+               if (pu->pu_flags & PUFFS_FLAG_OPDUMP)
+                       puffsdump_rv(preq);
+
+               puffs_framev_enqueue_justsend(pu, pu->pu_fd,
+                   pcc->pcc_pb, 0, 0);
+       } else {
+               puffs_framebuf_destroy(pcc->pcc_pb);
+       }
+
+       /* who needs information when you're living on borrowed time? */
+       if (pcc->pcc_flags & PCC_BORROWED) {
+               puffs_cc_yield(pcc); /* back to borrow source */
+       }
+       pcc->pcc_flags = 0;
+}
+
+/* public, but not really tested and only semi-supported */
+int
+puffs_dispatch_create(struct puffs_usermount *pu, struct puffs_framebuf *pb,
+       struct puffs_cc **pccp)
+{
+       struct puffs_cc *pcc;
+
+       if (puffs__cc_create(pu, dispatch, &pcc) == -1)
+               return -1;
+
+       pcc->pcc_pb = pb;
+       *pccp = pcc;
+
+       return 0;
+}
+
+int
+puffs_dispatch_exec(struct puffs_cc *pcc, struct puffs_framebuf **pbp)
+{
+       int rv;
+
+       puffs_cc_continue(pcc);
+
+       if (pcc->pcc_flags & PCC_DONE) {
+               rv = 1;
+               *pbp = pcc->pcc_pb;
+               pcc->pcc_flags = 0;
+               puffs__cc_destroy(pcc, 0);
+       } else {
+               rv = 0;
+       }
+
+       return rv;
+}
+
+static void
+dispatch(struct puffs_cc *pcc)
+{
+       struct puffs_usermount *pu = pcc->pcc_pu;
+       struct puffs_ops *pops = &pu->pu_ops;
+       struct puffs_req *preq = puffs__framebuf_getdataptr(pcc->pcc_pb);
+       void *auxbuf; /* help with typecasting */
+       puffs_cookie_t opcookie;
+       int error = 0, buildpath;
+
+       /* XXX: smaller hammer, please */
+       if ((PUFFSOP_OPCLASS(preq->preq_opclass == PUFFSOP_VFS &&
+           preq->preq_optype == PUFFS_VFS_VPTOFH)) ||
+           (PUFFSOP_OPCLASS(preq->preq_opclass) == PUFFSOP_VN &&
+           (preq->preq_optype == PUFFS_VN_READDIR
+           || preq->preq_optype == PUFFS_VN_READ))) {
+               if (puffs_framebuf_reserve_space(pcc->pcc_pb,
+                   PUFFS_MSG_MAXSIZE) == -1)
+                       error = errno;
+               preq = puffs__framebuf_getdataptr(pcc->pcc_pb);
+       }
+
+       auxbuf = preq;
+       opcookie = preq->preq_cookie;
+
+       assert((pcc->pcc_flags & PCC_DONE) == 0);
+
+       buildpath = pu->pu_flags & PUFFS_FLAG_BUILDPATH;
+       preq->preq_setbacks = 0;
+
+       if (pu->pu_flags & PUFFS_FLAG_OPDUMP)
+               puffsdump_req(preq);
+
+       puffs__cc_setcaller(pcc, preq->preq_pid, preq->preq_lid);
+
+       /* pre-operation */
+       if (pu->pu_oppre)
+               pu->pu_oppre(pu);
+
+       if (error)
+               goto out;
+
+       /* Execute actual operation */
+       if (PUFFSOP_OPCLASS(preq->preq_opclass) == PUFFSOP_VFS) {
+               switch (preq->preq_optype) {
+               case PUFFS_VFS_UNMOUNT:
+               {
+                       struct puffs_vfsmsg_unmount *auxt = auxbuf;
+
+                       PU_SETSTATE(pu, PUFFS_STATE_UNMOUNTING);
+                       error = pops->puffs_fs_unmount(pu, auxt->pvfsr_flags);
+                       if (!error)
+                               PU_SETSTATE(pu, PUFFS_STATE_UNMOUNTED);
+                       else
+                               PU_SETSTATE(pu, PUFFS_STATE_RUNNING);
+                       break;
+               }
+
+               case PUFFS_VFS_STATVFS:
+               {
+                       struct puffs_vfsmsg_statvfs *auxt = auxbuf;
+
+                       error = pops->puffs_fs_statvfs(pu, &auxt->pvfsr_sb);
+                       break;
+               }
+
+               case PUFFS_VFS_SYNC:
+               {
+                       struct puffs_vfsmsg_sync *auxt = auxbuf;
+
+                       error = pops->puffs_fs_sync(pu,
+                           auxt->pvfsr_waitfor);
+                       break;
+               }
+
+               case PUFFS_VFS_FHTOVP:
+               {
+                       struct puffs_vfsmsg_fhtonode *auxt = auxbuf;
+                       struct puffs_newinfo pni;
+
+                       pni.pni_cookie = &auxt->pvfsr_fhcookie;
+                       pni.pni_vtype = &auxt->pvfsr_vtype;
+                       pni.pni_size = &auxt->pvfsr_size;
+
+                       error = pops->puffs_fs_fhtonode(pu, auxt->pvfsr_data,
+                           auxt->pvfsr_dsize, &pni);
+
+                       break;
+               }
+
+               case PUFFS_VFS_VPTOFH:
+               {
+                       struct puffs_vfsmsg_nodetofh *auxt = auxbuf;
+
+                       error = pops->puffs_fs_nodetofh(pu,
+                           auxt->pvfsr_fhcookie, auxt->pvfsr_data,
+                           &auxt->pvfsr_dsize);
+
+                       break;
+               }
+
+               case PUFFS_VFS_EXTATTRCTL:
+               {
+                       struct puffs_vfsmsg_extattrctl *auxt = auxbuf;
+                       const char *attrname;
+                       int flags;
+
+                       if (pops->puffs_fs_extattrctl == NULL) {
+                               error = EOPNOTSUPP;
+                               break;
+                       }
+
+                       if (auxt->pvfsr_flags & PUFFS_EXTATTRCTL_HASATTRNAME)
+                               attrname = auxt->pvfsr_attrname;
+                       else
+                               attrname = NULL;
+
+                       flags = auxt->pvfsr_flags & PUFFS_EXTATTRCTL_HASNODE;
+                       error = pops->puffs_fs_extattrctl(pu, auxt->pvfsr_cmd,
+                           opcookie, flags,
+                           auxt->pvfsr_attrnamespace, attrname);
+                       break;
+               }
+
+               default:
+                       /*
+                        * I guess the kernel sees this one coming
+                        */
+                       error = EINVAL;
+                       break;
+               }
+
+       /* XXX: audit return values */
+       /* XXX: sync with kernel */
+       } else if (PUFFSOP_OPCLASS(preq->preq_opclass) == PUFFSOP_VN) {
+               switch (preq->preq_optype) {
+               case PUFFS_VN_LOOKUP:
+               {
+                       struct puffs_vnmsg_lookup *auxt = auxbuf;
+                       struct puffs_newinfo pni;
+                       struct puffs_cn pcn;
+
+                       pcn.pcn_pkcnp = &auxt->pvnr_cn;
+                       PUFFS_KCREDTOCRED(pcn.pcn_cred, &auxt->pvnr_cn_cred);
+                       pni.pni_cookie = &auxt->pvnr_newnode;
+                       pni.pni_vtype = &auxt->pvnr_vtype;
+                       pni.pni_size = &auxt->pvnr_size;
+
+                       if (buildpath) {
+                               error = puffs_path_pcnbuild(pu, &pcn, opcookie);
+                               if (error)
+                                       break;
+                       }
+
+                       /* lookup *must* be present */
+                       error = pops->puffs_node_lookup(pu, opcookie,
+                           &pni, &pcn);
+
+                       if (buildpath) {
+                               if (error) {
+                                       pu->pu_pathfree(pu, &pcn.pcn_po_full);
+                               } else {
+                                       struct puffs_node *pn;
+
+                                       /*
+                                        * did we get a new node or a
+                                        * recycled node?
+                                        */
+                                       pn = PU_CMAP(pu, auxt->pvnr_newnode);
+                                       if (pn->pn_po.po_path == NULL)
+                                               pn->pn_po = pcn.pcn_po_full;
+                                       else
+                                               pu->pu_pathfree(pu,
+                                                   &pcn.pcn_po_full);
+                               }
+                       }
+
+                       break;
+               }
+
+               case PUFFS_VN_LOOKUPDOTDOT:
+               {
+                       struct puffs_kcn kcn = {
+                               .pkcn_name = "..",
+                               .pkcn_namelen = 2,
+                       };
+                       struct puffs_vnmsg_lookupdotdot *auxt = auxbuf;
+                       struct puffs_newinfo pni;
+                       struct puffs_cn pcn;
+
+                       pcn.pcn_pkcnp = &kcn;
+                       PUFFS_KCREDTOCRED(pcn.pcn_cred, &auxt->pvnr_cred);
+                       pni.pni_cookie = &auxt->pvnr_newnode;
+                       pni.pni_vtype = NULL;
+                       pni.pni_size = NULL;
+
+                       if (buildpath) {
+                               error = puffs_path_pcnbuild(pu, &pcn, opcookie);
+                               if (error)
+                                       break;
+                       }
+
+                       /* lookup *must* be present */
+                       error = pops->puffs_node_lookupdotdot(pu, opcookie,
+                           &pni, &pcn);
+
+                       if (buildpath) {
+                               if (error) {
+                                       pu->pu_pathfree(pu, &pcn.pcn_po_full);
+                               } else {
+                                       struct puffs_node *pn;
+
+                                       /*
+                                        * did we get a new node or a
+                                        * recycled node?
+                                        */
+                                       pn = PU_CMAP(pu, auxt->pvnr_newnode);
+                                       if (pn->pn_po.po_path == NULL)
+                                               pn->pn_po = pcn.pcn_po_full;
+                                       else
+                                               pu->pu_pathfree(pu,
+                                                   &pcn.pcn_po_full);
+                               }
+                       }
+                       break;
+               }
+
+               case PUFFS_VN_CREATE:
+               {
+                       struct puffs_vnmsg_create *auxt = auxbuf;
+                       struct puffs_newinfo pni;
+                       struct puffs_cn pcn;
+
+                       if (pops->puffs_node_create == NULL) {
+                               error = 0;
+                               break;
+                       }
+
+                       pcn.pcn_pkcnp = &auxt->pvnr_cn;
+                       PUFFS_KCREDTOCRED(pcn.pcn_cred, &auxt->pvnr_cn_cred);
+
+                       memset(&pni, 0, sizeof(pni));
+                       pni.pni_cookie = &auxt->pvnr_newnode;
+
+                       if (buildpath) {
+                               error = puffs_path_pcnbuild(pu, &pcn, opcookie);
+                               if (error)
+                                       break;
+                       }
+
+                       error = pops->puffs_node_create(pu,
+                           opcookie, &pni, &pcn, &auxt->pvnr_va);
+
+                       if (buildpath) {
+                               if (error) {
+                                       pu->pu_pathfree(pu, &pcn.pcn_po_full);
+                               } else {
+                                       struct puffs_node *pn;
+
+                                       pn = PU_CMAP(pu, auxt->pvnr_newnode);
+                                       pn->pn_po = pcn.pcn_po_full;
+                               }
+                       }
+
+                       break;
+               }
+
+               case PUFFS_VN_MKNOD:
+               {
+                       struct puffs_vnmsg_mknod *auxt = auxbuf;
+                       struct puffs_newinfo pni;
+                       struct puffs_cn pcn;
+
+                       if (pops->puffs_node_mknod == NULL) {
+                               error = 0;
+                               break;
+                       }
+
+                       pcn.pcn_pkcnp = &auxt->pvnr_cn;
+                       PUFFS_KCREDTOCRED(pcn.pcn_cred, &auxt->pvnr_cn_cred);
+
+                       memset(&pni, 0, sizeof(pni));
+                       pni.pni_cookie = &auxt->pvnr_newnode;
+
+                       if (buildpath) {
+                               error = puffs_path_pcnbuild(pu, &pcn, opcookie);
+                               if (error)
+                                       break;
+                       }
+
+                       error = pops->puffs_node_mknod(pu,
+                           opcookie, &pni, &pcn, &auxt->pvnr_va);
+
+                       if (buildpath) {
+                               if (error) {
+                                       pu->pu_pathfree(pu, &pcn.pcn_po_full);
+                               } else {
+                                       struct puffs_node *pn;
+
+                                       pn = PU_CMAP(pu, auxt->pvnr_newnode);
+                                       pn->pn_po = pcn.pcn_po_full;
+                               }
+                       }
+
+                       break;
+               }
+
+               case PUFFS_VN_OPEN:
+               {
+                       struct puffs_vnmsg_open *auxt = auxbuf;
+                       PUFFS_MAKECRED(pcr, &auxt->pvnr_cred);
+
+                       if (pops->puffs_node_open == NULL) {
+                               error = 0;
+                               break;
+                       }
+
+                       error = pops->puffs_node_open(pu,
+                           opcookie, auxt->pvnr_mode, pcr);
+                       break;
+               }
+
+               case PUFFS_VN_CLOSE:
+               {
+                       struct puffs_vnmsg_close *auxt = auxbuf;
+
+                       if (pops->puffs_node_close == NULL) {
+                               error = 0;
+                               break;
+                       }
+
+                       error = pops->puffs_node_close(pu,
+                           opcookie, auxt->pvnr_fflag);
+                       break;
+               }
+
+               case PUFFS_VN_ACCESS:
+               {
+                       struct puffs_vnmsg_access *auxt = auxbuf;
+                       PUFFS_MAKECRED(pcr, &auxt->pvnr_cred);
+
+                       if (pops->puffs_node_access == NULL) {
+                               error = 0;
+                               break;
+                       }
+
+                       error = pops->puffs_node_access(pu,
+                           opcookie, auxt->pvnr_mode, pcr);
+                       break;
+               }
+
+               case PUFFS_VN_GETATTR:
+               {
+                       struct puffs_vnmsg_getattr *auxt = auxbuf;
+                       PUFFS_MAKECRED(pcr, &auxt->pvnr_cred);
+
+                       if (pops->puffs_node_getattr == NULL) {
+                               error = EOPNOTSUPP;
+                               break;
+                       }
+
+                       error = pops->puffs_node_getattr(pu,
+                           opcookie, &auxt->pvnr_va, pcr);
+                       break;
+               }
+
+               case PUFFS_VN_SETATTR:
+               {
+                       struct puffs_vnmsg_setattr *auxt = auxbuf;
+                       PUFFS_MAKECRED(pcr, &auxt->pvnr_cred);
+
+                       if (pops->puffs_node_setattr == NULL) {
+                               error = EOPNOTSUPP;
+                               break;
+                       }
+
+                       error = pops->puffs_node_setattr(pu,
+                           opcookie, &auxt->pvnr_va, pcr);
+                       break;
+               }
+
+               case PUFFS_VN_MMAP:
+               {
+                       struct puffs_vnmsg_mmap *auxt = auxbuf;
+                       PUFFS_MAKECRED(pcr, &auxt->pvnr_cred);
+
+                       if (pops->puffs_node_mmap == NULL) {
+                               error = 0;
+                               break;
+                       }
+
+                       error = pops->puffs_node_mmap(pu,
+                           opcookie, auxt->pvnr_prot, pcr);
+                       break;
+               }
+
+               case PUFFS_VN_FSYNC:
+               {
+                       struct puffs_vnmsg_fsync *auxt = auxbuf;
+
+                       if (pops->puffs_node_fsync == NULL) {
+                               error = 0;
+                               break;
+                       }
+
+                       error = pops->puffs_node_fsync(pu, opcookie,
+                           auxt->pvnr_flags);
+                       break;
+               }
+
+               case PUFFS_VN_SEEK:
+               {
+                       struct puffs_vnmsg_seek *auxt = auxbuf;
+                       PUFFS_MAKECRED(pcr, &auxt->pvnr_cred);
+
+                       if (pops->puffs_node_seek == NULL) {
+                               error = 0;
+                               break;
+                       }
+
+                       error = pops->puffs_node_seek(pu,
+                           opcookie, auxt->pvnr_oldoff,
+                           auxt->pvnr_newoff, pcr);
+                       break;
+               }
+
+               case PUFFS_VN_REMOVE:
+               {
+                       struct puffs_vnmsg_remove *auxt = auxbuf;
+                       struct puffs_cn pcn;
+                       if (pops->puffs_node_remove == NULL) {
+                               error = 0;
+                               break;
+                       }
+
+                       pcn.pcn_pkcnp = &auxt->pvnr_cn;
+                       PUFFS_KCREDTOCRED(pcn.pcn_cred, &auxt->pvnr_cn_cred);
+
+                       error = pops->puffs_node_remove(pu,
+                           opcookie, auxt->pvnr_cookie_targ, &pcn);
+                       break;
+               }
+
+               case PUFFS_VN_LINK:
+               {
+                       struct puffs_vnmsg_link *auxt = auxbuf;
+                       struct puffs_cn pcn;
+                       if (pops->puffs_node_link == NULL) {
+                               error = 0;
+                               break;
+                       }
+
+                       pcn.pcn_pkcnp = &auxt->pvnr_cn;
+                       PUFFS_KCREDTOCRED(pcn.pcn_cred, &auxt->pvnr_cn_cred);
+
+                       if (buildpath) {
+                               error = puffs_path_pcnbuild(pu, &pcn, opcookie);
+                               if (error)
+                                       break;
+                       }
+
+                       error = pops->puffs_node_link(pu,
+                           opcookie, auxt->pvnr_cookie_targ, &pcn);
+                       if (buildpath)
+                               pu->pu_pathfree(pu, &pcn.pcn_po_full);
+
+                       break;
+               }
+
+               case PUFFS_VN_RENAME:
+               {
+                       struct puffs_vnmsg_rename *auxt = auxbuf;
+                       struct puffs_cn pcn_src, pcn_targ;
+                       struct puffs_node *pn_src;
+
+                       if (pops->puffs_node_rename == NULL) {
+                               error = 0;
+                               break;
+                       }
+
+                       pcn_src.pcn_pkcnp = &auxt->pvnr_cn_src;
+                       PUFFS_KCREDTOCRED(pcn_src.pcn_cred,
+                           &auxt->pvnr_cn_src_cred);
+
+                       pcn_targ.pcn_pkcnp = &auxt->pvnr_cn_targ;
+                       PUFFS_KCREDTOCRED(pcn_targ.pcn_cred,
+                           &auxt->pvnr_cn_targ_cred);
+
+                       if (buildpath) {
+                               pn_src = auxt->pvnr_cookie_src;
+                               pcn_src.pcn_po_full = pn_src->pn_po;
+
+                               error = puffs_path_pcnbuild(pu, &pcn_targ,
+                                   auxt->pvnr_cookie_targdir);
+                               if (error)
+                                       break;
+                       }
+
+                       error = pops->puffs_node_rename(pu,
+                           opcookie, auxt->pvnr_cookie_src,
+                           &pcn_src, auxt->pvnr_cookie_targdir,
+                           auxt->pvnr_cookie_targ, &pcn_targ);
+
+                       if (buildpath) {
+                               if (error) {
+                                       pu->pu_pathfree(pu,
+                                           &pcn_targ.pcn_po_full);
+                               } else {
+                                       struct puffs_pathinfo pi;
+                                       struct puffs_pathobj po_old;
+
+                                       /* handle this node */
+                                       po_old = pn_src->pn_po;
+                                       pn_src->pn_po = pcn_targ.pcn_po_full;
+
+                                       if (pn_src->pn_va.va_type != VDIR) {
+                                               pu->pu_pathfree(pu, &po_old);
+                                               break;
+                                       }
+
+                                       /* handle all child nodes for DIRs */
+                                       pi.pi_old = &pcn_src.pcn_po_full;
+                                       pi.pi_new = &pcn_targ.pcn_po_full;
+
+                                       PU_LOCK();
+                                       if (puffs_pn_nodewalk(pu,
+                                           puffs_path_prefixadj, &pi) != NULL)
+                                               error = ENOMEM;
+                                       PU_UNLOCK();
+                                       pu->pu_pathfree(pu, &po_old);
+                               }
+                       }
+                       break;
+               }
+
+               case PUFFS_VN_MKDIR:
+               {
+                       struct puffs_vnmsg_mkdir *auxt = auxbuf;
+                       struct puffs_newinfo pni;
+                       struct puffs_cn pcn;
+
+                       if (pops->puffs_node_mkdir == NULL) {
+                               error = 0;
+                               break;
+                       }
+
+                       pcn.pcn_pkcnp = &auxt->pvnr_cn;
+                       PUFFS_KCREDTOCRED(pcn.pcn_cred, &auxt->pvnr_cn_cred);
+
+                       memset(&pni, 0, sizeof(pni));
+                       pni.pni_cookie = &auxt->pvnr_newnode;
+
+                       if (buildpath) {
+                               error = puffs_path_pcnbuild(pu, &pcn, opcookie);
+                               if (error)
+                                       break;
+                       }
+
+                       error = pops->puffs_node_mkdir(pu,
+                           opcookie, &pni, &pcn, &auxt->pvnr_va);
+
+                       if (buildpath) {
+                               if (error) {
+                                       pu->pu_pathfree(pu, &pcn.pcn_po_full);
+                               } else {
+                                       struct puffs_node *pn;
+
+                                       pn = PU_CMAP(pu, auxt->pvnr_newnode);
+                                       pn->pn_po = pcn.pcn_po_full;
+                               }
+                       }
+
+                       break;
+               }
+
+               case PUFFS_VN_RMDIR:
+               {
+                       struct puffs_vnmsg_rmdir *auxt = auxbuf;
+                       struct puffs_cn pcn;
+                       if (pops->puffs_node_rmdir == NULL) {
+                               error = 0;
+                               break;
+                       }
+
+                       pcn.pcn_pkcnp = &auxt->pvnr_cn;
+                       PUFFS_KCREDTOCRED(pcn.pcn_cred, &auxt->pvnr_cn_cred);
+
+                       error = pops->puffs_node_rmdir(pu,
+                           opcookie, auxt->pvnr_cookie_targ, &pcn);
+                       break;
+               }
+
+               case PUFFS_VN_SYMLINK:
+               {
+                       struct puffs_vnmsg_symlink *auxt = auxbuf;
+                       struct puffs_newinfo pni;
+                       struct puffs_cn pcn;
+
+                       if (pops->puffs_node_symlink == NULL) {
+                               error = 0;
+                               break;
+                       }
+
+                       pcn.pcn_pkcnp = &auxt->pvnr_cn;
+                       PUFFS_KCREDTOCRED(pcn.pcn_cred, &auxt->pvnr_cn_cred);
+
+                       memset(&pni, 0, sizeof(pni));
+                       pni.pni_cookie = &auxt->pvnr_newnode;
+
+                       if (buildpath) {
+                               error = puffs_path_pcnbuild(pu, &pcn, opcookie);
+                               if (error)
+                                       break;
+                       }
+
+                       error = pops->puffs_node_symlink(pu,
+                           opcookie, &pni, &pcn,
+                           &auxt->pvnr_va, auxt->pvnr_link);
+
+                       if (buildpath) {
+                               if (error) {
+                                       pu->pu_pathfree(pu, &pcn.pcn_po_full);
+                               } else {
+                                       struct puffs_node *pn;
+
+                                       pn = PU_CMAP(pu, auxt->pvnr_newnode);
+                                       pn->pn_po = pcn.pcn_po_full;
+                               }
+                       }
+
+                       break;
+               }
+
+               case PUFFS_VN_READDIR:
+               {
+                       struct puffs_vnmsg_readdir *auxt = auxbuf;
+                       PUFFS_MAKECRED(pcr, &auxt->pvnr_cred);
+                       struct dirent *dent;
+                       off_t *cookies;
+                       size_t res, origcookies;
+
+                       if (pops->puffs_node_readdir == NULL) {
+                               error = 0;
+                               break;
+                       }
+
+                       if (auxt->pvnr_ncookies) {
+                               /* LINTED: pvnr_data is __aligned() */
+                               cookies = (off_t *)auxt->pvnr_data;
+                               origcookies = auxt->pvnr_ncookies;
+                       } else {
+                               cookies = NULL;
+                               origcookies = 0;
+                       }
+                       /* LINTED: dentoff is aligned in the kernel */
+                       dent = (struct dirent *)
+                           (auxt->pvnr_data + auxt->pvnr_dentoff);
+
+                       res = auxt->pvnr_resid;
+                       error = pops->puffs_node_readdir(pu,
+                           opcookie, dent, &auxt->pvnr_offset,
+                           &auxt->pvnr_resid, pcr, &auxt->pvnr_eofflag,
+                           cookies, &auxt->pvnr_ncookies);
+
+                       /* much easier to track non-working NFS */
+                       assert(auxt->pvnr_ncookies <= origcookies);
+
+                       /* need to move a bit more */
+                       preq->preq_buflen = sizeof(struct puffs_vnmsg_readdir)
+                           + auxt->pvnr_dentoff + (res - auxt->pvnr_resid);
+                       break;
+               }
+
+               case PUFFS_VN_READLINK:
+               {
+                       struct puffs_vnmsg_readlink *auxt = auxbuf;
+                       PUFFS_MAKECRED(pcr, &auxt->pvnr_cred);
+
+                       if (pops->puffs_node_readlink == NULL) {
+                               error = EOPNOTSUPP;
+                               break;
+                       }
+
+                       /*LINTED*/
+                       error = pops->puffs_node_readlink(pu, opcookie, pcr,
+                           auxt->pvnr_link, &auxt->pvnr_linklen);
+                       break;
+               }
+
+               case PUFFS_VN_RECLAIM:
+               {
+
+                       if (pops->puffs_node_reclaim == NULL) {
+                               error = 0;
+                               break;
+                       }
+
+                       error = pops->puffs_node_reclaim(pu, opcookie);
+                       break;
+               }
+
+               case PUFFS_VN_INACTIVE:
+               {
+
+                       if (pops->puffs_node_inactive == NULL) {
+                               error = EOPNOTSUPP;
+                               break;
+                       }
+
+                       error = pops->puffs_node_inactive(pu, opcookie);
+                       break;
+               }
+
+               case PUFFS_VN_PATHCONF:
+               {
+                       struct puffs_vnmsg_pathconf *auxt = auxbuf;
+                       if (pops->puffs_node_pathconf == NULL) {
+                               error = 0;
+                               break;
+                       }
+
+                       error = pops->puffs_node_pathconf(pu,
+                           opcookie, auxt->pvnr_name,
+                           &auxt->pvnr_retval);
+                       break;
+               }
+
+               case PUFFS_VN_ADVLOCK:
+               {
+                       struct puffs_vnmsg_advlock *auxt = auxbuf;
+                       if (pops->puffs_node_advlock == NULL) {
+                               error = 0;
+                               break;
+                       }
+
+                       error = pops->puffs_node_advlock(pu,
+                           opcookie, auxt->pvnr_id, auxt->pvnr_op,
+                           &auxt->pvnr_fl, auxt->pvnr_flags);
+                       break;
+               }
+
+               case PUFFS_VN_PRINT:
+               {
+                       if (pops->puffs_node_print == NULL) {
+                               error = 0;
+                               break;
+                       }
+
+                       error = pops->puffs_node_print(pu,
+                           opcookie);
+                       break;
+               }
+
+               case PUFFS_VN_ABORTOP:
+               {
+                       struct puffs_vnmsg_abortop *auxt = auxbuf;
+                       struct puffs_cn pcn;
+
+                       if (pops->puffs_node_abortop == NULL) {
+                               error = 0;
+                               break;
+                       }
+
+                       pcn.pcn_pkcnp = &auxt->pvnr_cn;
+                       PUFFS_KCREDTOCRED(pcn.pcn_cred, &auxt->pvnr_cn_cred);
+
+                       error = pops->puffs_node_abortop(pu, opcookie, &pcn);
+
+                       break;
+               }
+
+               case PUFFS_VN_READ:
+               {
+                       struct puffs_vnmsg_read *auxt = auxbuf;
+                       PUFFS_MAKECRED(pcr, &auxt->pvnr_cred);
+                       size_t res;
+
+                       if (pops->puffs_node_read == NULL) {
+                               error = EIO;
+                               break;
+                       }
+
+                       res = auxt->pvnr_resid;
+                       error = pops->puffs_node_read(pu,
+                           opcookie, auxt->pvnr_data,
+                           auxt->pvnr_offset, &auxt->pvnr_resid,
+                           pcr, auxt->pvnr_ioflag);
+
+                       /* need to move a bit more */
+                       preq->preq_buflen = sizeof(struct puffs_vnmsg_read)
+                           + (res - auxt->pvnr_resid);
+                       break;
+               }
+
+               case PUFFS_VN_WRITE:
+               {
+                       struct puffs_vnmsg_write *auxt = auxbuf;
+                       PUFFS_MAKECRED(pcr, &auxt->pvnr_cred);
+
+                       if (pops->puffs_node_write == NULL) {
+                               error = EIO;
+                               break;
+                       }
+
+                       error = pops->puffs_node_write(pu,
+                           opcookie, auxt->pvnr_data,
+                           auxt->pvnr_offset, &auxt->pvnr_resid,
+                           pcr, auxt->pvnr_ioflag);
+
+                       /* don't need to move data back to the kernel */
+                       preq->preq_buflen = sizeof(struct puffs_vnmsg_write);
+                       break;
+               }
+
+               case PUFFS_VN_POLL:
+               {
+                       struct puffs_vnmsg_poll *auxt = auxbuf;
+
+                       if (pops->puffs_node_poll == NULL) {
+                               error = 0;
+
+                               /* emulate genfs_poll() */
+                               auxt->pvnr_events &= (POLLIN | POLLOUT
+                                                   | POLLRDNORM | POLLWRNORM);
+
+                               break;
+                       }
+
+                       error = pops->puffs_node_poll(pu,
+                           opcookie, &auxt->pvnr_events);
+                       break;
+               }
+
+               case PUFFS_VN_GETEXTATTR:
+               {
+                       struct puffs_vnmsg_getextattr *auxt = auxbuf;
+                       PUFFS_MAKECRED(pcr, &auxt->pvnr_cred);
+                       size_t res, *resp, *sizep;
+                       uint8_t *data;
+
+                       if (pops->puffs_node_getextattr == NULL) {
+                               error = EOPNOTSUPP;
+                               break;
+                       }
+
+                       if (auxt->pvnr_datasize)
+                               sizep = &auxt->pvnr_datasize;
+                       else
+                               sizep = NULL;
+
+                       res = auxt->pvnr_resid;
+                       if (res > 0) {
+                               data = auxt->pvnr_data;
+                               resp = &auxt->pvnr_resid;
+                       } else {
+                               data = NULL;
+                               resp = NULL;
+                       }
+
+                       error = pops->puffs_node_getextattr(pu,
+                           opcookie, auxt->pvnr_attrnamespace,
+                           auxt->pvnr_attrname, sizep, data, resp, pcr);
+
+                       /* need to move a bit more? */
+                       preq->preq_buflen =
+                           sizeof(struct puffs_vnmsg_getextattr)
+                           + (res - auxt->pvnr_resid);
+                       break;
+               }
+
+               case PUFFS_VN_SETEXTATTR:
+               {
+                       struct puffs_vnmsg_setextattr *auxt = auxbuf;
+                       PUFFS_MAKECRED(pcr, &auxt->pvnr_cred);
+                       size_t *resp;
+                       uint8_t *data;
+
+                       if (pops->puffs_node_setextattr == NULL) {
+                               error = EOPNOTSUPP;
+                               break;
+                       }
+
+                       if (auxt->pvnr_resid > 0) {
+                               data = auxt->pvnr_data;
+                               resp = &auxt->pvnr_resid;
+                       } else {
+                               data = NULL;
+                               resp = NULL;
+                       }
+
+                       error = pops->puffs_node_setextattr(pu,
+                           opcookie, auxt->pvnr_attrnamespace,
+                           auxt->pvnr_attrname, data, resp, pcr);
+                       break;
+               }
+
+               case PUFFS_VN_LISTEXTATTR:
+               {
+                       struct puffs_vnmsg_listextattr *auxt = auxbuf;
+                       PUFFS_MAKECRED(pcr, &auxt->pvnr_cred);
+                       size_t res, *resp, *sizep;
+                       int flag;
+                       uint8_t *data;
+
+                       if (pops->puffs_node_listextattr == NULL) {
+                               error = EOPNOTSUPP;
+                               break;
+                       }
+
+                       if (auxt->pvnr_datasize)
+                               sizep = &auxt->pvnr_datasize;
+                       else
+                               sizep = NULL;
+
+                       res = auxt->pvnr_resid;
+                       if (res > 0) {
+                               data = auxt->pvnr_data;
+                               resp = &auxt->pvnr_resid;
+                       } else {
+                               data = NULL;
+                               resp = NULL;
+                       }
+
+                       res = auxt->pvnr_resid;
+                       flag = auxt->pvnr_flag;
+                       error = pops->puffs_node_listextattr(pu,
+                           opcookie, auxt->pvnr_attrnamespace,
+                           sizep, data, resp, flag, pcr);
+
+                       /* need to move a bit more? */
+                       preq->preq_buflen =
+                           sizeof(struct puffs_vnmsg_listextattr)
+                           + (res - auxt->pvnr_resid);
+                       break;
+               }
+
+               case PUFFS_VN_DELETEEXTATTR:
+               {
+                       struct puffs_vnmsg_deleteextattr *auxt = auxbuf;
+                       PUFFS_MAKECRED(pcr, &auxt->pvnr_cred);
+
+                       if (pops->puffs_node_deleteextattr == NULL) {
+                               error = EOPNOTSUPP;
+                               break;
+                       }
+
+                       error = pops->puffs_node_deleteextattr(pu,
+                           opcookie, auxt->pvnr_attrnamespace,
+                           auxt->pvnr_attrname, pcr);
+                       break;
+               }
+
+               default:
+                       printf("inval op %d\n", preq->preq_optype);
+                       error = EINVAL;
+                       break;
+               }
+
+#if 0
+       /* not issued by kernel currently */
+       } else if (PUFFSOP_OPCLASS(preq->preq_opclass) == PUFFSOP_CACHE) {
+               struct puffs_cacheinfo *pci = (void *)preq;
+
+               if (pu->pu_ops.puffs_cache_write) {
+                       pu->pu_ops.puffs_cache_write(pu, preq->preq_cookie,
+                           pci->pcache_nruns, pci->pcache_runs);
+               }
+               error = 0;
+#endif
+
+       } else if (PUFFSOP_OPCLASS(preq->preq_opclass) == PUFFSOP_ERROR) {
+               struct puffs_error *perr = (void *)preq;
+
+               pu->pu_errnotify(pu, preq->preq_optype,
+                   perr->perr_error, perr->perr_str, preq->preq_cookie);
+               error = 0;
+       } else {
+               /*
+                * I guess the kernel sees this one coming also
+                */
+               error = EINVAL;
+       }
+
+ out:
+       preq->preq_rv = error;
+
+       if (pu->pu_oppost)
+               pu->pu_oppost(pu);
+
+       pcc->pcc_flags |= PCC_DONE;
+}
diff --git a/lib/libpuffs/flush.c b/lib/libpuffs/flush.c
new file mode 100644 (file)
index 0000000..cf149f2
--- /dev/null
@@ -0,0 +1,130 @@
+/*     $NetBSD: flush.c,v 1.16 2008/08/12 19:44:39 pooka Exp $ */
+
+/*
+ * Copyright (c) 2007  Antti Kantee.  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 AUTHOR ``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 AUTHOR 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.
+ */
+
+/*
+ * Flushing / invalidation routines
+ */
+
+#include <sys/types.h>
+
+#include <assert.h>
+#include <err.h>
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include "puffs.h"
+#include "puffs_priv.h"
+
+#if 0
+int
+puffs_inval_namecache_node(struct puffs_usermount *pu, puffs_cookie_t cookie,
+       const char *name)
+{
+
+       return EOPNOTSUPP;
+}
+#endif
+
+static int
+doflush(struct puffs_usermount *pu, puffs_cookie_t cookie, int op,
+       off_t start, off_t end)
+{
+       struct puffs_framebuf *pb;
+       struct puffs_flush *pf;
+       size_t winlen;
+       int rv;
+
+       pb = puffs_framebuf_make();
+       if (pb == NULL)
+               return ENOMEM;
+
+       winlen = sizeof(struct puffs_flush);
+       if ((rv = puffs_framebuf_getwindow(pb, 0, (void *)&pf, &winlen)) == -1)
+               goto out;
+       assert(winlen == sizeof(struct puffs_flush));
+
+       pf->pf_req.preq_buflen = sizeof(struct puffs_flush);
+       pf->pf_req.preq_opclass = PUFFSOP_FLUSH;
+       pf->pf_req.preq_id = puffs__nextreq(pu);
+
+       pf->pf_op = op;
+       pf->pf_cookie = cookie;
+       pf->pf_start = start;
+       pf->pf_end = end;
+
+       rv = puffs_framev_enqueue_cc(puffs_cc_getcc(pu),
+           puffs_getselectable(pu), pb, 0);
+
+ out:
+       puffs_framebuf_destroy(pb);
+       return rv;
+}
+
+int
+puffs_inval_namecache_dir(struct puffs_usermount *pu, puffs_cookie_t cookie)
+{
+
+       return doflush(pu, cookie, PUFFS_INVAL_NAMECACHE_DIR, 0, 0);
+}
+
+int
+puffs_inval_namecache_all(struct puffs_usermount *pu)
+{
+
+       return doflush(pu, NULL, PUFFS_INVAL_NAMECACHE_ALL, 0, 0);
+}
+
+int
+puffs_inval_pagecache_node(struct puffs_usermount *pu, puffs_cookie_t cookie)
+{
+
+       return doflush(pu, cookie, PUFFS_INVAL_PAGECACHE_NODE_RANGE, 0, 0);
+}
+
+int
+puffs_inval_pagecache_node_range(struct puffs_usermount *pu,
+       puffs_cookie_t cookie, off_t start, off_t end)
+{
+
+       return doflush(pu, cookie, PUFFS_INVAL_PAGECACHE_NODE_RANGE, start,end);
+}
+
+int
+puffs_flush_pagecache_node(struct puffs_usermount *pu, puffs_cookie_t cookie)
+{
+
+       return doflush(pu, cookie, PUFFS_FLUSH_PAGECACHE_NODE_RANGE, 0, 0);
+}
+
+int
+puffs_flush_pagecache_node_range(struct puffs_usermount *pu,
+       puffs_cookie_t cookie, off_t start, off_t end)
+{
+
+       return doflush(pu, cookie, PUFFS_FLUSH_PAGECACHE_NODE_RANGE, start,end);
+}
diff --git a/lib/libpuffs/framebuf.c b/lib/libpuffs/framebuf.c
new file mode 100644 (file)
index 0000000..814cd15
--- /dev/null
@@ -0,0 +1,1066 @@
+/*     $NetBSD: framebuf.c,v 1.30 2010/01/12 18:42:38 pooka Exp $      */
+
+/*
+ * Copyright (c) 2007  Antti Kantee.  All Rights Reserved.
+ *
+ * Development of this software was supported by the
+ * Finnish Cultural Foundation.
+ *
+ * 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 AUTHOR ``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 AUTHOR 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.
+ */
+
+/*
+ * The event portion of this code is a twisty maze of pointers,
+ * flags, yields and continues.  Sincere aplogies.
+ */
+
+#include <sys/types.h>
+#include <sys/event.h>
+#include <sys/queue.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <poll.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "puffs.h"
+#include "puffs_priv.h"
+
+struct puffs_framebuf {
+       struct puffs_cc *pcc;   /* pcc to continue with */
+       /* OR */
+       puffs_framev_cb fcb;    /* non-blocking callback */
+       void *fcb_arg;          /* argument for previous */
+
+       uint8_t *buf;           /* buffer base */
+       size_t len;             /* total length */
+
+       size_t offset;          /* cursor, telloff() */
+       size_t maxoff;          /* maximum offset for data, tellsize() */
+
+       volatile int rv;        /* errno value */
+
+       int     istat;
+
+       TAILQ_ENTRY(puffs_framebuf) pfb_entries;
+};
+#define ISTAT_NODESTROY        0x01    /* indestructible by framebuf_destroy() */
+#define ISTAT_INTERNAL 0x02    /* never leaves library                 */
+#define ISTAT_NOREPLY  0x04    /* nuke after sending                   */
+#define ISTAT_DIRECT   0x08    /* receive directly, no moveinfo        */
+
+#define ISTAT_ONQUEUE  ISTAT_NODESTROY /* alias */
+
+#define PUFBUF_INCRALLOC 4096
+#define PUFBUF_REMAIN(p) (p->len - p->offset)
+
+/* for poll/kqueue */
+struct puffs_fbevent {
+       struct puffs_cc *pcc;
+       int what;
+       volatile int rv;
+
+       LIST_ENTRY(puffs_fbevent) pfe_entries;
+};
+
+static struct puffs_fctrl_io *
+getfiobyfd(struct puffs_usermount *pu, int fd)
+{
+       struct puffs_fctrl_io *fio;
+
+       LIST_FOREACH(fio, &pu->pu_ios, fio_entries)
+               if (fio->io_fd == fd)
+                       return fio;
+       return NULL;
+}
+
+struct puffs_framebuf *
+puffs_framebuf_make()
+{
+       struct puffs_framebuf *pufbuf;
+
+       pufbuf = malloc(sizeof(struct puffs_framebuf));
+       if (pufbuf == NULL)
+               return NULL;
+       memset(pufbuf, 0, sizeof(struct puffs_framebuf));
+
+       pufbuf->buf = malloc(PUFBUF_INCRALLOC);
+       if (pufbuf->buf == NULL) {
+               free(pufbuf);
+               return NULL;
+       }
+       pufbuf->len = PUFBUF_INCRALLOC;
+
+       puffs_framebuf_recycle(pufbuf);
+       return pufbuf;
+}
+
+void
+puffs_framebuf_destroy(struct puffs_framebuf *pufbuf)
+{
+
+       assert((pufbuf->istat & ISTAT_NODESTROY) == 0);
+
+       free(pufbuf->buf);
+       free(pufbuf);
+}
+
+void
+puffs_framebuf_recycle(struct puffs_framebuf *pufbuf)
+{
+
+       assert((pufbuf->istat & ISTAT_NODESTROY) == 0);
+
+       pufbuf->offset = 0;
+       pufbuf->maxoff = 0;
+       pufbuf->istat = 0;
+}
+
+static int
+reservespace(struct puffs_framebuf *pufbuf, size_t off, size_t wantsize)
+{
+       size_t incr;
+       void *nd;
+
+       if (off <= pufbuf->len && pufbuf->len - off >= wantsize)
+               return 0;
+
+       for (incr = PUFBUF_INCRALLOC;
+           pufbuf->len + incr < off + wantsize;
+           incr += PUFBUF_INCRALLOC)
+               continue;
+
+       nd = realloc(pufbuf->buf, pufbuf->len + incr);
+       if (nd == NULL)
+               return -1;
+
+       pufbuf->buf = nd;
+       pufbuf->len += incr;
+
+       return 0;
+}
+
+int
+puffs_framebuf_dup(struct puffs_framebuf *pb, struct puffs_framebuf **pbp)
+{
+       struct puffs_framebuf *newpb;
+
+       newpb = puffs_framebuf_make();
+       if (newpb == NULL) {
+               errno = ENOMEM;
+               return -1;
+       }
+       memcpy(newpb, pb, sizeof(struct puffs_framebuf));
+
+       newpb->buf = NULL;
+       newpb->len = 0;
+       if (reservespace(newpb, 0, pb->maxoff) == -1) {
+               puffs_framebuf_destroy(newpb);
+               return -1;
+       }
+
+       memcpy(newpb->buf, pb->buf, pb->maxoff);
+       newpb->istat = 0;
+       *pbp = newpb;
+
+       return 0;
+}
+
+int
+puffs_framebuf_reserve_space(struct puffs_framebuf *pufbuf, size_t wantsize)
+{
+
+       return reservespace(pufbuf, pufbuf->offset, wantsize);
+}
+
+int
+puffs_framebuf_putdata(struct puffs_framebuf *pufbuf,
+       const void *data, size_t dlen)
+{
+
+       if (PUFBUF_REMAIN(pufbuf) < dlen)
+               if (puffs_framebuf_reserve_space(pufbuf, dlen) == -1)
+                       return -1;
+
+       memcpy(pufbuf->buf + pufbuf->offset, data, dlen);
+       pufbuf->offset += dlen;
+
+       if (pufbuf->offset > pufbuf->maxoff)
+               pufbuf->maxoff = pufbuf->offset;
+
+       return 0;
+}
+
+int
+puffs_framebuf_putdata_atoff(struct puffs_framebuf *pufbuf, size_t offset,
+       const void *data, size_t dlen)
+{
+
+       if (reservespace(pufbuf, offset, dlen) == -1)
+               return -1;
+
+       memcpy(pufbuf->buf + offset, data, dlen);
+
+       if (offset + dlen > pufbuf->maxoff)
+               pufbuf->maxoff = offset + dlen;
+
+       return 0;
+}
+
+int
+puffs_framebuf_getdata(struct puffs_framebuf *pufbuf, void *data, size_t dlen)
+{
+
+       if (pufbuf->maxoff < pufbuf->offset + dlen) {
+               errno = ENOBUFS;
+               return -1;
+       }
+
+       memcpy(data, pufbuf->buf + pufbuf->offset, dlen);
+       pufbuf->offset += dlen;
+
+       return 0;
+}
+
+int
+puffs_framebuf_getdata_atoff(struct puffs_framebuf *pufbuf, size_t offset,
+       void *data, size_t dlen)
+{
+
+       if (pufbuf->maxoff < offset + dlen) {
+               errno = ENOBUFS;
+               return -1;
+       }
+
+       memcpy(data, pufbuf->buf + offset, dlen);
+       return 0;
+}
+
+size_t
+puffs_framebuf_telloff(struct puffs_framebuf *pufbuf)
+{
+
+       return pufbuf->offset;
+}
+
+size_t
+puffs_framebuf_tellsize(struct puffs_framebuf *pufbuf)
+{
+
+       return pufbuf->maxoff;
+}
+
+size_t
+puffs_framebuf_remaining(struct puffs_framebuf *pufbuf)
+{
+
+       return puffs_framebuf_tellsize(pufbuf) - puffs_framebuf_telloff(pufbuf);
+}
+
+int
+puffs_framebuf_seekset(struct puffs_framebuf *pufbuf, size_t newoff)
+{
+
+       if (reservespace(pufbuf, newoff, 0) == -1)
+               return -1;
+
+       pufbuf->offset = newoff;
+       return 0;
+}
+
+int
+puffs_framebuf_getwindow(struct puffs_framebuf *pufbuf, size_t winoff,
+       void **data, size_t *dlen)
+{
+       size_t winlen;
+
+#ifdef WINTESTING
+       winlen = MIN(*dlen, 32);
+#else
+       winlen = *dlen;
+#endif
+
+       if (reservespace(pufbuf, winoff, winlen) == -1)
+               return -1;
+
+       *data = pufbuf->buf + winoff;
+       if (pufbuf->maxoff < winoff + winlen)
+               pufbuf->maxoff = winoff + winlen;
+
+       return 0;
+}
+
+void *
+puffs__framebuf_getdataptr(struct puffs_framebuf *pufbuf)
+{
+
+       return pufbuf->buf;
+}
+
+static void
+errnotify(struct puffs_usermount *pu, struct puffs_framebuf *pufbuf, int error)
+{
+
+       pufbuf->rv = error;
+       if (pufbuf->pcc) {
+               puffs__goto(pufbuf->pcc);
+       } else if (pufbuf->fcb) {
+               pufbuf->istat &= ~ISTAT_NODESTROY;
+               pufbuf->fcb(pu, pufbuf, pufbuf->fcb_arg, error);
+       } else {
+               pufbuf->istat &= ~ISTAT_NODESTROY;
+               puffs_framebuf_destroy(pufbuf);
+       }
+}
+
+#define GETFIO(fd)                                                     \
+do {                                                                   \
+       fio = getfiobyfd(pu, fd);                                       \
+       if (fio == NULL) {                                              \
+               errno = EINVAL;                                         \
+               return -1;                                              \
+       }                                                               \
+       if (fio->stat & FIO_WRGONE) {                                   \
+               errno = ESHUTDOWN;                                      \
+               return -1;                                              \
+       }                                                               \
+} while (/*CONSTCOND*/0)
+
+int
+puffs_framev_enqueue_cc(struct puffs_cc *pcc, int fd,
+       struct puffs_framebuf *pufbuf, int flags)
+{
+       struct puffs_usermount *pu = pcc->pcc_pu;
+       struct puffs_fctrl_io *fio;
+
+       /*
+        * Technically we shouldn't allow this if RDGONE, but it's
+        * difficult to trap write close without allowing writes.
+        * And besides, there's probably a disconnect sequence in
+        * the protocol, so unexpectedly getting a closed fd is
+        * most likely an error condition.
+        */
+       GETFIO(fd);
+
+       pufbuf->pcc = pcc;
+       pufbuf->fcb = NULL;
+       pufbuf->fcb_arg = NULL;
+
+       pufbuf->offset = 0;
+       pufbuf->istat |= ISTAT_NODESTROY;
+
+       if (flags & PUFFS_FBQUEUE_URGENT)
+               TAILQ_INSERT_HEAD(&fio->snd_qing, pufbuf, pfb_entries);
+       else
+               TAILQ_INSERT_TAIL(&fio->snd_qing, pufbuf, pfb_entries);
+
+       puffs_cc_yield(pcc);
+       if (pufbuf->rv) {
+               pufbuf->istat &= ~ISTAT_NODESTROY;
+               errno = pufbuf->rv;
+               return -1;
+       }
+
+       return 0;
+}
+
+int
+puffs_framev_enqueue_cb(struct puffs_usermount *pu, int fd,
+       struct puffs_framebuf *pufbuf, puffs_framev_cb fcb, void *arg,
+       int flags)
+{
+       struct puffs_fctrl_io *fio;
+
+       /* see enqueue_cc */
+       GETFIO(fd);
+
+       pufbuf->pcc = NULL;
+       pufbuf->fcb = fcb;
+       pufbuf->fcb_arg = arg;
+
+       pufbuf->offset = 0;
+       pufbuf->istat |= ISTAT_NODESTROY;
+
+       if (flags & PUFFS_FBQUEUE_URGENT)
+               TAILQ_INSERT_HEAD(&fio->snd_qing, pufbuf, pfb_entries);
+       else
+               TAILQ_INSERT_TAIL(&fio->snd_qing, pufbuf, pfb_entries);
+
+       return 0;
+}
+
+int
+puffs_framev_enqueue_justsend(struct puffs_usermount *pu, int fd,
+       struct puffs_framebuf *pufbuf, int reply, int flags)
+{
+       struct puffs_fctrl_io *fio;
+
+       assert((pufbuf->istat & ISTAT_INTERNAL) == 0);
+
+       GETFIO(fd);
+
+       pufbuf->pcc = NULL;
+       pufbuf->fcb = NULL;
+       pufbuf->fcb_arg = NULL;
+
+       pufbuf->offset = 0;
+       pufbuf->istat |= ISTAT_NODESTROY;
+       if (!reply)
+               pufbuf->istat |= ISTAT_NOREPLY;
+
+       if (flags & PUFFS_FBQUEUE_URGENT)
+               TAILQ_INSERT_HEAD(&fio->snd_qing, pufbuf, pfb_entries);
+       else
+               TAILQ_INSERT_TAIL(&fio->snd_qing, pufbuf, pfb_entries);
+
+       return 0;
+}
+
+/* ARGSUSED */
+int
+puffs_framev_enqueue_directreceive(struct puffs_cc *pcc, int fd,
+       struct puffs_framebuf *pufbuf, int flags /* used in the future */)
+{
+       struct puffs_usermount *pu = pcc->pcc_pu;
+       struct puffs_fctrl_io *fio;
+
+       assert((pufbuf->istat & ISTAT_INTERNAL) == 0);
+
+       fio = getfiobyfd(pu, fd);
+       if (fio == NULL) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       /* XXX: should have cur_in queue */
+       assert(fio->cur_in == NULL);
+       fio->cur_in = pufbuf;
+
+       pufbuf->pcc = pcc;
+       pufbuf->fcb = NULL;
+       pufbuf->fcb_arg = NULL;
+
+       pufbuf->offset = 0;
+       pufbuf->istat |= ISTAT_NODESTROY | ISTAT_DIRECT;
+
+       puffs_cc_yield(pcc);
+       pufbuf->istat &= ~ISTAT_NODESTROY; /* XXX: not the right place */
+       if (pufbuf->rv) {
+               errno = pufbuf->rv;
+               return -1;
+       }
+
+       return 0;
+}
+
+int
+puffs_framev_enqueue_directsend(struct puffs_cc *pcc, int fd,
+       struct puffs_framebuf *pufbuf, int flags)
+{
+       struct puffs_usermount *pu = pcc->pcc_pu;
+       struct puffs_fctrl_io *fio;
+
+       assert((pufbuf->istat & ISTAT_INTERNAL) == 0);
+
+       if (flags & PUFFS_FBQUEUE_URGENT)
+               abort(); /* EOPNOTSUPP for now */
+
+       GETFIO(fd);
+
+       pufbuf->pcc = pcc;
+       pufbuf->fcb = NULL;
+       pufbuf->fcb_arg = NULL;
+
+       pufbuf->offset = 0;
+       pufbuf->istat |= ISTAT_NODESTROY | ISTAT_DIRECT;
+
+       TAILQ_INSERT_TAIL(&fio->snd_qing, pufbuf, pfb_entries);
+
+       puffs_cc_yield(pcc);
+       if (pufbuf->rv) {
+               pufbuf->istat &= ~ISTAT_NODESTROY;
+               errno = pufbuf->rv;
+               return -1;
+       }
+
+       return 0;
+}
+
+int
+puffs_framev_framebuf_ccpromote(struct puffs_framebuf *pufbuf,
+       struct puffs_cc *pcc)
+{
+
+       if ((pufbuf->istat & ISTAT_ONQUEUE) == 0) {
+               errno = EBUSY;
+               return -1;
+       }
+
+       pufbuf->pcc = pcc;
+       pufbuf->fcb = NULL;
+       pufbuf->fcb_arg = NULL;
+       pufbuf->istat &= ~ISTAT_NOREPLY;
+
+       puffs_cc_yield(pcc);
+
+       return 0;
+}
+
+int
+puffs_framev_enqueue_waitevent(struct puffs_cc *pcc, int fd, int *what)
+{
+       struct puffs_usermount *pu = pcc->pcc_pu;
+       struct puffs_fctrl_io *fio;
+       struct puffs_fbevent feb;
+       struct kevent kev;
+       int rv, svwhat;
+
+       svwhat = *what;
+
+       if (*what == 0) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       fio = getfiobyfd(pu, fd);
+       if (fio == NULL) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       feb.pcc = pcc;
+       feb.what = *what & (PUFFS_FBIO_READ|PUFFS_FBIO_WRITE|PUFFS_FBIO_ERROR);
+
+       if (*what & PUFFS_FBIO_READ)
+               if ((fio->stat & FIO_ENABLE_R) == 0)
+                       EV_SET(&kev, fd, EVFILT_READ, EV_ENABLE,
+                           0, 0, fio);
+
+       rv = kevent(pu->pu_kq, &kev, 1, NULL, 0, NULL);
+       if (rv != 0)
+               return errno;
+
+       if (*what & PUFFS_FBIO_READ)
+               fio->rwait++;
+       if (*what & PUFFS_FBIO_WRITE)
+               fio->wwait++;
+
+       LIST_INSERT_HEAD(&fio->ev_qing, &feb, pfe_entries);
+       puffs_cc_yield(pcc);
+
+       assert(svwhat == *what);
+
+       if (*what & PUFFS_FBIO_READ) {
+               fio->rwait--;
+               if (fio->rwait == 0 && (fio->stat & FIO_ENABLE_R) == 0) {
+                       EV_SET(&kev, fd, EVFILT_READ, EV_DISABLE,
+                           0, 0, fio);
+                       rv = kevent(pu->pu_kq, &kev, 1, NULL, 0, NULL);
+#if 0
+                       if (rv != 0)
+                               /* XXXXX oh dear */;
+#endif
+               }
+       }
+       if (*what & PUFFS_FBIO_WRITE)
+               fio->wwait--;
+
+       if (feb.rv == 0) {
+               *what = feb.what;
+               rv = 0;
+       } else {
+               *what = PUFFS_FBIO_ERROR;
+               errno = feb.rv;
+               rv = -1;
+       }
+
+       return rv;
+}
+
+void
+puffs__framev_notify(struct puffs_fctrl_io *fio, int what)
+{
+       struct puffs_fbevent *fbevp;
+
+ restart:
+       LIST_FOREACH(fbevp, &fio->ev_qing, pfe_entries) {
+               if (fbevp->what & what) {
+                       fbevp->what = what;
+                       fbevp->rv = 0;
+                       LIST_REMOVE(fbevp, pfe_entries);
+                       puffs_cc_continue(fbevp->pcc);
+                       goto restart;
+               }
+       }
+}
+
+static struct puffs_framebuf *
+findbuf(struct puffs_usermount *pu, struct puffs_framectrl *fctrl,
+       struct puffs_fctrl_io *fio, struct puffs_framebuf *findme)
+{
+       struct puffs_framebuf *cand;
+       int notresp = 0;
+
+       TAILQ_FOREACH(cand, &fio->res_qing, pfb_entries)
+               if (fctrl->cmpfb(pu, findme, cand, &notresp) == 0 || notresp)
+                       break;
+
+       assert(!(notresp && cand == NULL));
+       if (notresp || cand == NULL)
+               return NULL;
+
+       TAILQ_REMOVE(&fio->res_qing, cand, pfb_entries);
+       return cand;
+}
+
+void
+puffs__framebuf_moveinfo(struct puffs_framebuf *from, struct puffs_framebuf *to)
+{
+
+       assert(from->istat & ISTAT_INTERNAL);
+
+       /* migrate buffer */
+       free(to->buf);
+       to->buf = from->buf;
+
+       /* migrate buffer info */
+       to->len = from->len;
+       to->offset = from->offset;
+       to->maxoff = from->maxoff;
+
+       from->buf = NULL;
+       from->len = 0;
+}
+
+void
+puffs__framev_input(struct puffs_usermount *pu, struct puffs_framectrl *fctrl,
+       struct puffs_fctrl_io *fio)
+{
+       struct puffs_framebuf *pufbuf, *appbuf;
+       int rv, complete;
+
+       while ((fio->stat & FIO_DEAD) == 0 && (fio->stat & FIO_ENABLE_R)) {
+               if ((pufbuf = fio->cur_in) == NULL) {
+                       pufbuf = puffs_framebuf_make();
+                       if (pufbuf == NULL)
+                               return;
+                       pufbuf->istat |= ISTAT_INTERNAL;
+                       fio->cur_in = pufbuf;
+               }
+
+               complete = 0;
+               rv = fctrl->rfb(pu, pufbuf, fio->io_fd, &complete);
+
+               /* error */
+               if (rv) {
+                       puffs__framev_readclose(pu, fio, rv);
+                       fio->cur_in = NULL;
+                       return;
+               }
+
+               /* partial read, come back to fight another day */
+               if (complete == 0)
+                       break;
+
+               /* else: full read, process */
+               fio->cur_in = NULL;
+               if ((pufbuf->istat & ISTAT_DIRECT) == 0) {
+                       appbuf = findbuf(pu, fctrl, fio, pufbuf);
+
+                       /*
+                        * No request for this frame?  If fs implements
+                        * gotfb, give frame to that.  Otherwise drop it.
+                        */
+                       if (appbuf == NULL) {
+                               if (fctrl->gotfb) {
+                                       pufbuf->istat &= ~ISTAT_INTERNAL;
+                                       fctrl->gotfb(pu, pufbuf);
+                               } else {
+                                       puffs_framebuf_destroy(pufbuf);
+                               }
+                               continue;
+                       }
+
+                       puffs__framebuf_moveinfo(pufbuf, appbuf);
+                       puffs_framebuf_destroy(pufbuf);
+               } else {
+                       appbuf = pufbuf;
+               }
+               appbuf->istat &= ~ISTAT_NODESTROY;
+
+               if (appbuf->pcc) {
+                       puffs__cc_cont(appbuf->pcc);
+               } else if (appbuf->fcb) {
+                       appbuf->fcb(pu, appbuf, appbuf->fcb_arg, 0);
+               } else {
+                       puffs_framebuf_destroy(appbuf);
+               }
+
+               /* hopeless romantics, here we go again */
+       }
+}
+
+int
+puffs__framev_output(struct puffs_usermount *pu, struct puffs_framectrl *fctrl,
+       struct puffs_fctrl_io *fio)
+{
+       struct puffs_framebuf *pufbuf;
+       int rv, complete, done;
+
+       if (fio->stat & FIO_DEAD)
+               return 0;
+
+       for (pufbuf = TAILQ_FIRST(&fio->snd_qing), done = 0;
+           pufbuf && (fio->stat & FIO_DEAD) == 0 && fio->stat & FIO_ENABLE_W;
+           pufbuf = TAILQ_FIRST(&fio->snd_qing)) {
+               complete = 0;
+               rv = fctrl->wfb(pu, pufbuf, fio->io_fd, &complete);
+
+               if (rv) {
+                       puffs__framev_writeclose(pu, fio, rv);
+                       done = 1;
+                       break;
+               }
+
+               /* partial write */
+               if (complete == 0)
+                       return done;
+
+               /* else, complete write */
+               TAILQ_REMOVE(&fio->snd_qing, pufbuf, pfb_entries);
+
+               /* can't wait for result if we can't read */
+               if (fio->stat & FIO_RDGONE) {
+                       errnotify(pu, pufbuf, ENXIO);
+                       done = 1;
+               } else if ((pufbuf->istat & ISTAT_DIRECT)) {
+                       pufbuf->istat &= ~ISTAT_NODESTROY;
+                       done = 1;
+                       puffs__cc_cont(pufbuf->pcc);
+               } else if ((pufbuf->istat & ISTAT_NOREPLY) == 0) {
+                       TAILQ_INSERT_TAIL(&fio->res_qing, pufbuf,
+                           pfb_entries);
+               } else {
+                       pufbuf->istat &= ~ISTAT_NODESTROY;
+                       puffs_framebuf_destroy(pufbuf);
+               }
+
+               /* omstart! */
+       }
+
+       return done;
+}
+
+int
+puffs__framev_addfd_ctrl(struct puffs_usermount *pu, int fd, int what,
+       struct puffs_framectrl *pfctrl)
+{
+       struct puffs_fctrl_io *fio;
+       struct kevent *newevs;
+       struct kevent kev[2];
+       size_t nevs;
+       int rv, readenable;
+
+       nevs = pu->pu_nevs+2;
+       newevs = realloc(pu->pu_evs, nevs*sizeof(struct kevent));
+       if (newevs == NULL)
+               return -1;
+       pu->pu_evs = newevs;
+
+       fio = malloc(sizeof(struct puffs_fctrl_io));
+       if (fio == NULL)
+               return -1;
+       memset(fio, 0, sizeof(struct puffs_fctrl_io));
+       fio->io_fd = fd;
+       fio->cur_in = NULL;
+       fio->fctrl = pfctrl;
+       TAILQ_INIT(&fio->snd_qing);
+       TAILQ_INIT(&fio->res_qing);
+       LIST_INIT(&fio->ev_qing);
+
+       readenable = 0;
+       if ((what & PUFFS_FBIO_READ) == 0)
+               readenable = EV_DISABLE;
+
+       if (pu->pu_state & PU_INLOOP) {
+               EV_SET(&kev[0], fd, EVFILT_READ,
+                   EV_ADD|readenable, 0, 0, fio);
+               EV_SET(&kev[1], fd, EVFILT_WRITE,
+                   EV_ADD|EV_DISABLE, 0, 0, fio);
+               rv = kevent(pu->pu_kq, kev, 2, NULL, 0, NULL);
+               if (rv == -1) {
+                       free(fio);
+                       return -1;
+               }
+       }
+       if (what & PUFFS_FBIO_READ)
+               fio->stat |= FIO_ENABLE_R;
+       if (what & PUFFS_FBIO_WRITE)
+               fio->stat |= FIO_ENABLE_W;
+
+       LIST_INSERT_HEAD(&pu->pu_ios, fio, fio_entries);
+       pu->pu_nevs = nevs;
+
+       return 0;
+}
+
+int
+puffs_framev_addfd(struct puffs_usermount *pu, int fd, int what)
+{
+
+       return puffs__framev_addfd_ctrl(pu, fd, what,
+           &pu->pu_framectrl[PU_FRAMECTRL_USER]);
+}
+
+/*
+ * XXX: the following en/disable should be coalesced and executed
+ * only during the actual kevent call.  So feel free to fix if
+ * threatened by mindblowing boredom.
+ */
+
+int
+puffs_framev_enablefd(struct puffs_usermount *pu, int fd, int what)
+{
+       struct kevent kev;
+       struct puffs_fctrl_io *fio;
+       int rv = 0;
+
+       assert((what & (PUFFS_FBIO_READ | PUFFS_FBIO_WRITE)) != 0);
+
+       fio = getfiobyfd(pu, fd);
+       if (fio == NULL) {
+               errno = ENXIO;
+               return -1;
+       }
+
+       /* write is enabled in the event loop if there is output */
+       if (what & PUFFS_FBIO_READ && fio->rwait == 0) {
+               EV_SET(&kev, fd, EVFILT_READ, EV_ENABLE, 0, 0, fio);
+               rv = kevent(pu->pu_kq, &kev, 1, NULL, 0, NULL);
+       }
+
+       if (rv == 0) {
+               if (what & PUFFS_FBIO_READ)
+                       fio->stat |= FIO_ENABLE_R;
+               if (what & PUFFS_FBIO_WRITE)
+                       fio->stat |= FIO_ENABLE_W;
+       }
+
+       return rv;
+}
+
+int
+puffs_framev_disablefd(struct puffs_usermount *pu, int fd, int what)
+{
+       struct kevent kev[2];
+       struct puffs_fctrl_io *fio;
+       size_t i;
+       int rv;
+
+       assert((what & (PUFFS_FBIO_READ | PUFFS_FBIO_WRITE)) != 0);
+
+       fio = getfiobyfd(pu, fd);
+       if (fio == NULL) {
+               errno = ENXIO;
+               return -1;
+       }
+
+       i = 0;
+       if (what & PUFFS_FBIO_READ && fio->rwait == 0) {
+               EV_SET(&kev[0], fd,
+                   EVFILT_READ, EV_DISABLE, 0, 0, fio);
+               i++;
+       }
+       if (what & PUFFS_FBIO_WRITE && fio->stat & FIO_WR && fio->wwait == 0) {
+               EV_SET(&kev[1], fd,
+                   EVFILT_WRITE, EV_DISABLE, 0, 0, fio);
+               i++;
+       }
+       if (i)
+               rv = kevent(pu->pu_kq, kev, i, NULL, 0, NULL);
+       else
+               rv = 0;
+
+       if (rv == 0) {
+               if (what & PUFFS_FBIO_READ)
+                       fio->stat &= ~FIO_ENABLE_R;
+               if (what & PUFFS_FBIO_WRITE)
+                       fio->stat &= ~FIO_ENABLE_W;
+       }
+
+       return rv;
+}
+
+void
+puffs__framev_readclose(struct puffs_usermount *pu,
+       struct puffs_fctrl_io *fio, int error)
+{
+       struct puffs_framebuf *pufbuf;
+       struct kevent kev;
+       int notflag;
+
+       if (fio->stat & FIO_RDGONE || fio->stat & FIO_DEAD)
+               return;
+       fio->stat |= FIO_RDGONE;
+
+       if (fio->cur_in) {
+               if ((fio->cur_in->istat & ISTAT_DIRECT) == 0) {
+                       puffs_framebuf_destroy(fio->cur_in);
+                       fio->cur_in = NULL;
+               } else {
+                       errnotify(pu, fio->cur_in, error);
+               }
+       }
+
+       while ((pufbuf = TAILQ_FIRST(&fio->res_qing)) != NULL) {
+               TAILQ_REMOVE(&fio->res_qing, pufbuf, pfb_entries);
+               errnotify(pu, pufbuf, error);
+       }
+
+       EV_SET(&kev, fio->io_fd, EVFILT_READ, EV_DELETE, 0, 0, 0);
+       (void) kevent(pu->pu_kq, &kev, 1, NULL, 0, NULL);
+
+       notflag = PUFFS_FBIO_READ;
+       if (fio->stat & FIO_WRGONE)
+               notflag |= PUFFS_FBIO_WRITE;
+
+       if (fio->fctrl->fdnotfn)
+               fio->fctrl->fdnotfn(pu, fio->io_fd, notflag);
+}
+
+void
+puffs__framev_writeclose(struct puffs_usermount *pu,
+       struct puffs_fctrl_io *fio, int error)
+{
+       struct puffs_framebuf *pufbuf;
+       struct kevent kev;
+       int notflag;
+
+       if (fio->stat & FIO_WRGONE || fio->stat & FIO_DEAD)
+               return;
+       fio->stat |= FIO_WRGONE;
+
+       while ((pufbuf = TAILQ_FIRST(&fio->snd_qing)) != NULL) {
+               TAILQ_REMOVE(&fio->snd_qing, pufbuf, pfb_entries);
+               errnotify(pu, pufbuf, error);
+       }
+
+       EV_SET(&kev, fio->io_fd, EVFILT_WRITE, EV_DELETE, 0, 0, 0);
+       (void) kevent(pu->pu_kq, &kev, 1, NULL, 0, NULL);
+
+       notflag = PUFFS_FBIO_WRITE;
+       if (fio->stat & FIO_RDGONE)
+               notflag |= PUFFS_FBIO_READ;
+
+       if (fio->fctrl->fdnotfn)
+               fio->fctrl->fdnotfn(pu, fio->io_fd, notflag);
+}
+
+static int
+removefio(struct puffs_usermount *pu, struct puffs_fctrl_io *fio, int error)
+{
+       struct puffs_fbevent *fbevp;
+
+       LIST_REMOVE(fio, fio_entries);
+       if (pu->pu_state & PU_INLOOP) {
+               puffs__framev_readclose(pu, fio, error);
+               puffs__framev_writeclose(pu, fio, error);
+       }
+
+       while ((fbevp = LIST_FIRST(&fio->ev_qing)) != NULL) {
+               fbevp->rv = error;
+               LIST_REMOVE(fbevp, pfe_entries);
+               puffs__goto(fbevp->pcc);
+       }
+
+       /* don't bother with realloc */
+       pu->pu_nevs -= 2;
+
+       /* don't free us yet, might have some references in event arrays */
+       fio->stat |= FIO_DEAD;
+       LIST_INSERT_HEAD(&pu->pu_ios_rmlist, fio, fio_entries);
+
+       return 0;
+
+}
+
+int
+puffs_framev_removefd(struct puffs_usermount *pu, int fd, int error)
+{
+       struct puffs_fctrl_io *fio;
+
+       fio = getfiobyfd(pu, fd);
+       if (fio == NULL) {
+               errno = ENXIO;
+               return -1;
+       }
+
+       return removefio(pu, fio, error ? error : ECONNRESET);
+}
+
+void
+puffs_framev_removeonclose(struct puffs_usermount *pu, int fd, int what)
+{
+
+       if (what == (PUFFS_FBIO_READ | PUFFS_FBIO_WRITE))
+               (void) puffs_framev_removefd(pu, fd, ECONNRESET);
+}
+
+void
+puffs_framev_unmountonclose(struct puffs_usermount *pu, int fd, int what)
+{
+
+       /* XXX & X: unmount is non-sensible */
+       puffs_framev_removeonclose(pu, fd, what);
+       if (what == (PUFFS_FBIO_READ | PUFFS_FBIO_WRITE))
+               PU_SETSTATE(pu, PUFFS_STATE_UNMOUNTED);
+}
+
+void
+puffs_framev_init(struct puffs_usermount *pu,
+       puffs_framev_readframe_fn rfb, puffs_framev_writeframe_fn wfb,
+       puffs_framev_cmpframe_fn cmpfb, puffs_framev_gotframe_fn gotfb,
+       puffs_framev_fdnotify_fn fdnotfn)
+{
+       struct puffs_framectrl *pfctrl;
+
+       pfctrl = &pu->pu_framectrl[PU_FRAMECTRL_USER];
+       pfctrl->rfb = rfb;
+       pfctrl->wfb = wfb;
+       pfctrl->cmpfb = cmpfb;
+       pfctrl->gotfb = gotfb;
+       pfctrl->fdnotfn = fdnotfn;
+}
+
+void
+puffs__framev_exit(struct puffs_usermount *pu)
+{
+       struct puffs_fctrl_io *fio;
+
+       while ((fio = LIST_FIRST(&pu->pu_ios)) != NULL)
+               removefio(pu, fio, ENXIO);
+       free(pu->pu_evs);
+
+       /* closing pu->pu_kq takes care of puffsfd */
+}
diff --git a/lib/libpuffs/hash.h b/lib/libpuffs/hash.h
new file mode 100644 (file)
index 0000000..45041ab
--- /dev/null
@@ -0,0 +1,103 @@
+/*     $NetBSD: hash.h,v 1.6 2008/04/28 20:24:10 martin Exp $  */
+
+/*-
+ * Copyright (c) 2001 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Luke Mewburn.
+ *
+ * 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        _SYS_HASH_H_
+#define        _SYS_HASH_H_
+
+#include <sys/types.h>
+#ifdef __HAVE_MACHINE_HASH_H
+#include <machine/hash.h>
+#endif
+
+
+#ifndef __HAVE_HASH32_BUF                      /* not overridden by MD hash */
+
+#define        HASH32_BUF_INIT 5381
+
+/*
+ * uint32_t
+ * hash32_buf(const void *bf, size_t len, uint32_t hash)
+ *     return a 32 bit hash of the binary buffer buf (size len),
+ *     seeded with an initial hash value of hash (usually HASH32_BUF_INIT).
+ */
+static __inline uint32_t
+hash32_buf(const void *bf, size_t len, uint32_t hash)
+{
+       const uint8_t *s = bf;
+
+       while (len-- != 0)                      /* "nemesi": k=257, r=r*257 */
+               hash = hash * 257 + *s++;
+       return (hash * 257);
+}
+#endif /* __HAVE_HASH32_BUF */
+
+
+#ifndef __HAVE_HASH32_STR                      /* not overridden by MD hash */
+
+#define        HASH32_STR_INIT 5381
+/*
+ * uint32_t
+ * hash32_str(const void *bf, uint32_t hash)
+ *     return a 32 bit hash of NUL terminated ASCII string buf,
+ *     seeded with an initial hash value of hash (usually HASH32_STR_INIT).
+ */
+static __inline uint32_t
+hash32_str(const void *bf, uint32_t hash)
+{
+       const uint8_t *s = bf;
+       uint8_t c;
+
+       while ((c = *s++) != 0)
+               hash = hash * 33 + c;           /* "perl": k=33, r=r+r/32 */
+       return (hash + (hash >> 5));
+}
+
+/*
+ * uint32_t
+ * hash32_strn(const void *bf, size_t len, uint32_t hash)
+ *     return a 32 bit hash of NUL terminated ASCII string buf up to
+ *     a maximum of len bytes,
+ *     seeded with an initial hash value of hash (usually HASH32_STR_INIT).
+ */
+static __inline uint32_t
+hash32_strn(const void *bf, size_t len, uint32_t hash)
+{
+       const uint8_t   *s = bf;
+       uint8_t c;
+
+       while ((c = *s++) != 0 && len-- != 0)
+               hash = hash * 33 + c;           /* "perl": k=33, r=r+r/32 */
+       return (hash + (hash >> 5));
+}
+#endif /* __HAVE_HASH32_STR */
+
+
+#endif /* !_SYS_HASH_H_ */
diff --git a/lib/libpuffs/null.c b/lib/libpuffs/null.c
new file mode 100644 (file)
index 0000000..42f8d9f
--- /dev/null
@@ -0,0 +1,654 @@
+/*     $NetBSD: null.c,v 1.30 2011/06/27 12:06:19 manu Exp $   */
+
+/*
+ * Copyright (c) 2007  Antti Kantee.  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 AUTHOR ``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 AUTHOR 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.
+ */
+
+/*
+ * A "nullfs" using puffs, i.e. maps one location in the hierarchy
+ * to another using standard system calls.
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+
+#include <assert.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "puffs.h"
+
+PUFFSOP_PROTOS(puffs_null)
+
+/*
+ * set attributes to what is specified.  XXX: no rollback in case of failure
+ */
+static int
+processvattr(const char *path, const struct vattr *va, int regular)
+{
+       struct timeval tv[2];
+
+       /* XXX: -1 == PUFFS_VNOVAL, but shouldn't trust that */
+       if (va->va_uid != (unsigned)-1 || va->va_gid != (unsigned)-1)
+               if (lchown(path, va->va_uid, va->va_gid) == -1)
+                       return errno;
+
+       if (va->va_mode != (unsigned)PUFFS_VNOVAL)
+               if (lchmod(path, va->va_mode) == -1)
+                       return errno;
+
+       /* sloppy */
+       if (va->va_atime.tv_sec != (time_t)PUFFS_VNOVAL
+           || va->va_mtime.tv_sec != (time_t)PUFFS_VNOVAL) {
+               TIMESPEC_TO_TIMEVAL(&tv[0], &va->va_atime);
+               TIMESPEC_TO_TIMEVAL(&tv[1], &va->va_mtime);
+
+               if (lutimes(path, tv) == -1)
+                       return errno;
+       }
+
+       if (regular && va->va_size != (u_quad_t)PUFFS_VNOVAL)
+               if (truncate(path, (off_t)va->va_size) == -1)
+                       return errno;
+
+       return 0;
+}
+
+/*
+ * Kludge to open files which aren't writable *any longer*.  This kinda
+ * works because the vfs layer does validation checks based on the file's
+ * permissions to allow writable opening before opening them.  However,
+ * the problem arises if we want to create a file, write to it (cache),
+ * adjust permissions and then flush the file.
+ */
+static int
+writeableopen(const char *path)
+{
+       struct stat sb;
+       mode_t origmode;
+       int sverr = 0;
+       int fd;
+
+       fd = open(path, O_WRONLY);
+       if (fd == -1) {
+               if (errno == EACCES) {
+                       if (stat(path, &sb) == -1)
+                               return -1;
+                       origmode = sb.st_mode & ALLPERMS;
+
+                       if (chmod(path, 0200) == -1)
+                               return -1;
+
+                       fd = open(path, O_WRONLY);
+                       if (fd == -1)
+                               sverr = errno;
+
+                       chmod(path, origmode);
+                       if (sverr)
+                               errno = sverr;
+               } else
+                       return -1;
+       }
+
+       return fd;
+}
+
+/*ARGSUSED*/
+static void *
+inodecmp(struct puffs_usermount *pu, struct puffs_node *pn, void *arg)
+{
+       ino_t *cmpino = arg;
+
+       if (pn->pn_va.va_fileid == *cmpino)
+               return pn;
+       return NULL;
+}
+
+static int
+makenode(struct puffs_usermount *pu, struct puffs_newinfo *pni,
+       const struct puffs_cn *pcn, const struct vattr *va, int regular)
+{
+       struct puffs_node *pn;
+       struct stat sb;
+       int rv;
+
+       if ((rv = processvattr(PCNPATH(pcn), va, regular)) != 0)
+               return rv;
+
+       pn = puffs_pn_new(pu, NULL);
+       if (!pn)
+               return ENOMEM;
+       puffs_setvattr(&pn->pn_va, va);
+
+       if (lstat(PCNPATH(pcn), &sb) == -1)
+               return errno;
+       puffs_stat2vattr(&pn->pn_va, &sb);
+
+       puffs_newinfo_setcookie(pni, pn);
+       return 0;
+}
+
+/* This should be called first and overriden from the file system */
+void
+puffs_null_setops(struct puffs_ops *pops)
+{
+
+       PUFFSOP_SET(pops, puffs_null, fs, statvfs);
+       PUFFSOP_SETFSNOP(pops, unmount);
+       PUFFSOP_SETFSNOP(pops, sync);
+       PUFFSOP_SET(pops, puffs_null, fs, fhtonode);
+       PUFFSOP_SET(pops, puffs_null, fs, nodetofh);
+
+       PUFFSOP_SET(pops, puffs_null, node, lookup);
+       PUFFSOP_SET(pops, puffs_null, node, create);
+       PUFFSOP_SET(pops, puffs_null, node, mknod);
+       PUFFSOP_SET(pops, puffs_null, node, getattr);
+       PUFFSOP_SET(pops, puffs_null, node, setattr);
+       PUFFSOP_SET(pops, puffs_null, node, fsync);
+       PUFFSOP_SET(pops, puffs_null, node, remove);
+       PUFFSOP_SET(pops, puffs_null, node, link);
+       PUFFSOP_SET(pops, puffs_null, node, rename);
+       PUFFSOP_SET(pops, puffs_null, node, mkdir);
+       PUFFSOP_SET(pops, puffs_null, node, rmdir);
+       PUFFSOP_SET(pops, puffs_null, node, symlink);
+       PUFFSOP_SET(pops, puffs_null, node, readlink);
+       PUFFSOP_SET(pops, puffs_null, node, readdir);
+       PUFFSOP_SET(pops, puffs_null, node, read);
+       PUFFSOP_SET(pops, puffs_null, node, write);
+       PUFFSOP_SET(pops, puffs_genfs, node, reclaim);
+}
+
+/*ARGSUSED*/
+int
+puffs_null_fs_statvfs(struct puffs_usermount *pu, struct statvfs *svfsb)
+{
+
+       if (statvfs(PNPATH(puffs_getroot(pu)), svfsb) == -1)
+               return errno;
+
+       return 0;
+}
+
+/*ARGSUSED*/
+static void *
+fhcmp(struct puffs_usermount *pu, struct puffs_node *pn, void *arg)
+{
+       struct fid *kf1, *kf2;
+
+       if ((kf1 = pn->pn_data) == NULL)
+               return NULL;
+       kf2 = arg;
+
+       if (kf1->fid_len != kf2->fid_len)
+               return NULL;
+
+       /*LINTED*/
+       if (memcmp(kf1, kf2, kf1->fid_len) == 0)
+               return pn;
+       return NULL;
+}
+
+/*
+ * This routine only supports file handles which have been issued while
+ * the server was alive.  Not really stable ones, that is.
+ */
+/*ARGSUSED*/
+int
+puffs_null_fs_fhtonode(struct puffs_usermount *pu, void *fid, size_t fidsize,
+       struct puffs_newinfo *pni)
+{
+       struct puffs_node *pn_res;
+
+       if (fidsize != sizeof(struct fid))
+               return EINVAL;
+
+       pn_res = puffs_pn_nodewalk(pu, fhcmp, fid);
+       if (pn_res == NULL)
+               return ENOENT;
+
+       puffs_newinfo_setcookie(pni, pn_res);
+       puffs_newinfo_setvtype(pni, pn_res->pn_va.va_type);
+       puffs_newinfo_setsize(pni, (voff_t)pn_res->pn_va.va_size);
+       return 0;
+}
+
+/*ARGSUSED*/
+int
+puffs_null_fs_nodetofh(struct puffs_usermount *pu, puffs_cookie_t opc,
+       void *fid, size_t *fidsize)
+{
+       struct puffs_node *pn = opc;
+       fhandle_t fh;
+       int rv;
+
+       if (*fidsize != sizeof(struct fid))
+               return EINVAL;
+
+       rv = 0;
+       if (getfh(PNPATH(pn), &fh) == -1)
+               rv = errno;
+       if (rv == 0) {
+               *(struct fid *)fid = fh.fh_fid;
+               pn->pn_data = malloc(*fidsize);
+               if (pn->pn_data == NULL)
+                       abort(); /* lazy */
+               memcpy(pn->pn_data, fid, *fidsize);
+       }
+
+       return rv;
+}
+
+int
+puffs_null_node_lookup(struct puffs_usermount *pu, puffs_cookie_t opc,
+       struct puffs_newinfo *pni, const struct puffs_cn *pcn)
+{
+       struct puffs_node *pn = opc, *pn_res;
+       struct stat sb;
+       int rv;
+
+       assert(pn->pn_va.va_type == VDIR);
+
+       /*
+        * Note to whoever is copypasting this: you must first check
+        * if the node is there and only then do nodewalk.  Alternatively
+        * you could make sure that you don't return unlinked/rmdir'd
+        * nodes in some other fashion
+        */
+       rv = lstat(PCNPATH(pcn), &sb);
+       if (rv)
+               return errno;
+
+       /* XXX2: nodewalk is a bit too slow here */
+       pn_res = puffs_pn_nodewalk(pu, inodecmp, &sb.st_ino);
+
+       if (pn_res == NULL) {
+               pn_res = puffs_pn_new(pu, NULL);
+               if (pn_res == NULL)
+                       return ENOMEM;
+               puffs_stat2vattr(&pn_res->pn_va, &sb);
+       }
+
+       puffs_newinfo_setcookie(pni, pn_res);
+       puffs_newinfo_setvtype(pni, pn_res->pn_va.va_type);
+       puffs_newinfo_setsize(pni, (voff_t)pn_res->pn_va.va_size);
+
+       return 0;
+}
+
+int
+puffs_null_node_lookupdotdot(struct puffs_usermount *pu, puffs_cookie_t opc,
+       struct puffs_newinfo *pni, const struct puffs_cn *pcn)
+{
+       return puffs_null_node_lookup(pu, opc, pni, pcn);
+}
+
+/*ARGSUSED*/
+int
+puffs_null_node_create(struct puffs_usermount *pu, puffs_cookie_t opc,
+       struct puffs_newinfo *pni, const struct puffs_cn *pcn,
+       const struct vattr *va)
+{
+       int fd, rv;
+
+       fd = open(PCNPATH(pcn), O_RDWR | O_CREAT | O_TRUNC);
+       if (fd == -1)
+               return errno;
+       close(fd);
+
+       rv = makenode(pu, pni, pcn, va, 1);
+       if (rv)
+               unlink(PCNPATH(pcn));
+       return rv;
+}
+
+/*ARGSUSED*/
+int
+puffs_null_node_mknod(struct puffs_usermount *pu, puffs_cookie_t opc,
+       struct puffs_newinfo *pni, const struct puffs_cn *pcn,
+       const struct vattr *va)
+{
+       mode_t mode;
+       int rv;
+
+       mode = puffs_addvtype2mode(va->va_mode, va->va_type);
+       switch (va->va_type) {
+       case VFIFO:
+               if (mkfifo(PCNPATH(pcn), mode) == -1)
+                       return errno;
+               break;
+       case VCHR:
+       case VBLK:
+               return ENOTSUP;
+       default:
+               return EINVAL;
+       }
+
+       rv = makenode(pu, pni, pcn, va, 0);
+       if (rv)
+               unlink(PCNPATH(pcn));
+       return rv;
+}
+
+/*ARGSUSED*/
+int
+puffs_null_node_getattr(struct puffs_usermount *pu, puffs_cookie_t opc,
+       struct vattr *va, const struct puffs_cred *pcred)
+{
+       struct puffs_node *pn = opc;
+       struct stat sb;
+
+       if (lstat(PNPATH(pn), &sb) == -1)
+               return errno;
+       puffs_stat2vattr(va, &sb);
+
+       return 0;
+}
+
+/*ARGSUSED*/
+int
+puffs_null_node_setattr(struct puffs_usermount *pu, puffs_cookie_t opc,
+       const struct vattr *va, const struct puffs_cred *pcred)
+{
+       struct puffs_node *pn = opc;
+       int rv;
+
+       rv = processvattr(PNPATH(pn), va, pn->pn_va.va_type == VREG);
+       if (rv)
+               return rv;
+
+       puffs_setvattr(&pn->pn_va, va);
+
+       return 0;
+}
+
+/*ARGSUSED*/
+int
+puffs_null_node_fsync(struct puffs_usermount *pu, puffs_cookie_t opc,
+       int how)
+{
+       struct puffs_node *pn = opc;
+       int fd, rv;
+       struct stat sb;
+
+       rv = 0;
+       if (stat(PNPATH(pn), &sb) == -1)
+               return errno;
+       if (S_ISDIR(sb.st_mode)) {
+               DIR *dirp;
+               if ((dirp = opendir(PNPATH(pn))) == 0)
+                       return errno;
+               fd = dirfd(dirp);
+               if (fd == -1)
+                       return errno;
+       } else {
+               fd = writeableopen(PNPATH(pn));
+               if (fd == -1)
+                       return errno;
+       }
+
+       if (fsync(fd) == -1)
+               rv = errno;
+
+       close(fd);
+
+       return rv;
+}
+
+/*ARGSUSED*/
+int
+puffs_null_node_remove(struct puffs_usermount *pu, puffs_cookie_t opc,
+       puffs_cookie_t targ, const struct puffs_cn *pcn)
+{
+       struct puffs_node *pn_targ = targ;
+
+       if (unlink(PNPATH(pn_targ)) == -1)
+               return errno;
+       puffs_pn_remove(pn_targ);
+
+       return 0;
+}
+
+/*ARGSUSED*/
+int
+puffs_null_node_link(struct puffs_usermount *pu, puffs_cookie_t opc,
+       puffs_cookie_t targ, const struct puffs_cn *pcn)
+{
+       struct puffs_node *pn_targ = targ;
+
+       if (link(PNPATH(pn_targ), PCNPATH(pcn)) == -1)
+               return errno;
+
+       return 0;
+}
+
+/*ARGSUSED*/
+int
+puffs_null_node_rename(struct puffs_usermount *pu, puffs_cookie_t opc,
+       puffs_cookie_t src, const struct puffs_cn *pcn_src,
+       puffs_cookie_t targ_dir, puffs_cookie_t targ,
+       const struct puffs_cn *pcn_targ)
+{
+
+       if (rename(PCNPATH(pcn_src), PCNPATH(pcn_targ)) == -1)
+               return errno;
+
+       return 0;
+}
+
+/*ARGSUSED*/
+int
+puffs_null_node_mkdir(struct puffs_usermount *pu, puffs_cookie_t opc,
+       struct puffs_newinfo *pni, const struct puffs_cn *pcn,
+       const struct vattr *va)
+{
+       int rv;
+
+       if (mkdir(PCNPATH(pcn), va->va_mode) == -1)
+               return errno;
+
+       rv = makenode(pu, pni, pcn, va, 0);
+       if (rv)
+               rmdir(PCNPATH(pcn));
+       return rv;
+}
+
+/*ARGSUSED*/
+int
+puffs_null_node_rmdir(struct puffs_usermount *pu, puffs_cookie_t opc,
+       puffs_cookie_t targ, const struct puffs_cn *pcn)
+{
+       struct puffs_node *pn_targ = targ;
+
+       if (rmdir(PNPATH(pn_targ)) == -1)
+               return errno;
+       puffs_pn_remove(pn_targ);
+
+       return 0;
+}
+
+/*ARGSUSED*/
+int
+puffs_null_node_symlink(struct puffs_usermount *pu, puffs_cookie_t opc,
+       struct puffs_newinfo *pni, const struct puffs_cn *pcn,
+       const struct vattr *va, const char *linkname)
+{
+       int rv;
+
+       if (symlink(linkname, PCNPATH(pcn)) == -1)
+               return errno;
+
+       rv = makenode(pu, pni, pcn, va, 0);
+       if (rv)
+               unlink(PCNPATH(pcn));
+       return rv;
+}
+
+/*ARGSUSED*/
+int
+puffs_null_node_readlink(struct puffs_usermount *pu, puffs_cookie_t opc,
+       const struct puffs_cred *pcred, char *linkname, size_t *linklen)
+{
+       struct puffs_node *pn = opc;
+       ssize_t rv;
+
+       rv = readlink(PNPATH(pn), linkname, *linklen);
+       if (rv == -1)
+               return errno;
+
+       *linklen = rv;
+       return 0;
+}
+
+/*ARGSUSED*/
+int
+puffs_null_node_readdir(struct puffs_usermount *pu, puffs_cookie_t opc,
+       struct dirent *de, off_t *off, size_t *reslen,
+       const struct puffs_cred *pcred, int *eofflag, off_t *cookies,
+       size_t *ncookies)
+{
+       struct puffs_node *pn = opc;
+       struct dirent entry, *result;
+       DIR *dp;
+       off_t i;
+       int rv;
+
+       *ncookies = 0;
+       dp = opendir(PNPATH(pn));
+       if (dp == NULL)
+               return errno;
+
+       rv = 0;
+       i = *off;
+
+       /*
+        * XXX: need to do trickery here, telldir/seekdir would be nice, but
+        * then we'd need to keep state, which I'm too lazy to keep
+        */
+       while (i--) {
+               rv = readdir_r(dp, &entry, &result);
+               if (rv != 0)
+                       goto out;
+
+               if (!result) {
+                       *eofflag = 1;
+                       goto out;
+               }
+       }
+
+       for (;;) {
+               rv = readdir_r(dp, &entry, &result);
+               if (rv != 0)
+                       goto out;
+
+               if (!result) {
+                       *eofflag = 1;
+                       goto out;
+               }
+
+               if (_DIRENT_DIRSIZ(result) > *reslen)
+                       goto out;
+
+               *de = *result;
+               *reslen -= _DIRENT_DIRSIZ(result);
+               de = _DIRENT_NEXT(de);
+
+               (*off)++;
+               PUFFS_STORE_DCOOKIE(cookies, ncookies, *off);
+       }
+
+ out:
+       closedir(dp);
+       return 0;
+}
+
+/*ARGSUSED*/
+int
+puffs_null_node_read(struct puffs_usermount *pu, puffs_cookie_t opc,
+       uint8_t *buf, off_t offset, size_t *buflen,
+       const struct puffs_cred *pcred, int ioflag)
+{
+       struct puffs_node *pn = opc;
+       ssize_t n;
+       off_t off;
+       int fd, rv;
+
+       rv = 0;
+       fd = open(PNPATH(pn), O_RDONLY);
+       if (fd == -1)
+               return errno;
+       off = lseek(fd, offset, SEEK_SET);
+       if (off == -1) {
+               rv = errno;
+               goto out;
+       }
+
+       n = read(fd, buf, *buflen);
+       if (n == -1)
+               rv = errno;
+       else
+               *buflen -= n;
+
+ out:
+       close(fd);
+       return rv;
+}
+
+/*ARGSUSED*/
+int
+puffs_null_node_write(struct puffs_usermount *pu, puffs_cookie_t opc,
+       uint8_t *buf, off_t offset, size_t *buflen,
+       const struct puffs_cred *pcred, int ioflag)
+{
+       struct puffs_node *pn = opc;
+       ssize_t n;
+       off_t off;
+       int fd, rv;
+
+       rv = 0;
+       fd = writeableopen(PNPATH(pn));
+       if (fd == -1)
+               return errno;
+
+       off = lseek(fd, offset, SEEK_SET);
+       if (off == -1) {
+               rv = errno;
+               goto out;
+       }
+
+       n = write(fd, buf, *buflen);
+       if (n == -1)
+               rv = errno;
+       else
+               *buflen -= n;
+
+ out:
+       close(fd);
+       return rv;
+}
diff --git a/lib/libpuffs/opdump.c b/lib/libpuffs/opdump.c
new file mode 100644 (file)
index 0000000..b21e29a
--- /dev/null
@@ -0,0 +1,515 @@
+/*     $NetBSD: opdump.c,v 1.35 2010/08/20 16:35:05 pooka Exp $        */
+
+/*
+ * Copyright (c) 2005, 2006  Antti Kantee.  All Rights Reserved.
+ *
+ * Development of this software was supported by the
+ * Google Summer of Code program and the Ulla Tuominen Foundation.
+ * The Google SoC project was mentored by Bill Studenmund.
+ *
+ * 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 AUTHOR ``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 AUTHOR 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.
+ */
+
+/* Pretty-printing helper routines for VFS/VOP request contents */
+
+/* yes, this is pretty much a mess */
+
+#include <sys/types.h>
+#include <sys/namei.h>
+#include <sys/time.h>
+
+#include <inttypes.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdint.h>
+
+#include "puffs.h"
+#include "puffsdump.h"
+#include "puffs_priv.h"
+
+#ifndef __arraycount
+#define __arraycount(__x)      (sizeof(__x) / sizeof(__x[0]))
+#endif
+
+#define DINT "    "
+
+const char *puffsdump_vfsop_revmap[] = {
+       "PUFFS_VFS_MOUNT",
+       "PUFFS_VFS_START",
+       "PUFFS_VFS_UNMOUNT",
+       "PUFFS_VFS_ROOT",
+       "PUFFS_VFS_QUOTACTL",
+       "PUFFS_VFS_STATVFS",
+       "PUFFS_VFS_SYNC",
+       "PUFFS_VFS_VGET",
+       "PUFFS_VFS_FHTOVP",
+       "PUFFS_VFS_VPTOFH",
+       "PUFFS_VFS_INIT",
+       "PUFFS_VFS_DONE",
+       "PUFFS_VFS_SNAPSHOT",
+       "PUFFS_VFS_EXTATTRCTL",
+       "PUFFS_VFS_SUSPEND"
+};
+size_t puffsdump_vfsop_count = __arraycount(puffsdump_vfsop_revmap);
+
+const char *puffsdump_vnop_revmap[] = {
+       "PUFFS_VN_LOOKUP",
+       "PUFFS_VN_CREATE",
+       "PUFFS_VN_MKNOD",
+       "PUFFS_VN_OPEN",
+       "PUFFS_VN_CLOSE",
+       "PUFFS_VN_ACCESS",
+       "PUFFS_VN_GETATTR",
+       "PUFFS_VN_SETATTR",
+       "PUFFS_VN_READ",
+       "PUFFS_VN_WRITE",
+       "PUFFS_VN_IOCTL",
+       "PUFFS_VN_FCNTL",
+       "PUFFS_VN_POLL",
+       "PUFFS_VN_KQFILTER",
+       "PUFFS_VN_REVOKE",
+       "PUFFS_VN_MMAP",
+       "PUFFS_VN_FSYNC",
+       "PUFFS_VN_SEEK",
+       "PUFFS_VN_REMOVE",
+       "PUFFS_VN_LINK",
+       "PUFFS_VN_RENAME",
+       "PUFFS_VN_MKDIR",
+       "PUFFS_VN_RMDIR",
+       "PUFFS_VN_SYMLINK",
+       "PUFFS_VN_READDIR",
+       "PUFFS_VN_READLINK",
+       "PUFFS_VN_ABORTOP",
+       "PUFFS_VN_INACTIVE",
+       "PUFFS_VN_RECLAIM",
+       "PUFFS_VN_LOCK",
+       "PUFFS_VN_UNLOCK",
+       "PUFFS_VN_BMAP",
+       "PUFFS_VN_STRATEGY",
+       "PUFFS_VN_PRINT",
+       "PUFFS_VN_ISLOCKED",
+       "PUFFS_VN_PATHCONF",
+       "PUFFS_VN_ADVLOCK",
+       "PUFFS_VN_LEASE",
+       "PUFFS_VN_WHITEOUT",
+       "PUFFS_VN_GETPAGES",
+       "PUFFS_VN_PUTPAGES",
+       "PUFFS_VN_GETEXTATTR",
+       "PUFFS_VN_LISTEXTATTR",
+       "PUFFS_VN_OPENEXTATTR",
+       "PUFFS_VN_DELETEEXTATTR",
+       "PUFFS_VN_SETEXTATTR",
+       "PUFFS_VN_CLOSEEXTATTR",
+};
+size_t puffsdump_vnop_count = __arraycount(puffsdump_vnop_revmap);
+
+/* XXX! */
+const char *puffsdump_cacheop_revmap[] = {
+       "PUFFS_CACHE_WRITE"
+};
+
+const char *puffsdump_errnot_revmap[] = {
+       "PUFFS_ERR_ERROR",
+       "PUFFS_ERR_MAKENODE",
+       "PUFFS_ERR_LOOKUP",
+       "PUFFS_ERR_READDIR",
+       "PUFFS_ERR_READLINK",
+       "PUFFS_ERR_READ",
+       "PUFFS_ERR_WRITE",
+       "PUFFS_ERR_VPTOFH",
+       "PUFFS_ERR_GETEXTATTR",
+       "PUFFS_ERR_LISTEXTATTR",
+};
+size_t puffsdump_errnot_count = __arraycount(puffsdump_errnot_revmap);
+
+const char *puffsdump_flush_revmap[] = {
+       "PUFFS_INVAL_NAMECACHE_NODE",
+       "PUFFS_INVAL_NAMECACHE_DIR",
+       "PUFFS_INVAL_NAMECACHE_ALL",
+       "PUFFS_INVAL_PAGECACHE_NODE_RANGE",
+       "PUFFS_FLUSH_PAGECACHE_NODE_RANGE",
+};
+size_t puffsdump_flush_count = __arraycount(puffsdump_flush_revmap);
+
+static void
+mydprintf(const char *fmt, ...)
+{
+       va_list ap;
+
+       va_start(ap, fmt);
+       vfprintf(stderr, fmt, ap);
+       va_end(ap);
+}
+
+void
+puffsdump_req(struct puffs_req *preq)
+{
+       char buf[128];
+       static struct timeval tv_prev;
+       struct timeval tv_now, tv;
+       const char **map;
+       const char *optype;
+       size_t maxhandle;
+       int opclass, isvn = 0;
+
+       mydprintf("reqid: %" PRIu64 ", ", preq->preq_id);
+       opclass = PUFFSOP_OPCLASS(preq->preq_opclass);
+       switch (opclass) {
+       case PUFFSOP_VFS:
+               map = puffsdump_vfsop_revmap;
+               maxhandle = puffsdump_vfsop_count;
+               break;
+       case PUFFSOP_VN:
+               map = puffsdump_vnop_revmap;
+               maxhandle = puffsdump_vnop_count;
+               isvn = 1;
+               break;
+       case PUFFSOP_CACHE:
+               map = puffsdump_cacheop_revmap;
+               maxhandle = __arraycount(puffsdump_cacheop_revmap);
+               break;
+       case PUFFSOP_ERROR:
+               map = puffsdump_errnot_revmap;
+               maxhandle = puffsdump_errnot_count;
+               break;
+       case PUFFSOP_FLUSH:
+               map = puffsdump_flush_revmap;
+               maxhandle = puffsdump_flush_count;
+               break;
+       default:
+               mydprintf("unhandled opclass %d\n", opclass);
+               return;
+       }
+
+       if (preq->preq_optype < maxhandle) {
+               optype = map[preq->preq_optype];
+       } else {
+               snprintf(buf, sizeof(buf), "UNKNOWN (%d)", preq->preq_optype);
+               optype = buf;
+       }
+
+       mydprintf("opclass %d%s, optype: %s, "
+           "cookie: %p,\n" DINT "aux: %p, auxlen: %zu, pid: %d, lwpid: %d\n",
+           opclass, PUFFSOP_WANTREPLY(preq->preq_opclass) ? "" : " (FAF)",
+           optype, preq->preq_cookie,
+           preq->preq_buf, preq->preq_buflen,
+           preq->preq_pid, preq->preq_lid);
+
+       if (isvn) {
+               switch (preq->preq_optype) {
+               case PUFFS_VN_LOOKUP:
+                       puffsdump_lookup(preq);
+                       break;
+               case PUFFS_VN_READ:
+               case PUFFS_VN_WRITE:
+                       puffsdump_readwrite(preq);
+                       break;
+               case PUFFS_VN_OPEN:
+                       puffsdump_open(preq);
+                       break;
+               case PUFFS_VN_REMOVE:
+               case PUFFS_VN_RMDIR:
+               case PUFFS_VN_LINK:
+                       puffsdump_targ(preq);
+                       break;
+               case PUFFS_VN_READDIR:
+                       puffsdump_readdir(preq);
+                       break;
+               case PUFFS_VN_CREATE:
+               case PUFFS_VN_MKDIR:
+               case PUFFS_VN_MKNOD:
+               case PUFFS_VN_SYMLINK:
+                       puffsdump_create(preq);
+                       break;
+               case PUFFS_VN_SETATTR:
+                       puffsdump_attr(preq);
+                       break;
+               default:
+                       break;
+               }
+       }
+
+       PU_LOCK();
+       gettimeofday(&tv_now, NULL);
+       timersub(&tv_now, &tv_prev, &tv);
+       mydprintf(DINT "since previous call: %lld.%06ld\n",
+           (long long)tv.tv_sec, (long)tv.tv_usec);
+       gettimeofday(&tv_prev, NULL);
+       PU_UNLOCK();
+}
+
+void
+puffsdump_rv(struct puffs_req *preq)
+{
+
+       if (PUFFSOP_OPCLASS(preq->preq_opclass) == PUFFSOP_VN) {
+               switch (preq->preq_optype) {
+               case PUFFS_VN_LOOKUP:
+                       puffsdump_lookup_rv(preq);
+                       break;
+               case PUFFS_VN_CREATE:
+               case PUFFS_VN_MKDIR:
+               case PUFFS_VN_MKNOD:
+               case PUFFS_VN_SYMLINK:
+                       puffsdump_create_rv(preq);
+                       break;
+               case PUFFS_VN_READ:
+               case PUFFS_VN_WRITE:
+                       puffsdump_readwrite_rv(preq);
+                       break;
+               case PUFFS_VN_READDIR:
+                       puffsdump_readdir_rv(preq);
+                       break;
+               case PUFFS_VN_GETATTR:
+                       puffsdump_attr(preq);
+                       break;
+               default:
+                       break;
+               }
+       }
+
+       mydprintf("RV reqid: %" PRIu64 ", result: %d %s\n",
+           preq->preq_id, preq->preq_rv,
+           preq->preq_rv ? strerror(preq->preq_rv) : "");
+}
+
+/*
+ * Slightly tedious print-routine so that we get a nice NOVAL instead
+ * of some tedious output representations for -1, especially (uint64_t)-1
+ *
+ * We use typecasting to make this work beyond time_t/dev_t size changes.
+ */
+static void
+dumpattr(struct vattr *vap)
+{
+       char buf[128];
+
+/* XXX: better readability.  and this is debug, so no cycle-sweat */
+#define DEFAULTBUF() snprintf(buf, sizeof(buf), "NOVAL")
+
+       mydprintf(DINT "vattr:\n");
+       mydprintf(DINT DINT "type: %d, ", vap->va_type);
+
+       DEFAULTBUF();
+       if (vap->va_mode != (mode_t)PUFFS_VNOVAL)
+               snprintf(buf, sizeof(buf), "0%o", vap->va_mode);
+       mydprintf("mode: %s, ", buf);
+
+       DEFAULTBUF();
+       if (vap->va_nlink != (nlink_t)PUFFS_VNOVAL)
+               snprintf(buf, sizeof(buf), "%ju", (uintmax_t)vap->va_nlink);
+       mydprintf("nlink: %s, ", buf);
+
+       DEFAULTBUF();
+       if (vap->va_uid != (uid_t)PUFFS_VNOVAL)
+               snprintf(buf, sizeof(buf), "%d", vap->va_uid);
+       mydprintf("uid: %s, ", buf);
+
+       DEFAULTBUF();
+       if (vap->va_gid != (gid_t)PUFFS_VNOVAL)
+               snprintf(buf, sizeof(buf), "%d", vap->va_gid);
+       mydprintf("gid: %s\n", buf);
+
+       DEFAULTBUF();
+       if ((unsigned long long)vap->va_fsid!=(unsigned long long)PUFFS_VNOVAL)
+               snprintf(buf, sizeof(buf), "0x%llx",
+                   (unsigned long long)vap->va_fsid);
+       mydprintf(DINT DINT "fsid: %s, ", buf);
+
+       DEFAULTBUF();
+       if (vap->va_fileid != (ino_t)PUFFS_VNOVAL)
+               snprintf(buf, sizeof(buf), "%" PRIu64, vap->va_fileid);
+       mydprintf("ino: %s, ", buf);
+
+       DEFAULTBUF();
+       if (vap->va_size != (u_quad_t)PUFFS_VNOVAL)
+               snprintf(buf, sizeof(buf), "%" PRIu64, vap->va_size);
+       mydprintf("size: %s, ", buf);
+
+       DEFAULTBUF();
+       if (vap->va_blocksize != (long)PUFFS_VNOVAL)
+               snprintf(buf, sizeof(buf), "%ld", vap->va_blocksize);
+       mydprintf("bsize: %s\n", buf);
+
+       DEFAULTBUF();
+       if (vap->va_atime.tv_sec != (time_t)PUFFS_VNOVAL)
+               snprintf(buf, sizeof(buf), "%lld",
+                   (long long)vap->va_atime.tv_sec);
+       mydprintf(DINT DINT "a.s: %s, ", buf);
+
+       DEFAULTBUF();
+       if (vap->va_atime.tv_nsec != (long)PUFFS_VNOVAL)
+               snprintf(buf, sizeof(buf), "%ld", vap->va_atime.tv_nsec);
+       mydprintf("a.ns: %s, ", buf);
+
+       DEFAULTBUF();
+       if (vap->va_mtime.tv_sec != (time_t)PUFFS_VNOVAL)
+               snprintf(buf, sizeof(buf), "%lld",
+                   (long long)vap->va_mtime.tv_sec);
+       mydprintf("m.s: %s, ", buf);
+
+       DEFAULTBUF();
+       if (vap->va_mtime.tv_nsec != (long)PUFFS_VNOVAL)
+               snprintf(buf, sizeof(buf), "%ld", vap->va_mtime.tv_nsec);
+       mydprintf("m.ns: %s\n", buf);
+
+       DEFAULTBUF();
+       if (vap->va_ctime.tv_sec != (time_t)PUFFS_VNOVAL)
+               snprintf(buf, sizeof(buf), "%lld",
+                   (long long)vap->va_ctime.tv_sec);
+       mydprintf(DINT DINT "c.s: %s, ", buf);
+
+       DEFAULTBUF();
+       if (vap->va_ctime.tv_nsec != (long)PUFFS_VNOVAL)
+               snprintf(buf, sizeof(buf), "%ld", vap->va_ctime.tv_nsec);
+       mydprintf("c.ns: %s, ", buf);
+
+       DEFAULTBUF();
+       if (vap->va_gen != (u_long)PUFFS_VNOVAL)
+               snprintf(buf, sizeof(buf), "%ju", (uintmax_t)vap->va_gen);
+       mydprintf(DINT DINT "gen: %s, ", buf);
+
+       DEFAULTBUF();
+       if (vap->va_flags != (u_long)PUFFS_VNOVAL)
+               snprintf(buf, sizeof(buf), "0x%lx", vap->va_flags);
+       mydprintf("flags: %s, ", buf);
+
+       DEFAULTBUF();
+       if (vap->va_bytes != (u_quad_t)PUFFS_VNOVAL)
+               snprintf(buf, sizeof(buf), "%" PRIu64, vap->va_bytes);
+       mydprintf(DINT DINT "bytes: %s, ", buf);
+
+       snprintf(buf, sizeof(buf), "%" PRIu64, vap->va_filerev);
+       mydprintf("filerev: %s, ", buf);
+
+       snprintf(buf, sizeof(buf), "0x%x", vap->va_vaflags);
+       mydprintf("vaflags: %s\n", buf);
+}
+
+void
+puffsdump_cookie(puffs_cookie_t c, const char *cookiename)
+{
+
+       mydprintf("%scookie: at %p\n", cookiename, c);
+}
+
+void
+puffsdump_cn(struct puffs_kcn *pkcn)
+{
+
+       mydprintf(DINT "puffs_cn: \"%s\", len %zu\n",
+           pkcn->pkcn_name, pkcn->pkcn_namelen);
+}
+
+void
+puffsdump_lookup(struct puffs_req *preq)
+{
+       struct puffs_vnmsg_lookup *lookup_msg = (void *)preq;
+
+       puffsdump_cn(&lookup_msg->pvnr_cn);
+}
+
+void
+puffsdump_lookup_rv(struct puffs_req *preq)
+{
+       struct puffs_vnmsg_lookup *lookup_msg = (void *)preq;
+
+       mydprintf(DINT "new %p, type 0x%x, size 0x%"PRIu64", dev 0x%llx\n",
+           lookup_msg->pvnr_newnode, lookup_msg->pvnr_vtype,
+           lookup_msg->pvnr_size, (unsigned long long)lookup_msg->pvnr_rdev);
+}
+
+void
+puffsdump_create(struct puffs_req *preq)
+{
+       /* XXX: wrong type, but we know it fits the slot */
+       struct puffs_vnmsg_create *create_msg = (void *)preq;
+
+       dumpattr(&create_msg->pvnr_va);
+}
+
+void
+puffsdump_create_rv(struct puffs_req *preq)
+{
+       /* XXX: wrong type, but we know it fits the slot */
+       struct puffs_vnmsg_create *create_msg = (void *)preq;
+
+       mydprintf(DINT "new %p\n", create_msg->pvnr_newnode);
+}
+
+void
+puffsdump_readwrite(struct puffs_req *preq)
+{
+       struct puffs_vnmsg_rw *rw_msg = (void *)preq;
+
+       mydprintf(DINT "offset: %" PRId64 ", resid %zu, ioflag 0x%x\n",
+           rw_msg->pvnr_offset, rw_msg->pvnr_resid, rw_msg->pvnr_ioflag);
+}
+
+void
+puffsdump_readwrite_rv(struct puffs_req *preq)
+{
+       struct puffs_vnmsg_rw *rw_msg = (void *)preq;
+
+       mydprintf(DINT "resid after op: %zu\n", rw_msg->pvnr_resid);
+}
+
+void
+puffsdump_readdir_rv(struct puffs_req *preq)
+{
+       struct puffs_vnmsg_readdir *readdir_msg = (void *)preq;
+
+       mydprintf(DINT "resid after op: %zu, eofflag %d\n",
+           readdir_msg->pvnr_resid, readdir_msg->pvnr_eofflag);
+}
+
+void
+puffsdump_open(struct puffs_req *preq)
+{
+       struct puffs_vnmsg_open *open_msg = (void *)preq;
+
+       mydprintf(DINT "mode: 0x%x\n", open_msg->pvnr_mode);
+}
+
+void
+puffsdump_targ(struct puffs_req *preq)
+{
+       struct puffs_vnmsg_remove *remove_msg = (void *)preq; /* XXX! */
+
+       mydprintf(DINT "target cookie: %p\n", remove_msg->pvnr_cookie_targ);
+}
+
+void
+puffsdump_readdir(struct puffs_req *preq)
+{
+       struct puffs_vnmsg_readdir *readdir_msg = (void *)preq;
+
+       mydprintf(DINT "read offset: %" PRId64 "\n", readdir_msg->pvnr_offset);
+}
+
+void
+puffsdump_attr(struct puffs_req *preq)
+{
+       struct puffs_vnmsg_setgetattr *attr_msg = (void *)preq;
+
+       dumpattr(&attr_msg->pvnr_va);
+}
diff --git a/lib/libpuffs/paths.c b/lib/libpuffs/paths.c
new file mode 100644 (file)
index 0000000..18fc67a
--- /dev/null
@@ -0,0 +1,293 @@
+/*     $NetBSD: paths.c,v 1.8 2008/08/12 19:44:39 pooka Exp $  */
+
+/*
+ * Copyright (c) 2007  Antti Kantee.  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 AUTHOR ``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 AUTHOR 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 <assert.h>
+#include <errno.h>
+#include <stdlib.h>
+
+#include "puffs.h"
+#include "puffs_priv.h"
+#include "hash.h"
+
+/*
+ * Generic routines for pathbuilding code
+ */
+
+int
+puffs_path_pcnbuild(struct puffs_usermount *pu, struct puffs_cn *pcn,
+       puffs_cookie_t parent)
+{
+       struct puffs_node *pn_parent = PU_CMAP(pu, parent);
+       struct puffs_cn pcn_orig;
+       struct puffs_pathobj po;
+       int rv;
+
+       assert(pn_parent->pn_po.po_path != NULL);
+       assert(pu->pu_flags & PUFFS_FLAG_BUILDPATH);
+
+       if (pu->pu_pathtransform) {
+               rv = pu->pu_pathtransform(pu, &pn_parent->pn_po, pcn, &po);
+               if (rv)
+                       return rv;
+       } else {
+               po.po_path = pcn->pcn_name;
+               po.po_len = pcn->pcn_namelen;
+       }
+
+       if (pu->pu_namemod) {
+               /* XXX: gcc complains if I do assignment */
+               memcpy(&pcn_orig, pcn, sizeof(pcn_orig));
+               rv = pu->pu_namemod(pu, &pn_parent->pn_po, pcn);
+               if (rv)
+                       return rv;
+       }
+
+       rv = pu->pu_pathbuild(pu, &pn_parent->pn_po, &po, 0,
+           &pcn->pcn_po_full);
+       puffs_path_buildhash(pu, &pcn->pcn_po_full);
+
+       if (pu->pu_pathtransform)
+               pu->pu_pathfree(pu, &po);
+
+       if (pu->pu_namemod && rv)
+               *pcn = pcn_orig;
+
+       return rv;
+}
+
+/*
+ * substitute all (child) patch prefixes.  called from nodewalk, which
+ * in turn is called from rename
+ */
+void *
+puffs_path_prefixadj(struct puffs_usermount *pu, struct puffs_node *pn,
+       void *arg)
+{
+       struct puffs_pathinfo *pi = arg;
+       struct puffs_pathobj localpo;
+       struct puffs_pathobj oldpo;
+       int rv;
+
+       /* can't be a path prefix */
+       if (pn->pn_po.po_len < pi->pi_old->po_len)
+               return NULL;
+
+       if (pu->pu_pathcmp(pu, &pn->pn_po, pi->pi_old, pi->pi_old->po_len, 1))
+               return NULL;
+
+       /* otherwise we'd have two nodes with an equal path */
+       assert(pn->pn_po.po_len > pi->pi_old->po_len);
+
+       /* found a matching prefix */
+       rv = pu->pu_pathbuild(pu, pi->pi_new, &pn->pn_po,
+           pi->pi_old->po_len, &localpo);
+       /*
+        * XXX: technically we shouldn't fail, but this is the only
+        * sensible thing to do here.  If the buildpath routine fails,
+        * we will have paths in an inconsistent state.  Should fix this,
+        * either by having two separate passes or by doing other tricks
+        * to make an invalid path with BUILDPATHS acceptable.
+        */
+       if (rv != 0)
+               abort();
+
+       /* adjust hash sum */
+       puffs_path_buildhash(pu, &localpo);
+
+       /* out with the old and in with the new */
+       oldpo = pn->pn_po;
+       pn->pn_po = localpo;
+       pu->pu_pathfree(pu, &oldpo);
+
+       /* continue the walk */
+       return NULL;
+}
+
+/*
+ * called from nodewalk, checks for exact match
+ */
+void *
+puffs_path_walkcmp(struct puffs_usermount *pu, struct puffs_node *pn, void *arg)
+{
+       struct puffs_pathobj *po = arg;
+       struct puffs_pathobj po2;
+
+       if (po->po_len != PNPLEN(pn))
+               return NULL;
+
+       /*
+        * If hashing and the hash doesn't match, we know this is
+        * definitely not a match.  Otherwise check for collisions.
+        */
+       if (pu->pu_flags & PUFFS_FLAG_HASHPATH)
+               if (pn->pn_po.po_hash != po->po_hash)
+                       return NULL;
+
+       po2.po_path = PNPATH(pn);
+       po2.po_len = PNPLEN(pn);
+
+       if (pu->pu_pathcmp(pu, po, &po2, PNPLEN(pn), 0) == 0)
+               return pn;
+       return NULL;
+}
+
+/*
+ * Hash sum building routine.  Use string hash if the buildpath routine
+ * is the standard one, otherwise use binary hashes.  A bit whimsical
+ * way to choose the routine, but the binary works for strings also,
+ * so don't sweat it.
+ */
+void
+puffs_path_buildhash(struct puffs_usermount *pu, struct puffs_pathobj *po)
+{
+
+       if ((pu->pu_flags & PUFFS_FLAG_HASHPATH) == 0)
+               return;
+
+       if (pu->pu_pathbuild == puffs_stdpath_buildpath)
+               po->po_hash = hash32_strn(po->po_path, po->po_len,
+                   HASH32_STR_INIT);
+       else
+               po->po_hash = hash32_buf(po->po_path, po->po_len,
+                   HASH32_BUF_INIT);
+}
+
+/*
+ * Routines provided to file systems which consider a path a tuple of
+ * strings and / the component separator.
+ */
+
+/*ARGSUSED*/
+int
+puffs_stdpath_cmppath(struct puffs_usermount *pu, struct puffs_pathobj *c1,
+       struct puffs_pathobj *c2, size_t clen, int checkprefix)
+{
+       char *p;
+       int rv;
+
+       rv = strncmp(c1->po_path, c2->po_path, clen);
+       if (rv)
+               return 1;
+
+       if (checkprefix == 0)
+               return 0;
+
+       /* sanity for next step */
+       if (!(c1->po_len > c2->po_len))
+               return 1;
+
+       /* check if it's really a complete path prefix */
+       p = c1->po_path;
+       if ((*(p + clen)) != '/')
+               return 1;
+
+       return 0;
+}
+
+/*ARGSUSED*/
+int
+puffs_stdpath_buildpath(struct puffs_usermount *pu,
+       const struct puffs_pathobj *po_pre, const struct puffs_pathobj *po_comp,
+       size_t offset, struct puffs_pathobj *newpath)
+{
+       char *path, *pcomp;
+       size_t plen, complen;
+       size_t prelen;
+       int isdotdot;
+
+       complen = po_comp->po_len - offset;
+
+       /* seek to correct place & remove all leading '/' from component */
+       pcomp = po_comp->po_path;
+       pcomp += offset;
+       while (*pcomp == '/') {
+               pcomp++;
+               complen--;
+       }
+
+       /* todotdot or nottodotdot */
+       if (complen == 2 && strcmp(pcomp, "..") == 0)
+               isdotdot = 1;
+       else
+               isdotdot = 0;
+
+       /*
+        * Strip trailing components from the preceending component.
+        * This is an issue only for the root node, which we might want
+        * to be at path "/" for some file systems.
+        */
+       prelen = po_pre->po_len;
+       while (prelen > 0 && *((char *)po_pre->po_path + (prelen-1)) == '/') {
+               assert(isdotdot == 0);
+               prelen--;
+       }
+
+       if (isdotdot) {
+               char *slash; /* sweet char of mine */
+
+               slash = strrchr(po_pre->po_path, '/');
+               assert(slash != NULL);
+
+               plen = slash - (char *)po_pre->po_path;
+
+               /*
+                * As the converse to not stripping the initial "/" above,
+                * don't nuke it here either.
+                */
+               if (plen == 0)
+                       plen++;
+
+               path = malloc(plen + 1);
+               if (path == NULL)
+                       return errno;
+
+               strlcpy(path, po_pre->po_path, plen+1);
+       } else {
+               /* + '/' + '\0' */
+               plen = prelen + 1 + complen;
+               path = malloc(plen + 1);
+               if (path == NULL)
+                       return errno;
+
+               strlcpy(path, po_pre->po_path, prelen+1);
+               strcat(path, "/");
+               strncat(path, pcomp, complen);
+       }
+
+       newpath->po_path = path;
+       newpath->po_len = plen;
+
+       return 0;
+}
+
+/*ARGSUSED*/
+void
+puffs_stdpath_freepath(struct puffs_usermount *pu, struct puffs_pathobj *po)
+{
+
+       free(po->po_path);
+}
diff --git a/lib/libpuffs/pnode.c b/lib/libpuffs/pnode.c
new file mode 100644 (file)
index 0000000..5cc4f84
--- /dev/null
@@ -0,0 +1,167 @@
+/*     $NetBSD: pnode.c,v 1.10 2008/08/12 19:44:39 pooka Exp $ */
+
+/*
+ * Copyright (c) 2006 Antti Kantee.  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 AUTHOR ``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 AUTHOR 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 <sys/types.h>
+
+#include <assert.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "puffs.h"
+#include "puffs_priv.h"
+
+/*
+ * Well, you're probably wondering why this isn't optimized.
+ * The reason is simple: my available time is not optimized for
+ * size ... so please be patient ;)
+ */
+struct puffs_node *
+puffs_pn_new(struct puffs_usermount *pu, void *privdata)
+{
+       struct puffs_node *pn;
+
+       pn = calloc(1, sizeof(struct puffs_node));
+       if (pn == NULL)
+               return NULL;
+
+       pn->pn_data = privdata;
+       pn->pn_mnt = pu;
+       puffs_vattr_null(&pn->pn_va);
+
+       LIST_INSERT_HEAD(&pu->pu_pnodelst, pn, pn_entries);
+
+       return pn;
+}
+
+void
+puffs_pn_remove(struct puffs_node *pn)
+{
+
+       LIST_REMOVE(pn, pn_entries);
+       pn->pn_flags |= PUFFS_NODE_REMOVED;
+}
+
+void
+puffs_pn_put(struct puffs_node *pn)
+{
+       struct puffs_usermount *pu = pn->pn_mnt;
+
+       pu->pu_pathfree(pu, &pn->pn_po);
+       if ((pn->pn_flags & PUFFS_NODE_REMOVED) == 0)
+               LIST_REMOVE(pn, pn_entries);
+       free(pn);
+}
+
+/* walk list, rv can be used either to halt or to return a value */
+void *
+puffs_pn_nodewalk(struct puffs_usermount *pu, puffs_nodewalk_fn fn, void *arg)
+{
+       struct puffs_node *pn_cur, *pn_next;
+       void *rv;
+
+       pn_cur = LIST_FIRST(&pu->pu_pnodelst);
+       while (pn_cur) {
+               pn_next = LIST_NEXT(pn_cur, pn_entries);
+               rv = fn(pu, pn_cur, arg);
+               if (rv)
+                       return rv;
+               pn_cur = pn_next;
+       }
+
+       return NULL;
+}
+
+struct vattr *
+puffs_pn_getvap(struct puffs_node *pn)
+{
+
+       return &pn->pn_va;
+}
+
+void *
+puffs_pn_getpriv(struct puffs_node *pn)
+{
+
+       return pn->pn_data;
+}
+
+void
+puffs_pn_setpriv(struct puffs_node *pn, void *priv)
+{
+
+       pn->pn_data = priv;
+}
+
+struct puffs_pathobj *
+puffs_pn_getpo(struct puffs_node *pn)
+{
+
+       return &pn->pn_po;
+}
+
+struct puffs_usermount *
+puffs_pn_getmnt(struct puffs_node *pn)
+{
+
+       return pn->pn_mnt;
+}
+
+/* convenience / shortcut */
+void *
+puffs_pn_getmntspecific(struct puffs_node *pn)
+{
+
+       return pn->pn_mnt->pu_privdata;
+}
+
+/*
+ * newnode parameters
+ */
+void
+puffs_newinfo_setcookie(struct puffs_newinfo *pni, puffs_cookie_t cookie)
+{
+
+       assert(pni->pni_cookie != NULL);
+       *pni->pni_cookie = cookie;
+}
+
+void
+puffs_newinfo_setvtype(struct puffs_newinfo *pni, enum vtype vt)
+{
+
+       if (pni->pni_vtype != NULL)
+               *pni->pni_vtype = vt;
+}
+
+void
+puffs_newinfo_setsize(struct puffs_newinfo *pni, voff_t size)
+{
+
+       if (pni->pni_size != NULL)
+               *pni->pni_size = size;
+}
diff --git a/lib/libpuffs/puffs.3 b/lib/libpuffs/puffs.3
new file mode 100644 (file)
index 0000000..f776dce
--- /dev/null
@@ -0,0 +1,563 @@
+.\"    $NetBSD: puffs.3,v 1.47 2010/01/12 18:42:38 pooka Exp $
+.\"
+.\" Copyright (c) 2006, 2007, 2008 Antti Kantee.  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 AUTHOR 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 AUTHOR 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 February 5, 2012
+.Dt PUFFS 3
+.Os
+.Sh NAME
+.Nm puffs
+.Nd Pass-to-Userspace Framework File System development interface
+.Sh LIBRARY
+.Lb libpuffs
+.Sh SYNOPSIS
+.In puffs.h
+.Ft struct puffs_usermount *
+.Fo puffs_init
+.Fa "struct puffs_ops *pops" "const char *mntfromname" "const char *puffsname"
+.Fa "void *private" "uint32_t flags"
+.Fc
+.Ft int
+.Fo puffs_mount
+.Fa "struct puffs_usermount *pu" "const char *dir" "int mntflags"
+.Fa "puffs_cookie_t root_cookie"
+.Fc
+.Ft int
+.Fn puffs_getselectable "struct puffs_usermount *pu"
+.Ft int
+.Fn puffs_setblockingmode "struct puffs_usermount *pu" "int mode"
+.Ft int
+.Fn puffs_getstate "struct puffs_usermount *pu"
+.Ft int
+.Fn puffs_setstacksize "struct puffs_usermount *pu" "size_t stacksize"
+.Ft void
+.Fn puffs_setroot "struct puffs_usermount *pu" "struct puffs_node *node"
+.Ft void
+.Fo puffs_setrootinfo
+.Fa "struct puffs_usermount *pu" "enum vtype vt" "vsize_t vsize" "dev_t rdev"
+.Fc
+.Ft struct puffs_node *
+.Fn puffs_getroot "struct puffs_usermount *pu"
+.Ft void *
+.Fn puffs_getspecific "struct puffs_usermount *pu"
+.Ft void
+.Fn puffs_setspecific "struct puffs_usermount *pu" "void *private"
+.Ft void
+.Fn puffs_setmaxreqlen "struct puffs_usermount *pu" "size_t maxreqlen"
+.Ft size_t
+.Fn puffs_getmaxreqlen "struct puffs_usermount *pu"
+.Ft void
+.Fn puffs_setfhsize "struct puffs_usermount *pu" "size_t fhsize" "int flags"
+.Ft void
+.Fn puffs_setncookiehash "struct puffs_usermount *pu" "int nhashes"
+.Ft void
+.Fn puffs_ml_loop_fn "struct puffs_usermount *pu"
+.Ft void
+.Fn puffs_ml_setloopfn "struct puffs_usermount *pu" "puffs_ml_loop_fn lfn"
+.Ft void
+.Fn puffs_ml_settimeout "struct puffs_usermount *pu" "struct timespec *ts"
+.Ft int
+.Fn puffs_daemon "struct puffs_usermount *pu" "int nochdir" "int noclose"
+.Ft int
+.Fn puffs_mainloop "struct puffs_usermount *pu"
+.Ft int
+.Fn puffs_unmountonsignal "int sig" "bool ignoresig"
+.Ft int
+.Fo puffs_dispatch_create
+.Fa "struct puffs_usermount *pu" "struct puffs_framebuf *pb"
+.Fa "struct puffs_cc **pccp"
+.Fc
+.Ft int
+.Fn puffs_dispatch_exec "struct puffs_cc *pcc" "struct puffs_framebuf **pbp"
+.Sh DESCRIPTION
+.Nm
+provides a framework for creating file systems as userspace servers.
+Operations are transported from the kernel virtual file system layer
+to the concrete implementation behind
+.Nm ,
+where they are processed and results are sent back to the kernel.
+.Pp
+It is possible to use
+.Nm
+in two different ways.
+Calling
+.Fn puffs_mainloop
+takes execution context away from the caller and automatically handles
+all requests by using the callbacks.
+By using
+.Xr puffs_framebuf 3
+in conjuction with
+.Fn puffs_mainloop ,
+it is possible to handle I/O to and from file descriptors.
+This is suited e.g. for distributed file servers.
+.Ss Library operation
+Operations on the library always require a pointer to the opaque context
+identifier,
+.Va struct puffs_usermount .
+It is obtained by calling
+.Fn puffs_init .
+.Pp
+.Nm
+operates using operation callbacks.
+They can be initialized using the macro
+.Fn PUFFSOP_SET pops fsname type opname ,
+which will initialize the operation
+.Fn puffs_type_opname
+in
+.Fa pops
+to
+.Fn fsname_type_opname .
+All operations are initialized to a default state with the call
+.Fn PUFFSOP_INIT pops .
+All of the VFS routines are mandatory, but all of the node operations
+with the exception of
+.Fn puffs_node_lookup
+are optional.
+However, leaving operations blank will naturally have an effect on the
+features available from the file system implementation.
+.Bl -tag -width xxxx
+.It Fn puffs_init pops mntfromname puffsname private flags
+Initializes the library context.
+.Ar pops
+specifies the callback operations vector.
+.Ar mntfromname
+is device the file system is mounted from.
+This can be for example a block device such as
+.Pa /dev/wd0a
+or, if the file system is pseudo file system, the
+.Nm
+device name can be given by
+.Dv _PATH_PUFFS .
+This value is used for example in the first column of the output of
+.Xr mount 8
+and
+.Xr df 1 .
+.Ar puffsname
+is the file system type.
+It will always be prepended with the string "puffs|".
+If possible, file server binaries should be named using the format
+"mount_myfsnamehere" and this value should equal "myfsnamehere".
+A file system specific context pointer can optionally be given in
+.Ar private .
+This can be retrieved by
+.Fn puffs_getspecific .
+Flags for
+.Nm
+can be given via
+.Fa pflags .
+Currently the following flags are supported:
+.Bl -tag -width "XPUFFS_KFLAG_LOOKUP_FULLPNBUF"
+.It Dv PUFFS_KFLAG_NOCACHE_NAME
+Do not enter pathname components into the name cache.
+This means that every time the kernel does a lookup for a
+componentname, the file server will be consulted.
+.It Dv PUFFS_KFLAG_NOCACHE_PAGE
+Do not use the page cache.
+This means that all reads and writes to regular file are
+propagated to the file server for handling.
+This option makes a difference only for regular files.
+.It Dv PUFFS_KFLAG_NOCACHE
+An alias for both
+.Dv PUFFS_KFLAG_NOCACHE_NAME
+and
+.Dv PUFFS_KFLAG_NOCACHE_PAGE .
+.It Dv PUFFS_KFLAG_ALLOPS
+This flag requests that all operations are sent to userspace.
+Normally the kernel shortcircuits unimplemented operations.
+This flag is mostly useful for debugging purposes.
+.It Dv PUFFS_KFLAG_WTCACHE
+Set the file system cache behavior as write-through.
+This means that all writes are immediately issued to the file server
+instead of being flushed in file system sync.
+This is useful especially for distributed file systems.
+.It Dv PUFFS_KFLAG_IAONDEMAND
+Issue inactive only on demand.
+If a file server defines the inactive method, call it only if the file
+server has explicitly requested that inactive be called for the
+node in question.
+Once inactive has been called for a node, it will not be called
+again unless the request to call inactive is reissued by the file server.
+See
+.Fn puffs_setback
+in
+.Xr puffs_ops 3
+for more information.
+.It Dv PUFFS_KFLAG_LOOKUP_FULLPNBUF
+This flag affects only the parameter
+.Ar pcn to
+.Fn puffs_node_lookup .
+If this flag is not given, only the next pathname component under
+lookup is found from
+.Ar pcn-\*[Gt]pcn_name .
+If this flag is given, the full path the kernel was
+asked to resolve can be found from there.
+.It Dv PUFFS_FLAG_BUILDPATH
+The framework will build a complete path name, which is supplied
+with each operation and can be found from the
+.Va pn_po.po_full_pcn
+field in a
+.Vt struct puffs_node .
+The option assumes that the framework can map a cookie to a
+.Vt struct puffs_node .
+See
+.Sx Cookies
+for more information on cookie mapping.
+See
+.Xr puffs_path 3
+for more information on library calls involving paths.
+.It Dv PUFFS_FLAG_HASHPATH
+Calculate a hash of the path into the path object field
+.Va po_hash .
+This hash value is used by
+.Fn puffs_path_walkcmp
+to avoid doing a full comparison for every path equal in length to
+the one searched for.
+Especially if the file system uses the abovementioned function, it
+is a good idea to define this flag.
+.It Dv PUFFS_FLAG_OPDUMP
+This option makes the framework dump a textual representation of
+each operation before executing it.
+It is useful for debugging purposes.
+.El
+.El
+.Pp
+The following functions can be used to query or modify the global
+state of the file system.
+Note, that all calls are not available at all times.
+.Bl -tag -width xxxx
+.It Fn puffs_getselectable "pu"
+Returns a handle to do I/O multiplexing with:
+.Xr select 2 ,
+.Xr poll 2 ,
+and
+.Xr kqueue 2
+are all examples of acceptable operations.
+.It Fn puffs_setblockingmode "pu" "mode"
+Sets the file system upstream access to blocking or non-blocking mode.
+Acceptable values for the argument are
+.Dv PUFFSDEV_BLOCK
+and
+.Dv PUFFSDEV_NONBLOCK .
+.Pp
+This routine can be called only after calling
+.Fn puffs_mount .
+.It Fn puffs_getstate "pu"
+Returns the state of the file system.
+It is maintained by the framework and is mostly useful for the framework
+itself.
+Possible values are
+.Dv PUFFS_STATE_BEFOREMOUNT ,
+.Dv PUFFS_STATE_RUNNING ,
+.Dv PUFFS_STATE_UNMOUNTING
+and
+.Dv PUFFS_STATE_UNMOUNTED .
+.It Fn puffs_setstacksize "pu" "stacksize"
+Sets the stack size used when running callbacks.
+The default is
+.Dv PUFFS_STACKSIZE_DEFAULT
+bytes of stack space per request.
+The minimum stacksize is architecture-dependent and can be specified
+by using the opaque constant
+.Dv PUFFS_STACKSIZE_MIN .
+.It Fn puffs_setroot "pu" "node"
+Sets the root node of mount
+.Fa pu
+to
+.Fa "node" .
+Setting the root node is currently required only if the path
+framework is used, see
+.Xr puffs_path 3 .
+.It Fn puffs_setrootinfo pu vt vsize rdev
+The default root node is a directory.
+In case the file system wants something different, it can call this
+function and set the type, size and possible device type to whatever
+it wants.
+This routine is independent of
+.Fn puffs_setroot .
+.It Fn puffs_getroot "pu"
+Returns the root node set earlier.
+.It Fn puffs_getspecific "pu"
+Returns the
+.Fa private
+argument of
+.Fn puffs_init .
+.It Fn puffs_setspecific "pu" "private"
+Can be used to set the specific data after the call to
+.Fn puffs_init .
+.It Fn puffs_setmaxreqlen "pu" "maxreqlen"
+In case the file system desires a maximum buffer length different from
+the default, the amount
+.Fa maxreqlen
+will be requested from the kernel when the file system is mounted.
+.Pp
+It is legal to call this function only between
+.Fn puffs_init
+and
+.Fn puffs_mount .
+.Pp
+.Em NOTE
+This does not currently work.
+.It Fn puffs_getmaxreqlen "pu"
+Returns the maximum request length the kernel will need for a single
+request.
+.Pp
+.Em NOTE
+This does not currently work.
+.It Fn puffs_setfhsize "pu" "fhsize" "flags"
+Sets the desired file handle size.
+This must be called if the file system wishes to support NFS exporting
+file systems of the
+.Fn fh*
+family of function calls.
+.Pp
+In case all nodes in the file system produce the same length file handle,
+it must be supplied as
+.Fa fhsize .
+In this case, the file system may ignore the length parameters in the
+file handle callback routines, as the kernel will always pass the
+correct length buffer.
+However, if the file handle size varies according to file, the argument
+.Fa fhsize
+defines the maximum size of a file handle for the file system.
+In this case the file system must take care of the handle lengths by
+itself in the file handle callbacks, see
+.Xr puffs_ops 3
+for more information.
+Also, the flag
+.Dv PUFFS_FHFLAG_DYNAMIC
+must be provided in the argument
+.Fa flags .
+.Pp
+In case the file system wants to sanity check its file handle lengths
+for the limits of NFS, it can supply
+.Dv PUFFS_FHFLAG_NFSV2
+and
+.Dv PUFFS_FHFLAG_NFSV3
+in the
+.Fa flags
+parameter.
+It is especially important to note that these are not directly the
+limits specified by the protocols, as the kernel uses some bytes from
+the buffer space.
+In case the file handles are too large, mount will return an error.
+.Pp
+It is legal to call this function only between
+.Fn puffs_init
+and
+.Fn puffs_mount .
+.It Fn puffs_setncookiehash "pu" "ncookiehash"
+The parameter
+.Fa ncookiehash
+controls the amount of hash buckets the kernel has for reverse lookups
+from cookie to vnode.
+Technically the default is enough, but a memory/time tradeoff can be
+made by increasing this for file systems which know they will have
+very many active files.
+.Pp
+It is legal to call this function only between
+.Fn puffs_init
+and
+.Fn puffs_mount .
+.El
+.Pp
+After the correct setup for the library has been established and the
+backend has been initialized the file system is made operational by calling
+.Fn puffs_mount .
+After this function returns the file system should start processing requests.
+.Bl -tag -width xxxx
+.It Fn puffs_mount pu dir mntflags root_cookie
+.Ar pu
+is the library context pointer from
+.Fn puffs_init .
+The argument
+.Fa dir
+signifies the mount point and
+.Fa mntflags
+is the flagset given to
+.Xr mount 2 .
+The value
+.Ar root_cookie
+will be used as the cookie for the file system root node.
+.El
+.Ss Using the built-in eventloop
+.Bl -tag -width xxxx
+.It Fn puffs_ml_loop_fn pu
+Loop function signature.
+.It Fn puffs_ml_setloopfn pu lfn
+Set loop function to
+.Ar lfn .
+This function is called once each time the event loop loops.
+It is not a well-defined interval, but it can be made fairly regular
+by setting the loop timeout by
+.Fn puffs_ml_settimeout .
+.It Fn puffs_ml_settimeout pu ts
+Sets the loop timeout to
+.Ar ts
+or disables it if
+.Ar ts
+is
+.Dv NULL .
+This can be used to roughly control how often the loop callback
+.Fn lfn
+is called
+.It Fn puffs_daemon pu nochdir noclose
+Detach from the console like
+.Fn daemon 3 .
+This call synchronizes with
+.Fn puffs_mount
+and the foreground process does not exit before the file system mount
+call has returned from the kernel.
+Since this routine internally calls fork, it has to be called
+.Em before
+.Fn puffs_mount .
+.It Fn puffs_mainloop pu flags
+Handle all requests automatically until the file system is unmounted.
+It returns 0 if the file system was successfully unmounted or \-1 if it
+was killed in action.
+.Pp
+In case
+.Xr puffs_framebuf 3
+has been initialized, I/O from the relevant descriptors is processed
+automatically by the eventloop.
+.It Fn puffs_unmountonsignal signum ignoresig
+Cause all file servers within the process to initiate unmount upon
+receipt of signal
+.Ar signum .
+This works only for servers which call
+.Fn puffs_mainloop
+and must be called before any server within the process enters the mainloop.
+The process signal handler is still called before starting the unmount
+procedure.
+The parameter
+.Ar ignoresig
+is provided as a convenience and tells if to install a signal handler
+to ignore
+.Ar sig
+so that the process will not e.g. terminate based on the default action
+before the file system unmount can be initiated.
+.It Fn puffs_dispatch_create pu pb pccp
+.It Fn puffs_dispatch_exec pcc pbp
+In case the use of
+.Fn puffs_mainloop
+is not possible, requests may be dispatched manually.
+However, as this is less efficient than using the mainloop,
+it should never be the first preference.
+.Pp
+Calling
+.Fn puffs_dispatch_create
+creates a dispatch request.
+The argument
+.Ar pb
+should contains a valid request and upon success
+.Ar pccp
+will contain a valid request context.
+This context is passed to
+.Fn puffs_dispatch_exec
+to execute the request.
+If the request yielded before completing, the routine returns 0,
+otherwise 1.
+When the routine completes,
+.Ar pcc
+is made invalid and a pointer to the processed buffer is placed in
+.Ar pbp .
+It is the responsibility of the caller to send the response (if
+necessary) and destroy the buffer.
+.Pp
+See
+.Xr puffs_cc 3
+and
+.Xr puffs_framebuf 3
+for further information.
+.El
+.Ss Cookies
+Every file (regular file, directory, device node, ...) instance is
+attached to the kernel using a cookie.
+A cookie should uniquely map to a file during its lifetime.
+If file instances are kept in memory, a simple strategy is to use
+the virtual address of the structure describing the file.
+The cookie can be recycled when
+.Fn puffs_node_reclaim
+is called for a node.
+.Pp
+For some operations (such as building paths) the framework needs to map
+the cookie to the framework-level structure describing a file,
+.Vt struct puffs_node .
+It is advisable to simply use the
+.Vt struct puffs_node
+address as a cookie and store file system specific data in the private
+portion of
+.Vt struct puffs_node .
+The library assumes this by default.
+If it is not desirable, the file system implementation can call
+.Fn puffs_set_cookiemap
+to provide an alternative cookie-to-node mapping function.
+.Sh SEE ALSO
+.Xr mount 2 ,
+.Xr puffs_cc 3 ,
+.Xr puffs_cred 3 ,
+.Xr puffs_flush 3 ,
+.Xr puffs_framebuf 3 ,
+.Xr puffs_node 3 ,
+.Xr puffs_ops 3 ,
+.Xr puffs_path 3 ,
+.Xr puffs_suspend 3 ,
+.Xr refuse 3 ,
+.Xr puffs 4
+.Rs
+.%A Antti Kantee
+.%D March 2007
+.%J Proceedings of AsiaBSDCon 2007
+.%P pp. 29-42
+.%T puffs - Pass-to-Userspace Framework File System
+.Re
+.Rs
+.%A Antti Kantee
+.%D September 2007
+.%I Helsinki University of Technology
+.%R Tech Report TKK-TKO-B157
+.%T Using puffs for Implementing Client-Server Distributed File Systems
+.Re
+.Rs
+.%A Antti Kantee
+.%A Alistair Crooks
+.%D September 2007
+.%J EuroBSDCon 2007
+.%T ReFUSE: Userspace FUSE Reimplementation Using puffs
+.Re
+.Rs
+.%A Antti Kantee
+.%D March 2008
+.%J Proceedings of AsiaBSDCon 2008
+.%P pp. 55-70
+.%T Send and Receive of File System Protocols: Userspace Approach With puffs
+.Re
+.Sh HISTORY
+An unsupported experimental version of
+.Nm
+first appeared in
+.Nx 4.0 .
+A stable version appeared in
+.Nx 5.0 .
+.Sh AUTHORS
+.An Antti Kantee Aq pooka@iki.fi
diff --git a/lib/libpuffs/puffs.c b/lib/libpuffs/puffs.c
new file mode 100644 (file)
index 0000000..b9e0fff
--- /dev/null
@@ -0,0 +1,1020 @@
+/*     $NetBSD: puffs.c,v 1.116 2011/05/03 13:16:47 manu Exp $ */
+
+/*
+ * Copyright (c) 2005, 2006, 2007  Antti Kantee.  All Rights Reserved.
+ *
+ * Development of this software was supported by the
+ * Google Summer of Code program and the Ulla Tuominen Foundation.
+ * The Google SoC project was mentored by Bill Studenmund.
+ *
+ * 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 AUTHOR ``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 AUTHOR 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 <sys/param.h>
+#include <sys/event.h>
+#include <sys/filio.h>
+#include <sys/mount.h>
+
+#include <assert.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <paths.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include <vfs/puffs/puffs_msgif.h>
+
+#include "puffs.h"
+#include "puffs_priv.h"
+
+/* Most file systems want this for opts, so just give it to them */
+const struct mntopt puffsmopts[] = {
+       MOPT_STDOPTS,
+       PUFFSMOPT_STD,
+       MOPT_NULL,
+};
+
+pthread_mutex_t pu_lock = PTHREAD_MUTEX_INITIALIZER;
+
+#define FILLOP(lower, upper)                                           \
+do {                                                                   \
+       if (pops->puffs_node_##lower)                                   \
+               opmask[PUFFS_VN_##upper] = 1;                           \
+} while (/*CONSTCOND*/0)
+static void
+fillvnopmask(struct puffs_ops *pops, struct puffs_kargs *pa)
+{
+       uint8_t *opmask = pa->pa_vnopmask;
+
+       memset(opmask, 0, sizeof(pa->pa_vnopmask));
+
+       FILLOP(create,   CREATE);
+       FILLOP(mknod,    MKNOD);
+       FILLOP(open,     OPEN);
+       FILLOP(close,    CLOSE);
+       FILLOP(access,   ACCESS);
+       FILLOP(getattr,  GETATTR);
+       FILLOP(setattr,  SETATTR);
+       FILLOP(poll,     POLL);
+       FILLOP(mmap,     MMAP);
+       FILLOP(fsync,    FSYNC);
+       FILLOP(seek,     SEEK);
+       FILLOP(remove,   REMOVE);
+       FILLOP(link,     LINK);
+       FILLOP(rename,   RENAME);
+       FILLOP(mkdir,    MKDIR);
+       FILLOP(rmdir,    RMDIR);
+       FILLOP(symlink,  SYMLINK);
+       FILLOP(readdir,  READDIR);
+       FILLOP(readlink, READLINK);
+       FILLOP(reclaim,  RECLAIM);
+       FILLOP(inactive, INACTIVE);
+       FILLOP(print,    PRINT);
+       FILLOP(read,     READ);
+       FILLOP(write,    WRITE);
+       FILLOP(advlock,  ADVLOCK);
+       FILLOP(abortop,  ABORTOP);
+       FILLOP(pathconf, PATHCONF);
+
+       FILLOP(getextattr,  GETEXTATTR);
+       FILLOP(setextattr,  SETEXTATTR);
+       FILLOP(listextattr, LISTEXTATTR);
+       FILLOP(deleteextattr, DELETEEXTATTR);
+}
+#undef FILLOP
+
+/*
+ * Go over all framev entries and write everything we can.  This is
+ * mostly for the benefit of delivering "unmount" to the kernel.
+ */
+static void
+finalpush(struct puffs_usermount *pu)
+{
+       struct puffs_fctrl_io *fio;
+
+       LIST_FOREACH(fio, &pu->pu_ios, fio_entries) {
+               if (fio->stat & FIO_WRGONE)
+                       continue;
+
+               puffs__framev_output(pu, fio->fctrl, fio);
+       }
+}
+
+/*ARGSUSED*/
+void
+puffs_kernerr_abort(struct puffs_usermount *pu, uint8_t type,
+       int error, const char *str, puffs_cookie_t cookie)
+{
+
+       fprintf(stderr, "abort: type %d, error %d, cookie %p (%s)\n",
+           type, error, cookie, str);
+       abort();
+}
+
+/*ARGSUSED*/
+void
+puffs_kernerr_log(struct puffs_usermount *pu, uint8_t type,
+       int error, const char *str, puffs_cookie_t cookie)
+{
+
+       syslog(LOG_WARNING, "kernel: type %d, error %d, cookie %p (%s)\n",
+           type, error, cookie, str);
+}
+
+int
+puffs_getselectable(struct puffs_usermount *pu)
+{
+
+       return pu->pu_fd;
+}
+
+uint64_t
+puffs__nextreq(struct puffs_usermount *pu)
+{
+       uint64_t rv;
+
+       PU_LOCK();
+       rv = pu->pu_nextreq++ | (uint64_t)1<<63;
+       PU_UNLOCK();
+
+       return rv;
+}
+
+int
+puffs_setblockingmode(struct puffs_usermount *pu, int mode)
+{
+       int rv, x;
+
+       assert(puffs_getstate(pu) == PUFFS_STATE_RUNNING);
+
+       if (mode != PUFFSDEV_BLOCK && mode != PUFFSDEV_NONBLOCK) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       x = mode;
+       rv = ioctl(pu->pu_fd, FIONBIO, &x);
+
+       if (rv == 0) {
+               if (mode == PUFFSDEV_BLOCK)
+                       pu->pu_state &= ~PU_ASYNCFD;
+               else
+                       pu->pu_state |= PU_ASYNCFD;
+       }
+
+       return rv;
+}
+
+int
+puffs_getstate(struct puffs_usermount *pu)
+{
+
+       return pu->pu_state & PU_STATEMASK;
+}
+
+void
+puffs_setstacksize(struct puffs_usermount *pu, size_t ss)
+{
+       long psize, minsize;
+       int stackshift;
+       int bonus;
+
+       assert(puffs_getstate(pu) == PUFFS_STATE_BEFOREMOUNT);
+
+       psize = sysconf(_SC_PAGESIZE);
+       minsize = 4*psize;
+       if (ss < (size_t)minsize || ss == PUFFS_STACKSIZE_MIN) {
+               if (ss != PUFFS_STACKSIZE_MIN)
+                       fprintf(stderr, "puffs_setstacksize: adjusting "
+                           "stacksize to minimum %ld\n", minsize);
+               ss = 4*psize;
+       }
+
+       stackshift = -1;
+       bonus = 0;
+       while (ss) {
+               if (ss & 0x1)
+                       bonus++;
+               ss >>= 1;
+               stackshift++;
+       }
+       if (bonus > 1) {
+               stackshift++;
+               fprintf(stderr, "puffs_setstacksize: using next power of two: "
+                   "%d\n", 1<<stackshift);
+       }
+
+       pu->pu_cc_stackshift = stackshift;
+}
+
+struct puffs_pathobj *
+puffs_getrootpathobj(struct puffs_usermount *pu)
+{
+       struct puffs_node *pnr;
+
+       pnr = pu->pu_pn_root;
+       if (pnr == NULL) {
+               errno = ENOENT;
+               return NULL;
+       }
+
+       return &pnr->pn_po;
+}
+
+void
+puffs_setroot(struct puffs_usermount *pu, struct puffs_node *pn)
+{
+
+       pu->pu_pn_root = pn;
+}
+
+struct puffs_node *
+puffs_getroot(struct puffs_usermount *pu)
+{
+
+       return pu->pu_pn_root;
+}
+
+void
+puffs_setrootinfo(struct puffs_usermount *pu, enum vtype vt,
+       vsize_t vsize, dev_t rdev)
+{
+       struct puffs_kargs *pargs = pu->pu_kargp;
+
+       if (puffs_getstate(pu) != PUFFS_STATE_BEFOREMOUNT) {
+               warnx("puffs_setrootinfo: call has effect only "
+                   "before mount\n");
+               return;
+       }
+
+       pargs->pa_root_vtype = vt;
+       pargs->pa_root_vsize = vsize;
+       pargs->pa_root_rdev = rdev;
+}
+
+void *
+puffs_getspecific(struct puffs_usermount *pu)
+{
+
+       return pu->pu_privdata;
+}
+
+void
+puffs_setspecific(struct puffs_usermount *pu, void *privdata)
+{
+
+       pu->pu_privdata = privdata;
+}
+
+void
+puffs_setmntinfo(struct puffs_usermount *pu,
+       const char *mntfromname, const char *puffsname)
+{
+       struct puffs_kargs *pargs = pu->pu_kargp;
+
+       (void)strlcpy(pargs->pa_mntfromname, mntfromname,
+           sizeof(pargs->pa_mntfromname));
+       (void)strlcpy(pargs->pa_typename, puffsname,
+           sizeof(pargs->pa_typename));
+}
+
+size_t
+puffs_getmaxreqlen(struct puffs_usermount *pu)
+{
+
+       return pu->pu_maxreqlen;
+}
+
+void
+puffs_setmaxreqlen(struct puffs_usermount *pu, size_t reqlen)
+{
+
+       if (puffs_getstate(pu) != PUFFS_STATE_BEFOREMOUNT)
+               warnx("puffs_setmaxreqlen: call has effect only "
+                   "before mount\n");
+
+       pu->pu_kargp->pa_maxmsglen = reqlen;
+}
+
+void
+puffs_setfhsize(struct puffs_usermount *pu, size_t fhsize, int flags)
+{
+
+       if (puffs_getstate(pu) != PUFFS_STATE_BEFOREMOUNT)
+               warnx("puffs_setfhsize: call has effect only before mount\n");
+
+       pu->pu_kargp->pa_fhsize = fhsize;
+       pu->pu_kargp->pa_fhflags = flags;
+}
+
+void
+puffs_setncookiehash(struct puffs_usermount *pu, int nhash)
+{
+
+       if (puffs_getstate(pu) != PUFFS_STATE_BEFOREMOUNT)
+               warnx("puffs_setfhsize: call has effect only before mount\n");
+
+       pu->pu_kargp->pa_nhashbuckets = nhash;
+}
+
+void
+puffs_set_pathbuild(struct puffs_usermount *pu, pu_pathbuild_fn fn)
+{
+
+       pu->pu_pathbuild = fn;
+}
+
+void
+puffs_set_pathtransform(struct puffs_usermount *pu, pu_pathtransform_fn fn)
+{
+
+       pu->pu_pathtransform = fn;
+}
+
+void
+puffs_set_pathcmp(struct puffs_usermount *pu, pu_pathcmp_fn fn)
+{
+
+       pu->pu_pathcmp = fn;
+}
+
+void
+puffs_set_pathfree(struct puffs_usermount *pu, pu_pathfree_fn fn)
+{
+
+       pu->pu_pathfree = fn;
+}
+
+void
+puffs_set_namemod(struct puffs_usermount *pu, pu_namemod_fn fn)
+{
+
+       pu->pu_namemod = fn;
+}
+
+void
+puffs_set_errnotify(struct puffs_usermount *pu, pu_errnotify_fn fn)
+{
+
+       pu->pu_errnotify = fn;
+}
+
+void
+puffs_set_cmap(struct puffs_usermount *pu, pu_cmap_fn fn)
+{
+
+       pu->pu_cmap = fn;
+}
+
+void
+puffs_ml_setloopfn(struct puffs_usermount *pu, puffs_ml_loop_fn lfn)
+{
+
+       pu->pu_ml_lfn = lfn;
+}
+
+void
+puffs_ml_settimeout(struct puffs_usermount *pu, struct timespec *ts)
+{
+
+       if (ts == NULL) {
+               pu->pu_ml_timep = NULL;
+       } else {
+               pu->pu_ml_timeout = *ts;
+               pu->pu_ml_timep = &pu->pu_ml_timeout;
+       }
+}
+
+void
+puffs_set_prepost(struct puffs_usermount *pu,
+       pu_prepost_fn pre, pu_prepost_fn pst)
+{
+
+       pu->pu_oppre = pre;
+       pu->pu_oppost = pst;
+}
+
+void
+puffs_setback(struct puffs_cc *pcc, int whatback)
+{
+       struct puffs_req *preq = puffs__framebuf_getdataptr(pcc->pcc_pb);
+
+       assert(PUFFSOP_OPCLASS(preq->preq_opclass) == PUFFSOP_VN && (
+           preq->preq_optype == PUFFS_VN_OPEN ||
+           preq->preq_optype == PUFFS_VN_MMAP ||
+           preq->preq_optype == PUFFS_VN_REMOVE ||
+           preq->preq_optype == PUFFS_VN_RMDIR ||
+           preq->preq_optype == PUFFS_VN_INACTIVE));
+
+       preq->preq_setbacks |= whatback & PUFFS_SETBACK_MASK;
+}
+
+int
+puffs_daemon(struct puffs_usermount *pu, int nochdir, int noclose)
+{
+       long int n;
+       int parent, value, fd;
+
+       if (pipe(pu->pu_dpipe) == -1)
+               return -1;
+
+       switch (fork()) {
+       case -1:
+               return -1;
+       case 0:
+               parent = 0;
+               break;
+       default:
+               parent = 1;
+               break;
+       }
+       pu->pu_state |= PU_PUFFSDAEMON;
+
+       if (parent) {
+               close(pu->pu_dpipe[1]);
+               n = read(pu->pu_dpipe[0], &value, sizeof(int));
+               if (n == -1)
+                       err(1, "puffs_daemon");
+               if (n != sizeof(value))
+                       errx(1, "puffs_daemon got %ld bytes", n);
+               if (value) {
+                       errno = value;
+                       err(1, "puffs_daemon");
+               }
+               exit(0);
+       } else {
+               if (setsid() == -1)
+                       goto fail;
+
+               if (!nochdir)
+                       chdir("/");
+
+               if (!noclose) {
+                       fd = open(_PATH_DEVNULL, O_RDWR, 0);
+                       if (fd == -1)
+                               goto fail;
+                       dup2(fd, STDIN_FILENO);
+                       dup2(fd, STDOUT_FILENO);
+                       dup2(fd, STDERR_FILENO);
+                       if (fd > STDERR_FILENO)
+                               close(fd);
+               }
+               return 0;
+       }
+
+ fail:
+       n = write(pu->pu_dpipe[1], &errno, sizeof(int));
+       assert(n == 4);
+       return -1;
+}
+
+static void
+shutdaemon(struct puffs_usermount *pu, int error)
+{
+       ssize_t n;
+
+       n = write(pu->pu_dpipe[1], &error, sizeof(int));
+       assert(n == 4);
+       close(pu->pu_dpipe[0]);
+       close(pu->pu_dpipe[1]);
+       pu->pu_state &= ~PU_PUFFSDAEMON;
+}
+
+int
+puffs_mount(struct puffs_usermount *pu, const char *dir, int mntflags,
+       puffs_cookie_t cookie)
+{
+       struct stat sb;
+       int rv, fd, sverrno;
+       char *comfd;
+
+       pu->pu_kargp->pa_root_cookie = cookie;
+
+       /* XXXkludgehere */
+       /* kauth doesn't provide this service any longer */
+       if (geteuid() != 0)
+               mntflags |= MNT_NOSUID | MNT_NODEV;
+
+       /*
+        * Undocumented...  Well, documented only here.
+        *
+        * This is used for imaginative purposes.  If the env variable is
+        * set, puffs_mount() doesn't do the regular mount procedure.
+        * Rather, it crams the mount data down the comfd and sets comfd as
+        * the puffs descriptor.
+        *
+        * This shouldn't be used unless you can read my mind ( ... or write
+        * it, not to mention execute it, but that's starting to get silly).
+        */
+       if ((comfd = getenv("PUFFS_COMFD")) != NULL) {
+               size_t len;
+
+               if (sscanf(comfd, "%d", &pu->pu_fd) != 1) {
+                       errno = EINVAL;
+                       rv = -1;
+                       goto out;
+               }
+               /* check that what we got at least resembles an fd */
+               if (fcntl(pu->pu_fd, F_GETFL) == -1) {
+                       rv = -1;
+                       goto out;
+               }
+
+#define allwrite(buf, len)                                             \
+do {                                                                   \
+       ssize_t al_rv;                                                  \
+       al_rv = write(pu->pu_fd, buf, len);                             \
+       if ((size_t)al_rv != len) {                                     \
+               if (al_rv != -1)                                        \
+                       errno = EIO;                                    \
+               rv = -1;                                                \
+               goto out;                                               \
+       }                                                               \
+} while (/*CONSTCOND*/0)
+               len = strlen(dir)+1;
+               allwrite(&len, sizeof(len));
+               allwrite(dir, len);
+               len = strlen(pu->pu_kargp->pa_mntfromname)+1;
+               allwrite(&len, sizeof(len));
+               allwrite(pu->pu_kargp->pa_mntfromname, len);
+               allwrite(&mntflags, sizeof(mntflags));
+               len = sizeof(*pu->pu_kargp);
+               allwrite(&len, sizeof(len));
+               allwrite(pu->pu_kargp, sizeof(*pu->pu_kargp));
+               allwrite(&pu->pu_flags, sizeof(pu->pu_flags));
+#undef allwrite
+
+               rv = 0;
+       } else {
+               char rp[MAXPATHLEN];
+
+               if (realpath(dir, rp) == NULL) {
+                       rv = -1;
+                       goto out;
+               }
+
+               if (strcmp(dir, rp) != 0) {
+                       warnx("puffs_mount: \"%s\" is a relative path.", dir);
+                       warnx("puffs_mount: using \"%s\" instead.", rp);
+               }
+
+               fd = open(_PATH_PUFFS, O_RDWR);
+               if (fd == -1) {
+                       warnx("puffs_mount: cannot open %s", _PATH_PUFFS);
+                       rv = -1;
+                       goto out;
+               }
+               if (fd <= 2)
+                       warnx("puffs_mount: device fd %d (<= 2), sure this is "
+                           "what you want?", fd);
+
+               pu->pu_fd = fd;
+               rv = fstat(fd, &sb);
+               if (rv == -1) {
+                       warnx("puffs_mount: putter device stat failed");
+                       goto out;
+               }
+               pu->pu_kargp->pa_minor = minor(sb.st_rdev);
+
+               if ((rv = mount(MOUNT_PUFFS, rp, mntflags,
+                   pu->pu_kargp)) == -1)
+                       goto out;
+       }
+
+       PU_SETSTATE(pu, PUFFS_STATE_RUNNING);
+
+ out:
+       if (rv != 0)
+               sverrno = errno;
+       else
+               sverrno = 0;
+       free(pu->pu_kargp);
+       pu->pu_kargp = NULL;
+
+       if (pu->pu_state & PU_PUFFSDAEMON)
+               shutdaemon(pu, sverrno);
+
+       errno = sverrno;
+       return rv;
+}
+
+struct puffs_usermount *
+puffs_init(struct puffs_ops *pops, const char *mntfromname,
+       const char *puffsname, void *priv, uint32_t pflags)
+{
+       struct puffs_usermount *pu;
+       struct puffs_kargs *pargs;
+       int sverrno;
+
+       if (puffsname == PUFFS_DEFER)
+               puffsname = "n/a";
+       if (mntfromname == PUFFS_DEFER)
+               mntfromname = "n/a";
+       if (priv == PUFFS_DEFER)
+               priv = NULL;
+
+       pu = malloc(sizeof(struct puffs_usermount));
+       if (pu == NULL)
+               goto failfree;
+       memset(pu, 0, sizeof(struct puffs_usermount));
+
+       pargs = pu->pu_kargp = malloc(sizeof(struct puffs_kargs));
+       if (pargs == NULL)
+               goto failfree;
+       memset(pargs, 0, sizeof(struct puffs_kargs));
+
+       pargs->pa_vers = PUFFSVERSION;
+       pargs->pa_flags = PUFFS_FLAG_KERN(pflags);
+       fillvnopmask(pops, pargs);
+       puffs_setmntinfo(pu, mntfromname, puffsname);
+
+       puffs_zerostatvfs(&pargs->pa_svfsb);
+       pargs->pa_root_cookie = NULL;
+       pargs->pa_root_vtype = VDIR;
+       pargs->pa_root_vsize = 0;
+       pargs->pa_root_rdev = 0;
+       pargs->pa_maxmsglen = 0;
+       if (/*CONSTCOND*/ sizeof(time_t) == 4)
+               pargs->pa_time32 = 1;
+       else
+               pargs->pa_time32 = 0;
+
+       pu->pu_flags = pflags;
+       pu->pu_ops = *pops;
+       free(pops); /* XXX */
+
+       pu->pu_privdata = priv;
+       pu->pu_cc_stackshift = PUFFS_CC_STACKSHIFT_DEFAULT;
+       LIST_INIT(&pu->pu_pnodelst);
+       LIST_INIT(&pu->pu_ios);
+       LIST_INIT(&pu->pu_ios_rmlist);
+       LIST_INIT(&pu->pu_ccmagazin);
+       TAILQ_INIT(&pu->pu_sched);
+
+       pu->pu_framectrl[PU_FRAMECTRL_FS].rfb = puffs__fsframe_read;
+       pu->pu_framectrl[PU_FRAMECTRL_FS].wfb = puffs__fsframe_write;
+       pu->pu_framectrl[PU_FRAMECTRL_FS].cmpfb = puffs__fsframe_cmp;
+       pu->pu_framectrl[PU_FRAMECTRL_FS].gotfb = puffs__fsframe_gotframe;
+       pu->pu_framectrl[PU_FRAMECTRL_FS].fdnotfn = puffs_framev_unmountonclose;
+
+       /* defaults for some user-settable translation functions */
+       pu->pu_cmap = NULL; /* identity translation */
+
+       pu->pu_pathbuild = puffs_stdpath_buildpath;
+       pu->pu_pathfree = puffs_stdpath_freepath;
+       pu->pu_pathcmp = puffs_stdpath_cmppath;
+       pu->pu_pathtransform = NULL;
+       pu->pu_namemod = NULL;
+
+       pu->pu_errnotify = puffs_kernerr_log;
+
+       PU_SETSTATE(pu, PUFFS_STATE_BEFOREMOUNT);
+
+       return pu;
+
+ failfree:
+       /* can't unmount() from here for obvious reasons */
+       sverrno = errno;
+       free(pu);
+       errno = sverrno;
+       return NULL;
+}
+
+void
+puffs_cancel(struct puffs_usermount *pu, int error)
+{
+
+       assert(puffs_getstate(pu) < PUFFS_STATE_RUNNING);
+       shutdaemon(pu, error);
+       free(pu);
+}
+
+/*ARGSUSED1*/
+int
+puffs_exit(struct puffs_usermount *pu, int unused /* strict compat */)
+{
+       struct puffs_framebuf *pb;
+       struct puffs_req *preq;
+       void *winp;
+       size_t winlen;
+       int sverrno;
+
+       pb = puffs_framebuf_make();
+       if (pb == NULL) {
+               errno = ENOMEM;
+               return -1;
+       }
+
+       winlen = sizeof(struct puffs_req);
+       if (puffs_framebuf_getwindow(pb, 0, &winp, &winlen) == -1) {
+               sverrno = errno;
+               puffs_framebuf_destroy(pb);
+               errno = sverrno;
+               return -1;
+       }
+       preq = winp;
+
+       preq->preq_buflen = sizeof(struct puffs_req);
+       preq->preq_opclass = PUFFSOP_UNMOUNT;
+       preq->preq_id = puffs__nextreq(pu);
+
+       puffs_framev_enqueue_justsend(pu, puffs_getselectable(pu), pb, 1, 0);
+
+       return 0;
+}
+
+/* no sigset_t static intializer */
+static int sigs[NSIG] = { 0, };
+static int sigcatch = 0;
+
+int
+puffs_unmountonsignal(int sig, bool sigignore)
+{
+
+       if (sig < 0 || sig >= (int)NSIG) {
+               errno = EINVAL;
+               return -1;
+       }
+       if (sigignore)
+               if (signal(sig, SIG_IGN) == SIG_ERR)
+                       return -1;
+
+       if (!sigs[sig])
+               sigcatch++;
+       sigs[sig] = 1;
+
+       return 0;
+}
+
+/*
+ * Actual mainloop.  This is called from a context which can block.
+ * It is called either from puffs_mainloop (indirectly, via
+ * puffs_cc_continue() or from puffs_cc_yield()).
+ */
+void
+puffs__theloop(struct puffs_cc *pcc)
+{
+       struct puffs_usermount *pu = pcc->pcc_pu;
+       struct puffs_framectrl *pfctrl;
+       struct puffs_fctrl_io *fio;
+       struct kevent *curev;
+       size_t nchanges;
+       int ndone;
+
+       while (puffs_getstate(pu) != PUFFS_STATE_UNMOUNTED) {
+
+               /*
+                * Schedule existing requests.
+                */
+               while ((pcc = TAILQ_FIRST(&pu->pu_sched)) != NULL) {
+                       TAILQ_REMOVE(&pu->pu_sched, pcc, pcc_schedent);
+                       puffs__goto(pcc);
+               }
+
+               if (pu->pu_ml_lfn)
+                       pu->pu_ml_lfn(pu);
+
+               /* XXX: can we still do these optimizations? */
+#if 0
+               /*
+                * Do this here, because:
+                *  a) loopfunc might generate some results
+                *  b) it's still "after" event handling (except for round 1)
+                */
+               if (puffs_req_putput(ppr) == -1)
+                       goto out;
+               puffs_req_resetput(ppr);
+
+               /* micro optimization: skip kevent syscall if possible */
+               if (pu->pu_nfds == 1 && pu->pu_ml_timep == NULL
+                   && (pu->pu_state & PU_ASYNCFD) == 0) {
+                       pfctrl = XXX->fctrl;
+                       puffs_framev_input(pu, pfctrl, XXX);
+                       continue;
+               }
+#endif
+
+               /* else: do full processing */
+               /* Don't bother worrying about O(n) for now */
+               LIST_FOREACH(fio, &pu->pu_ios, fio_entries) {
+                       if (fio->stat & FIO_WRGONE)
+                               continue;
+
+                       pfctrl = fio->fctrl;
+
+                       /*
+                        * Try to write out everything to avoid the
+                        * need for enabling EVFILT_WRITE.  The likely
+                        * case is that we can fit everything into the
+                        * socket buffer.
+                        */
+                       puffs__framev_output(pu, pfctrl, fio);
+               }
+
+               /*
+                * Build list of which to enable/disable in writecheck.
+                */
+               nchanges = 0;
+               LIST_FOREACH(fio, &pu->pu_ios, fio_entries) {
+                       if (fio->stat & FIO_WRGONE)
+                               continue;
+
+                       /* en/disable write checks for kqueue as needed */
+                       assert((FIO_EN_WRITE(fio) && FIO_RM_WRITE(fio)) == 0);
+                       if (FIO_EN_WRITE(fio)) {
+                               EV_SET(&pu->pu_evs[nchanges], fio->io_fd,
+                                   EVFILT_WRITE, EV_ENABLE, 0, 0,
+                                   fio);
+                               fio->stat |= FIO_WR;
+                               nchanges++;
+                       }
+                       if (FIO_RM_WRITE(fio)) {
+                               EV_SET(&pu->pu_evs[nchanges], fio->io_fd,
+                                   EVFILT_WRITE, EV_DISABLE, 0, 0,
+                                   fio);
+                               fio->stat &= ~FIO_WR;
+                               nchanges++;
+                       }
+               }
+
+               ndone = kevent(pu->pu_kq, pu->pu_evs, nchanges,
+                   pu->pu_evs, pu->pu_nevs, pu->pu_ml_timep);
+
+               if (ndone == -1) {
+                       if (errno != EINTR)
+                               break;
+                       else
+                               continue;
+               }
+
+               /* uoptimize */
+               if (ndone == 0)
+                       continue;
+
+               /* iterate over the results */
+               for (curev = pu->pu_evs; ndone--; curev++) {
+                       int what;
+
+#if 0
+                       /* get & possibly dispatch events from kernel */
+                       if (curev->ident == puffsfd) {
+                               if (puffs_req_handle(pgr, ppr, 0) == -1)
+                                       goto out;
+                               continue;
+                       }
+#endif
+
+                       fio = (void *)curev->udata;
+                       if (__predict_true(fio != NULL))
+                               pfctrl = fio->fctrl;
+                       else
+                               pfctrl = NULL;
+                       if (curev->flags & EV_ERROR) {
+                               assert(curev->filter == EVFILT_WRITE);
+                               fio->stat &= ~FIO_WR;
+
+                               /* XXX: how to know if it's a transient error */
+                               puffs__framev_writeclose(pu, fio,
+                                   (int)curev->data);
+                               puffs__framev_notify(fio, PUFFS_FBIO_ERROR);
+                               continue;
+                       }
+
+                       what = 0;
+                       if (curev->filter == EVFILT_READ) {
+                               puffs__framev_input(pu, pfctrl, fio);
+                               what |= PUFFS_FBIO_READ;
+                       }
+
+                       else if (curev->filter == EVFILT_WRITE) {
+                               puffs__framev_output(pu, pfctrl, fio);
+                               what |= PUFFS_FBIO_WRITE;
+                       }
+
+                       else if (__predict_false(curev->filter==EVFILT_SIGNAL)){
+                               if ((pu->pu_state & PU_DONEXIT) == 0) {
+                                       PU_SETSFLAG(pu, PU_DONEXIT);
+                                       puffs_exit(pu, 0);
+                               }
+                       }
+                       if (what)
+                               puffs__framev_notify(fio, what);
+               }
+
+               /*
+                * Really free fd's now that we don't have references
+                * to them.
+                */
+               while ((fio = LIST_FIRST(&pu->pu_ios_rmlist)) != NULL) {
+                       LIST_REMOVE(fio, fio_entries);
+                       free(fio);
+               }
+       }
+
+       if (puffs__cc_restoremain(pu) == -1)
+               warn("cannot restore main context.  impending doom");
+}
+int
+puffs_mainloop(struct puffs_usermount *pu)
+{
+       struct puffs_fctrl_io *fio;
+       struct puffs_cc *pcc;
+       struct kevent *curev;
+       size_t nevs;
+       int sverrno, i;
+
+       assert(puffs_getstate(pu) >= PUFFS_STATE_RUNNING);
+
+       pu->pu_kq = kqueue();
+       if (pu->pu_kq == -1)
+               goto out;
+       pu->pu_state |= PU_HASKQ;
+
+       puffs_setblockingmode(pu, PUFFSDEV_NONBLOCK);
+       if (puffs__framev_addfd_ctrl(pu, puffs_getselectable(pu),
+           PUFFS_FBIO_READ | PUFFS_FBIO_WRITE,
+           &pu->pu_framectrl[PU_FRAMECTRL_FS]) == -1)
+               goto out;
+
+       nevs = pu->pu_nevs + sigcatch;
+       curev = realloc(pu->pu_evs, nevs * sizeof(struct kevent));
+       if (curev == NULL)
+               goto out;
+       pu->pu_evs = curev;
+       pu->pu_nevs = nevs;
+
+       LIST_FOREACH(fio, &pu->pu_ios, fio_entries) {
+               EV_SET(curev, fio->io_fd, EVFILT_READ, EV_ADD,
+                   0, 0, fio);
+               curev++;
+               EV_SET(curev, fio->io_fd, EVFILT_WRITE, EV_ADD | EV_DISABLE,
+                   0, 0, fio);
+               curev++;
+       }
+       for (i = 0; i < NSIG; i++) {
+               if (sigs[i]) {
+                       EV_SET(curev, i, EVFILT_SIGNAL, EV_ADD | EV_ENABLE,
+                           0, 0, 0);
+                       curev++;
+               }
+       }
+       assert(curev - pu->pu_evs == (ssize_t)pu->pu_nevs);
+       if (kevent(pu->pu_kq, pu->pu_evs, pu->pu_nevs, NULL, 0, NULL) == -1)
+               goto out;
+
+       pu->pu_state |= PU_INLOOP;
+
+       /*
+        * Create alternate execution context and jump to it.  Note
+        * that we come "out" of savemain twice.  Where we come out
+        * of it depends on the architecture.  If the return address is
+        * stored on the stack, we jump out from puffs_cc_continue(),
+        * for a register return address from puffs__cc_savemain().
+        * PU_MAINRESTORE makes sure we DTRT in both cases.
+        */
+       if (puffs__cc_create(pu, puffs__theloop, &pcc) == -1) {
+               goto out;
+       }
+       if (puffs__cc_savemain(pu) == -1) {
+               goto out;
+       }
+       if ((pu->pu_state & PU_MAINRESTORE) == 0)
+               puffs_cc_continue(pcc);
+
+       finalpush(pu);
+       errno = 0;
+
+ out:
+       /* store the real error for a while */
+       sverrno = errno;
+
+       errno = sverrno;
+       if (errno)
+               return -1;
+       else
+               return 0;
+}
diff --git a/lib/libpuffs/puffs.h b/lib/libpuffs/puffs.h
new file mode 100644 (file)
index 0000000..2a7bc66
--- /dev/null
@@ -0,0 +1,687 @@
+/*     $NetBSD: puffs.h,v 1.118 2011/07/04 08:07:30 manu Exp $ */
+
+/*
+ * Copyright (c) 2005, 2006, 2007  Antti Kantee.  All Rights Reserved.
+ *
+ * Development of this software was supported by the
+ * Google Summer of Code program and the Ulla Tuominen Foundation.
+ * The Google SoC project was mentored by Bill Studenmund.
+ *
+ * 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 AUTHOR ``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 AUTHOR 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 _PUFFS_H_
+#define _PUFFS_H_
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/mount.h>
+#include <sys/namei.h>
+#include <sys/stat.h>
+#include <sys/statvfs.h>
+#include <sys/time.h>
+#include <sys/vnode.h>
+
+#include <vfs/puffs/puffs_msgif.h>
+
+#include <mntopts.h>
+#include <stdbool.h>
+#include <string.h>
+
+/* XXXDF */
+#define _PATH_PUFFS    "/dev/putter"
+#define MOUNT_PUFFS    "puffs"         /* Pass-to-Userspace filesystem */
+
+/* forwards */
+struct puffs_cc;
+
+struct puffs_getreq;
+struct puffs_cred;
+struct puffs_newinfo;
+
+/* paths */
+struct puffs_pathobj {
+       void            *po_path;
+       size_t          po_len;
+       uint32_t        po_hash;
+};
+
+/* for prefix rename */
+struct puffs_pathinfo {
+       struct puffs_pathobj *pi_old;
+       struct puffs_pathobj *pi_new;
+};
+
+/* describes one segment cached in the kernel */
+struct puffs_kcache {
+       off_t   pkc_start;
+       off_t   pkc_end;
+
+       LIST_ENTRY(puffs_kcache) pkc_entries;
+};
+
+/* XXX: might disappear from here into a private header */
+struct puffs_node {
+       off_t                   pn_size;
+       int                     pn_flags;
+       struct vattr            pn_va;
+
+       void                    *pn_data;       /* private data         */
+
+       struct puffs_pathobj    pn_po;          /* PUFFS_FLAG_BUILDPATH */
+
+       struct puffs_usermount  *pn_mnt;
+       LIST_ENTRY(puffs_node)  pn_entries;
+
+       LIST_HEAD(,puffs_kcache)pn_cacheinfo;   /* PUFFS_KFLAG_CACHE    */
+
+       void                    *pn_spare[4];
+};
+#define PUFFS_NODE_REMOVED     0x01            /* not on entry list    */
+
+
+struct puffs_usermount;
+
+/*
+ * megaXXX: these are values from inside _KERNEL
+ * need to work on the translation for ALL the necessary values.
+ */
+#define PUFFS_VNOVAL (-1)
+
+#define PUFFS_IO_APPEND 0x020
+#define PUFFS_IO_NDELAY        0x100
+
+#define PUFFS_VEXEC    01
+#define PUFFS_VWRITE   02
+#define PUFFS_VREAD    04
+
+#define PUFFS_FSYNC_DATAONLY 0x0002
+#define PUFFS_FSYNC_CACHE    0x0100
+
+#define PUFFS_EXTATTR_LIST_LENPREFIX 1
+/*
+ * Magic constants
+ */
+#define PUFFS_CC_STACKSHIFT_DEFAULT 18
+
+struct puffs_cn {
+       struct puffs_kcn        *pcn_pkcnp;     /* kernel input */
+       struct puffs_cred       *pcn_cred;      /* cred used for lookup */
+
+       struct puffs_pathobj    pcn_po_full;    /* PUFFS_FLAG_BUILDPATH */
+};
+#define pcn_name       pcn_pkcnp->pkcn_name
+#define pcn_namelen    pcn_pkcnp->pkcn_namelen
+
+/*
+ * Puffs options to mount
+ */
+/* kernel */
+#define        PUFFSMOPT_NAMECACHE     { "namecache", 1, PUFFS_KFLAG_NOCACHE_NAME, 1 }
+#define        PUFFSMOPT_PAGECACHE     { "pagecache", 1, PUFFS_KFLAG_NOCACHE_PAGE, 1 }
+#define        PUFFSMOPT_ATTRCACHE     { "attrcache", 1, PUFFS_KFLAG_NOCACHE_ATTR, 1 }
+#define        PUFFSMOPT_CACHE         { "cache", 1, PUFFS_KFLAG_NOCACHE, 1 }
+#define PUFFSMOPT_ALLOPS       { "allops", 0, PUFFS_KFLAG_ALLOPS, 1 }
+
+/* libpuffs */
+#define PUFFSMOPT_DUMP         { "dump", 0, PUFFS_FLAG_OPDUMP, 1 }
+
+#define PUFFSMOPT_STD                                                  \
+       PUFFSMOPT_NAMECACHE,                                            \
+       PUFFSMOPT_PAGECACHE,                                            \
+       PUFFSMOPT_ATTRCACHE,                                            \
+       PUFFSMOPT_CACHE,                                                \
+       PUFFSMOPT_ALLOPS,                                               \
+       PUFFSMOPT_DUMP
+
+extern const struct mntopt puffsmopts[]; /* puffs.c */
+
+/* callbacks for operations */
+struct puffs_ops {
+       int (*puffs_fs_unmount)(struct puffs_usermount *, int);
+       int (*puffs_fs_statvfs)(struct puffs_usermount *, struct statvfs *);
+       int (*puffs_fs_sync)(struct puffs_usermount *, int);
+       int (*puffs_fs_fhtonode)(struct puffs_usermount *, void *, size_t,
+           struct puffs_newinfo *);
+       int (*puffs_fs_nodetofh)(struct puffs_usermount *, puffs_cookie_t,
+           void *, size_t *);
+       int (*puffs_fs_extattrctl)(struct puffs_usermount *, int,
+           puffs_cookie_t, int, int, const char *);
+
+       int (*puffs_node_lookup)(struct puffs_usermount *,
+           puffs_cookie_t, struct puffs_newinfo *, const struct puffs_cn *);
+       int (*puffs_node_lookupdotdot)(struct puffs_usermount *,
+           puffs_cookie_t, struct puffs_newinfo *, const struct puffs_cn *);
+       int (*puffs_node_create)(struct puffs_usermount *,
+           puffs_cookie_t, struct puffs_newinfo *, const struct puffs_cn *,
+           const struct vattr *);
+       int (*puffs_node_mknod)(struct puffs_usermount *,
+           puffs_cookie_t, struct puffs_newinfo *, const struct puffs_cn *,
+           const struct vattr *);
+       int (*puffs_node_open)(struct puffs_usermount *,
+           puffs_cookie_t, int, const struct puffs_cred *);
+       int (*puffs_node_close)(struct puffs_usermount *,
+           puffs_cookie_t, int);
+       int (*puffs_node_access)(struct puffs_usermount *,
+           puffs_cookie_t, int, const struct puffs_cred *);
+       int (*puffs_node_getattr)(struct puffs_usermount *,
+           puffs_cookie_t, struct vattr *, const struct puffs_cred *);
+       int (*puffs_node_setattr)(struct puffs_usermount *,
+           puffs_cookie_t, const struct vattr *, const struct puffs_cred *);
+       int (*puffs_node_poll)(struct puffs_usermount *, puffs_cookie_t, int *);
+       int (*puffs_node_mmap)(struct puffs_usermount *,
+           puffs_cookie_t, vm_prot_t, const struct puffs_cred *);
+       int (*puffs_node_fsync)(struct puffs_usermount *,
+           puffs_cookie_t, int);
+       int (*puffs_node_seek)(struct puffs_usermount *,
+           puffs_cookie_t, off_t, off_t, const struct puffs_cred *);
+       int (*puffs_node_remove)(struct puffs_usermount *,
+           puffs_cookie_t, puffs_cookie_t, const struct puffs_cn *);
+       int (*puffs_node_link)(struct puffs_usermount *,
+           puffs_cookie_t, puffs_cookie_t, const struct puffs_cn *);
+       int (*puffs_node_rename)(struct puffs_usermount *,
+           puffs_cookie_t, puffs_cookie_t, const struct puffs_cn *,
+           puffs_cookie_t, puffs_cookie_t, const struct puffs_cn *);
+       int (*puffs_node_mkdir)(struct puffs_usermount *,
+           puffs_cookie_t, struct puffs_newinfo *, const struct puffs_cn *,
+           const struct vattr *);
+       int (*puffs_node_rmdir)(struct puffs_usermount *,
+           puffs_cookie_t, puffs_cookie_t, const struct puffs_cn *);
+       int (*puffs_node_symlink)(struct puffs_usermount *,
+           puffs_cookie_t, struct puffs_newinfo *, const struct puffs_cn *,
+           const struct vattr *,
+           const char *);
+       int (*puffs_node_readdir)(struct puffs_usermount *,
+           puffs_cookie_t, struct dirent *, off_t *, size_t *,
+           const struct puffs_cred *, int *, off_t *, size_t *);
+       int (*puffs_node_readlink)(struct puffs_usermount *,
+           puffs_cookie_t, const struct puffs_cred *, char *, size_t *);
+       int (*puffs_node_reclaim)(struct puffs_usermount *, puffs_cookie_t);
+       int (*puffs_node_inactive)(struct puffs_usermount *, puffs_cookie_t);
+       int (*puffs_node_print)(struct puffs_usermount *, puffs_cookie_t);
+       int (*puffs_node_pathconf)(struct puffs_usermount *,
+           puffs_cookie_t, int, register_t *);
+       int (*puffs_node_advlock)(struct puffs_usermount *,
+           puffs_cookie_t, void *, int, struct flock *, int);
+       int (*puffs_node_read)(struct puffs_usermount *, puffs_cookie_t,
+           uint8_t *, off_t, size_t *, const struct puffs_cred *, int);
+       int (*puffs_node_write)(struct puffs_usermount *, puffs_cookie_t,
+           uint8_t *, off_t, size_t *, const struct puffs_cred *, int);
+       int (*puffs_node_abortop)(struct puffs_usermount *, puffs_cookie_t,
+           const struct puffs_cn *);
+       int (*puffs_node_getextattr)(struct puffs_usermount *, puffs_cookie_t,
+           int, const char *, size_t *, uint8_t *, size_t *,
+           const struct puffs_cred *);
+       int (*puffs_node_setextattr)(struct puffs_usermount *, puffs_cookie_t,
+           int, const char *, uint8_t *, size_t *, const struct puffs_cred *);
+       int (*puffs_node_listextattr)(struct puffs_usermount *, puffs_cookie_t,
+           int, size_t *, uint8_t *, size_t *, int, const struct puffs_cred *);
+       int (*puffs_node_deleteextattr)(struct puffs_usermount *,
+           puffs_cookie_t, int, const char *, const struct puffs_cred *);
+
+       void *puffs_ops_spare[32];
+};
+
+typedef        int (*pu_pathbuild_fn)(struct puffs_usermount *,
+                              const struct puffs_pathobj *,
+                              const struct puffs_pathobj *, size_t,
+                              struct puffs_pathobj *);
+typedef int (*pu_pathtransform_fn)(struct puffs_usermount *,
+                                  const struct puffs_pathobj *,
+                                  const struct puffs_cn *,
+                                  struct puffs_pathobj *);
+typedef int (*pu_pathcmp_fn)(struct puffs_usermount *, struct puffs_pathobj *,
+                         struct puffs_pathobj *, size_t, int);
+typedef void (*pu_pathfree_fn)(struct puffs_usermount *,
+                              struct puffs_pathobj *);
+typedef int (*pu_namemod_fn)(struct puffs_usermount *,
+                            struct puffs_pathobj *, struct puffs_cn *);
+
+typedef void (*pu_errnotify_fn)(struct puffs_usermount *,
+                               uint8_t, int, const char *, puffs_cookie_t);
+
+typedef void (*pu_prepost_fn)(struct puffs_usermount *);
+
+typedef struct puffs_node *(*pu_cmap_fn)(struct puffs_usermount *,
+                                        puffs_cookie_t);
+
+enum {
+       PUFFS_STATE_BEFOREMOUNT,        PUFFS_STATE_RUNNING,
+       PUFFS_STATE_UNMOUNTING,         PUFFS_STATE_UNMOUNTED
+};
+
+#define PUFFS_FLAG_BUILDPATH   0x80000000      /* node paths in pnode */
+#define PUFFS_FLAG_OPDUMP      0x40000000      /* dump all operations */
+#define PUFFS_FLAG_HASHPATH    0x20000000      /* speedup: hash paths */
+#define PUFFS_FLAG_MASK                0xe0000000
+
+#define PUFFS_FLAG_KERN(a)     ((a) & PUFFS_KFLAG_MASK)
+#define PUFFS_FLAG_LIB(a)      ((a) & PUFFS_FLAG_MASK)
+
+/* blocking mode argument */
+#define PUFFSDEV_BLOCK 0
+#define PUFFSDEV_NONBLOCK 1
+
+#define PUFFS_STACKSIZE_DEFAULT (1<<PUFFS_CC_STACKSHIFT_DEFAULT)
+#define PUFFS_STACKSIZE_MIN ((size_t)-1)
+
+#define                DENT_DOT        0
+#define                DENT_DOTDOT     1
+#define                DENT_ADJ(a)     ((a)-2) /* nth request means dir's n-2th */
+
+
+/*
+ * protos
+ */
+
+#define PUFFSOP_PROTOS(fsname)                                         \
+       int fsname##_fs_unmount(struct puffs_usermount *, int);         \
+       int fsname##_fs_statvfs(struct puffs_usermount *,               \
+           struct statvfs *);                                          \
+       int fsname##_fs_sync(struct puffs_usermount *, int,             \
+           const struct puffs_cred *cred);                             \
+       int fsname##_fs_fhtonode(struct puffs_usermount *, void *,      \
+           size_t, struct puffs_newinfo *);                            \
+       int fsname##_fs_nodetofh(struct puffs_usermount *,              \
+           puffs_cookie_t, void *, size_t *);                          \
+       int fsname##_fs_extattrctl(struct puffs_usermount *, int,       \
+           puffs_cookie_t, int, int, const char *);                    \
+                                                                       \
+       int fsname##_node_lookup(struct puffs_usermount *,              \
+           puffs_cookie_t, struct puffs_newinfo *,                     \
+           const struct puffs_cn *);                                   \
+       int fsname##_node_lookupdotdot(struct puffs_usermount *,        \
+           puffs_cookie_t, struct puffs_newinfo *,                     \
+           const struct puffs_cn *);                                   \
+       int fsname##_node_create(struct puffs_usermount *,              \
+           puffs_cookie_t, struct puffs_newinfo *,                     \
+           const struct puffs_cn *, const struct vattr *);             \
+       int fsname##_node_mknod(struct puffs_usermount *,               \
+           puffs_cookie_t, struct puffs_newinfo *,                     \
+           const struct puffs_cn *, const struct vattr *);             \
+       int fsname##_node_open(struct puffs_usermount *,                \
+           puffs_cookie_t, int, const struct puffs_cred *);            \
+       int fsname##_node_close(struct puffs_usermount *,               \
+           puffs_cookie_t, int, const struct puffs_cred *);            \
+       int fsname##_node_access(struct puffs_usermount *,              \
+           puffs_cookie_t, int, const struct puffs_cred *);            \
+       int fsname##_node_getattr(struct puffs_usermount *,             \
+           puffs_cookie_t, struct vattr *, const struct puffs_cred *); \
+       int fsname##_node_setattr(struct puffs_usermount *,             \
+           puffs_cookie_t, const struct vattr *,                       \
+           const struct puffs_cred *);                                 \
+       int fsname##_node_poll(struct puffs_usermount *,                \
+           puffs_cookie_t, int *);                                     \
+       int fsname##_node_mmap(struct puffs_usermount *,                \
+           puffs_cookie_t, vm_prot_t, const struct puffs_cred *);      \
+       int fsname##_node_fsync(struct puffs_usermount *,               \
+           puffs_cookie_t,int);                                        \
+       int fsname##_node_seek(struct puffs_usermount *,                \
+           puffs_cookie_t, off_t, off_t, const struct puffs_cred *);   \
+       int fsname##_node_remove(struct puffs_usermount *,              \
+           puffs_cookie_t, puffs_cookie_t, const struct puffs_cn *);   \
+       int fsname##_node_link(struct puffs_usermount *,                \
+           puffs_cookie_t, puffs_cookie_t, const struct puffs_cn *);   \
+       int fsname##_node_rename(struct puffs_usermount *,              \
+           puffs_cookie_t, puffs_cookie_t, const struct puffs_cn *,    \
+           puffs_cookie_t, puffs_cookie_t, const struct puffs_cn *);   \
+       int fsname##_node_mkdir(struct puffs_usermount *,               \
+           puffs_cookie_t, struct puffs_newinfo *,                     \
+           const struct puffs_cn *, const struct vattr *);             \
+       int fsname##_node_rmdir(struct puffs_usermount *,               \
+           puffs_cookie_t, puffs_cookie_t, const struct puffs_cn *);   \
+       int fsname##_node_symlink(struct puffs_usermount *,             \
+           puffs_cookie_t, struct puffs_newinfo *,                     \
+           const struct puffs_cn *, const struct vattr *,              \
+           const char *);                                              \
+       int fsname##_node_readdir(struct puffs_usermount *,             \
+           puffs_cookie_t, struct dirent *, off_t *, size_t *,         \
+           const struct puffs_cred *, int *, off_t *, size_t *);       \
+       int fsname##_node_readlink(struct puffs_usermount *,            \
+           puffs_cookie_t, const struct puffs_cred *, char *,          \
+           size_t *);                                                  \
+       int fsname##_node_reclaim(struct puffs_usermount *,             \
+           puffs_cookie_t);                                            \
+       int fsname##_node_inactive(struct puffs_usermount *,            \
+           puffs_cookie_t);                                            \
+       int fsname##_node_print(struct puffs_usermount *,               \
+           puffs_cookie_t);                                            \
+       int fsname##_node_pathconf(struct puffs_usermount *,            \
+           puffs_cookie_t, int, register_t *);                         \
+       int fsname##_node_advlock(struct puffs_usermount *,             \
+           puffs_cookie_t, void *, int, struct flock *, int);          \
+       int fsname##_node_read(struct puffs_usermount *, puffs_cookie_t,\
+           uint8_t *, off_t, size_t *, const struct puffs_cred *, int);\
+       int fsname##_node_write(struct puffs_usermount *,               \
+           puffs_cookie_t, uint8_t *, off_t, size_t *,                 \
+           const struct puffs_cred *, int);                            \
+       int fsname##_node_abortop(struct puffs_usermount *,             \
+           puffs_cookie_t, const struct puffs_cn *);                   \
+       int fsname##_node_getextattr(struct puffs_usermount *,          \
+           puffs_cookie_t, int, const char *, size_t *, uint8_t *,     \
+           size_t *, const struct puffs_cred *);                       \
+       int fsname##_node_setextattr(struct puffs_usermount *,          \
+           puffs_cookie_t, int, const char *, uint8_t *, size_t *,     \
+           const struct puffs_cred *);                                 \
+       int fsname##_node_listextattr(struct puffs_usermount *,         \
+           puffs_cookie_t, int, size_t *, uint8_t *, size_t *,         \
+           int, const struct puffs_cred *);                            \
+       int fsname##_node_deleteextattr(struct puffs_usermount *,       \
+           puffs_cookie_t, int, const char *,                          \
+           const struct puffs_cred *);
+
+#define PUFFSOP_INIT(ops)                                              \
+    ops = malloc(sizeof(struct puffs_ops));                            \
+    memset(ops, 0, sizeof(struct puffs_ops))
+#define PUFFSOP_SET(ops, fsname, fsornode, opname)                     \
+    (ops)->puffs_##fsornode##_##opname = fsname##_##fsornode##_##opname
+#define PUFFSOP_SETFSNOP(ops, opname)                                  \
+    (ops)->puffs_fs_##opname = puffs_fsnop_##opname
+
+PUFFSOP_PROTOS(puffs_null)     /* XXX */
+
+#define PNPATH(pnode)  ((pnode)->pn_po.po_path)
+#define PNPLEN(pnode)  ((pnode)->pn_po.po_len)
+#define PCNPATH(pcnode)        ((pcnode)->pcn_po_full.po_path)
+#define PCNPLEN(pcnode)        ((pcnode)->pcn_po_full.po_len)
+
+#define PUFFS_STORE_DCOOKIE(cp, ncp, off)                              \
+if (cp)        {                                                               \
+       *((cp)++) = off;                                                \
+       (*(ncp))++;                                                     \
+}
+
+/* mainloop */
+typedef void (*puffs_ml_loop_fn)(struct puffs_usermount *);
+
+/* framebuf stuff */
+struct puffs_framebuf;
+typedef int (*puffs_framev_readframe_fn)(struct puffs_usermount *,
+                                          struct puffs_framebuf *,
+                                          int, int *);
+typedef        int (*puffs_framev_writeframe_fn)(struct puffs_usermount *,
+                                           struct puffs_framebuf *,
+                                           int, int *);
+typedef int (*puffs_framev_cmpframe_fn)(struct puffs_usermount *,
+                                        struct puffs_framebuf *,
+                                        struct puffs_framebuf *,
+                                        int *);
+typedef void (*puffs_framev_fdnotify_fn)(struct puffs_usermount *, int, int);
+typedef void (*puffs_framev_gotframe_fn)(struct puffs_usermount *,
+                                       struct puffs_framebuf *);
+typedef void (*puffs_framev_cb)(struct puffs_usermount *,
+                               struct puffs_framebuf *,
+                               void *, int);
+#define PUFFS_FBIO_READ                0x01
+#define PUFFS_FBIO_WRITE       0x02
+#define PUFFS_FBIO_ERROR       0x04
+
+#define PUFFS_FBQUEUE_URGENT   0x01
+
+
+__BEGIN_DECLS
+
+#define PUFFS_DEFER ((void *)-1)
+struct puffs_usermount *puffs_init(struct puffs_ops *, const char *,
+                                   const char *, void *, uint32_t);
+int            puffs_mount(struct puffs_usermount *, const char *, int, void*);
+int            puffs_exit(struct puffs_usermount *, int);
+void           puffs_cancel(struct puffs_usermount *, int);
+int            puffs_mainloop(struct puffs_usermount *);
+int            puffs_daemon(struct puffs_usermount *, int, int);
+
+int            puffs_unmountonsignal(int, bool);
+
+
+int    puffs_getselectable(struct puffs_usermount *);
+int    puffs_setblockingmode(struct puffs_usermount *, int);
+int    puffs_getstate(struct puffs_usermount *);
+void   puffs_setstacksize(struct puffs_usermount *, size_t);
+
+void   puffs_ml_setloopfn(struct puffs_usermount *, puffs_ml_loop_fn);
+void   puffs_ml_settimeout(struct puffs_usermount *, struct timespec *);
+
+void                   puffs_setroot(struct puffs_usermount *,
+                                     struct puffs_node *);
+struct puffs_node      *puffs_getroot(struct puffs_usermount *);
+void                   puffs_setrootinfo(struct puffs_usermount *,
+                                         enum vtype, vsize_t, dev_t);
+
+void                   *puffs_getspecific(struct puffs_usermount *);
+void                   puffs_setspecific(struct puffs_usermount *, void *);
+void                   puffs_setmaxreqlen(struct puffs_usermount *, size_t);
+size_t                 puffs_getmaxreqlen(struct puffs_usermount *);
+void                   puffs_setfhsize(struct puffs_usermount *, size_t, int);
+void                   puffs_setmntinfo(struct puffs_usermount *,
+                                        const char *, const char *);
+
+void                   puffs_setncookiehash(struct puffs_usermount *, int);
+
+struct puffs_pathobj   *puffs_getrootpathobj(struct puffs_usermount *);
+
+void                   puffs_setback(struct puffs_cc *, int);
+
+struct puffs_node      *puffs_pn_new(struct puffs_usermount *, void *);
+void                   puffs_pn_remove(struct puffs_node *);
+void                   puffs_pn_put(struct puffs_node *);
+struct vattr           *puffs_pn_getvap(struct puffs_node *);
+void *                 puffs_pn_getpriv(struct puffs_node *);
+void                   puffs_pn_setpriv(struct puffs_node *, void *);
+struct puffs_pathobj   *puffs_pn_getpo(struct puffs_node *);
+struct puffs_usermount *puffs_pn_getmnt(struct puffs_node *);
+
+void   puffs_newinfo_setcookie(struct puffs_newinfo *, puffs_cookie_t);
+void   puffs_newinfo_setvtype(struct puffs_newinfo *, enum vtype);
+void   puffs_newinfo_setsize(struct puffs_newinfo *, voff_t);
+
+void                   *puffs_pn_getmntspecific(struct puffs_node *);
+
+typedef                void *  (*puffs_nodewalk_fn)(struct puffs_usermount *,
+                                            struct puffs_node *, void *);
+void                   *puffs_pn_nodewalk(struct puffs_usermount *,
+                                          puffs_nodewalk_fn, void *);
+
+void                   puffs_setvattr(struct vattr *, const struct vattr *);
+void                   puffs_vattr_null(struct vattr *);
+
+void                   puffs_null_setops(struct puffs_ops *);
+
+int                    puffs_dispatch_create(struct puffs_usermount *,
+                                             struct puffs_framebuf *,
+                                             struct puffs_cc **);
+int                    puffs_dispatch_exec(struct puffs_cc *,
+                                           struct puffs_framebuf **);
+
+/*
+ * generic/dummy routines applicable for some file systems
+ */
+int  puffs_fsnop_unmount(struct puffs_usermount *, int);
+int  puffs_fsnop_statvfs(struct puffs_usermount *, struct statvfs *);
+void puffs_zerostatvfs(struct statvfs *);
+int  puffs_fsnop_sync(struct puffs_usermount *, int waitfor);
+
+int  puffs_genfs_node_getattr(struct puffs_usermount *, puffs_cookie_t,
+                             struct vattr *, const struct puffs_cred *);
+int  puffs_genfs_node_reclaim(struct puffs_usermount *, puffs_cookie_t);
+
+/*
+ * Subroutine stuff
+ */
+
+int            puffs_gendotdent(struct dirent **, ino_t, int, size_t *);
+int            puffs_nextdent(struct dirent **, const char *, ino_t,
+                              uint8_t, size_t *);
+int            puffs_vtype2dt(enum vtype);
+enum vtype     puffs_mode2vt(mode_t);
+void           puffs_stat2vattr(struct vattr *va, const struct stat *);
+mode_t         puffs_addvtype2mode(mode_t, enum vtype);
+
+
+/*
+ * credentials & permissions
+ */
+
+/* Credential fetch */
+int    puffs_cred_getuid(const struct puffs_cred *, uid_t *);
+int    puffs_cred_getgid(const struct puffs_cred *, gid_t *);
+int    puffs_cred_getgroups(const struct puffs_cred *, gid_t *, short *);
+
+/* Credential check */
+bool   puffs_cred_isuid(const struct puffs_cred *, uid_t);
+bool   puffs_cred_hasgroup(const struct puffs_cred *, gid_t);
+bool   puffs_cred_isregular(const struct puffs_cred *);
+bool   puffs_cred_iskernel(const struct puffs_cred *);
+bool   puffs_cred_isfs(const struct puffs_cred *);
+bool   puffs_cred_isjuggernaut(const struct puffs_cred *);
+
+/* misc */
+int    puffs_access(enum vtype, mode_t, uid_t, gid_t, mode_t,
+                    const struct puffs_cred *);
+int    puffs_access_chown(uid_t, gid_t, uid_t, gid_t,
+                          const struct puffs_cred *);
+int    puffs_access_chmod(uid_t, gid_t, enum vtype, mode_t,
+                          const struct puffs_cred *);
+int    puffs_access_times(uid_t, gid_t, mode_t, int,
+                          const struct puffs_cred *);
+
+
+/*
+ * Call Context interfaces relevant for user.
+ */
+
+void                   puffs_cc_yield(struct puffs_cc *);
+void                   puffs_cc_continue(struct puffs_cc *);
+void                   puffs_cc_schedule(struct puffs_cc *);
+int                    puffs_cc_getcaller(struct puffs_cc *,pid_t *,lwpid_t *);
+struct puffs_cc                *puffs_cc_getcc(struct puffs_usermount *);
+
+/*
+ * Flushing / invalidation routines
+ */
+
+int    puffs_inval_namecache_dir(struct puffs_usermount *, puffs_cookie_t);
+int    puffs_inval_namecache_all(struct puffs_usermount *);
+
+int    puffs_inval_pagecache_node(struct puffs_usermount *, puffs_cookie_t);
+int    puffs_inval_pagecache_node_range(struct puffs_usermount *,
+                                        puffs_cookie_t, off_t, off_t);
+int    puffs_flush_pagecache_node(struct puffs_usermount *, puffs_cookie_t);
+int    puffs_flush_pagecache_node_range(struct puffs_usermount *,
+                                        puffs_cookie_t, off_t, off_t);
+
+/*
+ * Path constructicons
+ */
+
+int    puffs_stdpath_buildpath(struct puffs_usermount *,
+                            const struct puffs_pathobj *,
+                            const struct puffs_pathobj *, size_t,
+                            struct puffs_pathobj *);
+int    puffs_stdpath_cmppath(struct puffs_usermount *, struct puffs_pathobj *,
+                          struct puffs_pathobj *, size_t, int);
+void   puffs_stdpath_freepath(struct puffs_usermount *,struct puffs_pathobj *);
+
+void   *puffs_path_walkcmp(struct puffs_usermount *,
+                           struct puffs_node *, void *);
+void   *puffs_path_prefixadj(struct puffs_usermount *,
+                             struct puffs_node *, void *);
+int    puffs_path_pcnbuild(struct puffs_usermount *,
+                           struct puffs_cn *, void *);
+void   puffs_path_buildhash(struct puffs_usermount *, struct puffs_pathobj *);
+void   puffs_set_pathbuild(struct puffs_usermount *, pu_pathbuild_fn); void    puffs_set_pathtransform(struct puffs_usermount *, pu_pathtransform_fn);
+void   puffs_set_pathcmp(struct puffs_usermount *, pu_pathcmp_fn);
+void   puffs_set_pathfree(struct puffs_usermount *, pu_pathfree_fn);
+void   puffs_set_namemod(struct puffs_usermount *, pu_namemod_fn);
+
+void   puffs_set_errnotify(struct puffs_usermount *, pu_errnotify_fn);
+void   puffs_kernerr_log(struct puffs_usermount *, uint8_t, int,
+                         const char *, puffs_cookie_t);
+void   puffs_kernerr_abort(struct puffs_usermount *, uint8_t, int,
+                           const char *, puffs_cookie_t);
+void   puffs_set_prepost(struct puffs_usermount *,
+                         pu_prepost_fn, pu_prepost_fn);
+void   puffs_set_cmap(struct puffs_usermount *, pu_cmap_fn);
+
+/*
+ * Suspension
+ */
+
+int    puffs_fs_suspend(struct puffs_usermount *);
+
+/*
+ * Frame buffering
+ */
+
+void   puffs_framev_init(struct puffs_usermount *,
+                         puffs_framev_readframe_fn,
+                         puffs_framev_writeframe_fn,
+                         puffs_framev_cmpframe_fn,
+                         puffs_framev_gotframe_fn,
+                         puffs_framev_fdnotify_fn);
+
+struct puffs_framebuf  *puffs_framebuf_make(void);
+void                   puffs_framebuf_destroy(struct puffs_framebuf *);
+int                    puffs_framebuf_dup(struct puffs_framebuf *,
+                                          struct puffs_framebuf **);
+void                   puffs_framebuf_recycle(struct puffs_framebuf *);
+int                    puffs_framebuf_reserve_space(struct puffs_framebuf *,
+                                                    size_t);
+
+int    puffs_framebuf_putdata(struct puffs_framebuf *, const void *, size_t);
+int    puffs_framebuf_putdata_atoff(struct puffs_framebuf *, size_t,
+                                    const void *, size_t);
+int    puffs_framebuf_getdata(struct puffs_framebuf *, void *, size_t);
+int    puffs_framebuf_getdata_atoff(struct puffs_framebuf *, size_t,
+                                    void *, size_t);
+
+size_t puffs_framebuf_telloff(struct puffs_framebuf *);
+size_t puffs_framebuf_tellsize(struct puffs_framebuf *);
+size_t puffs_framebuf_remaining(struct puffs_framebuf *);
+int    puffs_framebuf_seekset(struct puffs_framebuf *, size_t);
+int    puffs_framebuf_getwindow(struct puffs_framebuf *, size_t,
+                                void **, size_t *);
+
+int    puffs_framev_enqueue_cc(struct puffs_cc *, int,
+                               struct puffs_framebuf *, int);
+int    puffs_framev_enqueue_cb(struct puffs_usermount *, int,
+                               struct puffs_framebuf *,
+                               puffs_framev_cb, void *, int);
+int    puffs_framev_enqueue_justsend(struct puffs_usermount *, int,
+                                     struct puffs_framebuf *, int, int);
+int    puffs_framev_enqueue_directreceive(struct puffs_cc *, int,
+                                          struct puffs_framebuf *, int);
+int    puffs_framev_enqueue_directsend(struct puffs_cc *, int,
+                                          struct puffs_framebuf *, int);
+int    puffs_framev_enqueue_waitevent(struct puffs_cc *, int, int *);
+int    puffs_framev_framebuf_ccpromote(struct puffs_framebuf *,
+                                       struct puffs_cc *);
+
+int    puffs_framev_addfd(struct puffs_usermount *, int, int);
+int    puffs_framev_enablefd(struct puffs_usermount *, int, int);
+int    puffs_framev_disablefd(struct puffs_usermount *, int, int);
+int    puffs_framev_removefd(struct puffs_usermount *, int, int);
+void   puffs_framev_removeonclose(struct puffs_usermount *, int, int);
+void   puffs_framev_unmountonclose(struct puffs_usermount *, int, int);
+
+__END_DECLS
+
+#endif /* _PUFFS_H_ */
diff --git a/lib/libpuffs/puffs_cc.3 b/lib/libpuffs/puffs_cc.3
new file mode 100644 (file)
index 0000000..4c87cd4
--- /dev/null
@@ -0,0 +1,94 @@
+.\"    $NetBSD: puffs_cc.3,v 1.14 2009/04/11 16:48:53 wiz Exp $
+.\"
+.\" Copyright (c) 2007, 2008 Antti Kantee.  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 AUTHOR 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 AUTHOR 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 February 5, 2012
+.Dt PUFFS_CC 3
+.Os
+.Sh NAME
+.Nm puffs_cc
+.Nd puffs continuation routines
+.Sh LIBRARY
+.Lb libpuffs
+.Sh SYNOPSIS
+.In puffs.h
+.Ft void
+.Fn puffs_cc_yield "struct puffs_cc *pcc"
+.Ft void
+.Fn puffs_cc_continue "struct puffs_cc *pcc"
+.Ft void
+.Fn puffs_cc_schedule "struct puffs_cc *pcc"
+.Ft struct puffs_cc *
+.Fn puffs_cc_getcc "struct puffs_usermount *pu"
+.Sh DESCRIPTION
+These routines are used for the cooperative multitasking suite present
+in puffs.
+.Pp
+.Bl -tag -width xxxx
+.It Fn puffs_cc_yield "pcc"
+Suspend and save the current execution context and return control
+to the previous point.
+In practice, from the file system author perspective, control returns
+back to where either the mainloop or where
+.Fn puffs_dispatch_exec
+was called from.
+.It Fn puffs_cc_continue pcc
+Will suspend current execution and return control to where it was
+before before calling
+.Fn puffs_cc_yield .
+This is rarely called directly but rather through
+.Fn puffs_dispatch_exec .
+.It Fn puffs_cc_schedule "pcc"
+Schedule a continuation.
+As opposed to
+.Fn puffs_cc_continue
+this call returns immediately.
+.Fa pcc
+will be scheduled sometime in the future.
+.It Fn puffs_cc_getcc "pu"
+Returns the current pcc or
+.Dv NULL
+if this is the main thread.
+.Em NOTE :
+The argument
+.Ar pu
+will most likely disappear at some point.
+.El
+.Pp
+Before calling
+.Fn puffs_cc_yield
+a file system will typically want to record some cookie value into its
+own internal bookkeeping.
+This cookie should be hooked to the
+.Va pcc
+so that the correct continuation can be continued when the event it
+was waiting for triggers.
+Alternatively, the
+.Xr puffs_framebuf 3
+framework and
+.Fn puffs_mainloop
+can be used for handling this automatically.
+.Sh SEE ALSO
+.Xr puffs 3 ,
+.Xr puffs_framebuf 3
diff --git a/lib/libpuffs/puffs_cred.3 b/lib/libpuffs/puffs_cred.3
new file mode 100644 (file)
index 0000000..21659a1
--- /dev/null
@@ -0,0 +1,167 @@
+.\"    $NetBSD: puffs_cred.3,v 1.5 2009/04/11 15:36:22 joerg Exp $
+.\"
+.\" Copyright (c) 2007 Antti Kantee.  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 AUTHOR 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 AUTHOR 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 February 5, 2012
+.Dt PUFFS_CRED 3
+.Os
+.Sh NAME
+.Nm puffs_cred
+.Nd puffs credential and access control routines
+.Sh LIBRARY
+.Lb libpuffs
+.Sh SYNOPSIS
+.In puffs.h
+.Ft int
+.Fn puffs_cred_getuid "const struct puffs_cred *pcr" "uid_t *uid"
+.Ft int
+.Fn puffs_cred_getgid "const struct puffs_cred *pcr" "gid_t *gid"
+.Ft int
+.Fo puffs_cred_getgroups
+.Fa "const struct puffs_cred *pcr" "gid_t *gids" "short *ngids"
+.Fc
+.Pp
+.Ft bool
+.Fn puffs_cred_isuid "const struct puffs_cred *pcr" "uid_t uid"
+.Ft bool
+.Fn puffs_cred_hasgroup "const struct puffs_cred *pcr" "gid_t gid"
+.Ft bool
+.Fn puffs_cred_iskernel "const struct puffs_cred *pcr"
+.Ft bool
+.Fn puffs_cred_isfs "const struct puffs_cred *pcr"
+.Ft bool
+.Fn puffs_cred_isjuggernaut "const struct puffs_cred *pcr"
+.Pp
+.Ft int
+.Fo puffs_access
+.Fa "enum vtype type" "mode_t file_mode" "uid_t owner" "gid_t group"
+.Fa "mode_t access_mode" "const struct puffs_cred *pcr"
+.Fc
+.Ft int
+.Fo puffs_access_chown
+.Fa "uid_t owner" "gid_t group" "uid_t newowner" "gid_t newgroup"
+.Fa "const struct puffs_cred *pcr"
+.Fc
+.Ft int
+.Fo puffs_access_chmod
+.Fa "uid_t owner" "gid_t group" "enum vtype type" "mode_t newmode"
+.Fa "const struct puffs_cred *pcr"
+.Fc
+.Ft int
+.Fo puffs_access_times
+.Fa "uid_t owner" "gid_t group" "mode_t file_mode" "int va_utimes_null"
+.Fa "const struct puffs_cred *pcr"
+.Fc
+.Sh DESCRIPTION
+These functions can be used to check operation credentials and perform
+access control.
+The structure
+.Vt struct puffs_cred
+can contain two types of credentials: ones belonging to users and
+ones belonging to the kernel.
+The latter is further divided into generic kernel credentials and
+file system credentials.
+The general rule is that these should be treated as more powerful
+than superuser credentials, but the file system is free to treat
+them as it sees fit.
+.Ss Credentials
+The
+.Fn puffs_cred_get
+family of functions fetch the uid or gid(s) from the given credential
+cookie.
+They return 0 for success or \-1 for an error and set
+.Va errno .
+An error happens when the credentials represent kernel or file system
+credentials and do not contain an uid or gid(s).
+.Pp
+For
+.Fn puffs_cred_getgroups ,
+the argument
+.Fa gids
+should point to an array with room for
+.Fa *ngids
+elements.
+.Pp
+The
+.Fn puffs_cred_is
+family of functions return 1 if the truth value of the function for
+.Fa pcr
+is true and 0 if it is false.
+The function
+.Fn puffs_cred_isjuggernaut
+is true if
+.Fa pcr
+describes superuser, kernel or file system credentials.
+.Ss Access Control
+To help the programmers task of emulating normal kernel file system
+access control semantics, several helper functions are provided to
+check if access is allowed.
+They return 0 if access should be permitted or an errno value to
+indicate that access should be denied with the returned value.
+.Pp
+.Fn puffs_access
+is equivalent to the kernel
+.Fn vaccess
+function.
+The arguments specify current information of the file to be
+tested with the exception of
+.Fa access_mode ,
+which is a combination of
+.Dv PUFFS_VREAD ,
+.Dv PUFFS_VWRITE
+and
+.Dv PUFFS_VEXEC
+indicating the desired file access mode.
+.Pp
+The rest of the functions provide UFS semantics for operations.
+.Fn puffs_access_chown
+checks if it is permissible to chown a file with the current uid
+and gid to the new uid and gid with the credentials of
+.Fa pcr .
+.Pp
+.Fn puffs_access_chmod
+checks against permission to chmod a file of type
+.Fa type
+to the mode
+.Fa newmode .
+.Pp
+Finally,
+.Fn puffs_access_times
+checks if it is allowable to update the timestamps of a file.
+The argument
+.Fa va_utimes_null
+signals if the flags
+.Dv VA_UTIMES_NULL
+was set in
+.Fa va_vaflags
+of
+.Va struct vattr .
+If coming from a path where this information is unavailable, passing
+0 as this argument restricts the permission of modification to the
+owner and superuser.
+Otherwise the function checks for write permissions to the node and
+returns success if the caller has write permissions.
+.Sh SEE ALSO
+.Xr puffs 3 ,
+.Xr vnode 9
diff --git a/lib/libpuffs/puffs_flush.3 b/lib/libpuffs/puffs_flush.3
new file mode 100644 (file)
index 0000000..bfcad3b
--- /dev/null
@@ -0,0 +1,113 @@
+.\"    $NetBSD: puffs_flush.3,v 1.8 2009/02/20 14:26:56 pooka Exp $
+.\"
+.\" Copyright (c) 2007 Antti Kantee.  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 AUTHOR 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 AUTHOR 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 February 5, 2012
+.Dt PUFFS_FLUSH 3
+.Os
+.Sh NAME
+.Nm puffs_flush
+.Nd puffs kernel cache flushing and invalidation routines
+.Sh LIBRARY
+.Lb libpuffs
+.Sh SYNOPSIS
+.In puffs.h
+.Ft int
+.Fo puffs_inval_namecache_dir
+.Fa "struct puffs_usermount *pu" "puffs_cookie_t cookie"
+.Fc
+.Ft int
+.Fn puffs_inval_namecache_all "struct puffs_usermount *pu"
+.Ft int
+.Fo puffs_inval_pagecache_node
+.Fa "struct puffs_usermount *pu" "puffs_cookie_t cookie"
+.Fc
+.Ft int
+.Fo puffs_inval_pagecache_node_range
+.Fa "struct puffs_usermount *pu" "puffs_cookie_t cookie" "off_t start"
+.Fa "off_t end"
+.Fc
+.Ft int
+.Fo puffs_flush_pagecache_node
+.Fa "struct puffs_usermount *pu" "puffs_cookie_t cookie"
+.Fc
+.Ft int
+.Fo puffs_flush_pagecache_node_range
+.Fa "struct puffs_usermount *pu" "puffs_cookie_t cookie" "off_t start"
+.Fa "off_t end"
+.Fc
+.Sh DESCRIPTION
+These routines are used inform the kernel that any information it might
+have cached is no longer valid.
+.Fn puffs_inval_namecache_dir
+invalidates the name cache for a given directory.
+The argument
+.Va cookie
+should describe an existing and valid directory cookie for the file
+system.
+Similarly,
+.Fn puffs_inval_namecache_all
+invalidates the name cache for the entire file system
+(this routine might go away).
+.Pp
+The cached pages (file contents) for a regular file described by
+.Va cookie
+are invalidated using
+.Fn puffs_inval_pagecache_node .
+A specific range can be invalidated using
+.Fn puffs_inval_pagecache_node_range
+for a platform specific page level granularity.
+The offset
+.Va start
+will be
+.Em truncated
+to a page boundary while
+.Va end
+will be
+.Em "rounded up"
+to the next page boundary.
+As a special case, specifying 0 as
+.Va end
+will invalidate all contents from
+.Va start
+to the end of the file.
+.Pp
+It is especially important to note that these routines will not only
+invalidate data in the "read cache", but also data in the "write back"
+cache (conceptually speaking; in reality they are the same cache), which
+has not yet been flushed to the file server.
+Therefore any unflushed data will be lost.
+.Pp
+The counterparts of the invalidation routines are the flushing routines
+.Fn puffs_flush_pagecache_node
+and
+.Fn puffs_flush_pagecache_node_range ,
+which force unwritten data from the kernel page cache to be written.
+For the flush range version, the same range rules as with the
+invalidation routine apply.
+The data is flushed asynchronously, i.e. if the routine returns
+successfully, all the caller knows is that the data has been queued
+for writing.
+.Sh SEE ALSO
+.Xr puffs 3
diff --git a/lib/libpuffs/puffs_framebuf.3 b/lib/libpuffs/puffs_framebuf.3
new file mode 100644 (file)
index 0000000..3f6bd86
--- /dev/null
@@ -0,0 +1,620 @@
+.\"    $NetBSD: puffs_framebuf.3,v 1.29 2010/04/01 09:57:00 pooka Exp $
+.\"
+.\" Copyright (c) 2007 Antti Kantee.  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 AUTHOR 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 AUTHOR 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 February 5, 2012
+.Dt PUFFS_FRAMEBUF 3
+.Os
+.Sh NAME
+.Nm puffs_framebuf
+.Nd buffering and event handling for networked file systems
+.Sh LIBRARY
+.Lb libpuffs
+.Sh SYNOPSIS
+.In puffs.h
+.Ft struct puffs_framebuf *
+.Fn puffs_framebuf_make
+.Ft void
+.Fn puffs_framebuf_destroy "struct puffs_framebuf *pufbuf"
+.Ft void
+.Fn puffs_framebuf_recycle "struct puffs_framebuf *pufbuf"
+.Ft int
+.Fn puffs_framebuf_reserve_space "struct puffs_framebuf *pufbuf" "size_t space"
+.Ft int
+.Fo puffs_framebuf_putdata
+.Fa "struct puffs_framebuf *pufbuf" "const void *data" "size_t dlen"
+.Fc
+.Ft int
+.Fo puffs_framebuf_putdata_atoff
+.Fa "struct puffs_framebuf *pufbuf" "size_t offset" "const void *data"
+.Fa "size_t dlen"
+.Fc
+.Ft int
+.Fo puffs_framebuf_getdata
+.Fa "struct puffs_framebuf *pufbuf" "void *data" "size_t dlen"
+.Fc
+.Ft int
+.Fo puffs_framebuf_getdata_atoff
+.Fa "struct puffs_framebuf *pufbuf" "size_t offset"
+.Fa "void *data" "size_t dlen"
+.Fc
+.Ft size_t
+.Fn puffs_framebuf_telloff "struct puffs_framebuf *pufbuf"
+.Ft size_t
+.Fn puffs_framebuf_tellsize "struct puffs_framebuf *pufbuf"
+.Ft size_t
+.Fn puffs_framebuf_remaining "struct puffs_framebuf *pufbuf"
+.Ft int
+.Fn puffs_framebuf_seekset "struct puffs_framebuf *pufbuf" "size_t offset"
+.Ft int
+.Fo puffs_framebuf_getwindow
+.Fa "struct puffs_framebuf *pufbuf" "size_t offset"
+.Fa "void **winp" "size_t *winlen"
+.Fc
+.Ft int
+.Fo puffs_framev_enqueue_cc
+.Fa "struct puffs_cc *pcc" "int fd" "struct puffs_framebuf *pufbuf" "int flags"
+.Fc
+.Ft void
+.Fo puffs_framev_cb
+.Fa "struct puffs_usermount *pu" "int fd" "struct puffs_framebuf *pufbuf"
+.Fa "void *arg" "int flags"
+.Fa "int error"
+.Fc
+.Ft void
+.Fo puffs_framev_enqueue_cb
+.Fa "struct puffs_usermount *pu" "int fd" "struct puffs_framebuf *pufbuf"
+.Fa "puffs_framebuf_cb fcb" "void *fcb_arg" "int flags"
+.Fc
+.Ft void
+.Fo puffs_framev_enqueue_justsend
+.Fa "struct puffs_usermount *pu" "int fd" "struct puffs_framebuf *pufbuf"
+.Fa "int waitreply" "int flags"
+.Fc
+.Ft void
+.Fo puffs_framev_enqueue_directsend
+.Fa "struct puffs_usermount *pu" "int fd" "struct puffs_framebuf *pufbuf"
+.Fa "int flags"
+.Fc
+.Ft void
+.Fo puffs_framev_enqueue_directreceive
+.Fa "struct puffs_usermount *pu" "int fd" "struct puffs_framebuf *pufbuf"
+.Fa "int flags"
+.Fc
+.Ft int
+.Fo puffs_framev_framebuf_ccpromote
+.Fa "struct puffs_framebuf *pufbuf" "struct puffs_cc *pcc"
+.Fc
+.Ft int
+.Fn puffs_framev_enqueue_waitevent "struct puffs_cc *pcc" "int fd" "int *what"
+.Ft int
+.Fo puffs_framev_readframe_fn
+.Fa "struct puffs_usermount *pu" "struct puffs_framebuf *pufbuf"
+.Fa "int fd" "int *done"
+.Fc
+.Ft int
+.Fo puffs_framev_writeframe_fn
+.Fa "struct puffs_usermount *pu" "struct puffs_framebuf *pufbuf"
+.Fa "int fd" "int *done"
+.Fc
+.Ft int
+.Fo puffs_framev_cmpframe_fn
+.Fa "struct puffs_usermount *pu"
+.Fa "struct puffs_framebuf *cmp1" "struct puffs_framebuf *cmp2" "int *notresp"
+.Fc
+.Ft void
+.Fo puffs_framev_gotframe_fn
+.Fa "struct puffs_usermount *pu" "struct puffs_framebuf *pufbuf"
+.Fc
+.Ft void
+.Fo puffs_framev_fdnotify_fn
+.Fa "struct puffs_usermount *pu" "int fd" "int what"
+.Fc
+.Ft void
+.Fo puffs_framev_init
+.Fa "struct puffs_usermount *pu"
+.Fa "puffs_framev_readframe_fn rfb" "puffs_framev_writeframe_fn wfb"
+.Fa "puffs_framev_cmpframe_fn cmpfb" "puffs_framev_gotframe_fn gotfb"
+.Fa "puffs_framev_fdnotify_fn fdnotfn"
+.Fc
+.Ft int
+.Fn puffs_framev_addfd "struct puffs_usermount *pu" "int fd" "int what"
+.Ft int
+.Fn puffs_framev_enablefd "struct puffs_usermount *pu" "int fd" "int what"
+.Ft int
+.Fn puffs_framev_disablefd "struct puffs_usermount *pu" "int fd" "int what"
+.Ft int
+.Fn puffs_framev_removefd "struct puffs_usermount *pu" "int fd" "int error"
+.Ft void
+.Fo puffs_framev_unmountonclose
+.Fa "struct puffs_usermount *pu" "int fd" "int what"
+.Fc
+.Sh DESCRIPTION
+The
+.Nm
+routines provide buffering and an event loop structured around the
+buffers.
+It operates on top of the puffs continuation framework,
+.Xr puffs_cc 3 ,
+and multiplexes execution automatically to an instance whenever
+one is runnable.
+.Pp
+The file system is entered in three different ways:
+.Bl -bullet -offset indent
+.It
+An event arrives from the kernel and the
+.Xr puffs_ops 3
+callbacks are called to start processing the event.
+.It
+A file system which has sent out a request receives a response.
+Execution is resumed from the place where the file system yielded.
+.It
+A request from a peer arrives.
+A request is an incoming PDU which is not a response to any outstanding
+request.
+.El
+.Pp
+.Nm
+is used by defining various callbacks and providing I/O descriptors,
+which are then monitored for activity by the library.
+A descriptor, when present, can be either enabled or disabled for
+input and output.
+If a descriptor is not enabled for a certain direction, the callbacks
+will not be called even if there were activity on the descriptor.
+For example, even if a network socket has been added and there is
+input data in the socket buffer, the read callback will be called
+only if the socket has been enabled for reading.
+.Pp
+File descriptors are treated like sockets: they have two sides, a read
+side and a write side.
+The framework determines that one side of the descriptor has been
+closed if the supplied I/O callbacks return an error or if the I/O
+multiplexing call says a side has been closed.
+It is still possible, from the framework perspective, to write to a
+file descriptor whose read side is closed.
+However, it is not possible to wait for a response on such a file
+descriptor.
+Conversely, it is possible to read responses from a descriptor whose
+write side is closed.
+It should be stressed that the implementation underlying the file
+descriptor might not support this.
+.Pp
+The following callbacks can be defined, cf.
+.Fn puffs_framev_init ,
+and all are optional.
+None of them should block, because this would cause the entire file server
+to block.
+One option is to make the descriptors non-blocking before adding them.
+.Bl -tag -width "xfdnotfnx"
+.It rfb
+Read a frame from the file descriptor onto the specified buffer.
+.It wfb
+Write a frame from the specified buffer into the file descriptor.
+.It cmpfb
+Identify if a buffer is the response to the specified buffer.
+.It gotfb
+Called iff no outstanding request matches the incoming frame.
+In other words, this is called when we receive a request from a peer.
+.It fdnotfn
+Receive notifications about a change-of-state in a file descriptor's
+status.
+.El
+.Pp
+Better descriptions for each callback are given below.
+.Pp
+The buffers of
+.Nm
+provide automatic memory management of buffers for the file servers.
+They provide a cursor to the current buffer offset.
+Reading or writing data through the normal routines will advance that cursor.
+Additionally, the buffer size is provided to the user.
+It represents the maximum offset where data was written.
+.Pp
+Generally the write functions will fail if the cannot allocate enough
+memory to satisfy the buffer length requirements.
+Read functions will fail if the amount of data written to the buffer
+is not large enough to satisfy the read.
+.Bl -tag -width xxxx
+.It Fn puffs_framebuf_make
+Create a buffer.
+Return the address of the buffer or
+.Dv NULL
+in case no memory was available.
+.It Fn puffs_framebuf_destroy pufbuf
+Free memory used by buffer.
+.It Fn puffs_framebuf_recycle pufbuf
+Reset offsets so that buffer can be reused.
+Does not free memory or reallocate memory.
+.It Fn puffs_framebuf_reserve_space pufbuf space
+Make sure that the buffer has
+.Ar space
+bytes of available memory starting from the current offset.
+This is not strictly necessary, but can be used for optimizations
+where it is known in advance how much memory will be required.
+.It Fn puffs_framebuf_putdata pufbuf data dlen
+Write
+.Ar dlen
+amount of data from the address
+.Ar data
+into the buffer.
+Moves the offset cursor forward
+.Ar dlen
+bytes.
+.It Fn puffs_framebuf_putdata_atoff pufbuf offset data dlen
+Like
+.Fn puffs_framebuf_putdata ,
+except writes data at buffer offset
+.Ar offset .
+It is legal to write past the current end of the buffer.
+Does NOT modify the current offset cursor.
+.It Fn puffs_framebuf_getdata pufbuf data dlen
+Read
+.Ar dlen
+bytes of data from the buffer into
+.Ar data .
+Advances the offset cursor.
+.It Fn puffs_framebuf_getdata_atoff pufbuf offset data dlen
+Read data from buffer position
+.Ar offset .
+Does NOT modify the offset cursor.
+.It Fn puffs_framebuf_telloff pufbuf
+Return the offset into the buffer.
+.It Fn puffs_framebuf_tellsize pufbuf
+Return the maximum offset where data has been written, i.e. buffer size.
+.It Fn puffs_framebuf_remaining pufbuf
+Distance from current offset to the end of the buffer, i.e. size-offset.
+.It Fn puffs_framebuf_seekset pufbuf offset
+Set the offset cursor to the position
+.Ar offset .
+This does NOT modify the buffer size, but reserves at least
+enough memory memory for a write to
+.Ar offset
+and will fail if memory cannot be allocated.
+.It Fn puffs_framebuf_getwindow pufbuf offset winp winlen
+Get a direct memory window into the buffer starting from
+.Ar offset .
+The maximum mapped window size will be
+.Ar winlen
+bytes, but this routine might return a smaller window and the caller
+should always check the actual mapped size after the call.
+The window is returned in
+.Ar winp .
+This function not modify the buffer offset, but it DOES set the buffer
+size to
+.Ar offset +
+.Ar winlen
+in case that value is greater than the current size.
+The window is valid until the next until the next
+.Fn puffs_framebuf
+call operating on the buffer in question.
+.It Fn puffs_framev_enqueue_cc pcc fd pufbuf flags
+Add the buffer
+.Ar pufbuf
+to outgoing queue of descriptor
+.Ar fd
+and yield with the continuation
+.Ar pcc .
+Execution is resumed once a response is received.
+Returns 0 if the buffer was successfully enqueued (not necessarily
+delivered) and non-zero to signal a non-recoverable error.
+.Pp
+Usually the buffer is placed at the end of the output queue.
+However, if
+.Ar flags
+contains
+.Dv PUFFS_FBQUEUE_URGENT ,
+.Ar pufbuf
+is placed in the front of the queue to be sent immediately after
+the current PDU (if any) has been sent.
+.It Fn puffs_framev_enqueue_cb pu fd pufbuf fcb fcb_arg flags
+Enqueue the buffer
+.Ar pufbuf
+for outgoing data and immediately return.
+Once a response arrives, the callback
+.Fn fcb
+will be called with the argument
+.Ar fcb_arg .
+The callback function
+.Fn fcb
+is responsible for freeing the buffer.
+Returns 0 if the buffer was successfully enqueued (not necessarily
+delivered) and non-zero to signal a non-recoverable error.
+.Pp
+See
+.Fn puffs_framev_enqueue_cc
+for
+.Ar flags .
+.It Fn puffs_framev_cb pu pufbuf arg error
+Callback function.
+Called when a response to a specific request arrives from the server.
+If
+.Ar error
+is non-zero, the framework was unable to obtain a response and the
+function should not examine the contents of
+.Ar pufbuf ,
+only do resource cleanup.
+May not block.
+.It Fn puffs_framev_enqueue_justsend pu fd pufbuf waitreply flags
+Enqueue the buffer
+.Ar pufbuf
+for outgoing traffic and immediately return.
+The parameter
+.Ar waitreply
+can be used to control if the buffer is to be freed immediately after
+sending of if a response is expected and the buffer should be freed
+only after the response arrives (receiving an unexpected message from
+the server is treated as an error).
+Returns 0 if the buffer was successfully enqueued (not necessarily
+delivered) and non-zero to signal a non-recoverable error.
+.Pp
+See
+.Fn puffs_framev_enqueue_cc
+for
+.Ar flags .
+.It Fn puffs_framev_enqueue_directsend pcc fd pufbuf flags
+Acts like
+.Fn puffs_framev_enqueue_justsend
+with the exception that the call yields until the frame has been sent.
+As opposed to
+.Fn puffs_framev_enqueue_cc ,
+the routine does not wait for input, but returns immediately after
+sending the frame.
+.Pp
+See
+.Fn puffs_framev_enqueue_cc
+for
+.Ar flags .
+.It Fn puffs_framev_enqueue_directreceive pcc fd pufbuf flags
+Receive data into
+.Ar pufbuf .
+This routine yields until a complete frame has been read into
+the buffer by the readframe routine.
+.Pp
+See
+.Fn puffs_framev_enqueue_cc
+for
+.Ar flags .
+.It Fn puffs_framev_framebuf_ccpromote pufbuf pcc
+Promote the framebuffer
+.Ar pufbuf
+sent with
+.Fn puffs_framev_enqueue_cb
+or
+.Fn puffs_framev_enqueue_justsend
+to a wait using
+.Ar pcc
+and yield until the result arrives.
+The response from the file server for
+.Ar pufbuf
+must not yet have arrived.
+If sent with
+.Fn puffs_framev_enqueue_justsend ,
+the call must be expecting a response.
+.It Fn puffs_framev_enqueue_waitevent pcc fd what
+Waits for an event in
+.Ar what
+to happen on file descriptor
+.Ar fd .
+The events which happened are returned back in
+.Ar what .
+The possible events are
+.Dv PUFFS_FBIO_READ ,
+.Dv PUFFS_FBIO_WRITE ,
+and
+.Dv PUFFS_FBIO_ERROR ,
+specifying read, write and error conditions, respectively.
+Error is always checked.
+.Pp
+This call does not depend on if the events were previously enabled on
+the file descriptor - the specified events are always checked
+regardless.
+.Pp
+There is currently no other way to cancel or timeout a call except by
+removing the file descriptor in question.
+This may change in the future.
+.It Fn puffs_framev_readframe_fn pu pufbuf fd done
+Callback function.
+Read at most one frame from file descriptor
+.Ar fd
+into the buffer
+.Ar pufbuf .
+If a complete frame is read, the value pointed to by
+.Ar done
+must be set to 1.
+This function should return 0 on success (even if a complete frame was not
+yet read) and a non-zero
+.Er errno
+to signal a fatal error.
+In case a fatal error is returned, the read side of the file descriptor
+is marked closed.
+This routine will be called with the same buffer argument until a
+complete frame has been read.
+May not block.
+.It Fn puffs_framev_writeframe_fn pu pufbuf fd done
+Write the frame contained in
+.Ar pufbuf
+to the file descriptor
+.Ar fd .
+In case the entire frame is successfully written,
+.Ar *done
+should be set to 1.
+This function should return 0 on success (even if a complete frame was not
+yet written) and a non-zero
+.Er errno
+to signal a fatal error.
+In case a fatal error is returned, the write side of the file descriptor
+is marked closed.
+This routine will be called with the same buffer argument until the
+complete frame has been written.
+May not block.
+.Pp
+It is a good idea to make sure that this function can handle a possible
+.Dv SIGPIPE
+caused by a closed connection.
+For example, the file server can opt to trap
+.Dv SIGPIPE
+or, if writing to a socket, call
+.Fn send
+with the flag
+.Dv MSG_NOSIGNAL
+instead of using
+.Fn write .
+.It Fn puffs_framev_cmpframe_fn pu pufbuf_cmp1 pufbuf_cmp2 notresp
+Compare the file system internal request tags in
+.Ar pufbuf_cmp1
+and
+.Ar pufbuf_cmp2 .
+Should return 0 if the tags are equal, 1 if first buffer's tag is
+greater than the second and \-1 if it is smaller.
+The definitions "greater" and "smaller" are used transparently by
+the library, e.g. like
+.Xr qsort 3 .
+If it can be determined from
+.Ar pufbuf_cmp1
+that it is not a response to any outstanding request,
+.Ar notresp
+should be set to non-zero.
+This will cause
+.Nm
+to skip the test of the buffer against the rest of the outstanding
+request.
+May not block.
+.It Fn puffs_framev_gotframe_fn pu pufbuf
+Called when no outstanding request matches an incoming frame.
+The ownership of
+.Ar pufbuf
+is transferred to the called function and must be destroyed once
+processing is over.
+May not block.
+.It Fn puffs_framev_fdnotify_fn pu fd what
+Is called when the read or write side of the file descriptor
+.Ar fd
+is closed.
+It is called once for each side, the bitmask parameter
+.Ar what
+specified what is currently closed:
+.Dv PUFFS_FBIO_READ
+and
+.Dv PUFFS_FBIO_WRITE
+for read and write, respectively.
+.It Fn puffs_framev_init pu rfb wfb cmpfb gotfb fdnotfn
+Initializes the given callbacks to the system.
+They will be used when
+.Fn puffs_mainloop
+is called.
+The framework provides the routines
+.Fn puffs_framev_removeonclose
+and
+.Fn puffs_framev_unmountonclose ,
+which can be given as
+.Ar fdnotfn .
+The first one removes the file descriptor once both sides are closed
+while the second one unmounts the file system and exits the mainloop.
+.It Fn puffs_framev_addfd pu fd what
+Add file descriptor
+.Ar fd
+to be handled by the framework.
+It is legal to add a file descriptor either before calling
+.Fn puffs_mainloop
+or at time when running.
+The parameter
+.Ar what
+controls enabling of input and output events and can be a bitwise
+combination of
+.Dv PUFFS_FBIO_READ
+and
+.Dv PUFFS_FBIO_WRITE .
+If not specified, the descriptor will be in a disabled state.
+.It Fn puffs_framev_enablefd pu fd what
+Enable events of type
+.Ar what
+for file descriptor
+.Ar fd .
+.It Fn puffs_framev_disablefd pu fd what
+Disable events of type
+.Ar what
+for file descriptor
+.Ar fd .
+.It Fn puffs_framev_removefd pu fd error
+Remove file descriptor
+.Ar fd
+from the list of descriptors handled by the framework.
+Removing a file descriptor causes all operations blocked either on
+output or input to be released with the error value
+.Ar error .
+In case 0 is supplied as this parameter,
+.Er ECONNRESET
+is used.
+.Pp
+The file system
+.Em must
+explicitly remove each fd it has added.
+A good place to do this is
+.Fn puffs_framev_fdnotify_fn
+or
+.Fn puffs_node_reclaim ,
+depending a little on the structure of the file system.
+.It Fn puffs_framev_unmountonclose pu fd what
+This is library provided convenience routine for
+.Fn puffs_framev_fdnotify_fn .
+It unmounts the file system when both the read and write side are
+closed.
+It is useful for file systems such as
+.Xr mount_psshfs 8
+which depend on a single connection.
+.El
+.Sh CODE REFERENCES
+The current users of
+.Nm
+in the tree are
+.Xr mount_psshfs 8
+and
+.Xr mount_9p 8 .
+See
+.Pa src/usr.sbin/puffs/mount_psshfs
+and
+.Pa src/usr.sbin/puffs/mount_9p
+for the respective usage examples.
+.Sh RETURN VALUES
+These functions generally return \-1 to signal error and set
+.Er errno
+to indicate the type of error.
+.Sh SEE ALSO
+.Xr puffs 3 ,
+.Xr puffs_cc 3 ,
+.Xr puffs_ops 3
+.Rs
+.%A Antti Kantee
+.%D September 2007
+.%I Helsinki University of Technology
+.%R Tech Report TKK-TKO-B157
+.%T Using puffs for Implementing Client-Server Distributed File Systems
+.Re
+.Rs
+.%A Antti Kantee
+.%D March 2008
+.%J Proceedings of AsiaBSDCon 2008
+.%P pp. 55-70
+.%T Send and Receive of File System Protocols: Userspace Approach With puffs
+.Re
diff --git a/lib/libpuffs/puffs_node.3 b/lib/libpuffs/puffs_node.3
new file mode 100644 (file)
index 0000000..1fb36e7
--- /dev/null
@@ -0,0 +1,101 @@
+.\"    $NetBSD: puffs_node.3,v 1.5 2009/05/13 22:42:31 wiz Exp $
+.\"
+.\" Copyright (c) 2007 Antti Kantee.  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 AUTHOR 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 AUTHOR 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 February 5, 2012
+.Dt PUFFS_NODE 3
+.Os
+.Sh NAME
+.Nm puffs_node
+.Nd puffs node routines
+.Sh LIBRARY
+.Lb libpuffs
+.Sh SYNOPSIS
+.In puffs.h
+.Ft struct puffs_node *
+.Fn puffs_pn_new "struct puffs_usermount *pu" "void *priv"
+.Ft void *
+.Fo puffs_nodewalk_fn
+.Fa "struct puffs_usermount *pu" "struct puffs_node *pn" "void *arg"
+.Fc
+.Ft void *
+.Fo puffs_pn_nodewalk
+.Fa "struct puffs_usermount *pu" "puffs_nodewalk_fn nwfn" "void *arg"
+.Fc
+.Ft void
+.Fn puffs_pn_remove "struct puffs_node *pn"
+.Ft void
+.Fn puffs_pn_put "struct puffs_node *pn"
+.Sh DESCRIPTION
+.Bl -tag -width xxxx
+.It Fn puffs_pn_new pu priv
+Create a new node and attach it to the mountpoint
+.Ar pu .
+The argument
+.Ar priv
+can be used for associating file system specific data with the new
+node and will not be accessed by puffs.
+.It Fn puffs_nodewalk_fn pu pn arg
+A callback for
+.Fn puffs_nodewalk .
+The list of nodes is iterated in the argument
+.Ar pn
+and the argument
+.Ar arg
+is the argument given to
+.Fn puffs_nodewalk .
+.It Fn puffs_nodewalk pu nwfn arg
+Walk all nodes associted with the mountpoint
+.Ar pu
+and call
+.Fn nwfn
+for them.
+The walk is aborted if
+.Fn puffs_nodewalk_fn
+returns a value which is not
+.Dv NULL .
+This value is also returned this function.
+In case the whole set of nodes is traversed,
+.Dv NULL
+is returned.
+This function is useful for example in handling the
+.Fn puffs_fs_sync
+callback, when cached data for every node should be flushed to stable
+storage.
+.It Fn puffs_pn_remove pn
+Signal that a node has been removed from the file system, but do not
+yet release resources associated with the node.
+This will prevent the nodewalk functions from accessing the node.
+If necessary, this is usually called from
+.Fn puffs_node_remove
+and
+.Fn puffs_node_rmdir .
+.It Fn puffs_pn_put pn
+Free all resources associated with node
+.Ar pn .
+This is typically called from
+.Fn puffs_node_reclaim .
+.El
+.Sh SEE ALSO
+.Xr puffs 3
diff --git a/lib/libpuffs/puffs_ops.3 b/lib/libpuffs/puffs_ops.3
new file mode 100644 (file)
index 0000000..ef4c6cd
--- /dev/null
@@ -0,0 +1,782 @@
+.\"    $NetBSD: puffs_ops.3,v 1.29 2011/07/04 08:07:30 manu Exp $
+.\"
+.\" Copyright (c) 2007 Antti Kantee.  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 AUTHOR 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 AUTHOR 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 February 5, 2012
+.Dt PUFFS_OPS 3
+.Os
+.Sh NAME
+.Nm puffs_ops
+.Nd puffs callback operations
+.Sh LIBRARY
+.Lb libpuffs
+.Sh SYNOPSIS
+.In puffs.h
+.Ft int
+.Fo puffs_fs_statvfs
+.Fa "struct puffs_usermount *pu" "struct statvfs *sbp"
+.Fc
+.Ft int
+.Fo puffs_fs_sync
+.Fa "struct puffs_usermount *pu" "int waitfor" "const struct puffs_cred *pcr"
+.Fc
+.Ft int
+.Fo puffs_fs_fhtonode
+.Fa "struct puffs_usermount *pu" "void *fid" "size_t fidsize"
+.Fa "struct puffs_newinfo *pni"
+.Fc
+.Ft int
+.Fo puffs_fs_nodetofh
+.Fa "struct puffs_usermount *pu" "puffs_cookie_t cookie" "void *fid"
+.Fa "size_t *fidsize"
+.Fc
+.Ft void
+.Fo puffs_fs_extattrctl
+.Fa "struct puffs_usermount *pu" "int cmd" "puffs_cookie_t cookie" "int flags"
+.Fa "int attrnamespace" "const char *attrname"
+.Fc
+.Ft int
+.Fo puffs_fs_unmount
+.Fa "struct puffs_usermount *pu" "int flags"
+.Fc
+.Ft int
+.Fo puffs_node_lookup
+.Fa "struct puffs_usermount *pu" "puffs_cookie_t opc"
+.Fa "struct puffs_newinfo *pni" "const struct puffs_cn *pcn"
+.Fc
+.Ft int
+.Fo puffs_node_create
+.Fa "struct puffs_usermount *pu" "puffs_cookie_t opc"
+.Fa "struct puffs_newinfo *pni" "const struct puffs_cn *pcn"
+.Fa "const struct vattr *vap"
+.Fc
+.Ft int
+.Fo puffs_node_mknod
+.Fa "struct puffs_usermount *pu" "puffs_cookie_t opc"
+.Fa "struct puffs_newinfo *pni" "const struct puffs_cn *pcn"
+.Fa "const struct vattr *vap"
+.Fc
+.Ft int
+.Fo puffs_node_open
+.Fa "struct puffs_usermount *pu" "puffs_cookie_t opc" "int mode"
+.Fa "const struct puffs_cred *pcr"
+.Fc
+.Ft int
+.Fo puffs_node_close
+.Fa "struct puffs_usermount *pu" "puffs_cookie_t opc" "int flags"
+.Fa "const struct puffs_cred *pcr"
+.Fc
+.Ft int
+.Fo puffs_node_access
+.Fa "struct puffs_usermount *pu" "puffs_cookie_t opc" "int mode"
+.Fa "const struct puffs_cred *pcr"
+.Fc
+.Ft int
+.Fo puffs_node_getattr
+.Fa "struct puffs_usermount *pu" "puffs_cookie_t opc" "struct vattr *vap"
+.Fa "const struct puffs_cred *pcr"
+.Fc
+.Ft int
+.Fo puffs_node_setattr
+.Fa "struct puffs_usermount *pu" "puffs_cookie_t opc" "const struct vattr *vap"
+.Fa "const struct puffs_cred *pcr"
+.Fc
+.Ft int
+.Fo puffs_node_poll
+.Fa "struct puffs_usermount *pu" "puffs_cookie_t opc" "int *events"
+.Fc
+.Ft int
+.Fo puffs_node_mmap
+.Fa "struct puffs_usermount *pu" "puffs_cookie_t opc" "int flags"
+.Fa "const struct puffs_cred *pcr"
+.Fc
+.Ft int
+.Fo puffs_node_fsync
+.Fa "struct puffs_usermount *pu" "puffs_cookie_t opc"
+.Fa "int flags"
+.Fc
+.Ft int
+.Fo puffs_node_seek
+.Fa "struct puffs_usermount *pu" "puffs_cookie_t opc" "off_t oldoff"
+.Fa "off_t newoff" "const struct puffs_cred *pcr"
+.Fc
+.Ft int
+.Fo puffs_node_remove
+.Fa "struct puffs_usermount *pu" "puffs_cookie_t opc" "puffs_cookie_t targ"
+.Fa "const struct puffs_cn *pcn"
+.Fc
+.Ft int
+.Fo puffs_node_link
+.Fa "struct puffs_usermount *pu" "puffs_cookie_t opc" "puffs_cookie_t targ"
+.Fa "const struct puffs_cn *pcn"
+.Fc
+.Ft int
+.Fo puffs_node_rename
+.Fa "struct puffs_usermount *pu" "puffs_cookie_t opc" "puffs_cookie_t src"
+.Fa "const struct puffs_cn *pcn_src" "puffs_cookie_t targ_dir"
+.Fa "puffs_cookie_t targ" "const struct puffs_cn *pcn_targ"
+.Fc
+.Ft int
+.Fo puffs_node_mkdir
+.Fa "struct puffs_usermount *pu" "puffs_cookie_t opc"
+.Fa "struct puffs_newinfo *pni" "const struct puffs_cn *pcn"
+.Fa "const struct vattr *vap"
+.Fc
+.Ft int
+.Fo puffs_node_rmdir
+.Fa "struct puffs_usermount *pu" "puffs_cookie_t opc" "puffs_cookie_t targ"
+.Fa "const struct puffs_cn *pcn"
+.Fc
+.Ft int
+.Fo puffs_node_readdir
+.Fa "struct puffs_usermount *pu" "puffs_cookie_t opc" "struct dirent *dent"
+.Fa "off_t *readoff" "size_t *reslen" "const struct puffs_cred *pcr"
+.Fa "int *eofflag" "off_t *cookies" "size_t *ncookies"
+.Fc
+.Ft int
+.Fo puffs_node_symlink
+.Fa "struct puffs_usermount *pu" "puffs_cookie_t opc"
+.Fa "struct puffs_newinfo *pni"
+.Fa "const struct puffs_cn *pcn_src" "const struct vattr *vap"
+.Fa "const char *link_target"
+.Fc
+.Ft int
+.Fo puffs_node_readlink
+.Fa "struct puffs_usermount *pu" "puffs_cookie_t opc"
+.Fa "const struct puffs_cred *pcr" "char *link" "size_t *linklen"
+.Fc
+.Ft int
+.Fo puffs_node_read
+.Fa "struct puffs_usermount *pu" "puffs_cookie_t opc" "uint8_t *buf"
+.Fa "off_t offset" "size_t *resid" "const struct puffs_cred *pcr" "int ioflag"
+.Fc
+.Ft int
+.Fo puffs_node_write
+.Fa "struct puffs_usermount *pu" "puffs_cookie_t opc" "uint8_t *buf"
+.Fa "off_t offset" "size_t *resid" "const struct puffs_cred *pcr" "int ioflag"
+.Fc
+.Ft int
+.Fo puffs_node_abortop
+.Fa "struct puffs_usermount *pu" "puffs_cookie_t opc"
+.Fa "const struct puffs_cn *pcn"
+.Fc
+.Ft int
+.Fo puffs_node_getextattr
+.Fa "struct puffs_usermount *pu" "puffs_cookie_t opc" "int attrnamespace"
+.Fa "const char *attrname" "size_t *attrsize" "uint8_t *attr" "size_t *resid"
+.Fa "const struct puffs_cred *pcr"
+.Fc
+.Ft int
+.Fo puffs_node_setextattr
+.Fa "struct puffs_usermount *pu" "puffs_cookie_t opc" "int attrnamespace"
+.Fa "const char *attrname" "uint8_t *attr" "size_t *resid"
+.Fa "const struct puffs_cred *pcr"
+.Fc
+.Ft int
+.Fo puffs_node_listextattr
+.Fa "struct puffs_usermount *pu" "puffs_cookie_t opc" "int attrnamespace"
+.Fa "size_t *attrssize" "uint8_t *attrs" "iint flag" "size_t *resid"
+.Fa "const struct puffs_cred *pcr"
+.Fc
+.Ft int
+.Fo puffs_node_deleteextattr
+.Fa "struct puffs_usermount *pu" "puffs_cookie_t opc" "int attrnamespace"
+.Fa "const char *attrname"
+.Fa "const struct puffs_cred *pcr"
+.Fc
+.Ft int
+.Fn puffs_node_print "struct puffs_usermount *pu" "puffs_cookie_t opc"
+.Ft int
+.Fo puffs_node_reclaim
+.Fa "struct puffs_usermount *pu" "puffs_cookie_t opc"
+.Fc
+.Ft int
+.Fo puffs_node_inactive
+.Fa "struct puffs_usermount *pu" "puffs_cookie_t opc"
+.Fc
+.Ft void
+.Fn puffs_setback "struct puffs_cc *pcc" "int op"
+.Ft void
+.Fn puffs_newinfo_setcookie "struct puffs_newinfo *pni" "puffs_cookie_t cookie"
+.Ft void
+.Fn puffs_newinfo_setvtype "struct puffs_newinfo *pni" "enum vtype vtype"
+.Ft void
+.Fn puffs_newinfo_setsize "struct puffs_newinfo *pni" "voff_t size"
+.Sh DESCRIPTION
+The operations
+.Nm puffs
+requires to function can be divided into two categories: file system
+callbacks and node callbacks.
+The former affect the entire file system while the latter are targeted
+at a file or a directory and a file.
+They are roughly equivalent to the vfs and vnode operations in the
+kernel.
+.Pp
+All callbacks can be prototyped with the file system name and operation
+name using the macro
+.Fn PUFFSOP_PROTOS fsname .
+.Ss File system callbacks (puffs_fs)
+.Bl -tag -width xxxx
+.It Fn puffs_fs_statvfs "pu" "sbp"
+The following fields of the argument
+.Fa sbp
+need to be filled:
+.Bd -literal
+ * unsigned long  f_bsize;     file system block size
+ * unsigned long  f_frsize;    fundamental file system block size
+ * fsblkcnt_t     f_blocks;    number of blocks in file system,
+ *                                      (in units of f_frsize)
+ *
+ * fsblkcnt_t     f_bfree;     free blocks avail in file system
+ * fsblkcnt_t     f_bavail;    free blocks avail to non-root
+ * fsblkcnt_t     f_bresvd;    blocks reserved for root
+
+ * fsfilcnt_t     f_files;     total file nodes in file system
+ * fsfilcnt_t     f_ffree;     free file nodes in file system
+ * fsfilcnt_t     f_favail;    free file nodes avail to non-root
+ * fsfilcnt_t     f_fresvd;    file nodes reserved for root
+
+.Ed
+.It Fn puffs_fs_sync "pu" "waitfor" "pcr"
+All the dirty buffers that have been cached at the file server
+level including metadata should be committed to stable storage.
+The
+.Fa waitfor
+parameter affects the operation.
+Possible values are:
+.Bl -tag -width XMNT_NOWAITX
+.It Dv MNT_WAIT
+Wait for all I/O for complete until returning.
+.It Dv MNT_NOWAIT
+Initiate I/O, but do not wait for completion.
+.It Dv MNT_LAZY
+Synchorize data not synchoronized by the file system syncer,
+i.e. data not written when
+.Fn node_fsync
+is called with
+.Dv FSYNC_LAZY .
+.El
+.Pp
+The credentials for the initiator of the sync operation are present in
+.Fa pcr
+and will usually be either file system or kernel credentials, but might
+also be user credentials.
+However, most of the time it is advisable to sync regardless of the
+credentials of the caller.
+.It Fn puffs_fs_fhtonode "pu" "fid" "fidsize" "pni"
+Translates a file handle
+.Fa fid
+to a node.
+The parameter
+.Fa fidsize
+indicates how large the file handle is.
+In case the file system's handles are static length, this parameter can
+be ignored as the kernel guarantees all file handles passed to the file
+server are of correct length.
+For dynamic length handles the field should be examined and
+.Er EINVAL
+returned in case the file handle length is not correct.
+.Pp
+This function provides essentially the same information to the kernel as
+.Fn puffs_node_lookup .
+The information is necessary for creating a new vnode corresponding to
+the file handle.
+.It Fn puffs_fs_nodetofh "pu" "cookie" "fid" "fidsize"
+Create a file handle from the node described by
+.Fa cookie .
+The file handle should contain enough information to reliably identify
+the node even after reboots and the pathname/inode being replaced by
+another file.
+If this is not possible, it is up to the author of the file system to
+act responsibly and decide if the file system can support file handles
+at all.
+.Pp
+For file systems which want dynamic length file handles, this function
+must check if the file handle space indicated by
+.Fa fidsize
+is large enough to accommodate the file handle for the node.
+If not, it must fill in the correct size and return
+.Er E2BIG .
+In either case, the handle length should be supplied to the kernel in
+.Fa fidsize .
+File systems with static length handles can ignore the size parameter as
+the kernel always supplies the correct size buffer.
+.It Fn puffs_fs_unmount "pu" "flags"
+Unmount the file system.
+The kernel has assumedly flushed all cached data when this callback
+is executed.
+If the file system cannot currently be safely be unmounted, for whatever
+reason, the kernel will honor an error value and not forcibly unmount.
+However, if the flag
+.Dv MNT_FORCE
+is not honored by the file server, the kernel will forcibly unmount
+the file system.
+.El
+.Ss Node callbacks
+These operations operate in the level of individual files.
+The file cookie is always provided as the second argument
+.Fa opc .
+If the operation is for a file, it will be the cookie of the file.
+The case the operation involves a directory (such as
+.Dq create file in directory ) ,
+the cookie will be for the directory.
+Some operations take additional cookies to describe the rest of
+the operands.
+The return value 0 signals success, else an appropriate errno value
+should be returned.
+Please note that neither this list nor the descriptions are complete.
+.Bl -tag -width xxxx
+.It Fn puffs_node_lookup "pu" "opc" "pni" "pcn"
+This function is used to locate nodes, or in other words translate
+pathname components to file system data structures.
+The implementation should match the name in
+.Fa pcn
+against the existing entries in the directory provided by the cookie
+.Fa opc .
+If found, the cookie for the located node should be set in
+.Fa pni
+using
+.Fn puffs_newinfo_setcookie .
+Additionally, the vnode type and size (latter applicable to regular files only)
+should be set using
+.Fn puffs_newinfo_setvtype
+and
+.Fn puffs_newinfo_setsize ,
+respectively.
+.Pp
+The type of operation is found from
+.Va pcn-\*[Gt]pcn_nameiop :
+.Bl -tag -width XNAMEI_LOOKUPX
+.It Dv NAMEI_LOOKUP
+Normal lookup operation.
+.It Dv NAMEI_CREATE
+Lookup to create a node.
+.It Dv NAMEI_DELETE
+Lookup for node deletion.
+.It Dv NAMEI_RENAME
+Lookup for the target of a rename operation (source will be looked
+up using
+.Dv NAMEI_DELETE ) .
+.El
+.Pp
+The final component from a pathname lookup usually requires special
+treatment.
+It can be identified by looking at the
+.Va pcn-\*[Gt]pcn_flags
+fields for the flag
+.Dv PUFFSLOOKUP_ISLASTCN .
+For example, in most cases the lookup operation will want to check if
+a delete, rename or create operation has enough credentials to perform
+the operation.
+.Pp
+The return value 0 signals a found node and a nonzero value signals
+an errno.
+As a special case,
+.Er ENOENT
+signals "success" for cases where the lookup operation is
+.Dv NAMEI_CREATE
+or
+.Dv NAMEI_RENAME .
+Failure in these cases can be signalled by returning another appropriate
+error code, for example
+.Er EACCESS .
+.Pp
+Usually a null-terminated string for the next pathname component is
+provided in
+.Ar pcn-\*[Gt]pcn_name .
+In case the file system is using the option
+.Dv PUFFS_KFLAG_LOOKUP_FULLPNBUF ,
+the remainder of the complete pathname under lookup is found in
+the same location.
+.Ar pcn-\*[Gt]pcn_namelen
+always specifies the length of the next component.
+If operating with a full path, the file system is allowed to consume
+more than the next component's length in node lookup.
+This is done by setting
+.Ar pcn-\*[Gt]pcn_consume
+to indicate the amount of
+.Em extra
+characters in addition to
+.Ar pcn-\*[Gt]pcn_namelen
+processed.
+.It Fn puffs_node_create "pu" "opc" "pni" "pcn" "va"
+.It Fn puffs_node_mkdir "pu" "opc" "pni" "pcn" "va"
+.It Fn puffs_node_mknod "pu" "opc" "pni" "pcn" "va"
+A file node is created in the directory denoted by the cookie
+.Fa opc
+by any of the above callbacks.
+The name of the new file can be found from
+.Fa pcn
+and the attributes are specified by
+.Fa va
+and the cookie for the newly created node should be set in
+.Fa pni .
+The only difference between these three is that they create a regular
+file, directory and device special file, respectively.
+.Pp
+In case of mknod, the device identifier can be found in
+.Fa va-\*[Gt]va_rdev .
+.It Fn puffs_node_open "pu" "opc" "mode" "pcr"
+Open the node denoted by the cookie
+.Fa opc .
+The parameter
+.Fa mode
+specifies the flags that
+.Xr open 2
+was called with, e.g.
+.Dv O_APPEND
+and
+.Dv O_NONBLOCK .
+.It Fn puffs_node_close "pu" "opc" "flags" "pcr"
+Close a node.
+The parameter
+.Fa flags
+parameter describes the flags that the file was opened with.
+.It Fn puffs_node_access "pu" "opc" "mode" "pcr"
+Check if the credentials of
+.Fa pcr
+have the right to perform the operation specified by
+.Fa mode
+onto the node
+.Fa opc .
+The argument
+.Fa mode
+can specify read, write or execute by
+.Dv PUFFS_VREAD ,
+.Dv PUFFS_VWRITE ,
+and
+.Dv PUFFS_VEXEC ,
+respectively.
+.It Fn puffs_node_getattr "pu" "opc" "va" "pcr"
+The attributes of the node specified by
+.Fa opc
+must be copied to the space pointed by
+.Fa va .
+.It Fn puffs_node_setattr "pu" "opc" "va" "pcr"
+The attributes for the node specified by
+.Fa opc
+must be set to those contained in
+.Fa va .
+Only fields of
+.Fa va
+which contain a value different from
+.Dv PUFFS_VNOVAL
+(typecast to the field's type!) contain a valid value.
+.It Fn puffs_node_poll "pu" "opc" "events"
+Poll for events on node
+.Ar opc .
+If
+.Xr poll 2
+events specified in
+.Ar events
+are available, the function should set the bitmask to match available
+events and return immediately.
+Otherwise, the function should block (yield) until some events in
+.Ar events
+become available and only then set the
+.Ar events
+bitmask and return.
+.Pp
+In case this function returns an error,
+.Dv POLLERR
+(or it's
+.Xr select 2
+equivalent) will be delivered to the calling process.
+.Pp
+.Em NOTE!
+The system call interface for
+.Fn poll
+contains a timeout parameter.
+At this level, however, the timeout is not supplied.
+In case input does not arrive, the file system should periodically
+unblock and return 0 new events to avoid hanging forever.
+This will hopefully be better supported by libpuffs in the future.
+.It Fn puffs_node_mmap "pu" "opc" "flags" "pcr"
+Called when a regular file is being memory mapped by
+.Xr mmap 2 .
+.Fa flags
+is currently always 0.
+.It Fn puffs_node_fsync "pu" "opc" "pcr" "flags" "offlo" "offhi"
+Sychronize a node's contents onto stable storage.
+This is necessary only if the file server caches some information
+before committing it.
+The parameter
+.Fa flags
+specifies the minimum level of sychronization required (XXX: they are
+not yet available).
+The parameters
+.Fa offlo
+and
+.Fa offhi
+specify the data offsets requiring to be synced.
+A high offset of 0 means sync from
+.Fa offlo
+to the end of the file.
+.It Fn puffs_node_seek "pu" "opc" "oldoff" "newoff" "pcr"
+Test if the node
+.Ar opc
+is seekable to the location
+.Ar newoff .
+The argument
+.Ar oldoff
+specifies the offset we are starting the seek from.
+Most file systems dealing only with regular will choose to not
+implement this.
+However, it is useful for example in cases where files are
+unseekable streams.
+.It Fn puffs_node_remove "pu" "opc" "targ" "pcn"
+.It Fn puffs_node_rmdir "pu" "opc" "targ" "pcn"
+Remove the node
+.Fa targ
+from the directory indicated by
+.Fa opc .
+The directory entry name to be removed is provided by
+.Fa pcn .
+The rmdir operation removes only directories, while the remove
+operation removes all other types except directories.
+.Pp
+It is paramount to note that the file system may not remove the
+node data structures at this point, only the directory entry and prevent
+lookups from finding the node again.
+This is to retain the
+.Ux
+open file semantics.
+The data may be removed only when
+.Fn puffs_node_reclaim
+is called for the node, as this assures there are no further users.
+.It Fn puffs_node_link "pu" "opc" "targ" "pcn"
+Create a hard link for the node
+.Fa targ
+into the directory
+.Fa opc .
+The argument
+.Fa pcn
+provides the directory entry name for the new link.
+.It Fn puffs_node_rename "pu" "src_dir" "src" "pcn_src" "targ_dir" "targ" "pcn_targ"
+Rename the node
+.Fa src
+with the name specified by
+.Fa pcn_src
+from the directory
+.Fa src_dir .
+The target directory and target name are given by
+.Fa targ_dir
+and
+.Fa pcn_targ ,
+respectively.
+.Em If
+the target node already exists, it is specified by
+.Fa targ
+and must be replaced atomically.
+Otherwise
+.Fa targ
+is gives as
+.Dv NULL .
+.Pp
+It is legal to replace a directory node by another directory node with
+the means of rename if the target directory is empty, otherwise
+.Er ENOTEMPTY
+should be returned.
+All other types can replace all other types.
+In case a rename between incompatible types is attempted, the errors
+.Er ENOTDIR
+or
+.Er EISDIR
+should be returned, depending on the target type.
+.It Fn puffs_node_readdir "pu" "opc" "dent" "readoff" "reslen" "pcr" "eofflag" "cookies" "ncookies"
+To read directory entries,
+.Fn puffs_node_readdir
+is called.
+It should store directories as
+.Va struct dirent
+in the space pointed to by
+.Fa dent .
+The amount of space available is given by
+.Fa reslen
+and before returning it should be set to the amount of space
+.Em remaining
+in the buffer.
+The argument
+.Fa offset
+is used to specify the offset to the directory.
+Its intepretation is up to the file system and it should be set to
+signal the continuation point when there is no more room for the next
+entry in
+.Fa dent .
+It is most performant to return the maximal amount of directory
+entries each call.
+It is easiest to generate directory entries by using
+.Fn puffs_nextdent ,
+which also automatically advances the necessary pointers.
+.Pp
+In case end-of-directory is reached,
+.Fa eofflag
+should be set to one.
+Note that even a new call to readdir may start where
+.Fa readoff
+points to end-of-directory.
+.Pp
+If the file system supports file handles, the arguments
+.Fa cookies
+and
+.Fa ncookies
+must be filled out.
+.Fa cookies
+is a vector for offsets corresponding to read offsets.
+One cookie should be filled out for each directory entry.
+The value of the cookie should equal the offset of the
+.Em next
+directory entry, i.e. which offset should be passed to readdir for
+the first entry read to be the entry following the current one.
+.Fa ncookies
+is the number of slots for cookies in the cookie vector upon entry to
+the function and must be set to the amount of cookies stored in the
+vector (i.e. amount of directory entries read) upon exit.
+There is always enough space in the cookie vector for the maximal number
+of entries that will fit into the directory entry buffer.
+For filling out the vector, the helper function
+.Fn PUFFS_STORE_DCOOKIE cookies ncookies offset
+can be used.
+This properly checks against
+.Fa cookies
+being
+.Dv NULL .
+Note that
+.Fa ncookies
+must be initialized to zero before the first call to
+.Fn PUFFS_STORE_DCOOKIE .
+.It Fn puffs_node_symlink "pu" "opc" "pni" "pcn_src" "va" "link_target"
+Create a symbolic link into the directory
+.Fa opc
+with the name in
+.Fa pcn_src
+and the initial attributes in
+.Fa va .
+The argument
+.Ar link_target
+contains a null-terminated string for the link target.
+The created node cookie should be set in
+.Fa pni .
+.It Fn puffs_node_readlink "pu" "opc" "pcr" "link" "linklen"
+Read the target of a symbolic link
+.Fa opc .
+The result is placed in the buffer pointed to by
+.Fa link .
+This buffer's length is given in
+.Fa linklen
+and it must be updated to reflect the real link length.
+A terminating nul character should not be put into the buffer and
+.Em "must not"
+be included in the link length.
+.It Fn puffs_node_read "pu" "opc" "buf" "offset" "resid" "pcr" "ioflag"
+Read the contents of a file
+.Fa opc .
+It will gather the data from
+.Fa offset
+in the file and read the number
+.Fa resid
+octets.
+The buffer is guaranteed to have this much space.
+The amount of data requested by
+.Fa resid
+should be read, except in the case of eof-of-file or an error.
+The parameter
+.Fa resid
+should be set to indicate the amount of request NOT completed.
+In the normal case this should be 0.
+.It Fn puffs_node_write "pu" "opc" "buf" "offset" "resid" "pcr" "ioflag"
+.Fn puffs_node_write
+Write data to a file
+.Fa opc
+at
+.Fa offset
+and extend the file if necessary.
+The number of octets written is indicated by
+.Fa resid ;
+everything must be written or an error will be generated.
+The parameter must be set to indicate the amount of data NOT written.
+In case the flag
+.Dv PUFFS_IO_APPEND
+is specified, the data should be appended to the end of the file.
+.It Fn puffs_node_print "pu" "opc"
+Print information about node.
+This is used only for kernel-initiated diagnostic purposes.
+.It Fn puffs_node_reclaim "pu" "opc"
+The kernel will no longer reference the cookie and resources associated
+with it may be freed.
+In case the file
+.Fa opc
+has a link count of zero, it may be safely removed now.
+.It Fn puffs_node_abortop "pu" "opc" "pcn"
+In case the operation following lookup (e.g. mkdir or remove) is not
+executed for some reason, abortop will be issued.
+This is useful only for servers which cache state between lookup
+and a directory operation and is generally left unimplemented.
+.It Fn puffs_node_inactive "pu" "opc"
+The node
+.Fa opc
+has lost its last reference in the kernel.
+However, the cookie must still remain valid until
+.Fn puffs_node_reclaim
+is called.
+.It Fn puffs_setback "pcc" "op"
+Issue a "setback" operation which will be handled when the request response
+is returned to the kernel.
+Currently this can be only called from mmap, open, remove and rmdir.
+The valid parameters for
+.Ar op
+are
+.Dv PUFFS_SETBACK_INACT_N1
+and
+.Dv PUFFS_SETBACK_INACT_N2 .
+These signal that a file system mounted with
+.Dv PUFFS_KFLAG_IAONDEMAND
+should call the file system inactive method for the specified node.
+The node number 1 always means the operation cookie
+.Ar opc ,
+while the node number 2 can be used to specify the second node argument
+present in some methods, e.g. remove.
+.It Fn puffs_newinfo_setcookie pni cookie
+Set cookie for node provided by this method to
+.Ar cookie .
+.It Fn puffs_newinfo_setvtype pni vtype
+Set the type of the newly located node to
+.Ar vtype .
+This call is valid only for
+.Fn lookup
+and
+.Fn fhtonode .
+.It Fn puffs_newinfo_setsize pni size
+Set the size of the newly located node to
+.Ar size .
+If left unset, the value defaults to 0.
+This call is valid only for
+.Fn lookup
+and
+.Fn fhtovp .
+.El
+.Sh SEE ALSO
+.Xr puffs 3 ,
+.Xr vfsops 9 ,
+.Xr vnodeops 9
diff --git a/lib/libpuffs/puffs_path.3 b/lib/libpuffs/puffs_path.3
new file mode 100644 (file)
index 0000000..2269a8a
--- /dev/null
@@ -0,0 +1,125 @@
+.\"    $NetBSD: puffs_path.3,v 1.4 2009/02/20 14:26:56 pooka Exp $
+.\"
+.\" Copyright (c) 2007 Antti Kantee.  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 AUTHOR 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 AUTHOR 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 February 5, 2012
+.Dt PUFFS_PATH 3
+.Os
+.Sh NAME
+.Nm puffs_path
+.Nd puffs pathbuilding routines
+.Sh LIBRARY
+.Lb libpuffs
+.Sh SYNOPSIS
+.In puffs.h
+.Ft int
+.Fo pu_pathbuild_fn
+.Fa "struct puffs_usermount *pu" "const struct puffs_pathobj *po_dir"
+.Fa "const struct puffs_pathobj *po_comp" "size_t offset"
+.Fa "struct puffs_pathobj *po_new"
+.Fc
+.Ft int
+.Fo pu_pathtransform_fn
+.Fa "struct puffs_usermount *pu" "const struct puffs_pathobj *po_base"
+.Fa "const struct puffs_cn *pcn" "struct puffs_pathobj *po_new"
+.Fc
+.Ft int
+.Fo pu_pathcmp_fn
+.Fa "struct puffs_usermount *pu" "struct puffs_pathobj *po1"
+.Fa "struct puffs_pathobj *po2" "size_t checklen" "int checkprefix"
+.Fc
+.Ft void
+.Fn pu_pathfree_fn "struct puffs_usermount *pu" "struct puffs_pathobj *po"
+.Ft int
+.Fo pu_namemod_fn
+.Fa "struct puffs_usermount *pu" "struct puffs_pathobj *po_dir"
+.Fa "struct puffs_cn *pcn"
+.Fc
+.Ft struct puffs_pathobj *
+.Fn puffs_getrootpathobj "struct puffs_usermount *pu"
+.Sh DESCRIPTION
+The puffs library has the ability to provide full pathnames for backends
+which require them.
+Normal file systems should be constructed without the file system
+node tied to a file name and should not used routines described herein.
+An example of a file system where the backend requires filenames is
+.Xr mount_psshfs 8 .
+.Pp
+The features described here are enabled by passing
+.Dv PUFFS_FLAG_BUILDPATH
+to
+.Fn puffs_init .
+This facility requires to use puffs nodes to store the contents of the
+pathname.
+Either the address of the operation cookie must directly be that of the
+puffs node, or
+.Fn puffs_set_cmap
+must be used to set a mapping function from the cookie to the puffs
+node associated with the cookie.
+Finally, the root node for the file system must be set using
+.Fn puffs_setroot
+and the root path object retrieved using
+.Fn puffs_getrootpathobj
+and initialized.
+.Pp
+There are two different places a filename can be retrieved from.
+It is available for each puffs node after the node has been registered
+with the framework, i.e.
+.Em after
+the routine creating the node returns.
+In other words, there is a window between the node is created and
+when the pathname is available and multithreaded file systems must
+take this into account.
+The second place where a pathname is available is from the componentname
+.Vt struct puffs_pcn
+in operations which are passed one.
+These can be retrieved using the convenience macros
+.Fn PNPATH
+and
+.Fn PCNPATH
+for node and componentname, respectively.
+The type of object they return is
+.Vt void * .
+.Pp
+By default the framework manages "regular" filenames, which consist
+of directory names separated by "/" and a final component.
+If the file system wishes to use pathnames of this format, all it
+has to do it enable the feature.
+Everything else, including bookkeeping for node and directory renames,
+is done by the library.
+The callback routines described next provide the ability to build
+non-standard pathnames.
+A
+.Fn pu_foo_fn
+callback is set using the
+.Fn puffs_set_foo
+routine.
+.Pp
+This manual page is still unfinished.
+Please take a number and wait in line.
+.Sh SEE ALSO
+.Xr puffs 3 ,
+.Xr puffs_node 3 ,
+.Xr mount_psshfs 8 ,
+.Xr mount_sysctlfs 8
diff --git a/lib/libpuffs/puffs_priv.h b/lib/libpuffs/puffs_priv.h
new file mode 100644 (file)
index 0000000..72b6673
--- /dev/null
@@ -0,0 +1,258 @@
+/*     $NetBSD: puffs_priv.h,v 1.44 2011/06/20 09:11:17 mrg Exp $      */
+
+/*
+ * Copyright (c) 2006, 2007, 2008 Antti Kantee.  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 AUTHOR ``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 AUTHOR 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 _PUFFS_PRIVATE_H_
+#define _PUFFS_PRIVATE_H_
+
+#include <sys/types.h>
+#include <vfs/puffs/puffs_msgif.h>
+
+#include <pthread.h>
+#include <ucontext.h>
+
+#include "puffs.h"
+
+extern pthread_mutex_t pu_lock;
+#define PU_LOCK() pthread_mutex_lock(&pu_lock)
+#define PU_UNLOCK() pthread_mutex_unlock(&pu_lock)
+
+#define PU_CMAP(pu, c) (pu->pu_cmap ? pu->pu_cmap(pu,c) : (struct puffs_node*)c)
+
+struct puffs_framectrl {
+       puffs_framev_readframe_fn rfb;
+       puffs_framev_writeframe_fn wfb;
+       puffs_framev_cmpframe_fn cmpfb;
+       puffs_framev_gotframe_fn gotfb;
+       puffs_framev_fdnotify_fn fdnotfn;
+};
+
+struct puffs_fctrl_io {
+       struct puffs_framectrl *fctrl;
+
+       int io_fd;
+       int stat;
+
+       int rwait;
+       int wwait;
+
+       struct puffs_framebuf *cur_in;
+
+       TAILQ_HEAD(, puffs_framebuf) snd_qing;  /* queueing to be sent */
+       TAILQ_HEAD(, puffs_framebuf) res_qing;  /* q'ing for rescue */
+       LIST_HEAD(, puffs_fbevent) ev_qing;     /* q'ing for events */
+
+       LIST_ENTRY(puffs_fctrl_io) fio_entries;
+};
+#define FIO_WR   &nb