..
printing
..
+ puffs
+ pnullfs
+ ..
+ ..
rconfig
..
scsi_target
.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)
# @(#)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:
libpcap \
libposix1e \
libprop \
+ libpuffs \
+ librefuse \
librpcsvc \
librt \
libsctp \
--- /dev/null
+# $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>
--- /dev/null
+ $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?)
--- /dev/null
+/* $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);
+}
--- /dev/null
+/* $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;
+}
--- /dev/null
+/* $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;
+}
--- /dev/null
+/* $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);
+}
--- /dev/null
+/* $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, ¬resp) == 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 */
+}
--- /dev/null
+/* $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_ */
--- /dev/null
+/* $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;
+}
--- /dev/null
+/* $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);
+}
--- /dev/null
+/* $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);
+}
--- /dev/null
+/* $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;
+}
--- /dev/null
+.\" $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
--- /dev/null
+/* $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;
+}
--- /dev/null
+/* $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_ */
--- /dev/null
+.\" $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
--- /dev/null
+.\" $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
--- /dev/null
+.\" $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
--- /dev/null
+.\" $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
--- /dev/null
+.\" $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
--- /dev/null
+.\" $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
--- /dev/null
+.\" $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
--- /dev/null
+/* $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.
+ */