]> Zhao Yanbai Git Server - minix.git/commitdiff
Import librefuse and libpuffs
authorThomas Veerman <thomas@minix3.org>
Mon, 14 Nov 2011 11:53:05 +0000 (11:53 +0000)
committerThomas Veerman <thomas@minix3.org>
Mon, 14 Nov 2011 11:53:05 +0000 (11:53 +0000)
Import libpuffs and our port of libpuffs. The port was done as part of
GSoC 2011 FUSE project, done by Evgeniy Ivanov. The librefuse import
did not require any porting efforts. Libpuffs has been modified to
understand our VFS-FS protocol and translate between that and PUFFS. As
an example that it works, fuse-ntfs-3g from pkgsrc can be compiled and
used to mount ntfs partitions:
mount -t ntfs-3g <device> <mountpoint>

FUSE only works with the asynchronous version of VFS. See <docs/UPDATING> on
how to run AVFS.

This patch further includes some changes to mount(1) and mount(2) so it's
possible to use file systems provided by pkgsrc (note: manual modifications
to /etc/system.conf are still needed. There has been made an exception for
fuse-ntfs-3g, so it already as an entry).

54 files changed:
commands/mount/mount.c
include/unistd.h
lib/Makefile
lib/libc/other/_mount.c
lib/libc/posix/pread.c
lib/libc/posix/pwrite.c
lib/libpuffs/Makefile [new file with mode: 0644]
lib/libpuffs/callcontext.c [new file with mode: 0644]
lib/libpuffs/creds.c [new file with mode: 0644]
lib/libpuffs/device.c [new file with mode: 0644]
lib/libpuffs/drivers.h [new file with mode: 0644]
lib/libpuffs/fs.h [new file with mode: 0644]
lib/libpuffs/glo.h [new file with mode: 0644]
lib/libpuffs/inode.c [new file with mode: 0644]
lib/libpuffs/link.c [new file with mode: 0644]
lib/libpuffs/misc.c [new file with mode: 0644]
lib/libpuffs/mntopts.h [new file with mode: 0644]
lib/libpuffs/mount.c [new file with mode: 0644]
lib/libpuffs/null.c [new file with mode: 0644]
lib/libpuffs/open.c [new file with mode: 0644]
lib/libpuffs/path.c [new file with mode: 0644]
lib/libpuffs/path_puffs.c [new file with mode: 0644]
lib/libpuffs/pnode.c [new file with mode: 0644]
lib/libpuffs/protect.c [new file with mode: 0644]
lib/libpuffs/proto.h [new file with mode: 0644]
lib/libpuffs/puffs.3 [new file with mode: 0644]
lib/libpuffs/puffs.c [new file with mode: 0644]
lib/libpuffs/puffs.h [new file with mode: 0644]
lib/libpuffs/puffs_cc.3 [new file with mode: 0644]
lib/libpuffs/puffs_cred.3 [new file with mode: 0644]
lib/libpuffs/puffs_msgif.h [new file with mode: 0644]
lib/libpuffs/puffs_node.3 [new file with mode: 0644]
lib/libpuffs/puffs_ops.3 [new file with mode: 0644]
lib/libpuffs/puffs_path.3 [new file with mode: 0644]
lib/libpuffs/puffs_priv.h [new file with mode: 0644]
lib/libpuffs/read.c [new file with mode: 0644]
lib/libpuffs/stadir.c [new file with mode: 0644]
lib/libpuffs/subr.c [new file with mode: 0644]
lib/libpuffs/table.c [new file with mode: 0644]
lib/libpuffs/time.c [new file with mode: 0644]
lib/libpuffs/utility.c [new file with mode: 0644]
lib/librefuse/Makefile [new file with mode: 0644]
lib/librefuse/TODO [new file with mode: 0644]
lib/librefuse/fuse.h [new file with mode: 0644]
lib/librefuse/fuse_opt.h [new file with mode: 0644]
lib/librefuse/refuse.3 [new file with mode: 0644]
lib/librefuse/refuse.c [new file with mode: 0644]
lib/librefuse/refuse_opt.c [new file with mode: 0644]
lib/librefuse/shlib_version [new file with mode: 0644]
lib/nbsd_libc/sys-minix/mount.c
lib/nbsd_libc/sys-minix/pread.c
lib/nbsd_libc/sys-minix/pwrite.c
nbsd_include/sys/ucred.h
nbsd_include/unistd.h

index 40015a4ce758a76c43b6d5e3565df91fae1a37be..3f80e9adba69d66ae51c1aa68430a3f2f9387ff3 100644 (file)
@@ -28,7 +28,7 @@ char *argv[];
 {
   int i, n, v = 0, mountflags, write_mtab;
   char **ap, *vs, *opt, *err, *type, *args, *device;
-  char special[PATH_MAX+1], mounted_on[PATH_MAX+1], version[10], rw_flag[10];
+  char special[PATH_MAX], mounted_on[PATH_MAX], version[10], rw_flag[10];
 
   if (argc == 1) list();       /* just list /etc/mtab */
   mountflags = 0;
@@ -104,9 +104,9 @@ char *argv[];
   /* For MFS, use a version number. Otherwise, use the FS type name. */
   if (!strcmp(type, MINIX_FS_TYPE)) {
        switch (v) {
-               case FSVERSION_MFS1: vs = "1"; break;
-               case FSVERSION_MFS2: vs = "2"; break;
-               case FSVERSION_MFS3: vs = "3"; break;           
+               case FSVERSION_MFS1: vs = "MFSv1"; break;
+               case FSVERSION_MFS2: vs = "MFSv2"; break;
+               case FSVERSION_MFS3: vs = "MFSv3"; break;
                default: vs = "0"; break;
        }
   } else {
@@ -131,7 +131,7 @@ char *argv[];
 void list()
 {
   int n;
-  char special[PATH_MAX+1], mounted_on[PATH_MAX+1], version[10], rw_flag[10];
+  char special[PATH_MAX], mounted_on[PATH_MAX], version[10], rw_flag[10];
 
   /* Read and print /etc/mtab. */
   n = load_mtab("mount");
index 3edcdbf83e4f7e38b2fdb8a9f2d1d33685b6e50c..87902b148f40f8da1df7a142ff3f21d5871483a2 100644 (file)
@@ -202,6 +202,9 @@ _PROTOTYPE( pid_t getnpid, (endpoint_t proc_ep)                             );
 _PROTOTYPE( uid_t getnuid, (endpoint_t proc_ep)                                );
 _PROTOTYPE( gid_t getngid, (endpoint_t proc_ep)                                );
 _PROTOTYPE( int getnucred, (endpoint_t proc_ep, struct ucred *ucred)   );
+_PROTOTYPE( ssize_t pread64, (int fd, void *buf, size_t count, u64_t where));
+_PROTOTYPE( ssize_t pwrite64, (int fd, const void *buf, size_t count,
+                               u64_t where));
 
 #endif
 
index 16cdf1382a12c1179d44df70dd0c02f8637d67bc..1e9879eb48ecd47ffa75399333755a84ba514391 100644 (file)
@@ -21,7 +21,8 @@ SUBDIR= csu ${LIBCOMPAT_DIR} ${LIBC_DIR} libdriver libnetdriver \
        libddekit libminixfs libbdev
 
 .if defined(NBSD_LIBC) && (${NBSD_LIBC} != "no")
-SUBDIR+= libelf libminc libcrypt libterminfo libcurses libvassert libutil
+SUBDIR+= libelf libminc libcrypt libterminfo libcurses libvassert libutil \
+        libpuffs librefuse
 .endif
 
 .if ${COMPILER_TYPE} == "ack"
index 51c10fc2edc199fde399f704e1c2b1487b288c71..4567b0004777f2c55689e333303cb163977fa47f 100644 (file)
 #include <minix/syslib.h>
 #include <minix/rs.h>
 #include <paths.h>
+#include <unistd.h>
 #define OK     0
 
-#define FSPATH "/sbin/"
 #define FSDEFAULT "mfs"
 
+static char fspath[] = "/sbin/:/usr/pkg/bin/"; /* Must include trailing '/' */
+
 PRIVATE int rs_down(char *label)
 {
        char cmd[200];
@@ -32,7 +34,7 @@ int mountflags;
   message m;
   struct stat statbuf;
   char label[16];
-  char path[60];
+  char path[PATH_MAX];
   char cmd[200];
   char *p;
   int reuse = 0;
@@ -42,6 +44,7 @@ int mountflags;
   if (type == NULL) type = FSDEFAULT;
   if (args == NULL) args = "";
   reuse = 0;
+  memset(path, '\0', sizeof(path));
 
   /* Check mount flags */
   if(mountflags & MS_REUSE) {
@@ -87,8 +90,6 @@ int mountflags;
   /* Tell VFS that we are passing in a 16-byte label. */
   mountflags |= MS_LABEL16;
 
-
-
   /* Sanity check on user input. */
   if(strchr(args, '\'')) {
        errno = EINVAL;
@@ -97,28 +98,40 @@ int mountflags;
   /* start the fs-server if not using existing one */
   if (!use_existing) {
        /* See if the given type is even remotely valid. */
-    if(strlen(FSPATH)+strlen(type) >= sizeof(path)) {
-               errno = E2BIG;
-               return -1;
-       }
-       strcpy(path, FSPATH);
-       strcat(path, type);
-  
-       if(stat(path, &statbuf) != 0) {
+
+       char *testpath;
+       testpath = strtok(fspath, ":");
+
+       do {
+               if (strlen(testpath) + strlen(type) >= sizeof(path)) {
+                       errno = E2BIG;
+                       return(-1);
+               }
+
+               strcpy(path, testpath);
+               strcat(path, type);
+
+               if (access(path, F_OK) == 0) break;
+
+       } while ((testpath = strtok(NULL, ":")) != NULL);
+
+       if (testpath == NULL) {
+               /* We were not able to find type somewhere in "fspath" */
                errno = EINVAL;
-               return -1;
+               return(-1);
        }
-       
-       if(strlen(_PATH_SERVICE)+strlen(path)+strlen(label)+
-               strlen(args)+50 >= sizeof(cmd)) {
+
+       if (strlen(_PATH_SERVICE) + strlen(path) + strlen(label) +
+           strlen(args) + 50 >= sizeof(cmd)) {
                errno = E2BIG;
                return -1;
        }
 
-       sprintf(cmd, _PATH_SERVICE " %sup %s -label '%s' -args '%s%s'",
-               reuse ? "-r ": "", path, label, args[0] ? "-o " : "", args);
+       sprintf(cmd, _PATH_SERVICE " %sup %s -label '%s' -args '%s %s %s%s'",
+               reuse ? "-r ": "", path, label, special, name,
+               args[0] ? "-o " : "", args);
 
-       if((r = system(cmd)) != 0) {
+       if ((r = system(cmd)) != 0) {
                fprintf(stderr, "mount: couldn't run %s\n", cmd);
                errno = r;
                return -1;
@@ -134,8 +147,8 @@ int mountflags;
   m.m1_p3 = label;
   r = _syscall(VFS_PROC_NR, MOUNT, &m);
 
-  if(r != OK && !use_existing) {
-       /* If mount() failed, tell RS to shutdown MFS process.
+  if (r != OK && !use_existing) {
+       /* If mount() failed, tell RS to shutdown FS process.
         * No error check - won't do anything with this error anyway.
         */
        rs_down(label);
index a1af0223ebf07205b8951d4c67d33b54e0f36f34..4ef6b5703ece1250845276085b5ab14f7ae1dde0 100644 (file)
@@ -1,5 +1,25 @@
 #include <lib.h>
 #include <unistd.h>
+#include <minix/u64.h>
+
+ssize_t pread64(int fd, void *buffer, size_t nbytes, u64_t where)
+{
+       u64_t here;
+       ssize_t r;
+
+       if (lseek64(fd, make64(0,0), SEEK_CUR, &here) < 0) return(-1);
+       if (lseek64(fd, where, SEEK_SET, NULL) < 0) return(-1);
+       if ((r = read(fd, buffer, nbytes)) < 0) {
+               int e ; errno;
+               lseek64(fd, here, SEEK_SET, NULL);
+               errno = e;
+               return(-1);
+       }
+
+       if (lseek64(fd, here, SEEK_SET, NULL) < 0) return(-1);
+
+       return(r);
+}
 
 ssize_t pread(int fd, void *buffer, size_t nbytes, off_t where)
 {
index 7cb3f52c4d728bc1dbcb78c9d5a63f8f37f4f2e8..94b1d4ad341c61fc6bbb2b4afbebe2533a76a7ac 100644 (file)
@@ -1,5 +1,25 @@
 #include <lib.h>
 #include <unistd.h>
+#include <minix/u64.h>
+
+ssize_t pwrite64(int fd, const void *buffer, size_t nbytes, u64_t where)
+{
+       u64_t here;
+       ssize_t w;
+
+       if (lseek64(fd, make64(0,0), SEEK_CUR, &here) < 0) return(-1);
+       if (lseek64(fd, where, SEEK_SET, NULL) < 0) return(-1);
+       if ((w = write(fd, buffer, nbytes)) < 0) {
+               int e = errno;
+               lseek64(fd, here, SEEK_SET, NULL);
+               errno = e;
+               return(-1);
+       }
+
+       if (lseek64(fd, here, SEEK_SET, NULL) < 0) return(-1);
+
+       return(w);
+}
 
 ssize_t pwrite(int fd, const void *buffer, size_t nbytes, off_t where)
 {
diff --git a/lib/libpuffs/Makefile b/lib/libpuffs/Makefile
new file mode 100644 (file)
index 0000000..6d18c71
--- /dev/null
@@ -0,0 +1,17 @@
+# Makefile for libpuffs
+
+LIB=    puffs
+CC=    clang
+SRCS=  callcontext.c  creds.c  null.c  pnode.c  puffs.c  subr.c\
+       table.c  link.c  misc.c  open.c  path.c  path_puffs.c  protect.c\
+       read.c  stadir.c  time.c  utility.c  mount.c  device.c  inode.c
+INCS=  puffs.h puffs_msgif.h
+INCSDIR=       /usr/include
+MAN=           puffs.3 puffs_cc.3 puffs_cred.3 puffs_node.3 \
+               puffs_ops.3 puffs_path.3
+
+DEFAULT_NR_BUFS= 1024
+
+CPPFLAGS+=     -D_MINIX -D_POSIX_SOURCE -D_POSIX_C_SOURCE -D_NETBSD_SOURCE -DDEFAULT_NR_BUFS=${DEFAULT_NR_BUFS}
+
+.include <bsd.lib.mk>
diff --git a/lib/libpuffs/callcontext.c b/lib/libpuffs/callcontext.c
new file mode 100644 (file)
index 0000000..5ecaf04
--- /dev/null
@@ -0,0 +1,350 @@
+/*     $NetBSD: callcontext.c,v 1.23 2008/08/11 16:23:37 pooka 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/cdefs.h>
+#if !defined(lint)
+__RCSID("$NetBSD: callcontext.c,v 1.23 2008/08/11 16:23:37 pooka Exp $");
+#endif /* !lint */
+
+#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 setcontenxt() 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;
+
+       if (puffs_fakecc)
+               return &fakecc;
+
+       sp = minix_mmap(NULL, stacksize, PROT_READ|PROT_WRITE,
+           MAP_ANON|MAP_PRIVATE, -1, 0);
+       if (sp == MAP_FAILED)
+               return NULL;
+
+       pcc = sp;
+       memset(pcc, 0, sizeof(struct puffs_cc));
+
+       /* initialize both ucontext's */
+       if (getcontext(&pcc->pcc_uc) == -1) {
+               minix_munmap(pcc, stacksize);
+               return NULL;
+       }
+       if (getcontext(&pcc->pcc_uc_ret) == -1) {
+               minix_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 = 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);
+       minix_munmap(pcc, stacksize);
+}
+
+void
+puffs__cc_destroy(struct puffs_cc *pcc, int nonuke)
+{
+       struct puffs_usermount *pu = pcc->pcc_pu;
+
+       assert(pcc->pcc_flags == 0);
+       assert(!puffs_fakecc);
+
+       /* not over limit?  stuff away in the store, otherwise nuke */
+       if (nonuke || pu->pu_cc_nstored < PUFFS_CCMAXSTORE) {
+               pcc->pcc_pb = NULL;
+               DPRINTF(("puffs__cc_destroy: storing pcc %p\n", pcc));
+               LIST_INSERT_HEAD(&pu->pu_ccmagazin, pcc, pcc_rope);
+               pu->pu_cc_nstored++;
+       } else {
+               cc_free(pcc);
+       }
+}
+
+void
+puffs__cc_exit(struct puffs_usermount *pu)
+{
+       struct puffs_cc *pcc;
+
+       while ((pcc = LIST_FIRST(&pu->pu_ccmagazin)) != NULL) {
+               LIST_REMOVE(pcc, pcc_rope);
+               cc_free(pcc);
+       }
+}
+
+struct puffs_cc *
+puffs_cc_getcc(struct puffs_usermount *pu)
+{
+       size_t stacksize = 1<<pu->pu_cc_stackshift;
+       uintptr_t bottom;
+
+       if (puffs_fakecc)
+               return &fakecc;
+
+       bottom = ((uintptr_t)&bottom) & ~(stacksize-1);
+       return (struct puffs_cc *)bottom;
+}
+
+int
+puffs__cc_savemain(struct puffs_usermount *pu)
+{
+
+       if (puffs_fakecc)
+               return 0;
+
+       PU_CLRSFLAG(pu, PU_MAINRESTORE);
+       return getcontext(&pu->pu_mainctx);
+}
+
+int
+puffs__cc_restoremain(struct puffs_usermount *pu)
+{
+
+       if (puffs_fakecc)
+               return 0;
+
+       puffs__cc_destroy(puffs_cc_getcc(pu), 1);
+       PU_SETSFLAG(pu, PU_MAINRESTORE);
+       return setcontext(&pu->pu_mainctx);
+}
diff --git a/lib/libpuffs/creds.c b/lib/libpuffs/creds.c
new file mode 100644 (file)
index 0000000..01b15e5
--- /dev/null
@@ -0,0 +1,259 @@
+/*     $NetBSD: creds.c,v 1.14.12.1 2009/11/28 16:01:03 bouyer 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.
+ */
+
+#include <sys/cdefs.h>
+#if !defined(lint)
+__RCSID("$NetBSD: creds.c,v 1.14.12.1 2009/11/28 16:01:03 bouyer Exp $");
+#endif /* !lint */
+
+/*
+ * Interface for dealing with credits.
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+
+#include <errno.h>
+#include <stdbool.h>
+#include <string.h>
+
+#include "puffs.h"
+#include "puffs_priv.h"
+
+#define UUCCRED(a) (a->pkcr_type == PUFFCRED_TYPE_UUC)
+#define INTCRED(a) (a->pkcr_type == PUFFCRED_TYPE_INTERNAL)
+
+int
+puffs_cred_getuid(const struct puffs_cred *pcr, uid_t *ruid)
+{
+       PUFFS_MAKEKCRED(pkcr, pcr);
+
+       if (!UUCCRED(pkcr)) {
+               errno = EOPNOTSUPP;
+               return -1;
+       }
+       *ruid = pkcr->pkcr_uuc.cr_uid;
+
+       return 0;
+}
+
+int
+puffs_cred_getgid(const struct puffs_cred *pcr, gid_t *rgid)
+{
+       PUFFS_MAKEKCRED(pkcr, pcr);
+
+       if (!UUCCRED(pkcr)) {
+               errno = EOPNOTSUPP;
+               return -1;
+       }
+       *rgid = pkcr->pkcr_uuc.cr_gid;
+
+       return 0;
+}
+
+int
+puffs_cred_getgroups(const struct puffs_cred *pcr, gid_t *rgids, short *ngids)
+{
+       PUFFS_MAKEKCRED(pkcr, pcr);
+       size_t ncopy;
+
+       if (!UUCCRED(pkcr)) {
+               errno = EOPNOTSUPP;
+               *ngids = 0;
+               return -1;
+       }
+
+       ncopy = MIN(*ngids, pkcr->pkcr_uuc.cr_ngroups);
+       (void)memcpy(rgids, pkcr->pkcr_uuc.cr_groups, sizeof(gid_t) * ncopy);
+       *ngids = (short)ncopy;
+
+       return 0;
+}
+
+bool
+puffs_cred_isuid(const struct puffs_cred *pcr, uid_t uid)
+{
+       PUFFS_MAKEKCRED(pkcr, pcr);
+
+       return UUCCRED(pkcr) && pkcr->pkcr_uuc.cr_uid == uid;
+}
+
+bool
+puffs_cred_hasgroup(const struct puffs_cred *pcr, gid_t gid)
+{
+       PUFFS_MAKEKCRED(pkcr, pcr);
+       short i;
+
+       if (!UUCCRED(pkcr))
+               return false;
+
+       if (pkcr->pkcr_uuc.cr_gid == gid)
+               return true;
+       for (i = 0; i < pkcr->pkcr_uuc.cr_ngroups; i++)
+               if (pkcr->pkcr_uuc.cr_groups[i] == gid)
+                       return true;
+
+       return false;
+}
+
+bool
+puffs_cred_isregular(const struct puffs_cred *pcr)
+{
+       PUFFS_MAKEKCRED(pkcr, pcr);
+
+       return UUCCRED(pkcr);
+}
+
+bool
+puffs_cred_iskernel(const struct puffs_cred *pcr)
+{
+       PUFFS_MAKEKCRED(pkcr, pcr);
+
+       return INTCRED(pkcr) && pkcr->pkcr_internal == PUFFCRED_CRED_NOCRED;
+}
+
+bool
+puffs_cred_isfs(const struct puffs_cred *pcr)
+{
+       PUFFS_MAKEKCRED(pkcr, pcr);
+
+       return INTCRED(pkcr) && pkcr->pkcr_internal == PUFFCRED_CRED_FSCRED;
+}
+
+bool
+puffs_cred_isjuggernaut(const struct puffs_cred *pcr)
+{
+
+       return puffs_cred_isuid(pcr, 0) || puffs_cred_iskernel(pcr)
+           || puffs_cred_isfs(pcr);
+}
+
+/*
+ * Generic routine for checking file access rights.  Modeled after
+ * vaccess() in the kernel.
+ */
+int
+puffs_access(enum vtype type, mode_t file_mode, uid_t uid, gid_t gid,
+       mode_t acc_mode, const struct puffs_cred *pcr)
+{
+       mode_t mask;
+
+       /* megapower */
+       if (puffs_cred_iskernel(pcr) || puffs_cred_isfs(pcr))
+               return 0;
+
+       /* superuser, allow all except exec if *ALL* exec bits are unset */
+       if (puffs_cred_isuid(pcr, 0)) {
+               if ((acc_mode & PUFFS_VEXEC) && type != VDIR &&
+                   (file_mode & (S_IXUSR|S_IXGRP|S_IXOTH)) == 0)
+                       return EACCES;
+               return 0;
+       }
+
+       mask = 0;
+       /* owner */
+       if (puffs_cred_isuid(pcr, uid)) {
+               if (acc_mode & PUFFS_VEXEC)
+                       mask |= S_IXUSR;
+               if (acc_mode & PUFFS_VREAD)
+                       mask |= S_IRUSR;
+               if (acc_mode & PUFFS_VWRITE)
+                       mask |= S_IWUSR;
+       /* group */
+       } else if (puffs_cred_hasgroup(pcr, gid)) {
+               if (acc_mode & PUFFS_VEXEC)
+                       mask |= S_IXGRP;
+               if (acc_mode & PUFFS_VREAD)
+                       mask |= S_IRGRP;
+               if (acc_mode & PUFFS_VWRITE)
+                       mask |= S_IWGRP;
+       /* other */
+       } else {
+               if (acc_mode & PUFFS_VEXEC)
+                       mask |= S_IXOTH;
+               if (acc_mode & PUFFS_VREAD)
+                       mask |= S_IROTH;
+               if (acc_mode & PUFFS_VWRITE)
+                       mask |= S_IWOTH;
+       }
+
+       if ((file_mode & mask) == mask)
+               return 0;
+       else
+               return EACCES;
+}
+
+int
+puffs_access_chown(uid_t owner, gid_t group, uid_t newowner, gid_t newgroup,
+       const struct puffs_cred *pcr)
+{
+
+       if (newowner == (uid_t)PUFFS_VNOVAL)
+               newowner = owner;
+       if (newgroup == (gid_t)PUFFS_VNOVAL)
+               newgroup = group;
+
+       if ((!puffs_cred_isuid(pcr, owner) || newowner != owner ||
+           ((newgroup != group && !puffs_cred_hasgroup(pcr, newgroup))))
+           && !puffs_cred_isjuggernaut(pcr))
+               return EPERM;
+
+       return 0;
+}
+
+int
+puffs_access_chmod(uid_t owner, gid_t group, enum vtype type, mode_t mode,
+       const struct puffs_cred *pcr)
+{
+
+       if (!puffs_cred_isuid(pcr, owner) && !puffs_cred_isjuggernaut(pcr))
+               return EPERM;
+
+       if (!puffs_cred_isjuggernaut(pcr)) {
+               if (type != VDIR && (mode & S_ISTXT))
+                       return EFTYPE;
+               if (!puffs_cred_hasgroup(pcr, group) && (mode & S_ISGID))
+                       return EPERM;
+       }
+
+       return 0;
+}
+
+int
+puffs_access_times(uid_t uid, gid_t gid, mode_t mode, int va_utimes_null,
+       const struct puffs_cred *pcr)
+{
+
+       if (!puffs_cred_isuid(pcr, uid) && !puffs_cred_isjuggernaut(pcr)
+           && (va_utimes_null == 0
+             || puffs_access(VNON, mode, uid, gid, PUFFS_VWRITE, pcr) != 0))
+               return EPERM;
+
+       return 0;
+}
diff --git a/lib/libpuffs/device.c b/lib/libpuffs/device.c
new file mode 100644 (file)
index 0000000..92720ed
--- /dev/null
@@ -0,0 +1,356 @@
+#include "fs.h"
+#include <minix/com.h>
+#include <minix/endpoint.h>
+#include <minix/safecopies.h>
+#include <minix/u64.h>
+#include <string.h>
+#include "drivers.h"
+
+#include <minix/vfsif.h>
+
+FORWARD _PROTOTYPE( int safe_io_conversion, (endpoint_t driver,
+  cp_grant_id_t *gid, int *op, cp_grant_id_t *gids, endpoint_t *io_ept,
+  void **buffer, int *vec_grants, size_t bytes));
+FORWARD _PROTOTYPE( void safe_io_cleanup, (cp_grant_id_t, cp_grant_id_t *,
+       int));
+FORWARD _PROTOTYPE( int gen_opcl, (endpoint_t driver_e, int op,
+                               dev_t dev, endpoint_t proc_e, int flags)        );
+FORWARD _PROTOTYPE( int gen_io, (endpoint_t task_nr, message *mess_ptr)        );
+
+
+/*===========================================================================*
+ *                             fs_new_driver                                *
+ *===========================================================================*/
+PUBLIC int fs_new_driver(void)
+{
+ /* New driver endpoint for this device */
+  dev_t dev;
+  dev = (dev_t) fs_m_in.REQ_DEV;
+  driver_endpoints[major(dev)].driver_e = (endpoint_t) fs_m_in.REQ_DRIVER_E;
+  return(OK);
+}
+
+
+/*===========================================================================*
+ *                             safe_io_conversion                           *
+ *===========================================================================*/
+PRIVATE int safe_io_conversion(driver, gid, op, gids, io_ept, buffer,
+                              vec_grants, bytes)
+endpoint_t driver;
+cp_grant_id_t *gid;
+int *op;
+cp_grant_id_t *gids;
+endpoint_t *io_ept;
+void **buffer;
+int *vec_grants;
+size_t bytes;
+{
+  unsigned int j;
+  int access;
+  iovec_t *v;
+  static iovec_t *new_iovec;
+
+  STATICINIT(new_iovec, NR_IOREQS);
+
+  /* Number of grants allocated in vector I/O. */
+  *vec_grants = 0;
+
+  /* Driver can handle it - change request to a safe one. */
+
+  *gid = GRANT_INVALID;
+
+  switch(*op) {
+       case MFS_DEV_READ:
+       case MFS_DEV_WRITE:
+         /* Change to safe op. */
+         *op = *op == MFS_DEV_READ ? DEV_READ_S : DEV_WRITE_S;
+         *gid = cpf_grant_direct(driver, (vir_bytes) *buffer, bytes,
+                                 *op == DEV_READ_S ? CPF_WRITE : CPF_READ);
+         if(*gid == GRANT_INVALID) {
+               panic("cpf_grant_magic of buffer failed");
+         }
+
+         break;
+       case MFS_DEV_GATHER:
+       case MFS_DEV_SCATTER:
+         /* Change to safe op. */
+         *op = *op == MFS_DEV_GATHER ? DEV_GATHER_S : DEV_SCATTER_S;
+
+         /* Grant access to my new i/o vector. */
+         *gid = cpf_grant_direct(driver, (vir_bytes) new_iovec,
+                                 bytes * sizeof(iovec_t), CPF_READ|CPF_WRITE);
+         if(*gid == GRANT_INVALID) {
+               panic("cpf_grant_direct of vector failed");
+         }
+
+         v = (iovec_t *) *buffer;
+
+         /* Grant access to i/o buffers. */
+         for(j = 0; j < bytes; j++) {
+               if(j >= NR_IOREQS)
+                       panic("vec too big: %u", bytes);
+               access = (*op == DEV_GATHER_S) ? CPF_WRITE : CPF_READ;
+               new_iovec[j].iov_addr = gids[j] =
+                       cpf_grant_direct(driver, (vir_bytes) v[j].iov_addr,
+                                        (size_t) v[j].iov_size, access);
+
+               if(!GRANT_VALID(gids[j])) {
+                       panic("ext2: grant to iovec buf failed");
+               }
+               new_iovec[j].iov_size = v[j].iov_size;
+               (*vec_grants)++;
+         }
+
+         /* Set user's vector to the new one. */
+         *buffer = new_iovec;
+         break;
+       default:
+         panic("Illegal operation %d\n", *op);
+         break;
+  }
+
+  /* If we have converted to a safe operation, I/O
+   * endpoint becomes FS if it wasn't already.
+   */
+  if(GRANT_VALID(*gid)) {
+       *io_ept = SELF_E;
+       return 1;
+  }
+
+  /* Not converted to a safe operation (because there is no
+   * copying involved in this operation).
+   */
+  return 0;
+}
+
+/*===========================================================================*
+ *                     safe_io_cleanup                                      *
+ *===========================================================================*/
+PRIVATE void safe_io_cleanup(gid, gids, gids_size)
+cp_grant_id_t gid;
+cp_grant_id_t *gids;
+int gids_size;
+{
+/* Free resources (specifically, grants) allocated by safe_io_conversion(). */
+  int j;
+
+  (void) cpf_revoke(gid);
+
+  for(j = 0; j < gids_size; j++)
+       (void) cpf_revoke(gids[j]);
+
+  return;
+}
+
+/*===========================================================================*
+ *                     block_dev_io                                         *
+ *===========================================================================*/
+PUBLIC int block_dev_io(
+  int op,                      /* MFS_DEV_READ, MFS_DEV_WRITE, etc. */
+  dev_t dev,                   /* major-minor device number */
+  endpoint_t proc_e,           /* in whose address space is buf? */
+  void *buffer,                        /* virtual address of the buffer */
+  u64_t pos,                   /* byte position */
+  size_t bytes                 /* how many bytes to transfer */
+)
+{
+/* Read or write from a device.  The parameter 'dev' tells which one. */
+  int r, safe;
+  message m;
+  cp_grant_id_t gid = GRANT_INVALID;
+  int vec_grants;
+  int op_used;
+  void *buf_used;
+  static cp_grant_id_t *gids;
+  endpoint_t driver_e;
+
+  STATICINIT(gids, NR_IOREQS);
+
+  /* Determine driver endpoint for this device */
+  driver_e = driver_endpoints[major(dev)].driver_e;
+
+  /* See if driver is roughly valid. */
+  if (driver_e == NONE) {
+       lpuffs_debug("%d block_dev_io: no driver for dev %x\n", SELF_E, dev);
+       return(EDEADEPT);
+  }
+
+  /* The io vector copying relies on this I/O being for FS itself. */
+  if(proc_e != SELF_E) {
+       lpuffs_debug("%d doing block_dev_io for non-self %d\n", SELF_E, proc_e);
+       panic("doing block_dev_io for non-self: %d", proc_e);
+  }
+
+  /* By default, these are right. */
+  m.USER_ENDPT = proc_e;
+  m.ADDRESS  = buffer;
+  buf_used = buffer;
+
+  /* Convert parameters to 'safe mode'. */
+  op_used = op;
+  safe = safe_io_conversion(driver_e, &gid, &op_used, gids, &m.USER_ENDPT,
+                           &buf_used, &vec_grants, bytes);
+
+  /* Set up rest of the message. */
+  if (safe) m.IO_GRANT = (char *) gid;
+
+  m.m_type   = op_used;
+  m.DEVICE   = minor(dev);
+  m.POSITION = ex64lo(pos);
+  m.COUNT    = bytes;
+  m.HIGHPOS  = ex64hi(pos);
+
+  /* Call the task. */
+  r = sendrec(driver_e, &m);
+  if(r == OK && m.REP_STATUS == ERESTART) r = EDEADEPT;
+
+  /* As block I/O never SUSPENDs, safe cleanup must be done whether
+   * the I/O succeeded or not. */
+  if (safe) safe_io_cleanup(gid, gids, vec_grants);
+
+  /* RECOVERY:
+   * - send back dead driver number
+   * - VFS unmaps it, waits for new driver
+   * - VFS sends the new driver endp for the FS proc and the request again
+   */
+  if (r != OK) {
+       if (r == EDEADSRCDST || r == EDEADEPT) {
+               lpuffs_debug("%d dead driver %d\n", SELF_E, driver_e);
+               driver_endpoints[major(dev)].driver_e = NONE;
+               return(r);
+       } else if (r == ELOCKED) {
+               lpuffs_debug("%d ELOCKED talking to %d\n", SELF_E, driver_e);
+               return(r);
+       } else
+               panic("call_task: can't send/receive: %d", r);
+  } else {
+       /* Did the process we did the sendrec() for get a result? */
+       if (m.REP_ENDPT != proc_e) {
+               lpuffs_debug("%d strange device reply from %d, type = %d, proc "
+                      "= %d (not %d) (2) ignored\n", SELF_E, m.m_source,
+                      m.m_type, proc_e, m.REP_ENDPT);
+               r = EIO;
+       }
+  }
+
+  /* Task has completed.  See if call completed. */
+  if (m.REP_STATUS == SUSPEND) {
+       panic("ext2 block_dev_io: driver returned SUSPEND");
+  }
+
+  if(buffer != buf_used && r == OK) {
+       memcpy(buffer, buf_used, bytes * sizeof(iovec_t));
+  }
+
+  return(m.REP_STATUS);
+}
+
+/*===========================================================================*
+ *                             dev_open                                     *
+ *===========================================================================*/
+PUBLIC int dev_open(
+  endpoint_t driver_e,
+  dev_t dev,                   /* device to open */
+  endpoint_t proc_e,           /* process to open for */
+  int flags                    /* mode bits and flags */
+)
+{
+  int major, r;
+
+  /* Determine the major device number call the device class specific
+   * open/close routine.  (This is the only routine that must check the
+   * device number for being in range.  All others can trust this check.)
+   */
+  major = major(dev);
+  if (major >= NR_DEVICES) {
+       lpuffs_debug("Major device number %d not in range\n", major(dev));
+       return(EIO);
+  }
+  r = gen_opcl(driver_e, DEV_OPEN, dev, proc_e, flags);
+  if (r == SUSPEND) panic("suspend on open from");
+  return(r);
+}
+
+
+/*===========================================================================*
+ *                             dev_close                                    *
+ *===========================================================================*/
+PUBLIC void dev_close(
+  endpoint_t driver_e,
+  dev_t dev                    /* device to close */
+)
+{
+  (void) gen_opcl(driver_e, DEV_CLOSE, dev, 0, 0);
+}
+
+
+/*===========================================================================*
+ *                             gen_opcl                                     *
+ *===========================================================================*/
+PRIVATE int gen_opcl(
+  endpoint_t driver_e,
+  int op,                      /* operation, DEV_OPEN or DEV_CLOSE */
+  dev_t dev,                   /* device to open or close */
+  endpoint_t proc_e,           /* process to open/close for */
+  int flags                    /* mode bits and flags */
+)
+{
+/* Called from the dmap struct in table.c on opens & closes of special files.*/
+  message dev_mess;
+
+  dev_mess.m_type   = op;
+  dev_mess.DEVICE   = minor(dev);
+  dev_mess.USER_ENDPT = proc_e;
+  dev_mess.COUNT    = flags;
+
+  /* Call the task. */
+  (void) gen_io(driver_e, &dev_mess);
+
+  return(dev_mess.REP_STATUS);
+}
+
+
+/*===========================================================================*
+ *                             gen_io                                       *
+ *===========================================================================*/
+PRIVATE int gen_io(
+  endpoint_t task_nr,          /* which task to call */
+  message *mess_ptr            /* pointer to message for task */
+)
+{
+/* All file system I/O ultimately comes down to I/O on major/minor device
+ * pairs.  These lead to calls on the following routines via the dmap table.
+ */
+
+  int r, proc_e;
+
+  proc_e = mess_ptr->USER_ENDPT;
+
+  r = sendrec(task_nr, mess_ptr);
+  if(r == OK && mess_ptr->REP_STATUS == ERESTART)
+       r = EDEADEPT;
+
+  if (r != OK) {
+       if (r == EDEADSRCDST || r == EDEADEPT) {
+               lpuffs_debug("fs: dead driver %d\n", task_nr);
+               panic("should handle crashed drivers");
+               return(r);
+       }
+       if (r == ELOCKED) {
+               lpuffs_debug("fs: ELOCKED talking to %d\n", task_nr);
+               return(r);
+       }
+       panic("call_task: can't send/receive: %d", r);
+  }
+
+   /* Did the process we did the sendrec() for get a result? */
+  if (mess_ptr->REP_ENDPT != proc_e) {
+       lpuffs_debug("fs: strange device reply from %d, type = %d, proc = %d (not "
+              "%d) (2) ignored\n", mess_ptr->m_source, mess_ptr->m_type,
+               proc_e,
+               mess_ptr->REP_ENDPT);
+       return(EIO);
+  }
+
+  return(OK);
+}
diff --git a/lib/libpuffs/drivers.h b/lib/libpuffs/drivers.h
new file mode 100644 (file)
index 0000000..cbe8032
--- /dev/null
@@ -0,0 +1,17 @@
+#ifndef LIBPUFFS_DRIVERS_H
+#define LIBPUFFS_DRIVERS_H
+
+/* Args to dev_bio/dev_io */
+#define MFS_DEV_READ    10001
+#define MFS_DEV_WRITE   10002
+#define MFS_DEV_SCATTER 10003
+#define MFS_DEV_GATHER  10004
+
+/* Driver endpoints for major devices. Only the block devices
+ * are mapped here, it's a subset of the mapping in the VFS */
+
+EXTERN struct driver_endpoints {
+    endpoint_t driver_e;
+} driver_endpoints[NR_DEVICES];
+
+#endif /* LIBPUFFS_DRIVERS_H */
diff --git a/lib/libpuffs/fs.h b/lib/libpuffs/fs.h
new file mode 100644 (file)
index 0000000..691b0dd
--- /dev/null
@@ -0,0 +1,31 @@
+/* This is the master header for fs.  It includes some other files
+ * and defines the principal constants.
+ */
+
+#ifndef LIBPUFFS_FS_H
+#define LIBPUFFS_FS_H
+
+#define _POSIX_SOURCE      1   /* tell headers to include POSIX stuff */
+#define _MINIX             1   /* tell headers to include MINIX stuff */
+#define _SYSTEM            1   /* tell headers that this is the kernel */
+
+#define VERBOSE                   0    /* show messages during initialization? */
+
+/* The following are so basic, all the *.c files get them automatically. */
+#include <minix/config.h>      /* MUST be first */
+#include <minix/ansi.h>                /* MUST be second */
+#include <sys/types.h>
+#include <minix/const.h>
+#include <minix/type.h>
+#include <minix/dmap.h>
+
+#include <limits.h>
+#include <errno.h>
+
+#include <minix/syslib.h>
+#include <minix/sysutil.h>
+
+#include "proto.h"
+#include "glo.h"
+
+#endif /* LIBPUFFS_FS_H */
diff --git a/lib/libpuffs/glo.h b/lib/libpuffs/glo.h
new file mode 100644 (file)
index 0000000..db34d56
--- /dev/null
@@ -0,0 +1,61 @@
+/* EXTERN should be extern except for the table file */
+
+#ifndef LIBPUFFS_GLO_H
+#define LIBPUFFS_GLO_H
+
+#ifdef _TABLE
+#undef EXTERN
+#define EXTERN
+#endif
+
+#include <minix/vfsif.h>
+
+#include "puffs_msgif.h"
+#include "drivers.h"
+
+EXTERN struct puffs_usermount *global_pu;
+
+EXTERN int is_readonly_fs;
+EXTERN int is_root_fs;
+EXTERN int buildpath;
+
+/* Sometimes user can call exit. If we received a message,
+ * report a failure to VFS before exiting. Especially on mount
+ * and unmount.
+ *
+ * Either transid of last request or 0.
+ */
+EXTERN int last_request_transid;
+
+/* The following variables are used for returning results to the caller. */
+EXTERN int err_code;        /* temporary storage for error number */
+
+/* TODO: it duplicates caller_uid and caller_gid */
+EXTERN struct puffs_kcred global_kcred;
+
+extern _PROTOTYPE (int (*fs_call_vec[]), (void) ); /* fs call table */
+
+EXTERN message fs_m_in;
+EXTERN message fs_m_out;
+EXTERN vfs_ucred_t credentials;
+
+EXTERN uid_t caller_uid;
+EXTERN gid_t caller_gid;
+
+EXTERN int req_nr;
+
+EXTERN endpoint_t SELF_E;
+
+EXTERN char user_path[PATH_MAX+1];  /* pathname to be processed */
+
+EXTERN dev_t fs_dev;              /* The device that is handled by this FS proc
+                                   */
+EXTERN char fs_dev_label[16];    /* Name of the device driver that is handled
+                                  * by this FS proc.
+                                  */
+EXTERN char fs_name[PATH_MAX+1];
+
+EXTERN int unmountdone;
+EXTERN int exitsignaled;
+
+#endif /* LIBPUFFS_GLO_H */
diff --git a/lib/libpuffs/inode.c b/lib/libpuffs/inode.c
new file mode 100644 (file)
index 0000000..510260b
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ * Created (MFS based):
+ *   June 2011 (Evgeniy Ivanov)
+ */
+
+#include "fs.h"
+#include <string.h>
+#include <assert.h>
+#include <minix/vfsif.h>
+
+#include "puffs.h"
+#include "puffs_priv.h"
+
+
+void release_node(struct puffs_usermount *pu, struct puffs_node *pn)
+{
+  assert(pn->pn_count == 0);
+
+  /* Required if puffs_node_reclaim() decides to leave node in the list */
+  pn->pn_mountpoint = FALSE;
+
+  if (pu->pu_ops.puffs_node_reclaim) {
+       if (global_pu->pu_ops.puffs_node_reclaim(global_pu, pn) != 0)
+               lpuffs_debug("Warning: reclaim failed\n");
+  } else {
+       puffs_pn_put(pn);
+  }
+}
+
+
+/*===========================================================================*
+ *                fs_putnode                                                 *
+ *===========================================================================*/
+PUBLIC int fs_putnode(void)
+{
+/* Find the pnode specified by the request message and decrease its counter.
+ * Release unused pnode.
+ */
+  struct puffs_node *pn;
+  int count = fs_m_in.REQ_COUNT;
+  ino_t inum = fs_m_in.REQ_INODE_NR;
+
+  if ((pn = puffs_pn_nodewalk(global_pu, 0, &inum)) == NULL) {
+       /* XXX Probably removed from the list, see puffs_pn_remove() */
+       struct puffs_node *pn_cur, *pn_next;
+       pn_cur = LIST_FIRST(&global_pu->pu_pnode_removed_lst);
+       while (pn_cur) {
+               pn_next = LIST_NEXT(pn_cur, pn_entries);
+               if (pn_cur->pn_va.va_fileid == inum) {
+                       pn = pn_cur;
+                       break;
+               }
+               pn_cur = pn_next;
+       }
+  }
+
+  if (pn == NULL) {
+       lpuffs_debug("%s:%d putnode: pnode #%ld dev: %d not found\n", __FILE__,
+               __LINE__, inum, fs_dev);
+       panic("fs_putnode failed");
+  }
+
+  if (count <= 0) {
+       lpuffs_debug("%s:%d putnode: bad value for count: %d\n", __FILE__,
+               __LINE__, count);
+       panic("fs_putnode failed");
+  } else if (pn->pn_count == 0) {
+       /* FUSE fs might store in the list pnodes, which we hasn't
+        * open, this means we got put request for file,
+        * which wasn't opened by VFS.
+        */
+       lpuffs_debug("%s:%d putnode: pn_count already zero\n", __FILE__,
+               __LINE__);
+       panic("fs_putnode failed");
+  } else if (count > pn->pn_count) {
+       struct puffs_node *pn_cur, *pn_next;
+       struct puffs_usermount *pu = global_pu;
+       ino_t ino = pn->pn_va.va_fileid;
+
+       pn_cur = LIST_FIRST(&pu->pu_pnodelst);
+       lpuffs_debug("inum  count  path  polen  hash\n");
+       while (pn_cur) {
+               pn_next = LIST_NEXT(pn_cur, pn_entries);
+               if (pn_cur->pn_va.va_fileid == ino) {
+                       lpuffs_debug("%ld: %d %s %u %u\n", ino, pn_cur->pn_count,
+                               pn_cur->pn_po.po_path,
+                               pn_cur->pn_po.po_len,
+                               pn_cur->pn_po.po_hash);
+               }
+               pn_cur = pn_next;
+       }
+       lpuffs_debug("%s:%d putnode: count too high: %d > %d\n", __FILE__,
+               __LINE__, count, pn->pn_count);
+       panic("fs_putnode failed");
+  }
+
+  pn->pn_count -= count;
+
+  if (pn->pn_count == 0)
+       release_node(global_pu, pn);
+
+  return(OK);
+}
diff --git a/lib/libpuffs/link.c b/lib/libpuffs/link.c
new file mode 100644 (file)
index 0000000..e852224
--- /dev/null
@@ -0,0 +1,560 @@
+#include "fs.h"
+
+#include <stdlib.h>
+#include <assert.h>
+
+#include "puffs.h"
+#include "puffs_priv.h"
+
+#define SAME 1000
+
+
+/*===========================================================================*
+ *                              fs_ftrunc                                    *
+ *===========================================================================*/
+PUBLIC int fs_ftrunc(void)
+{
+  int r;
+  struct puffs_node *pn;
+  off_t start, end;
+  PUFFS_MAKECRED(pcr, &global_kcred);
+
+  if ((pn = puffs_pn_nodewalk(global_pu, 0, &fs_m_in.REQ_INODE_NR)) == NULL)
+          return(EINVAL);
+
+  start = fs_m_in.REQ_TRC_START_LO;
+  end = fs_m_in.REQ_TRC_END_LO;
+
+  if (end == 0) {
+       struct vattr va;
+
+       if (pn->pn_va.va_size == start)
+               return(OK);
+
+       if (global_pu->pu_ops.puffs_node_setattr == NULL)
+               return(EINVAL);
+       
+       puffs_vattr_null(&va);
+       va.va_size = start;
+
+       r = global_pu->pu_ops.puffs_node_setattr(global_pu, pn, &va, pcr);
+       if (r) return(EINVAL);
+  } else {
+       /* XXX zerofill the given region. Can we make a hole? */
+       off_t bytes_left = end - start;
+       char* rw_buf;
+
+       if (global_pu->pu_ops.puffs_node_write == NULL)
+               return(EINVAL);
+
+       /* XXX split into chunks? */
+       rw_buf = malloc(bytes_left);
+       if (!rw_buf)
+               panic("fs_ftrunc: failed to allocated memory\n");
+       memset(rw_buf, 0, bytes_left);
+
+       r = global_pu->pu_ops.puffs_node_write(global_pu, pn, (uint8_t *)rw_buf,
+                                       start, (size_t *) &bytes_left, pcr, 0);
+       free(rw_buf);
+       if (r) return(EINVAL);
+  }
+
+  update_times(pn, CTIME | MTIME, 0);
+
+  return(r);
+}
+
+
+/*===========================================================================*
+ *                              fs_link                                      *
+ *===========================================================================*/
+PUBLIC int fs_link()
+{
+/* Perform the link(name1, name2) system call. */
+
+  register int r;
+  char string[NAME_MAX + 1];
+  phys_bytes len;
+  struct puffs_node *pn, *pn_dir, *new_pn;
+  time_t cur_time;
+  struct puffs_kcn pkcnp;
+  PUFFS_MAKECRED(pcr, &global_kcred);
+  struct puffs_cn pcn = {&pkcnp, (struct puffs_cred *)pcr, {0}};
+
+  if (global_pu->pu_ops.puffs_node_link == NULL)
+       return(OK);
+
+  /* Copy the link name's last component */
+  len = fs_m_in.REQ_PATH_LEN;
+  if (len > NAME_MAX + 1)
+        return(ENAMETOOLONG);
+
+  r = sys_safecopyfrom(VFS_PROC_NR, (cp_grant_id_t) fs_m_in.REQ_GRANT, 0,
+                       (vir_bytes) string, (size_t) len, D);
+  if (r != OK) return(r);
+  NUL(string, len, sizeof(string));
+
+  if ((pn = puffs_pn_nodewalk(global_pu, 0, &fs_m_in.REQ_INODE_NR)) == NULL)
+       return(EINVAL);
+
+  /* Check to see if the file has maximum number of links already. */
+  if (pn->pn_va.va_nlink >= LINK_MAX)
+       return(EMLINK);
+
+  /* Only super_user may link to directories. */
+  if ((pn->pn_va.va_mode & I_TYPE) == I_DIRECTORY && caller_uid != SU_UID)
+       return(EPERM);
+
+  if ((pn_dir = puffs_pn_nodewalk(global_pu, 0, &fs_m_in.REQ_DIR_INO)) == NULL)
+        return(EINVAL);
+
+  if (pn_dir->pn_va.va_nlink == NO_LINK) {
+       /* Dir does not actually exist */
+        return(ENOENT);
+  }
+
+  /* If 'name2' exists in full (even if no space) set 'r' to error. */
+  if ((new_pn = advance(pn_dir, string, IGN_PERM)) == NULL) {
+        r = err_code;
+        if (r == ENOENT) r = OK;
+  } else {
+        r = EEXIST;
+  }
+
+  if (r != OK) return(r);
+
+  /* Try to link. */
+  pcn.pcn_namelen = strlen(string);
+  assert(pcn.pcn_namelen <= MAXPATHLEN);
+  strcpy(pcn.pcn_name, string);
+
+  if (buildpath) {
+       if (puffs_path_pcnbuild(global_pu, &pcn, pn_dir) != 0)
+               return(EINVAL);
+  }
+
+  if (global_pu->pu_ops.puffs_node_link(global_pu, pn_dir, pn, &pcn) != 0)
+       r = EINVAL;
+
+  if (buildpath)
+       global_pu->pu_pathfree(global_pu, &pcn.pcn_po_full);
+  
+  if (r != OK) return(EINVAL);
+
+  cur_time = clock_time();
+  update_times(pn, CTIME, cur_time);
+  update_times(pn_dir, MTIME | CTIME, cur_time);
+
+  return(OK);
+}
+
+
+/*===========================================================================*
+ *                             fs_rdlink                                     *
+ *===========================================================================*/
+PUBLIC int fs_rdlink()
+{
+  register int r;              /* return value */
+  size_t copylen;
+  struct puffs_node *pn;
+  char user_path[PATH_MAX];
+  PUFFS_MAKECRED(pcr, &global_kcred);
+
+  copylen = fs_m_in.REQ_MEM_SIZE < UMAX_FILE_POS ?
+               fs_m_in.REQ_MEM_SIZE : UMAX_FILE_POS;
+  
+  assert(copylen <= PATH_MAX);
+
+  if ((pn = puffs_pn_nodewalk(global_pu, 0, &fs_m_in.REQ_INODE_NR)) == NULL)
+       return(EINVAL);
+
+  if (!S_ISLNK(pn->pn_va.va_mode))
+       return(EACCES);
+
+  if (global_pu->pu_ops.puffs_node_readlink == NULL)
+       return(EINVAL);
+
+  r = global_pu->pu_ops.puffs_node_readlink(global_pu, pn, pcr, user_path,
+                                               &copylen);
+  if (r != OK) {
+       if (r > 0) r = -r;
+       return(r);
+  }
+
+  r = sys_safecopyto(VFS_PROC_NR, (cp_grant_id_t) fs_m_in.REQ_GRANT,
+                 (vir_bytes) 0, (vir_bytes) user_path, (size_t) copylen, D);
+  if (r == OK)
+         fs_m_out.RES_NBYTES = copylen;
+
+  return(r);
+}
+
+
+FORWARD _PROTOTYPE( void release_node, (struct puffs_usermount *pu,
+                        struct puffs_node *pn ));
+
+/*===========================================================================*
+ *                              fs_rename                                    *
+ *===========================================================================*/
+PUBLIC int fs_rename()
+{
+/* Perform the rename(name1, name2) system call. */
+  struct puffs_node *old_dirp, *old_ip;      /* ptrs to old dir, file pnodes */
+  struct puffs_node *new_dirp, *new_ip;      /* ptrs to new dir, file pnodes */
+  struct puffs_kcn pkcnp_src;
+  PUFFS_MAKECRED(pcr_src, &global_kcred);
+  struct puffs_cn pcn_src = {&pkcnp_src, (struct puffs_cred *) pcr_src, {0}};
+  struct puffs_kcn pkcnp_dest;
+  PUFFS_MAKECRED(pcr_dest, &global_kcred);
+  struct puffs_cn pcn_targ = {&pkcnp_dest, (struct puffs_cred *) pcr_dest, {0}};
+  int r = OK;                           /* error flag; initially no error */
+  int odir, ndir;                       /* TRUE iff {old|new} file is dir */
+  int same_pdir;                        /* TRUE iff parent dirs are the same */
+  phys_bytes len;
+  time_t cur_time;
+
+  if (global_pu->pu_ops.puffs_node_rename == NULL)
+       return(EINVAL);
+
+  /* Copy the last component of the old name */
+  len = fs_m_in.REQ_REN_LEN_OLD; /* including trailing '\0' */
+  if (len > NAME_MAX + 1)
+        return(ENAMETOOLONG);
+
+  r = sys_safecopyfrom(VFS_PROC_NR, (cp_grant_id_t) fs_m_in.REQ_REN_GRANT_OLD,
+                (vir_bytes) 0, (vir_bytes) pcn_src.pcn_name, (size_t) len, D);
+  if (r != OK) return(r);
+  NUL(pcn_src.pcn_name, len, sizeof(pcn_src.pcn_name));
+  pcn_src.pcn_namelen = len - 1;
+
+  /* Copy the last component of the new name */
+  len = fs_m_in.REQ_REN_LEN_NEW; /* including trailing '\0' */
+  if (len > NAME_MAX + 1)
+        return(ENAMETOOLONG);
+
+  r = sys_safecopyfrom(VFS_PROC_NR, (cp_grant_id_t) fs_m_in.REQ_REN_GRANT_NEW,
+                (vir_bytes) 0, (vir_bytes) pcn_targ.pcn_name, (size_t) len, D);
+  if (r != OK) return(r);
+  NUL(pcn_targ.pcn_name, len, sizeof(pcn_targ.pcn_name));
+  pcn_targ.pcn_namelen = len - 1;
+
+  /* Get old dir pnode */
+  if ((old_dirp = puffs_pn_nodewalk(global_pu, 0, &fs_m_in.REQ_REN_OLD_DIR))
+         == NULL)
+        return(ENOENT);
+
+  old_ip = advance(old_dirp, pcn_src.pcn_name, IGN_PERM);
+  if (!old_ip) {
+       return(ENOENT);
+  }
+  r = err_code;
+
+  if (r == EENTERMOUNT || r == ELEAVEMOUNT) {
+        old_ip = NULL;
+        if (r == EENTERMOUNT) r = EXDEV;        /* should this fail at all? */
+        else if (r == ELEAVEMOUNT) r = EINVAL;  /* rename on dot-dot */
+  }
+
+  /* Get new dir pnode */
+  if ((new_dirp = puffs_pn_nodewalk(global_pu, 0, &fs_m_in.REQ_REN_NEW_DIR))
+         == NULL) {
+        r = ENOENT;
+  } else {
+        if (new_dirp->pn_va.va_nlink == NO_LINK) {
+               /* Dir does not actually exist */
+                return(ENOENT);
+        }
+  }
+
+  /* not required to exist */
+  new_ip = advance(new_dirp, pcn_targ.pcn_name, IGN_PERM);
+
+  /* However, if the check failed because the file does exist, don't continue.
+   * Note that ELEAVEMOUNT is covered by the dot-dot check later. */
+  if (err_code == EENTERMOUNT) {
+        new_ip = NULL;
+        r = EBUSY;
+  }
+
+  if (old_ip != NULL) {
+       /* TRUE iff dir */
+       odir = ((old_ip->pn_va.va_mode & I_TYPE) == I_DIRECTORY);
+  } else {
+       odir = FALSE;
+  }
+
+  if (r != OK) return(r);
+
+  /* Check for a variety of possible errors. */
+  same_pdir = (old_dirp == new_dirp);
+
+  /* The old or new name must not be . or .. */
+  if (strcmp(pcn_src.pcn_name, ".") == 0 ||
+      strcmp(pcn_src.pcn_name, "..") == 0 ||
+      strcmp(pcn_targ.pcn_name, ".") == 0 ||
+      strcmp(pcn_targ.pcn_name, "..") == 0) {
+       r = EINVAL;
+  }
+
+  /* Some tests apply only if the new path exists. */
+  if (new_ip == NULL) {
+       if (odir && (new_dirp->pn_va.va_nlink >= SHRT_MAX ||
+                    new_dirp->pn_va.va_nlink >= LINK_MAX) &&
+           !same_pdir && r == OK) {
+               r = EMLINK;
+       }
+  } else {
+       if (old_ip == new_ip) r = SAME; /* old=new */
+
+       /* dir ? */
+       ndir = ((new_ip->pn_va.va_mode & I_TYPE) == I_DIRECTORY);
+       if (odir == TRUE && ndir == FALSE) r = ENOTDIR;
+       if (odir == FALSE && ndir == TRUE) r = EISDIR;
+  }
+
+  if (r == SAME) {
+       r = OK;
+       goto rename_out;
+  }
+
+  if (r != OK) return(r);
+
+  /* If a process has another root directory than the system root, we might
+   * "accidently" be moving it's working directory to a place where it's
+   * root directory isn't a super directory of it anymore. This can make
+   * the function chroot useless. If chroot will be used often we should
+   * probably check for it here. */
+
+  /* The rename will probably work. Only two things can go wrong now:
+   * 1. being unable to remove the new file. (when new file already exists)
+   * 2. being unable to make the new directory entry. (new file doesn't exists)
+   *     [directory has to grow by one block and cannot because the disk
+   *      is completely full].
+   * 3. Something (doubtfully) else depending on the FS.
+   */
+
+  if (buildpath) {
+       pcn_src.pcn_po_full = old_ip->pn_po;
+
+       if (puffs_path_pcnbuild(global_pu, &pcn_targ, new_dirp) != 0)
+               return(EINVAL);
+  }
+
+  r = global_pu->pu_ops.puffs_node_rename(global_pu, old_dirp, old_ip, &pcn_src,
+                                               new_dirp, new_ip, &pcn_targ);
+  if (r > 0) r = -r;
+
+  if (buildpath) {
+       if (r) {
+               global_pu->pu_pathfree(global_pu, &pcn_targ.pcn_po_full);
+       } else {
+               struct puffs_pathinfo pi;
+               struct puffs_pathobj po_old;
+
+               /* handle this node */
+               po_old = old_ip->pn_po;
+               old_ip->pn_po = pcn_targ.pcn_po_full;
+
+               if (old_ip->pn_va.va_type != VDIR) {
+                       global_pu->pu_pathfree(global_pu, &po_old);
+                       return(OK);
+               }
+
+               /* 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(global_pu, puffs_path_prefixadj, &pi)
+                               != NULL) {
+                       /* Actually nomem */
+                       return(EINVAL);
+               }
+               PU_UNLOCK();
+               global_pu->pu_pathfree(global_pu, &po_old);
+       }
+  }
+
+rename_out: 
+  cur_time = clock_time();
+  update_times(old_dirp, MTIME | CTIME, cur_time);
+  update_times(new_dirp, MTIME | CTIME, cur_time);
+
+  /* XXX see release_node comment in fs_unlink */
+  if (new_ip && new_ip->pn_count == 0) {
+       release_node(global_pu, new_ip);
+  }
+
+  return(r);
+}
+
+FORWARD _PROTOTYPE( int remove_dir, (struct puffs_node *pn_dir,
+                       struct puffs_node *pn, struct puffs_cn *pcn));
+FORWARD _PROTOTYPE( int unlink_file, (struct puffs_node *dirp,
+                       struct puffs_node *pn, struct puffs_cn *pcn));
+
+/*===========================================================================*
+ *                              fs_unlink                                    *
+ *===========================================================================*/
+PUBLIC int fs_unlink()
+{
+/* Perform the unlink(name) or rmdir(name) system call. The code for these two
+ * is almost the same.  They differ only in some condition testing.  Unlink()
+ * may be used by the superuser to do dangerous things; rmdir() may not.
+ */
+  int r;
+  struct puffs_node *pn, *pn_dir;
+  time_t cur_time;
+  struct puffs_kcn pkcnp;
+  struct puffs_cn pcn = {&pkcnp, 0, {0}};
+  PUFFS_KCREDTOCRED(pcn.pcn_cred, &global_kcred);
+  int len;
+
+  /* Copy the last component */
+  len = fs_m_in.REQ_PATH_LEN;
+  pcn.pcn_namelen = len - 1;
+  if (pcn.pcn_namelen > NAME_MAX)
+        return(ENAMETOOLONG);
+
+  r = sys_safecopyfrom(VFS_PROC_NR, (cp_grant_id_t) fs_m_in.REQ_GRANT,
+                      (vir_bytes) 0, (vir_bytes) pcn.pcn_name,
+                      (size_t) len, D);
+  if (r != OK) return (r);
+  NUL(pcn.pcn_name, len, sizeof(pcn.pcn_name));
+
+  if ((pn_dir = puffs_pn_nodewalk(global_pu, 0, &fs_m_in.REQ_INODE_NR)) == NULL)
+       return(EINVAL);
+
+  /* The last directory exists. Does the file also exist? */
+  pn = advance(pn_dir, pcn.pcn_name, IGN_PERM);
+  r = err_code;
+
+  /* If error, return pnode. */
+  if (r != OK) {
+        /* Mount point? */
+        if (r == EENTERMOUNT || r == ELEAVEMOUNT) {
+                r = EBUSY;
+        }
+        return(r);
+  }
+
+  /* Now test if the call is allowed, separately for unlink() and rmdir(). */
+  if (fs_m_in.m_type == REQ_UNLINK) {
+       /* Only the su may unlink directories, but the su can unlink any dir */
+       if ((pn->pn_va.va_mode & I_TYPE) == I_DIRECTORY)
+               r = EPERM;
+       if (r == OK)
+               r = unlink_file(pn_dir, pn, &pcn);
+  } else {
+       r = remove_dir(pn_dir, pn, &pcn); /* call is RMDIR */
+  }
+
+  if (pn->pn_va.va_nlink != 0) {
+       cur_time = clock_time();
+       update_times(pn, CTIME, cur_time);
+       update_times(pn_dir, MTIME | CTIME, cur_time);
+  }
+
+  /* XXX Ideally, we should check pn->pn_flags & PUFFS_NODE_REMOVED, but
+   * librefuse doesn't set it (neither manually or via puffs_pn_remove() ).
+   * Thus we just check that "pn_count == 0". Otherwise release_node()
+   * will be called in fs_put().
+   */
+  if (pn->pn_count == 0)
+       release_node(global_pu, pn);
+
+  return(r);
+}
+
+
+/*===========================================================================*
+ *                             remove_dir                                   *
+ *===========================================================================*/
+PRIVATE int remove_dir(pn_dir, pn, pcn)
+struct puffs_node *pn_dir;             /* parent directory */
+struct puffs_node *pn;                 /* directory to be removed */
+struct puffs_cn *pcn;                  /* Name, creads of directory */
+{
+  /* A directory file has to be removed. Five conditions have to met:
+   *   - The file must be a directory
+   *   - The directory must be empty (except for . and ..)
+   *   - The final component of the path must not be . or ..
+   *   - The directory must not be the root of a mounted file system (VFS)
+   *   - The directory must not be anybody's root/working directory (VFS)
+   */
+
+  /* "." and ".." dentries can be stored in 28 bytes */
+  #define EMPTY_DIR_DENTRIES_SIZE      28
+  int r;
+  char remove_dir_buf[EMPTY_DIR_DENTRIES_SIZE];
+  struct dirent *dent = (struct dirent*) remove_dir_buf;
+  int buf_left = EMPTY_DIR_DENTRIES_SIZE;
+  off_t pos = 0;
+  int eofflag = 0;
+
+  if (global_pu->pu_ops.puffs_node_rmdir == NULL)
+       return(EINVAL);
+
+  if (!S_ISDIR(pn->pn_va.va_mode))
+       return(ENOTDIR);
+
+  /* Check if directory is empty */
+  r = global_pu->pu_ops.puffs_node_readdir(global_pu, pn, dent, &pos,
+                       (size_t *)&buf_left, pcn->pcn_cred, &eofflag, 0, 0);
+  if (r) return(EINVAL);
+  if (!eofflag) return(ENOTEMPTY);
+
+  if (strcmp(pcn->pcn_name, ".") == 0 || strcmp(pcn->pcn_name, "..") == 0)
+       return(EINVAL);
+
+  if (pn->pn_va.va_fileid == global_pu->pu_pn_root->pn_va.va_fileid)
+       return(EBUSY); /* can't remove 'root' */
+  
+  if (buildpath) {
+       r = puffs_path_pcnbuild(global_pu, pcn, pn_dir);
+       if (r) return(EINVAL);
+  }
+
+  r = global_pu->pu_ops.puffs_node_rmdir(global_pu, pn_dir, pn, pcn);
+
+  global_pu->pu_pathfree(global_pu, &pcn->pcn_po_full);
+
+  if (r) return(EINVAL);
+
+  return(OK);
+}
+
+
+/*===========================================================================*
+ *                             unlink_file                                  *
+ *===========================================================================*/
+PRIVATE int unlink_file(dirp, pn, pcn)
+struct puffs_node *dirp;       /* parent directory of file */
+struct puffs_node *pn;         /* pnode of file, may be NULL too. */
+struct puffs_cn *pcn;          /* Name, creads of file */
+{
+/* Unlink 'file_name'; pn must be the pnode of 'file_name' */
+  int  r;
+
+  assert(pn != NULL);
+
+  if (global_pu->pu_ops.puffs_node_remove == NULL)
+       return(EINVAL);
+
+  if (S_ISDIR(pn->pn_va.va_mode))
+       return(EINVAL);
+
+  if (buildpath) {
+       r = puffs_path_pcnbuild(global_pu, pcn, dirp);
+       if (r)
+               return(EINVAL);
+  }
+
+  r = global_pu->pu_ops.puffs_node_remove(global_pu, dirp, pn, pcn);
+
+  global_pu->pu_pathfree(global_pu, &pcn->pcn_po_full);
+
+  if (r) return(EINVAL);
+
+  return(OK);
+}
diff --git a/lib/libpuffs/misc.c b/lib/libpuffs/misc.c
new file mode 100644 (file)
index 0000000..d969438
--- /dev/null
@@ -0,0 +1,53 @@
+/* Created (MFS based):
+ *   June 2011 (Evgeniy Ivanov)
+ */
+
+#include "fs.h"
+#include <assert.h>
+#include <minix/vfsif.h>
+
+#include "puffs.h"
+#include "puffs_priv.h"
+
+/*===========================================================================*
+ *                             fs_sync                                      *
+ *===========================================================================*/
+PUBLIC int fs_sync()
+{
+/* Perform the sync() system call.  Flush all the tables.
+ * The order in which the various tables are flushed is critical.
+ */
+  int r;
+  PUFFS_MAKECRED(pcr, &global_kcred);
+
+  if (is_readonly_fs)
+       return(OK); /* nothing to sync */
+
+  r = global_pu->pu_ops.puffs_fs_sync(global_pu, MNT_WAIT, pcr);
+  if (r) {
+       lpuffs_debug("Warning: sync failed!\n");
+  }
+
+  return(OK);          /* sync() can't fail */
+}
+
+
+/*===========================================================================*
+ *                             fs_flush                                     *
+ *===========================================================================*/
+PUBLIC int fs_flush()
+{
+/* Flush the blocks of a device from the cache after writing any dirty blocks
+ * to disk.
+ */
+#if 0
+  dev_t dev = (dev_t) fs_m_in.REQ_DEV;
+
+  if(dev == fs_dev) return(EBUSY);
+
+  flushall(dev);
+  invalidate(dev);
+#endif
+
+  return(OK);
+}
diff --git a/lib/libpuffs/mntopts.h b/lib/libpuffs/mntopts.h
new file mode 100644 (file)
index 0000000..1db0d2b
--- /dev/null
@@ -0,0 +1,13 @@
+#ifndef _MNTOPTS_H_
+#define _MNTOPTS_H_
+
+struct mntopt {
+        const char *m_option;   /* option name */
+        int m_inverse;          /* if a negative option, eg "dev" */
+        int m_flag;             /* bit to set, eg. MNT_RDONLY */
+        int m_altloc;           /* 1 => set bit in altflags */
+};
+
+
+
+#endif
diff --git a/lib/libpuffs/mount.c b/lib/libpuffs/mount.c
new file mode 100644 (file)
index 0000000..eef46a4
--- /dev/null
@@ -0,0 +1,141 @@
+/* Created (MFS based):
+ *   June 2011 (Evgeniy Ivanov)
+ */
+
+#include "fs.h"
+#include <fcntl.h>
+#include <string.h>
+#include <minix/com.h>
+#include <sys/stat.h>
+#include <minix/ds.h>
+#include <minix/vfsif.h>
+
+#include "puffs_priv.h"
+
+/*===========================================================================*
+ *                             fs_readsuper                                 *
+ *===========================================================================*/
+PUBLIC int fs_readsuper()
+{
+  int r = OK;
+  cp_grant_id_t label_gid;
+  size_t label_len;
+  endpoint_t driver_e;
+  struct vattr *root_va;
+
+  fs_dev    = fs_m_in.REQ_DEV;
+  label_gid = fs_m_in.REQ_GRANT;
+  label_len = fs_m_in.REQ_PATH_LEN;
+  is_readonly_fs  = (fs_m_in.REQ_FLAGS & REQ_RDONLY) ? 1 : 0;
+  is_root_fs    = (fs_m_in.REQ_FLAGS & REQ_ISROOT) ? 1 : 0;
+
+  if (label_len > sizeof(fs_dev_label))
+       return(EINVAL);
+
+  r = sys_safecopyfrom(fs_m_in.m_source, label_gid, 0,
+                      (vir_bytes)fs_dev_label, label_len, D);
+  if (r != OK) {
+       lpuffs_debug("%s:%d fs_readsuper: safecopyfrom failed: %d\n",
+              __FILE__, __LINE__, r);
+       return(EINVAL);
+  }
+
+  fs_m_out.RES_DEV = NO_DEV;
+
+  if (strlen(fs_dev_label)) {
+       /* Map the driver endpoint for this major */
+       r= ds_retrieve_label_endpt(fs_dev_label, &driver_e);
+       if (r != OK)
+       {
+               lpuffs_debug("fs_readsuper: ds_retrieve_label_endpt failed for '%s': %d\n",
+                       fs_dev_label, r);
+               return EINVAL;
+       }
+
+       driver_endpoints[(fs_dev >> MAJOR) & BYTE].driver_e =  driver_e;
+
+       /* Open the device the file system lives on. */
+       if (dev_open(driver_e, fs_dev, driver_e,
+                    is_readonly_fs ? R_BIT : (R_BIT|W_BIT)) != OK) {
+               return(EINVAL);
+       }
+        fs_m_out.RES_DEV = fs_dev;
+  }
+
+  /* Open root pnode */
+  global_pu->pu_pn_root->pn_count = 1;
+
+  /* Root pnode properties */
+  root_va = &global_pu->pu_pn_root->pn_va;
+  fs_m_out.RES_INODE_NR = root_va->va_fileid;
+  fs_m_out.RES_MODE = root_va->va_mode;
+  fs_m_out.RES_FILE_SIZE_LO = root_va->va_size;
+  fs_m_out.RES_UID = root_va->va_uid;
+  fs_m_out.RES_GID = root_va->va_gid;
+
+  return(r);
+}
+
+
+/*===========================================================================*
+ *                             fs_mountpoint                                *
+ *===========================================================================*/
+PUBLIC int fs_mountpoint()
+{
+/* This function looks up the mount point, it checks the condition whether
+ * the partition can be mounted on the pnode or not.
+ */
+  int r = OK;
+  struct puffs_node *pn;
+  mode_t bits;
+
+  /*
+   * XXX: we assume that lookup was done first, so pnode can be found with
+   * puffs_pn_nodewalk.
+   */
+  if ((pn = puffs_pn_nodewalk(global_pu, 0, &fs_m_in.REQ_INODE_NR)) == NULL)
+       return(EINVAL);
+
+
+  if (pn->pn_mountpoint) r = EBUSY;
+
+  /* It may not be special. */
+  bits = pn->pn_va.va_mode & I_TYPE;
+  if(bits == I_BLOCK_SPECIAL || bits == I_CHAR_SPECIAL) r = ENOTDIR;
+
+  if (r == OK)
+       pn->pn_mountpoint = TRUE;
+
+  return(r);
+}
+
+
+/*===========================================================================*
+ *                             fs_unmount                                   *
+ *===========================================================================*/
+PUBLIC int fs_unmount()
+{
+  int error;
+
+  /* XXX there is no information about flags, 0 should be safe enough */
+  error = global_pu->pu_ops.puffs_fs_unmount(global_pu, 0);
+  if (error) {
+        /* XXX we can't return any error to VFS */
+       lpuffs_debug("user handler failed to unmount filesystem!\
+               Force unmount!\n");
+  } 
+  
+  fs_sync();
+
+  if (strlen(fs_dev_label)) {
+       /* Close the device the file system lives on. */
+       dev_close(driver_endpoints[(fs_dev >> MAJOR) & BYTE].driver_e, fs_dev);
+  }
+
+  /* Finish off the unmount. */
+  PU_SETSTATE(global_pu, PUFFS_STATE_UNMOUNTED);
+  unmountdone = TRUE;
+  global_pu->pu_pn_root->pn_count--;
+
+  return(OK);
+}
diff --git a/lib/libpuffs/null.c b/lib/libpuffs/null.c
new file mode 100644 (file)
index 0000000..d1aad76
--- /dev/null
@@ -0,0 +1,589 @@
+/*     $NetBSD: null.c,v 1.25 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 <sys/cdefs.h>
+#if !defined(lint)
+__RCSID("$NetBSD: null.c,v 1.25 2008/08/12 19:44:39 pooka Exp $");
+#endif /* !lint */
+
+/*
+ * A "nullfs" using puffs, i.e. maps one location in the hierarchy
+ * to another using standard system calls.
+ */
+
+#include <sys/types.h>
+#include <sys/time.h>
+
+#include <assert.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <utime.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 utimbuf tbuf;
+
+       /* XXX: -1 == PUFFS_VNOVAL, but shouldn't trust that */
+       if (va->va_uid != (unsigned)-1 || va->va_gid != (unsigned)-1)
+                /* FIXME: lchown */
+               if (chown(path, va->va_uid, va->va_gid) == -1)
+                       return errno;
+
+       if (va->va_mode != (unsigned)PUFFS_VNOVAL)
+                /* FIXME: lchmod */
+               if (chmod(path, va->va_mode) == -1)
+                       return errno;
+
+       /* sloppy */
+       if (va->va_atime.tv_sec != (unsigned)PUFFS_VNOVAL
+           || va->va_mtime.tv_sec != (unsigned)PUFFS_VNOVAL) {
+               /* FIXME: nsec too */
+               tbuf.actime = va->va_atime.tv_sec;
+               tbuf.modtime = va->va_mtime.tv_sec;
+
+                /* FIXME: lutimes */
+               if (utime(path, &tbuf) == -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, 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;
+}
+
+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);
+       puffs_newinfo_setrdev(pni, pn_res->pn_va.va_rdev);
+
+       return 0;
+}
+
+/*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);
+       if (mknod(PCNPATH(pcn), mode, va->va_rdev) == -1)
+               return errno;
+
+       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,
+       const struct puffs_cred *pcred, int how,
+       off_t offlo, off_t offhi)
+{
+/* FIXME: implement me. */
+#if 0
+       struct puffs_node *pn = opc;
+       int fd, rv;
+       int fflags;
+
+       rv = 0;
+       fd = writeableopen(PNPATH(pn));
+       if (fd == -1)
+               return errno;
+
+       if (how & PUFFS_FSYNC_DATAONLY)
+               fflags = FDATASYNC;
+       else
+               fflags = FFILESYNC;
+       if (how & PUFFS_FSYNC_CACHE)
+               fflags |= FDISKSYNC;
+
+       if (fsync_range(fd, fflags, offlo, offhi - offlo) == -1)
+               rv = errno;
+
+       close(fd);
+
+       return rv;
+#endif
+       return 0;
+}
+
+/*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(PCNPATH(pcn)) == -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)
+{
+       struct puffs_node *pn_targ = targ;
+
+       if (rename(PCNPATH(pcn_src), PCNPATH(pcn_targ)) == -1)
+               return errno;
+
+       if (pn_targ)
+               puffs_pn_remove(pn_targ);
+
+       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)
+{
+       /* TODO: use original code since we have libc from NetBSD */
+       struct puffs_node *pn = opc;
+       struct dirent *entry;
+       DIR *dp;
+       off_t i;
+       int rv;
+
+       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--) {
+               entry = readdir(dp);
+               if (!entry) {
+                       *eofflag = 1;
+                       goto out;
+               }
+       }
+
+       for (;;) {
+                /* FIXME: DIRENT_SIZE macro? For now do calculations here */
+               int namelen;
+               char* cp;
+               int dirent_size;
+
+                entry = readdir(dp);
+
+               if (!entry) {
+                       *eofflag = 1;
+                       goto out;
+               }
+                       
+               cp = memchr(entry->d_name, '\0', NAME_MAX);
+               if (cp == NULL)
+                       namelen = NAME_MAX;
+               else
+                       namelen = cp - (entry->d_name);
+               dirent_size = _DIRENT_RECLEN(entry, namelen);
+
+               if (dirent_size > *reslen)
+                       goto out;
+
+               *de = *entry;
+               strncpy(de->d_name, entry->d_name, namelen);
+               *reslen -= dirent_size;
+
+               de = _DIRENT_NEXT(de);
+               (*off)++;
+       }
+
+ out:
+       closedir(dp);
+       return 0;
+}
+
+/*ARGSUSED*/
+int
+puffs_null_node_read(struct puffs_usermount *pu, puffs_cookie_t opc,
+       uint8_t *buf, off_t offset, size_t *buflen,
+       const struct puffs_cred *pcred, int ioflag)
+{
+       struct puffs_node *pn = opc;
+       ssize_t n;
+       off_t off;
+       int fd, rv;
+
+       rv = 0;
+       fd = open(PNPATH(pn), O_RDONLY);
+       if (fd == -1)
+               return errno;
+       off = lseek(fd, offset, SEEK_SET);
+       if (off == -1) {
+               rv = errno;
+               goto out;
+       }
+
+       n = read(fd, buf, *buflen);
+       if (n == -1)
+               rv = errno;
+       else
+               *buflen -= n;
+
+ out:
+       close(fd);
+       return rv;
+}
+
+/*ARGSUSED*/
+int
+puffs_null_node_write(struct puffs_usermount *pu, puffs_cookie_t opc,
+       uint8_t *buf, off_t offset, size_t *buflen,
+       const struct puffs_cred *pcred, int ioflag)
+{
+       struct puffs_node *pn = opc;
+       ssize_t n;
+       off_t off;
+       int fd, rv;
+
+       rv = 0;
+       fd = writeableopen(PNPATH(pn));
+       if (fd == -1)
+               return errno;
+
+       off = lseek(fd, offset, SEEK_SET);
+       if (off == -1) {
+               rv = errno;
+               goto out;
+       }
+
+       n = write(fd, buf, *buflen);
+       if (n == -1)
+               rv = errno;
+       else
+               *buflen -= n;
+
+ out:
+       close(fd);
+       return rv;
+}
diff --git a/lib/libpuffs/open.c b/lib/libpuffs/open.c
new file mode 100644 (file)
index 0000000..da3e1f0
--- /dev/null
@@ -0,0 +1,378 @@
+/* Created (MFS based):
+ *   June 2011 (Evgeniy Ivanov)
+ */
+
+#include "fs.h"
+#include <sys/stat.h>
+#include <string.h>
+#include <assert.h>
+#include <minix/com.h>
+#include <minix/vfsif.h>
+
+#include "puffs.h"
+#include "puffs_priv.h"
+
+
+/*===========================================================================*
+ *                             fs_create                                    *
+ *===========================================================================*/
+PUBLIC int fs_create()
+{
+  int r;
+  struct puffs_node *pn_dir;
+  struct puffs_node *pn;
+  mode_t omode;
+  struct puffs_newinfo pni;
+  struct puffs_kcn pkcnp;
+  PUFFS_MAKECRED(pcr, &global_kcred);
+  struct puffs_cn pcn = {&pkcnp, (struct puffs_cred *) pcr, {0}};
+  struct vattr va;
+  time_t cur_time;
+  int len;
+
+  if (global_pu->pu_ops.puffs_node_create == NULL) {
+       lpuffs_debug("No puffs_node_create");
+       return(ENFILE);
+  }
+
+  /* Read request message */
+  omode = (mode_t) fs_m_in.REQ_MODE;
+  caller_uid = (uid_t) fs_m_in.REQ_UID;
+  caller_gid = (gid_t) fs_m_in.REQ_GID;
+
+  /* Copy the last component (i.e., file name) */
+  len = fs_m_in.REQ_PATH_LEN;
+  pcn.pcn_namelen = len - 1;
+  if (pcn.pcn_namelen > NAME_MAX)
+       return(ENAMETOOLONG);
+
+  err_code = sys_safecopyfrom(VFS_PROC_NR, (cp_grant_id_t) fs_m_in.REQ_GRANT,
+                             (vir_bytes) 0, (vir_bytes) pcn.pcn_name,
+                             (size_t) len, D);
+  if (err_code != OK) return(err_code);
+  NUL(pcn.pcn_name, len, sizeof(pcn.pcn_name));
+
+  /* Get last directory pnode (i.e., directory that will hold the new pnode) */
+  if ((pn_dir = puffs_pn_nodewalk(global_pu, 0, &fs_m_in.REQ_INODE_NR)) == NULL)
+       return(ENOENT);
+
+  memset(&pni, 0, sizeof(pni));
+  pni.pni_cookie = (void** )&pn;
+
+  cur_time = clock_time();
+  
+  memset(&va, 0, sizeof(va));
+  va.va_type = VREG;
+  va.va_mode = omode;
+  va.va_uid = caller_uid;
+  va.va_gid = caller_gid;
+  va.va_atime.tv_sec = va.va_mtime.tv_sec = va.va_ctime.tv_sec = cur_time;
+
+  if (buildpath) {
+       r = puffs_path_pcnbuild(global_pu, &pcn, pn_dir);
+       if (r) {
+               lpuffs_debug("pathbuild error\n");
+               return(ENOENT);
+       }
+  }
+
+  r = global_pu->pu_ops.puffs_node_create(global_pu, pn_dir, &pni, &pcn, &va);
+  if (buildpath) {
+       if (r) {
+               global_pu->pu_pathfree(global_pu, &pcn.pcn_po_full);
+       } else {
+               struct puffs_node *_pn;
+
+               _pn = PU_CMAP(global_pu, pn);
+               _pn->pn_po = pcn.pcn_po_full;
+       }
+  }
+
+  if (r != OK) {
+       if (r > 0) r = -r;
+       return(r);
+  }
+
+  /* Open pnode */
+  pn->pn_count++;
+
+  update_times(pn_dir, MTIME | CTIME, cur_time);
+
+  /* Reply message */
+  fs_m_out.RES_INODE_NR = pn->pn_va.va_fileid;
+  fs_m_out.RES_MODE = pn->pn_va.va_mode;
+  fs_m_out.RES_FILE_SIZE_LO = pn->pn_va.va_size;
+
+  /* This values are needed for the execution */
+  fs_m_out.RES_UID = pn->pn_va.va_uid;
+  fs_m_out.RES_GID = pn->pn_va.va_gid;
+
+  return(OK);
+}
+
+
+/*===========================================================================*
+ *                             fs_mknod                                     *
+ *===========================================================================*/
+PUBLIC int fs_mknod()
+{
+  int r;
+  struct puffs_node *pn_dir;
+  struct puffs_node *pn;
+  struct puffs_newinfo pni;
+  struct puffs_kcn pkcnp;
+  PUFFS_MAKECRED(pcr, &global_kcred);
+  struct puffs_cn pcn = {&pkcnp, (struct puffs_cred *) pcr, {0}};
+  struct vattr va;
+  time_t cur_time;
+  int len;
+
+  if (global_pu->pu_ops.puffs_node_mknod == NULL) {
+       lpuffs_debug("No puffs_node_mknod");
+       return(ENFILE);
+  }
+
+  /* Copy the last component and set up caller's user and group id */
+  len = fs_m_in.REQ_PATH_LEN;
+  pcn.pcn_namelen = len - 1;
+  if (pcn.pcn_namelen > NAME_MAX)
+       return(ENAMETOOLONG);
+
+  err_code = sys_safecopyfrom(VFS_PROC_NR, (cp_grant_id_t) fs_m_in.REQ_GRANT,
+                             (vir_bytes) 0, (vir_bytes) pcn.pcn_name,
+                            (size_t) len, D);
+  if (err_code != OK) return(err_code);
+  NUL(pcn.pcn_name, len, sizeof(pcn.pcn_name));
+
+  caller_uid = (uid_t) fs_m_in.REQ_UID;
+  caller_gid = (gid_t) fs_m_in.REQ_GID;
+
+  /* Get last directory pnode */
+  if ((pn_dir = puffs_pn_nodewalk(global_pu, 0, &fs_m_in.REQ_INODE_NR)) == NULL)
+       return(ENOENT);
+
+  memset(&pni, 0, sizeof(pni));
+  pni.pni_cookie = (void** )&pn;
+
+  cur_time = clock_time();
+
+  memset(&va, 0, sizeof(va));
+  va.va_type = VDIR;
+  va.va_mode = (mode_t) fs_m_in.REQ_MODE;
+  va.va_uid = caller_uid;
+  va.va_gid = caller_gid;
+  va.va_rdev = (dev_t) fs_m_in.REQ_DEV;
+  va.va_atime.tv_sec = va.va_mtime.tv_sec = va.va_ctime.tv_sec = cur_time;
+
+  if (buildpath) {
+       if (puffs_path_pcnbuild(global_pu, &pcn, pn_dir) != 0) {
+               lpuffs_debug("pathbuild error\n");
+               return(ENOENT);
+       }
+  }
+
+  r = global_pu->pu_ops.puffs_node_mknod(global_pu, pn_dir, &pni, &pcn, &va);
+  if (buildpath) {
+       if (r) {
+               global_pu->pu_pathfree(global_pu, &pcn.pcn_po_full);
+       } else {
+               struct puffs_node *_pn;
+
+               _pn = PU_CMAP(global_pu, pn);
+               _pn->pn_po = pcn.pcn_po_full;
+         }
+  }
+
+  if (r != OK) {
+       if (r > 0) r = -r;
+       return(r);
+  }
+
+  update_times(pn_dir, MTIME | CTIME, cur_time);
+
+  return(OK);
+}
+
+
+/*===========================================================================*
+ *                             fs_mkdir                                     *
+ *===========================================================================*/
+PUBLIC int fs_mkdir()
+{
+  int r;
+  struct puffs_node *pn_dir;
+  struct puffs_node *pn;
+  struct puffs_newinfo pni;
+  struct puffs_kcn pkcnp;
+  PUFFS_MAKECRED(pcr, &global_kcred);
+  struct puffs_cn pcn = {&pkcnp, (struct puffs_cred *) pcr, {0}};
+  struct vattr va;
+  time_t cur_time;
+  int len;
+
+  if (global_pu->pu_ops.puffs_node_mkdir == NULL) {
+       lpuffs_debug("No puffs_node_mkdir");
+       return(ENFILE);
+  }
+
+  /* Copy the last component and set up caller's user and group id */
+  len = fs_m_in.REQ_PATH_LEN;
+  pcn.pcn_namelen = len - 1;
+  if (pcn.pcn_namelen > NAME_MAX)
+       return(ENAMETOOLONG);
+
+  err_code = sys_safecopyfrom(VFS_PROC_NR, (cp_grant_id_t) fs_m_in.REQ_GRANT,
+                             (vir_bytes) 0, (vir_bytes) pcn.pcn_name,
+                             (phys_bytes) len, D);
+  if (err_code != OK) return(err_code);
+  NUL(pcn.pcn_name, len, sizeof(pcn.pcn_name));
+
+  caller_uid = (uid_t) fs_m_in.REQ_UID;
+  caller_gid = (gid_t) fs_m_in.REQ_GID;
+
+  /* Get last directory pnode */
+  if ((pn_dir = puffs_pn_nodewalk(global_pu, 0, &fs_m_in.REQ_INODE_NR)) == NULL)
+       return(ENOENT);
+  
+  cur_time = clock_time();
+
+  memset(&pni, 0, sizeof(pni));
+  pni.pni_cookie = (void** )&pn;
+
+  memset(&va, 0, sizeof(va));
+  va.va_type = VDIR;
+  va.va_mode = (mode_t) fs_m_in.REQ_MODE;
+  va.va_uid = caller_uid;
+  va.va_gid = caller_gid;
+  va.va_atime.tv_sec = va.va_mtime.tv_sec = va.va_ctime.tv_sec = cur_time;
+
+  if (buildpath) {
+       r = puffs_path_pcnbuild(global_pu, &pcn, pn_dir);
+       if (r) {
+               lpuffs_debug("pathbuild error\n");
+               return(ENOENT);
+       }
+  }
+
+  r = global_pu->pu_ops.puffs_node_mkdir(global_pu, pn_dir, &pni, &pcn, &va);
+  if (buildpath) {
+       if (r) {
+               global_pu->pu_pathfree(global_pu, &pcn.pcn_po_full);
+       } else {
+               struct puffs_node *_pn;
+
+               _pn = PU_CMAP(global_pu, pn);
+               _pn->pn_po = pcn.pcn_po_full;
+       }
+  }
+
+  if (r != OK) {
+       if (r > 0) r = -r;
+       return(r);
+  }
+
+  update_times(pn_dir, MTIME | CTIME, cur_time);
+
+  return(OK);
+}
+
+
+/*===========================================================================*
+ *                             fs_slink                                     *
+ *===========================================================================*/
+PUBLIC int fs_slink()
+{
+  int r;
+  struct pnode *pn;            /* pnode containing symbolic link */
+  struct pnode *pn_dir;                /* directory containing link */
+  char target[PATH_MAX + 1];   /* target path */
+  struct puffs_newinfo pni;
+  struct puffs_kcn pkcnp;
+  PUFFS_MAKECRED(pcr, &global_kcred);
+  struct puffs_cn pcn = {&pkcnp, (struct puffs_cred *) pcr, {0}};
+  struct vattr va;
+  int len;
+
+  caller_uid = (uid_t) fs_m_in.REQ_UID;
+  caller_gid = (gid_t) fs_m_in.REQ_GID;
+
+  /* Copy the link name's last component */
+  len = fs_m_in.REQ_PATH_LEN;
+  pcn.pcn_namelen = len - 1;
+  if (pcn.pcn_namelen > NAME_MAX)
+       return(ENAMETOOLONG);
+
+  if (fs_m_in.REQ_MEM_SIZE >= PATH_MAX)
+       return(ENAMETOOLONG);
+
+  r = sys_safecopyfrom(VFS_PROC_NR, (cp_grant_id_t) fs_m_in.REQ_GRANT,
+                      (vir_bytes) 0, (vir_bytes) pcn.pcn_name,
+                      (size_t) len, D);
+  if (r != OK) return(r);
+  NUL(pcn.pcn_name, len, sizeof(pcn.pcn_name));
+
+  /* Copy the target path (note that it's not null terminated) */
+  r = sys_safecopyfrom(VFS_PROC_NR, (cp_grant_id_t) fs_m_in.REQ_GRANT3,
+                      (vir_bytes) 0, (vir_bytes) target, 
+                      (size_t) fs_m_in.REQ_MEM_SIZE, D);
+  if (r != OK) return(r);
+  target[fs_m_in.REQ_MEM_SIZE] = '\0';
+
+  if (strlen(target) != fs_m_in.REQ_MEM_SIZE) {
+       /* This can happen if the user provides a buffer
+        * with a \0 in it. This can cause a lot of trouble
+        * when the symlink is used later. We could just use
+        * the strlen() value, but we want to let the user
+        * know he did something wrong. ENAMETOOLONG doesn't
+        * exactly describe the error, but there is no
+        * ENAMETOOWRONG.
+        */
+       return(ENAMETOOLONG);
+  }
+
+  if ((pn_dir = puffs_pn_nodewalk(global_pu, 0, &fs_m_in.REQ_INODE_NR)) == NULL)
+       return(EINVAL);
+
+  memset(&pni, 0, sizeof(pni));
+  pni.pni_cookie = (void** )&pn;
+
+  memset(&va, 0, sizeof(va));
+  va.va_type = VLNK;
+  va.va_mode = (mode_t) (I_SYMBOLIC_LINK | RWX_MODES);
+  va.va_uid = caller_uid;
+  va.va_gid = caller_gid;
+  va.va_atime.tv_sec = va.va_mtime.tv_sec = va.va_ctime.tv_sec = clock_time();
+
+  if (buildpath) {
+       r = puffs_path_pcnbuild(global_pu, &pcn, pn_dir);
+       if (r) {
+               lpuffs_debug("pathbuild error\n");
+               return(ENOENT);
+       }
+  }
+
+  r = global_pu->pu_ops.puffs_node_symlink(global_pu, pn_dir, &pni, &pcn, &va, target);
+  if (buildpath) {
+       if (r) {
+               global_pu->pu_pathfree(global_pu, &pcn.pcn_po_full);
+       } else {
+               struct puffs_node *_pn;
+
+               _pn = PU_CMAP(global_pu, pn);
+               _pn->pn_po = pcn.pcn_po_full;
+       }
+  }
+
+  if (r > 0) r = -r;
+
+  return(r);
+}
+
+
+/*===========================================================================*
+ *                             fs_inhibread                                 *
+ *===========================================================================*/
+PUBLIC int fs_inhibread()
+{
+  return(OK);
+}
diff --git a/lib/libpuffs/path.c b/lib/libpuffs/path.c
new file mode 100644 (file)
index 0000000..211afbf
--- /dev/null
@@ -0,0 +1,550 @@
+/*
+ * This file contains the procedures that look up path names in the directory
+ * system and determine the pnode number that goes with a given path name.
+ *
+ * Created (based on MFS):
+ *   June 2011 (Evgeniy Ivanov)
+ */
+
+#include "fs.h"
+
+#include <sys/cdefs.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <puffs.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <minix/endpoint.h>
+#include <minix/vfsif.h>
+#include <minix/libminixfs.h>
+
+#include "puffs.h"
+#include "puffs_priv.h"
+
+PUBLIC char dot2[3] = "..";    /* permissions for . and ..                 */
+
+FORWARD _PROTOTYPE( char *get_name, (char *name, char string[NAME_MAX+1]) );
+FORWARD _PROTOTYPE( int ltraverse, (struct puffs_node *pn, char *suffix)  );
+FORWARD _PROTOTYPE( int parse_path, (ino_t dir_ino, ino_t root_ino,
+                                       int flags, struct puffs_node **res_inop,
+                                       size_t *offsetp, int *symlinkp)   );
+
+/*===========================================================================*
+ *                             fs_lookup                                    *
+ *===========================================================================*/
+PUBLIC int fs_lookup()
+{
+  cp_grant_id_t grant;
+  int r, r1, flags, symlinks;
+  unsigned int len;
+  size_t offset = 0, path_size;
+  ino_t dir_ino, root_ino;
+  struct puffs_node *pn;
+
+  grant                = (cp_grant_id_t) fs_m_in.REQ_GRANT;
+  path_size    = (size_t) fs_m_in.REQ_PATH_SIZE;       /* Size of the buffer */
+  len          = (int) fs_m_in.REQ_PATH_LEN;   /* including terminating nul */
+  dir_ino      = (ino_t) fs_m_in.REQ_DIR_INO;
+  root_ino     = (ino_t) fs_m_in.REQ_ROOT_INO;
+  flags                = (int) fs_m_in.REQ_FLAGS;
+
+  /* Check length. */
+  if (len > sizeof(user_path)) return(E2BIG);  /* too big for buffer */
+  if (len == 0) return(EINVAL);                        /* too small */
+
+  /* Copy the pathname and set up caller's user and group id */
+  r = sys_safecopyfrom(VFS_PROC_NR, grant, /*offset*/ 0,
+            (vir_bytes) user_path, (size_t) len, D);
+  if (r != OK) return(r);
+
+  /* Verify this is a null-terminated path. */
+  if (user_path[len - 1] != '\0') return(EINVAL);
+
+  memset(&credentials, 0, sizeof(credentials));
+  if(!(flags & PATH_GET_UCRED)) { /* Do we have to copy uid/gid credentials? */
+        caller_uid      = (uid_t) fs_m_in.REQ_UID;
+        caller_gid      = (gid_t) fs_m_in.REQ_GID;
+  } else {
+       if((r=fs_lookup_credentials(&credentials,
+               &caller_uid, &caller_gid,
+               (cp_grant_id_t) fs_m_in.REQ_GRANT2,
+               (size_t) fs_m_in.REQ_UCRED_SIZE)) != OK)
+               return r;
+  }
+
+
+  /* Lookup pnode */
+  pn = NULL;
+  r = parse_path(dir_ino, root_ino, flags, &pn, &offset, &symlinks);
+
+  if (symlinks != 0 && (r == ELEAVEMOUNT || r == EENTERMOUNT || r == ESYMLINK)){
+       len = strlen(user_path)+1;
+       if (len > path_size) return(ENAMETOOLONG);
+
+       r1 = sys_safecopyto(VFS_PROC_NR, grant, (vir_bytes) 0,
+                           (vir_bytes) user_path, (size_t) len, D);
+       if (r1 != OK) return(r1);
+  }
+
+  if (r == ELEAVEMOUNT || r == ESYMLINK) {
+       /* Report offset and the error */
+       fs_m_out.RES_OFFSET = offset;
+       fs_m_out.RES_SYMLOOP = symlinks;
+       
+       return(r);
+  }
+
+  if (r != OK && r != EENTERMOUNT) {
+       return(r);
+  }
+
+  if (r == OK) {
+       /* Open pnode */
+       pn->pn_count++;
+  }
+
+  fs_m_out.RES_INODE_NR                = pn->pn_va.va_fileid;
+  fs_m_out.RES_MODE            = pn->pn_va.va_mode;
+  fs_m_out.RES_FILE_SIZE_LO    = pn->pn_va.va_size;
+  fs_m_out.RES_SYMLOOP         = symlinks;
+  fs_m_out.RES_UID             = pn->pn_va.va_uid;
+  fs_m_out.RES_GID             = pn->pn_va.va_gid;
+
+  /* This is only valid for block and character specials. But it doesn't
+   * cause any harm to set RES_DEV always. */
+  fs_m_out.RES_DEV             = pn->pn_va.va_rdev;
+
+  if (r == EENTERMOUNT) {
+       fs_m_out.RES_OFFSET     = offset;
+  }
+  
+  return(r);
+}
+
+
+/*===========================================================================*
+ *                             parse_path                                   *
+ *===========================================================================*/
+PRIVATE int parse_path(dir_ino, root_ino, flags, res_inop, offsetp, symlinkp)
+ino_t dir_ino;
+ino_t root_ino;
+int flags;
+struct puffs_node **res_inop;
+size_t *offsetp;
+int *symlinkp;
+{
+  /* Parse the path in user_path, starting at dir_ino. If the path is the empty
+   * string, just return dir_ino. It is upto the caller to treat an empty
+   * path in a special way. Otherwise, if the path consists of just one or
+   * more slash ('/') characters, the path is replaced with ".". Otherwise,
+   * just look up the first (or only) component in path after skipping any
+   * leading slashes.
+   */
+  int r, leaving_mount;
+  struct puffs_node *pn, *pn_dir;
+  char *cp, *next_cp; /* component and next component */
+  char component[NAME_MAX+1];
+
+  /* Start parsing path at the first component in user_path */
+  cp = user_path;
+
+  /* No symlinks encountered yet */
+  *symlinkp = 0;
+
+  /* Find starting pnode according to the request message */
+  /* XXX it's deffinitely OK to use nodewalk here */
+  if ((pn = puffs_pn_nodewalk(global_pu, 0, &dir_ino)) == NULL) {
+       lpuffs_debug("nodewalk failed\n");
+       return(ENOENT);
+  }
+
+  /* If dir has been removed return ENOENT. */
+  if (pn->pn_va.va_nlink == NO_LINK) return(ENOENT);
+
+  /* If the given start pnode is a mountpoint, we must be here because the file
+   * system mounted on top returned an ELEAVEMOUNT error. In this case, we must
+   * only accept ".." as the first path component.
+   */
+  leaving_mount = pn->pn_mountpoint; /* True iff pn is a mountpoint */
+
+  /* Scan the path component by component. */
+  while (TRUE) {
+       if (cp[0] == '\0') {
+               /* We're done; either the path was empty or we've parsed all
+                  components of the path */
+
+               *res_inop = pn;
+               *offsetp += cp - user_path;
+
+               /* Return EENTERMOUNT if we are at a mount point */
+               if (pn->pn_mountpoint) return(EENTERMOUNT);
+
+               return(OK);
+       }
+
+       while(cp[0] == '/') cp++;
+       next_cp = get_name(cp, component);
+       if (next_cp == NULL) {
+               return(err_code);
+       }
+
+       /* Special code for '..'. A process is not allowed to leave a chrooted
+        * environment. A lookup of '..' at the root of a mounted filesystem
+        * has to return ELEAVEMOUNT. In both cases, the caller needs search
+        * permission for the current pnode, as it is used as directory.
+        */
+       if (strcmp(component, "..") == 0) {
+               /* 'pn' is now accessed as directory */
+               if ((r = forbidden(pn, X_BIT)) != OK) {
+                       return(r);
+               }
+
+               if (pn->pn_va.va_fileid == root_ino) {
+                       cp = next_cp;
+                       continue;       /* Ignore the '..' at a process' root
+                                          and move on to the next component */
+               }
+
+               if (pn->pn_va.va_fileid == global_pu->pu_pn_root->pn_va.va_fileid
+                       && !is_root_fs) {
+                       /* Climbing up to parent FS */
+                       *offsetp += cp - user_path;
+                       return(ELEAVEMOUNT);
+               }
+       }
+
+       /* Only check for a mount point if we are not coming from one. */
+       if (!leaving_mount && pn->pn_mountpoint) {
+               /* Going to enter a child FS */
+
+               *res_inop = pn;
+               *offsetp += cp - user_path;
+               return(EENTERMOUNT);
+       }
+
+       /* There is more path.  Keep parsing.
+        * If we're leaving a mountpoint, skip directory permission checks.
+        */
+       pn_dir = pn;
+       if ((pn_dir->pn_va.va_mode & I_TYPE) != I_DIRECTORY)  {
+               return(ENOTDIR);
+       }
+       pn = advance(pn_dir, leaving_mount ? dot2 : component, CHK_PERM);
+       if (err_code == ELEAVEMOUNT || err_code == EENTERMOUNT)
+               err_code = OK;
+
+       if (err_code != OK) {
+               return(err_code);
+       }
+       
+       assert(pn != NULL);
+
+       leaving_mount = 0;
+
+       /* The call to advance() succeeded.  Fetch next component. */
+       if (S_ISLNK(pn->pn_va.va_mode)) {
+               if (next_cp[0] == '\0' && (flags & PATH_RET_SYMLINK)) {
+                       *res_inop = pn;
+                       *offsetp += next_cp - user_path;
+                       return(OK);
+               }
+
+               /* Extract path name from the symlink file */
+               r = ltraverse(pn, next_cp);
+               next_cp = user_path;
+               *offsetp = 0;
+
+               /* Symloop limit reached? */
+               if (++(*symlinkp) > SYMLOOP_MAX)
+                       r = ELOOP;
+
+               if (r != OK)
+                       return(r);
+
+               if (next_cp[0] == '/')
+                        return(ESYMLINK);
+
+               pn = pn_dir;
+       }
+
+       cp = next_cp; /* Process subsequent component in next round */
+  }
+
+}
+
+
+/*===========================================================================*
+ *                             ltraverse                                    *
+ *===========================================================================*/
+PRIVATE int ltraverse(pn, suffix)
+register struct puffs_node *pn;/* symbolic link */
+char *suffix;                  /* current remaining path. Has to point in the
+                                * user_path buffer
+                                */
+{
+/* Traverse a symbolic link. Copy the link text from the pnode and insert
+ * the text into the path. Return error code or report success. Base
+ * directory has to be determined according to the first character of the
+ * new pathname.
+ */
+  int r;
+  char sp[PATH_MAX];
+  size_t llen = PATH_MAX;/* length of link */
+  size_t slen;         /* length of suffix */
+  PUFFS_MAKECRED(pcr, &global_kcred);
+
+
+  if (!S_ISLNK(pn->pn_va.va_mode))
+       r = EACCES;
+
+  if (global_pu->pu_ops.puffs_node_readlink == NULL)
+       return(EINVAL);
+
+  if (global_pu->pu_ops.puffs_node_readlink(global_pu, pn, pcr, sp, &llen) != 0)
+       return(EINVAL);
+
+  slen = strlen(suffix);
+
+  /* The path we're parsing looks like this:
+   * /already/processed/path/<link> or
+   * /already/processed/path/<link>/not/yet/processed/path
+   * After expanding the <link>, the path will look like
+   * <expandedlink> or
+   * <expandedlink>/not/yet/processed
+   * In both cases user_path must have enough room to hold <expandedlink>.
+   * However, in the latter case we have to move /not/yet/processed to the
+   * right place first, before we expand <link>. When strlen(<expandedlink>) is
+   * smaller than strlen(/already/processes/path), we move the suffix to the
+   * left. Is strlen(<expandedlink>) greater then we move it to the right. Else
+   * we do nothing.
+   */
+
+  if (slen > 0) { /* Do we have path after the link? */
+       /* For simplicity we require that suffix starts with a slash */
+       if (suffix[0] != '/') {
+               panic("ltraverse: suffix does not start with a slash");
+       }
+
+       /* To be able to expand the <link>, we have to move the 'suffix'
+        * to the right place.
+        */
+       if (slen + llen + 1 > sizeof(user_path))
+               return(ENAMETOOLONG);/* <expandedlink>+suffix+\0 does not fit*/
+       if ((unsigned)(suffix - user_path) != llen) {
+               /* Move suffix left or right if needed */
+               memmove(&user_path[llen], suffix, slen+1);
+       }
+  } else {
+       if (llen + 1 > sizeof(user_path))
+               return(ENAMETOOLONG); /* <expandedlink> + \0 does not fit */
+
+       /* Set terminating nul */
+       user_path[llen]= '\0';
+  }
+
+  /* Everything is set, now copy the expanded link to user_path */
+  memmove(user_path, sp, llen);
+
+  return(OK);
+}
+
+
+/*===========================================================================*
+ *                             advance                                      *
+ *===========================================================================*/
+PUBLIC struct puffs_node *advance(pn_dir, string, chk_perm)
+struct puffs_node *pn_dir;     /* pnode for directory to be searched */
+char string[NAME_MAX + 1];     /* component name to look for */
+int chk_perm;                  /* check permissions when string is looked up*/
+{
+/* Given a directory and a component of a path, look up the component in
+ * the directory, find the pnode, open it, and return a pointer to its pnode
+ * slot.
+ * TODO: instead of string, should get pcn.
+ */
+  struct puffs_node *pn;
+
+  struct puffs_newinfo pni;
+
+  struct puffs_kcn pkcnp;
+  PUFFS_MAKECRED(pcr, &global_kcred);
+  struct puffs_cn pcn = {&pkcnp, (struct puffs_cred *) pcr, {0}};
+
+  enum vtype node_vtype;
+  voff_t size;
+  dev_t rdev;
+  int error;
+
+  err_code = OK;
+
+  /* If 'string' is empty, return an error. */
+  if (string[0] == '\0') {
+       err_code = ENOENT;
+       return(NULL);
+  }
+
+  /* Check for NULL. */
+  if (pn_dir == NULL)
+       return(NULL);
+
+  if (chk_perm) {
+       /* Just search permission is checked */
+       if (forbidden(pn_dir, X_BIT)) {
+               err_code = EACCES;
+               return(NULL);
+       }
+  }
+
+  if (strcmp(string, ".") == 0) {
+       /* Otherwise we will fall into trouble: path for pnode to be looked up
+        * will be parent path (same pnode as the one to be looked up) +
+        * requested path. E.g. after several lookups we might get advance
+        * for "." with parent path "/././././././././.".
+        *
+        * Another problem is that after lookup pnode will be added
+        * to the pu_pnodelst, which already contains pnode instance for this
+        * pnode. It will cause lot of troubles.
+        */
+       return pn_dir;
+  }
+
+  pni.pni_cookie = (void** )&pn;
+  pni.pni_vtype = &node_vtype;
+  pni.pni_size = &size;
+  pni.pni_rdev = &rdev;
+
+  pcn.pcn_namelen = strlen(string);
+  assert(pcn.pcn_namelen <= MAXPATHLEN);
+  strcpy(pcn.pcn_name, string);
+
+  if (buildpath) {
+       if (puffs_path_pcnbuild(global_pu, &pcn, pn_dir) != 0) {
+               lpuffs_debug("pathbuild error\n");
+               err_code = ENOENT;
+               return(NULL);
+       }
+  }
+
+  /* lookup *must* be present */
+  error = global_pu->pu_ops.puffs_node_lookup(global_pu, pn_dir,
+                 &pni, &pcn);
+
+  if (buildpath) {
+       if (error) {
+               global_pu->pu_pathfree(global_pu, &pcn.pcn_po_full);
+               err_code = ENOENT;
+               return(NULL);
+       } else {
+               struct puffs_node *_pn;
+
+               /*
+                * did we get a new node or a
+                * recycled node?
+                */
+               _pn = PU_CMAP(global_pu, pn);
+               if (_pn->pn_po.po_path == NULL)
+                       _pn->pn_po = pcn.pcn_po_full;
+               else
+                       global_pu->pu_pathfree(global_pu, &pcn.pcn_po_full);
+       }
+  }
+
+  if (error) {
+       err_code = error < 0 ? error : -error;
+       return(NULL);
+  } else {
+       /* In MFS/ext2 it's set by search_dir, puffs_node_lookup error codes are unclear,
+        * so we use error variable there
+        */
+       err_code = OK;
+  }
+
+  assert(pn != NULL);
+
+  /* The following test is for "mountpoint/.." where mountpoint is a
+   * mountpoint. ".." will refer to the root of the mounted filesystem,
+   * but has to become a reference to the parent of the 'mountpoint'
+   * directory.
+   *
+   * This case is recognized by the looked up name pointing to a
+   * root pnode, and the directory in which it is held being a
+   * root pnode, _and_ the name[1] being '.'. (This is a test for '..'
+   * and excludes '.'.)
+   */
+  if (pn->pn_va.va_fileid == global_pu->pu_pn_root->pn_va.va_fileid) {
+         if (pn_dir->pn_va.va_fileid == global_pu->pu_pn_root->pn_va.va_fileid) {
+                 if (string[1] == '.') {
+                         if (!is_root_fs) {
+                                 /* Climbing up mountpoint */
+                                 err_code = ELEAVEMOUNT;
+                         }
+                 }
+         }
+  }
+
+  /* See if the pnode is mounted on.  If so, switch to root directory of the
+   * mounted file system.  The super_block provides the linkage between the
+   * pnode mounted on and the root directory of the mounted file system.
+   */
+  if (pn->pn_mountpoint) {
+         /* Mountpoint encountered, report it */
+         err_code = EENTERMOUNT;
+  }
+
+  return(pn);
+}
+
+
+/*===========================================================================*
+ *                             get_name                                     *
+ *===========================================================================*/
+PRIVATE char *get_name(path_name, string)
+char *path_name;               /* path name to parse */
+char string[NAME_MAX+1];       /* component extracted from 'old_name' */
+{
+/* Given a pointer to a path name in fs space, 'path_name', copy the first
+ * component to 'string' (truncated if necessary, always nul terminated).
+ * A pointer to the string after the first component of the name as yet
+ * unparsed is returned.  Roughly speaking,
+ * 'get_name' = 'path_name' - 'string'.
+ *
+ * This routine follows the standard convention that /usr/ast, /usr//ast,
+ * //usr///ast and /usr/ast/ are all equivalent.
+ *
+ * If len of component is greater, than allowed, then return 0.
+ */
+  size_t len;
+  char *cp, *ep;
+
+  cp = path_name;
+
+  /* Skip leading slashes */
+  while (cp[0] == '/') cp++;
+
+  /* Find the end of the first component */
+  ep = cp;
+  while (ep[0] != '\0' && ep[0] != '/')
+       ep++;
+
+  len = (size_t) (ep - cp);
+  /* XXX we don't check name_max of fileserver (probably we can't) */
+  if (len > NAME_MAX) {
+       err_code = ENAMETOOLONG;
+       return(NULL);
+  }
+
+  /* Special case of the string at cp is empty */
+  if (len == 0)
+       strcpy(string, ".");  /* Return "." */
+  else {
+       memcpy(string, cp, len);
+       string[len]= '\0';
+  }
+
+  return(ep);
+}
diff --git a/lib/libpuffs/path_puffs.c b/lib/libpuffs/path_puffs.c
new file mode 100644 (file)
index 0000000..027f1f8
--- /dev/null
@@ -0,0 +1,299 @@
+/* 
+ * 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 <sys/cdefs.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <sys/hash.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <puffs.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "puffs.h"
+#include "puffs_priv.h"
+
+
+/*
+ * Generic routines for pathbuilding code
+ */
+
+int
+puffs_path_pcnbuild(struct puffs_usermount *pu, struct puffs_cn *pcn,
+       puffs_cookie_t parent)
+{
+       struct puffs_node *pn_parent = PU_CMAP(pu, parent);
+       struct puffs_cn pcn_orig;
+       struct puffs_pathobj po;
+       int rv;
+
+       assert(pn_parent->pn_po.po_path != NULL);
+       assert(pu->pu_flags & PUFFS_FLAG_BUILDPATH);
+
+       if (pu->pu_pathtransform) {
+               rv = pu->pu_pathtransform(pu, &pn_parent->pn_po, pcn, &po);
+               if (rv)
+                       return rv;
+       } else {
+               po.po_path = pcn->pcn_name;
+               po.po_len = pcn->pcn_namelen;
+       }
+
+       if (pu->pu_namemod) {
+               /* XXX: gcc complains if I do assignment */
+               memcpy(&pcn_orig, pcn, sizeof(pcn_orig));
+               rv = pu->pu_namemod(pu, &pn_parent->pn_po, pcn);
+               if (rv)
+                       return rv;
+       }
+
+       rv = pu->pu_pathbuild(pu, &pn_parent->pn_po, &po, 0,
+           &pcn->pcn_po_full);
+       puffs_path_buildhash(pu, &pcn->pcn_po_full);
+
+       if (pu->pu_pathtransform)
+               pu->pu_pathfree(pu, &po);
+
+       if (pu->pu_namemod && rv)
+               *pcn = pcn_orig;
+
+       return rv;
+}
+
+/*
+ * substitute all (child) patch prefixes.  called from nodewalk, which
+ * in turn is called from rename
+ */
+void *
+puffs_path_prefixadj(struct puffs_usermount *pu, struct puffs_node *pn,
+       void *arg)
+{
+       struct puffs_pathinfo *pi = arg;
+       struct puffs_pathobj localpo;
+       struct puffs_pathobj oldpo;
+       int rv;
+
+       /* can't be a path prefix */
+       if (pn->pn_po.po_len < pi->pi_old->po_len)
+               return NULL;
+
+       if (pu->pu_pathcmp(pu, &pn->pn_po, pi->pi_old, pi->pi_old->po_len, 1))
+               return NULL;
+
+       /* otherwise we'd have two nodes with an equal path */
+       assert(pn->pn_po.po_len > pi->pi_old->po_len);
+
+       /* found a matching prefix */
+       rv = pu->pu_pathbuild(pu, pi->pi_new, &pn->pn_po,
+           pi->pi_old->po_len, &localpo);
+       /*
+        * XXX: technically we shouldn't fail, but this is the only
+        * sensible thing to do here.  If the buildpath routine fails,
+        * we will have paths in an inconsistent state.  Should fix this,
+        * either by having two separate passes or by doing other tricks
+        * to make an invalid path with BUILDPATHS acceptable.
+        */
+       if (rv != 0)
+               abort();
+
+       /* adjust hash sum */
+       puffs_path_buildhash(pu, &localpo);
+
+       /* out with the old and in with the new */
+       oldpo = pn->pn_po;
+       pn->pn_po = localpo;
+       pu->pu_pathfree(pu, &oldpo);
+
+       /* continue the walk */
+       return NULL;
+}
+
+/*
+ * called from nodewalk, checks for exact match
+ */
+void *
+puffs_path_walkcmp(struct puffs_usermount *pu, struct puffs_node *pn, void *arg)
+{
+       struct puffs_pathobj *po = arg;
+       struct puffs_pathobj po2;
+
+       if (po->po_len != PNPLEN(pn))
+               return NULL;
+
+       /*
+        * If hashing and the hash doesn't match, we know this is
+        * definitely not a match.  Otherwise check for collisions.
+        */
+       if (pu->pu_flags & PUFFS_FLAG_HASHPATH)
+               if (pn->pn_po.po_hash != po->po_hash)
+                       return NULL;
+
+       po2.po_path = PNPATH(pn);
+       po2.po_len = PNPLEN(pn);
+
+       if (pu->pu_pathcmp(pu, po, &po2, PNPLEN(pn), 0) == 0)
+               return pn;
+       return NULL;
+}
+
+/*
+ * Hash sum building routine.  Use string hash if the buildpath routine
+ * is the standard one, otherwise use binary hashes.  A bit whimsical
+ * way to choose the routine, but the binary works for strings also,
+ * so don't sweat it.
+ */
+void
+puffs_path_buildhash(struct puffs_usermount *pu, struct puffs_pathobj *po)
+{
+
+       if ((pu->pu_flags & PUFFS_FLAG_HASHPATH) == 0)
+               return;
+
+       if (pu->pu_pathbuild == puffs_stdpath_buildpath)
+               po->po_hash = hash32_strn(po->po_path, po->po_len,
+                   HASH32_STR_INIT);
+       else
+               po->po_hash = hash32_buf(po->po_path, po->po_len,
+                   HASH32_BUF_INIT);
+}
+
+/*
+ * Routines provided to file systems which consider a path a tuple of
+ * strings and / the component separator.
+ */
+
+/*ARGSUSED*/
+int
+puffs_stdpath_cmppath(struct puffs_usermount *pu, struct puffs_pathobj *c1,
+       struct puffs_pathobj *c2, size_t clen, int checkprefix)
+{
+       char *p;
+       int rv;
+
+       rv = strncmp(c1->po_path, c2->po_path, clen);
+       if (rv)
+               return 1;
+
+       if (checkprefix == 0)
+               return 0;
+
+       /* sanity for next step */
+       if (!(c1->po_len > c2->po_len))
+               return 1;
+
+       /* check if it's really a complete path prefix */
+       p = c1->po_path;
+       if ((*(p + clen)) != '/')
+               return 1;
+
+       return 0;
+}
+
+/*ARGSUSED*/
+int
+puffs_stdpath_buildpath(struct puffs_usermount *pu,
+       const struct puffs_pathobj *po_pre, const struct puffs_pathobj *po_comp,
+       size_t offset, struct puffs_pathobj *newpath)
+{
+       char *path, *pcomp;
+       size_t plen, complen;
+       size_t prelen;
+       int isdotdot;
+
+       complen = po_comp->po_len - offset;
+
+       /* seek to correct place & remove all leading '/' from component */
+       pcomp = po_comp->po_path;
+       pcomp += offset;
+       while (*pcomp == '/') {
+               pcomp++;
+               complen--;
+       }
+
+       /* todotdot or nottodotdot */
+       if (complen == 2 && strcmp(pcomp, "..") == 0)
+               isdotdot = 1;
+       else
+               isdotdot = 0;
+
+       /*
+        * Strip trailing components from the preceending component.
+        * This is an issue only for the root node, which we might want
+        * to be at path "/" for some file systems.
+        */
+       prelen = po_pre->po_len;
+       while (prelen > 0 && *((char *)po_pre->po_path + (prelen-1)) == '/') {
+               assert(isdotdot == 0);
+               prelen--;
+       }
+
+       if (isdotdot) {
+               char *slash; /* sweet char of mine */
+               
+               slash = strrchr(po_pre->po_path, '/');
+               assert(slash != NULL);
+
+               plen = slash - (char *)po_pre->po_path;
+
+               /*
+                * As the converse to not stripping the initial "/" above,
+                * don't nuke it here either.
+                */
+               if (plen == 0)
+                       plen++;
+
+               path = malloc(plen + 1);
+               if (path == NULL)
+                       return errno;
+
+               strlcpy(path, po_pre->po_path, plen+1);
+       } else {
+               /* + '/' + '\0' */
+               plen = prelen + 1 + complen;
+               path = malloc(plen + 1);
+               if (path == NULL)
+                       return errno;
+
+               strlcpy(path, po_pre->po_path, prelen+1);
+               strcat(path, "/");
+               strncat(path, pcomp, complen);
+       }
+
+       newpath->po_path = path;
+       newpath->po_len = plen;
+
+       return 0;
+}
+
+/*ARGSUSED*/
+void
+puffs_stdpath_freepath(struct puffs_usermount *pu, struct puffs_pathobj *po)
+{
+
+       free(po->po_path);
+}
diff --git a/lib/libpuffs/pnode.c b/lib/libpuffs/pnode.c
new file mode 100644 (file)
index 0000000..b0e665f
--- /dev/null
@@ -0,0 +1,221 @@
+/*     $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/cdefs.h>
+#if !defined(lint)
+__RCSID("$NetBSD: pnode.c,v 1.10 2008/08/12 19:44:39 pooka Exp $");
+#endif /* !lint */
+
+#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)
+{
+       struct puffs_usermount *pu = pn->pn_mnt;
+       assert(pu != NULL);
+
+       LIST_REMOVE(pn, pn_entries);
+       pn->pn_flags |= PUFFS_NODE_REMOVED;
+       if (pn->pn_count != 0) {
+               /* XXX FS removes this pn from the list to prevent further
+                * lookups from finding node after remove/rm/rename op.
+                * But VFS still uses it, i.e. pnode is still open, and
+                * will put it later. Keep it in separate list to do reclaim
+                * in fs_put().
+                */
+               LIST_INSERT_HEAD(&pu->pu_pnode_removed_lst, pn, pn_entries);
+       }
+}
+
+void
+puffs_pn_put(struct puffs_node *pn)
+{
+       struct puffs_usermount *pu = pn->pn_mnt;
+
+       pu->pu_pathfree(pu, &pn->pn_po);
+       /* Removes either from pu_pnodelst or pu_pnode_removed_lst */
+       LIST_REMOVE(pn, pn_entries);
+       free(pn);
+}
+
+/* walk list, rv can be used either to halt or to return a value
+ * XXX (MINIX note): if fn is 0, then arg is ino_t and we search
+ * node with ino_t. TODO: modify docs.
+ */
+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);
+               if (fn) {
+                       rv = fn(pu, pn_cur, arg);
+                       if (rv)
+                               return rv;
+               } else {
+                       if (pn_cur->pn_va.va_fileid == *((ino_t*) arg))
+                               return pn_cur;
+               }
+               pn_cur = pn_next;
+       }
+
+       return NULL;
+}
+
+void*
+puffs_pn_nodeprint(struct puffs_usermount *pu, struct puffs_node *pn, void *arg)
+{
+       /* If arg is specified, print only pnodes with inum (should be only one,
+        * otherwise - all.
+        */
+       if (arg != NULL) {
+               ino_t inum = *(ino_t*)arg;
+               if (pn->pn_va.va_fileid != inum) {
+                       return NULL;
+               }
+       }
+       lpuffs_debug(" ino %ld used %d %s\n", pn->pn_va.va_fileid, pn->pn_count,
+                               pn->pn_po.po_path);
+       /* If arg specified, it should be the only one pnode to be printed,
+        * but we walk through the rest of list for debugging purposes.
+        */
+       return NULL;
+}
+
+void
+puffs_pn_nodeprintall(struct puffs_usermount *pu)
+{
+       puffs_pn_nodewalk(pu, puffs_pn_nodeprint, 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)
+{
+
+       *pni->pni_cookie = cookie;
+}
+
+void
+puffs_newinfo_setvtype(struct puffs_newinfo *pni, enum vtype vt)
+{
+
+       *pni->pni_vtype = vt;
+}
+
+void
+puffs_newinfo_setsize(struct puffs_newinfo *pni, voff_t size)
+{
+
+       *pni->pni_size = size;
+}
+
+void
+puffs_newinfo_setrdev(struct puffs_newinfo *pni, dev_t rdev)
+{
+
+       *pni->pni_rdev = rdev;
+}
diff --git a/lib/libpuffs/protect.c b/lib/libpuffs/protect.c
new file mode 100644 (file)
index 0000000..48043ff
--- /dev/null
@@ -0,0 +1,145 @@
+/* Created (MFS based):
+ *   June 2011 (Evgeniy Ivanov)
+ */
+
+#include "fs.h"
+#include <minix/vfsif.h>
+
+#include "puffs.h"
+#include "puffs_priv.h"
+
+FORWARD _PROTOTYPE( int in_group, (gid_t grp)                          );
+
+
+/*===========================================================================*
+ *                             fs_chmod                                     *
+ *===========================================================================*/
+PUBLIC int fs_chmod()
+{
+/* Perform the chmod(name, mode) system call. */
+  int r;
+  struct puffs_node *pn;
+  mode_t mode;
+  struct vattr va;
+  PUFFS_MAKECRED(pcr, &global_kcred);
+
+  if (global_pu->pu_ops.puffs_node_setattr == NULL)
+       return(EINVAL);
+
+  mode = (mode_t) fs_m_in.REQ_MODE;
+
+  if ((pn = puffs_pn_nodewalk(global_pu, 0, &fs_m_in.REQ_INODE_NR)) == NULL)
+       return(EINVAL);
+   
+  puffs_vattr_null(&va);
+  /* Clear setgid bit if file is not in caller's grp */
+  va.va_mode = (pn->pn_va.va_mode & ~ALL_MODES) | (mode & ALL_MODES);
+  va.va_ctime.tv_nsec = 0;
+  va.va_ctime.tv_sec = clock_time();
+
+  if (global_pu->pu_ops.puffs_node_setattr(global_pu, pn, &va, pcr) != 0)
+       return(EINVAL);
+
+  /* Return full new mode to caller. */
+  fs_m_out.RES_MODE = pn->pn_va.va_mode;
+
+  return(OK);
+}
+
+
+/*===========================================================================*
+ *                             fs_chown                                     *
+ *===========================================================================*/
+PUBLIC int fs_chown()
+{
+  int r;
+  struct puffs_node *pn;
+  struct vattr va;
+  PUFFS_MAKECRED(pcr, &global_kcred);
+
+  if ((pn = puffs_pn_nodewalk(global_pu, 0, &fs_m_in.REQ_INODE_NR)) == NULL)
+       return(EINVAL);
+
+  /* Not permitted to change the owner of a file on a read-only file sys. */
+  if (!is_readonly_fs) {
+       puffs_vattr_null(&va);
+       va.va_uid = fs_m_in.REQ_UID;
+       va.va_gid = fs_m_in.REQ_GID;
+       va.va_mode = pn->pn_va.va_mode & ~(I_SET_UID_BIT | I_SET_GID_BIT);
+       va.va_ctime.tv_nsec = 0;
+       va.va_ctime.tv_sec = clock_time();
+
+       if (global_pu->pu_ops.puffs_node_setattr(global_pu, pn, &va, pcr) != 0)
+               return(EINVAL);
+  }
+
+  /* Update caller on current mode, as it may have changed. */
+  fs_m_out.RES_MODE = pn->pn_va.va_mode;
+
+  return(OK);
+}
+
+
+/*===========================================================================*
+ *                             forbidden                                    *
+ *===========================================================================*/
+PUBLIC int forbidden(register struct puffs_node *pn, mode_t access_desired)
+{
+/* Given a pointer to an pnode, 'pn', and the access desired, determine
+ * if the access is allowed, and if not why not.  The routine looks up the
+ * caller's uid in the 'fproc' table.  If access is allowed, OK is returned
+ * if it is forbidden, EACCES is returned.
+ */
+
+  register mode_t bits, perm_bits;
+  int r, shift;
+
+  /* Isolate the relevant rwx bits from the mode. */
+  bits = pn->pn_va.va_mode;
+  if (caller_uid == SU_UID) {
+       /* Grant read and write permission.  Grant search permission for
+        * directories.  Grant execute permission (for non-directories) if
+        * and only if one of the 'X' bits is set.
+        */
+       if ( (bits & I_TYPE) == I_DIRECTORY ||
+            bits & ((X_BIT << 6) | (X_BIT << 3) | X_BIT))
+               perm_bits = R_BIT | W_BIT | X_BIT;
+       else
+               perm_bits = R_BIT | W_BIT;
+  } else {
+       if (caller_uid == pn->pn_va.va_uid) shift = 6;  /* owner */
+       else if (caller_gid == pn->pn_va.va_gid) shift = 3;     /* group */
+       else if (in_group(pn->pn_va.va_gid) == OK) shift = 3;   /* other groups */
+       else shift = 0;                                 /* other */
+       perm_bits = (bits >> shift) & (R_BIT | W_BIT | X_BIT);
+  }
+
+  /* If access desired is not a subset of what is allowed, it is refused. */
+  r = OK;
+  if ((perm_bits | access_desired) != perm_bits) r = EACCES;
+
+  /* Check to see if someone is trying to write on a file system that is
+   * mounted read-only.
+   */
+  if (r == OK) {
+       if (access_desired & W_BIT) {
+               r = is_readonly_fs ? EROFS : OK;
+       }
+  }
+
+  return(r);
+}
+
+
+/*===========================================================================*
+ *                             in_group                                     *
+ *===========================================================================*/
+PRIVATE int in_group(gid_t grp)
+{
+  int i;
+  for(i = 0; i < credentials.vu_ngroups; i++)
+       if (credentials.vu_sgroups[i] == grp)
+               return(OK);
+
+  return(EINVAL);
+}
diff --git a/lib/libpuffs/proto.h b/lib/libpuffs/proto.h
new file mode 100644 (file)
index 0000000..da1c95d
--- /dev/null
@@ -0,0 +1,71 @@
+#ifndef PUFFS_PROTO_H
+#define PUFFS_PROTO_H
+
+/* Function prototypes. */
+
+_PROTOTYPE( int fs_new_driver, (void)                                  );
+
+/* inode.c */
+_PROTOTYPE( int fs_putnode, (void)                                     );
+
+/* device.c */
+_PROTOTYPE( int dev_open, (endpoint_t driver_e, dev_t dev,
+                       endpoint_t proc_e, int flags)                   );
+_PROTOTYPE( void dev_close, (endpoint_t driver_e, dev_t dev)           );
+
+/* link.c */
+_PROTOTYPE( int fs_ftrunc, (void)                                      );
+_PROTOTYPE( int fs_link, (void)                                                );
+_PROTOTYPE( int fs_rdlink, (void)                                      );
+_PROTOTYPE( int fs_rename, (void)                                      );
+_PROTOTYPE( int fs_unlink, (void)                                      );
+
+/* misc.c */
+_PROTOTYPE( int fs_flush, (void)                                       );
+_PROTOTYPE( int fs_sync, (void)                                                );
+
+/* mount.c */
+_PROTOTYPE( int fs_mountpoint, (void)                                  );
+_PROTOTYPE( int fs_readsuper, (void)                                    );
+_PROTOTYPE( int fs_unmount, (void)                                     );
+
+/* open.c */
+_PROTOTYPE( int fs_create, (void)                                      );
+_PROTOTYPE( int fs_inhibread, (void)                                   );
+_PROTOTYPE( int fs_mkdir, (void)                                       );
+_PROTOTYPE( int fs_mknod, (void)                                       );
+_PROTOTYPE( int fs_slink, (void)                                       );
+
+/* path.c */
+_PROTOTYPE( int fs_lookup, (void)                                      );
+_PROTOTYPE( struct puffs_node *advance, (struct puffs_node *dirp,
+                                char string[NAME_MAX + 1], int chk_perm));
+
+/* protect.c */
+_PROTOTYPE( int fs_chmod, (void)                                       );
+_PROTOTYPE( int fs_chown, (void)                                       );
+_PROTOTYPE( int fs_getdents, (void)                                    );
+_PROTOTYPE( int forbidden, (struct puffs_node *rip,
+                       mode_t access_desired)                          );
+
+/* read.c */
+_PROTOTYPE( int fs_breadwrite, (void)                                  );
+_PROTOTYPE( int fs_readwrite, (void)                                   );
+
+/* stadir.c */
+_PROTOTYPE( int fs_fstatfs, (void)                                     );
+_PROTOTYPE( int fs_stat, (void)                                                );
+_PROTOTYPE( int fs_statvfs, (void)                                     );
+
+/* time.c */
+_PROTOTYPE( int fs_utime, (void)                                       );
+
+/* utility.c */
+_PROTOTYPE( int no_sys, (void)                                          );
+_PROTOTYPE( void mfs_nul_f, (char *file, int line, char *str,
+                             unsigned int len, unsigned int maxlen)     );
+_PROTOTYPE( time_t clock_time, (void)                                  );
+_PROTOTYPE( int update_times, (struct puffs_node *pn, int fl, time_t t) );
+_PROTOTYPE( void lpuffs_debug, (char *format, ...)                     );
+
+#endif /* PUFFS_PROTO_H */
diff --git a/lib/libpuffs/puffs.3 b/lib/libpuffs/puffs.3
new file mode 100644 (file)
index 0000000..abc276d
--- /dev/null
@@ -0,0 +1,542 @@
+.\"    $NetBSD: puffs.3,v 1.42.4.2 2009/10/16 12:07:23 sborrill 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 September 6, 2008
+.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
+.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 .Fa "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 pcn_po_full.po_path
+field in a
+.Vt struct puffs_cn .
+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.
+.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_dispatch_create pu pb pccp
+.It Fn puffs_dispatch_exec pcc pbp
+In case the use of
+.Fn puffs_mainloop
+is not possible, requests may be dispatched manually.
+However, as this is less efficient than using the mainloop,
+it should never be the first preference.
+.Pp
+Calling
+.Fn puffs_dispatch_create
+creates a dispatch request.
+The argument
+.Ar pb
+should contains a valid request and upon success
+.Ar pccp
+will contain a valid request context.
+This context is passed to
+.Fn puffs_dispatch_exec
+to execute the request.
+If the request yielded before completing, the routine returns 0,
+otherwise 1.
+When the routine completes,
+.Ar pcc
+is made invalid and a pointer to the processed buffer is placed in
+.Ar pbp .
+It is the responsibility of the caller to send the response (if
+necessary) and destroy the buffer.
+.Pp
+See
+.Xr puffs_cc 3
+and
+.Xr puffs_framebuf 3
+for further information.
+.El
+.Ss Cookies
+Every file (regular file, directory, device node, ...) instance is
+attached to the kernel using a cookie.
+A cookie should uniquely map to a file during its lifetime.
+If file instances are kept in memory, a simple strategy is to use
+the virtual address of the structure describing the file.
+The cookie can be recycled when
+.Fn puffs_node_reclaim
+is called for a node.
+.Pp
+For some operations (such as building paths) the framework needs to map
+the cookie to the framework-level structure describing a file,
+.Vt struct puffs_node .
+It is advisable to simply use the
+.Vt struct puffs_node
+address as a cookie and store file system specific data in the private
+portion of
+.Vt struct puffs_node .
+The library assumes this by default.
+If it is not desirable, the file system implementation can call
+.Fn puffs_set_cookiemap
+to provide an alternative cookie-to-node mapping function.
+.Sh SEE ALSO
+.Xr mount 2 ,
+.Xr puffs_cc 3 ,
+.Xr puffs_cred 3 ,
+.Xr puffs_flush 3 ,
+.Xr puffs_framebuf 3 ,
+.Xr puffs_node 3 ,
+.Xr puffs_ops 3 ,
+.Xr puffs_path 3 ,
+.Xr puffs_suspend 3 ,
+.Xr refuse 3 ,
+.Xr puffs 4
+.Rs
+.%A Antti Kantee
+.%D March 2007
+.%J Proceedings of AsiaBSDCon 2007
+.%P pp. 29-42
+.%T puffs - Pass-to-Userspace Framework File System
+.Re
+.Rs
+.%A Antti Kantee
+.%D September 2007
+.%I Helsinki University of Technology
+.%R Tech Report TKK-TKO-B157
+.%T Using puffs for Implementing Client-Server Distributed File Systems
+.Re
+.Rs
+.%A Antti Kantee
+.%A Alistair Crooks
+.%D September 2007
+.%J EuroBSDCon 2007
+.%T ReFUSE: Userspace FUSE Reimplementation Using puffs
+.Re
+.Rs
+.%A Antti Kantee
+.%D March 2008
+.%J Proceedings of AsiaBSDCon 2008
+.%P pp. 55-70
+.%T Send and Receive of File System Protocols: Userspace Approach With puffs
+.Re
+.Sh HISTORY
+An unsupported experimental version of
+.Nm
+first appeared in
+.Nx 4.0 .
+A stable version appeared in
+.Nx 5.0 .
+.Sh AUTHORS
+.An Antti Kantee Aq pooka@iki.fi
diff --git a/lib/libpuffs/puffs.c b/lib/libpuffs/puffs.c
new file mode 100644 (file)
index 0000000..dcedaf3
--- /dev/null
@@ -0,0 +1,774 @@
+/*     $NetBSD: puffs.c,v 1.92.4.4 2009/10/27 20:37:38 bouyer 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.
+ */
+
+/* TODO: We don't support PUFFS_COMFD used in original puffs_mount,
+ * add it to the docs if any.
+ *
+ */
+
+#include "fs.h"
+#include <sys/cdefs.h>
+#if !defined(lint)
+__RCSID("$NetBSD: puffs.c,v 1.92.4.4 2009/10/27 20:37:38 bouyer Exp $");
+#endif /* !lint */
+
+#include <sys/param.h>
+#include <sys/mount.h>
+
+#include <minix/endpoint.h>
+#include <minix/vfsif.h>
+
+#include <assert.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include "puffs.h"
+#include "puffs_priv.h"
+
+#ifdef PUFFS_WITH_THREADS
+#include <pthread.h>
+pthread_mutex_t pu_lock = PTHREAD_MUTEX_INITIALIZER;
+#endif
+
+
+/* Declare some local functions. */
+FORWARD _PROTOTYPE(void get_work, (message *m_in)                      );
+FORWARD _PROTOTYPE( void reply, (endpoint_t who, message *m_out)               );
+
+/* SEF functions and variables. */
+FORWARD _PROTOTYPE( void sef_local_startup, (void) );
+FORWARD _PROTOTYPE( int sef_cb_init_fresh, (int type, sef_init_info_t *info) );
+FORWARD _PROTOTYPE( void sef_cb_signal_handler, (int signo) );
+
+EXTERN int env_argc;
+EXTERN char **env_argv;
+
+
+#define PUFFS_MAX_ARGS 20
+
+int __real_main(int argc, char* argv[]);
+
+int __wrap_main(int argc, char *argv[])
+{
+  int i;
+  int new_argc = 0;
+  static char* new_argv[PUFFS_MAX_ARGS];
+  char *name;
+
+  /* SEF local startup. */
+  env_setargs(argc, argv);
+  sef_local_startup();
+
+  global_kcred.pkcr_type = PUFFCRED_TYPE_INTERNAL;
+
+  if (argc < 3) {
+       panic("Unexpected arguments, use:\
+               mount -t fs /dev/ /dir [-o option1,option2]\n");
+  }
+
+  name = argv[0] + strlen(argv[0]);
+  while (*name != '/' && name != argv[0])
+         name--;
+  if (name != argv[0])
+         name++;
+  strcpy(fs_name, name);
+
+  new_argv[new_argc] = argv[0];
+  new_argc++;
+  
+  for (i = 1; i < argc; i++) {
+       if (new_argc >= PUFFS_MAX_ARGS) {
+               panic("Too many arguments, change PUFFS_MAX_ARGS");
+       }
+       new_argv[new_argc] = argv[i];
+       new_argc++;
+  }
+
+  assert(new_argc > 0);
+
+  get_work(&fs_m_in);
+
+  return __real_main(new_argc, new_argv);
+}
+
+
+#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, uint8_t *opmask)
+{
+
+       memset(opmask, 0, PUFFS_VN_MAX);
+
+       FILLOP(create,   CREATE);
+       FILLOP(mknod,    MKNOD);
+       FILLOP(open,     OPEN);
+       FILLOP(close,    CLOSE);
+       FILLOP(access,   ACCESS);
+       FILLOP(getattr,  GETATTR);
+       FILLOP(setattr,  SETATTR);
+       FILLOP(poll,     POLL); /* XXX: not ready in kernel */
+       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(abortop,  ABORTOP);
+}
+#undef FILLOP
+
+
+/*ARGSUSED*/
+static void
+puffs_defaulterror(struct puffs_usermount *pu, uint8_t type,
+       int error, const char *str, puffs_cookie_t cookie)
+{
+
+       lpuffs_debug("abort: type %d, error %d, cookie %p (%s)\n",
+           type, error, cookie, str);
+       abort();
+}
+
+
+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 < minsize || ss == PUFFS_STACKSIZE_MIN) {
+               if (ss != PUFFS_STACKSIZE_MIN)
+                       lpuffs_debug("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++;
+               lpuffs_debug("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;
+}
+
+int
+puffs_mount(struct puffs_usermount *pu, const char *dir, int mntflags,
+       puffs_cookie_t cookie)
+{
+        endpoint_t src;
+        int error, ind;
+
+        pu->pu_kargp->pa_root_cookie = cookie;
+       
+        src = fs_m_in.m_source;
+        error = OK;
+        caller_uid = INVAL_UID; /* To trap errors */
+        caller_gid = INVAL_GID;
+        req_nr = fs_m_in.m_type;
+
+        if (req_nr < VFS_BASE) {
+                fs_m_in.m_type += VFS_BASE;
+                req_nr = fs_m_in.m_type;
+        }
+        ind = req_nr - VFS_BASE;
+
+        assert(ind == REQ_READ_SUPER);
+
+        if (ind < 0 || ind >= NREQS) {
+                error = EINVAL;
+        } else {
+                error = (*fs_call_vec[ind])();
+        }
+
+        fs_m_out.m_type = error;
+       if (IS_VFS_FS_TRANSID(last_request_transid)) {
+               /* If a transaction ID was set, reset it */
+               fs_m_out.m_type = TRNS_ADD_ID(fs_m_out.m_type,
+                                             last_request_transid);
+       }
+        reply(src, &fs_m_out);
+
+        if (error) {
+                free(pu->pu_kargp);
+                pu->pu_kargp = NULL;
+                errno = error;
+                return -1;
+        }
+
+        PU_SETSTATE(pu, PUFFS_STATE_RUNNING);
+        return 0;
+}
+
+/*ARGSUSED*/
+struct puffs_usermount *
+_puffs_init(int dummy, 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 = PUFFSDEVELVERS | PUFFSVERSION;
+       pargs->pa_flags = PUFFS_FLAG_KERN(pflags);
+       fillvnopmask(pops, pargs->pa_vnopmask);
+       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;
+
+       pu->pu_flags = pflags;
+       buildpath = pu->pu_flags & PUFFS_FLAG_BUILDPATH; /* XXX */
+       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_pnode_removed_lst);
+       LIST_INIT(&pu->pu_ios);
+       LIST_INIT(&pu->pu_ios_rmlist);
+       LIST_INIT(&pu->pu_ccmagazin);
+       TAILQ_INIT(&pu->pu_sched);
+
+       /* 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_defaulterror;
+
+       PU_SETSTATE(pu, PUFFS_STATE_BEFOREMOUNT);
+
+       global_pu = pu;
+
+       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);
+       free(pu);
+}
+
+/*ARGSUSED1*/
+int
+puffs_exit(struct puffs_usermount *pu, int force)
+{
+       struct puffs_node *pn;
+        
+        lpuffs_debug("puffs_exit\n");
+
+       while ((pn = LIST_FIRST(&pu->pu_pnodelst)) != NULL)
+               puffs_pn_put(pn);
+
+       while ((pn = LIST_FIRST(&pu->pu_pnode_removed_lst)) != NULL)
+               puffs_pn_put(pn);
+
+       puffs__cc_exit(pu);
+       if (pu->pu_state & PU_HASKQ)
+               close(pu->pu_kq);
+       free(pu);
+
+       return 0; /* always succesful for now, WILL CHANGE */
+}
+
+/*
+ * 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;
+       int error, ind;
+
+       while (!unmountdone || !exitsignaled) {
+               endpoint_t src;
+
+               /*
+                * Schedule existing requests.
+                */
+               while ((pcc = TAILQ_FIRST(&pu->pu_sched)) != NULL) {
+                       lpuffs_debug("scheduling existing tasks\n");
+                       TAILQ_REMOVE(&pu->pu_sched, pcc, pcc_schedent);
+                       puffs__goto(pcc);
+               }
+
+               if (pu->pu_ml_lfn) {
+                        lpuffs_debug("Calling user mainloop handler\n");
+                       pu->pu_ml_lfn(pu);
+               }
+
+               /* Wait for request message. */
+               get_work(&fs_m_in);
+
+               src = fs_m_in.m_source;
+               error = OK;
+               caller_uid = INVAL_UID; /* To trap errors */
+               caller_gid = INVAL_GID;
+               req_nr = fs_m_in.m_type;
+
+               if (req_nr < VFS_BASE) {
+                       fs_m_in.m_type += VFS_BASE;
+                       req_nr = fs_m_in.m_type;
+               }
+               ind = req_nr - VFS_BASE;
+
+               if (ind < 0 || ind >= NREQS) {
+                       error = EINVAL;
+               } else {
+                       error = (*fs_call_vec[ind])();
+               }
+
+               fs_m_out.m_type = error;
+               if (IS_VFS_FS_TRANSID(last_request_transid)) {
+                       /* If a transaction ID was set, reset it */
+                       fs_m_out.m_type = TRNS_ADD_ID(fs_m_out.m_type, last_request_transid);
+               }
+               reply(src, &fs_m_out);
+       }
+
+       if (puffs__cc_restoremain(pu) == -1)
+               warn("cannot restore main context.  impending doom");
+
+       /* May get here, if puffs_fakecc is set to 1. Currently librefuse sets it.
+        * Now we just return to the caller.
+        */
+}
+
+int
+puffs_mainloop(struct puffs_usermount *pu)
+{
+       struct puffs_cc *pcc;
+       int sverrno;
+
+       assert(puffs_getstate(pu) >= PUFFS_STATE_RUNNING);
+
+       pu->pu_state |= PU_HASKQ | 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);
+
+       errno = 0;
+
+ out:
+       /* store the real error for a while */
+       sverrno = errno;
+
+       errno = sverrno;
+       if (errno)
+               return -1;
+       else
+               return 0;
+}
+
+
+/*===========================================================================*
+ *                            sef_local_startup                             *
+ *===========================================================================*/
+PRIVATE void sef_local_startup()
+{
+  /* Register init callbacks. */
+  sef_setcb_init_fresh(sef_cb_init_fresh);
+  sef_setcb_init_restart(sef_cb_init_fail);
+
+  /* No live update support for now. */
+
+  /* Register signal callbacks. */
+  sef_setcb_signal_handler(sef_cb_signal_handler);
+
+  /* Let SEF perform startup. */
+  sef_startup();
+}
+
+/*===========================================================================*
+ *                         sef_cb_init_fresh                                *
+ *===========================================================================*/
+PRIVATE int sef_cb_init_fresh(int type, sef_init_info_t *info)
+{
+/* Initialize the Minix file server. */
+  SELF_E = getprocnr();
+  return(OK);
+}
+
+/*===========================================================================*
+ *                        sef_cb_signal_handler                             *
+ *===========================================================================*/
+PRIVATE void sef_cb_signal_handler(int signo)
+{
+  /* Only check for termination signal, ignore anything else. */
+  if (signo != SIGTERM) return;
+
+  exitsignaled = 1;
+  fs_sync();
+
+  /* If unmounting has already been performed, exit immediately.
+   * We might not get another message.
+   */
+  if (unmountdone) {
+        if (puffs__cc_restoremain(global_pu) == -1)
+                warn("cannot restore main context.  impending doom");
+       /* May happen if puffs_fakecc is set to 1. Currently librefuse sets it.
+        * There is a chance, that main loop hangs in receive() and we will
+        * never get any new message, so we have to exit() here.
+        */
+       exit(0);
+  }
+}
+
+/*===========================================================================*
+ *                             get_work                                     *
+ *===========================================================================*/
+PRIVATE void get_work(m_in)
+message *m_in;                         /* pointer to message */
+{
+  int r, srcok = 0;
+  endpoint_t src;
+
+  do {
+       if ((r = sef_receive(ANY, m_in)) != OK)         /* wait for message */
+               panic("sef_receive failed: %d", r);
+       src = m_in->m_source;
+
+       if(src == VFS_PROC_NR) {
+               if(unmountdone)
+                       lpuffs_debug("libpuffs: unmounted: unexpected message from FS\n");
+               else
+                       srcok = 1;              /* Normal FS request. */
+
+       } else
+               lpuffs_debug("libpuffs: unexpected source %d\n", src);
+  } while(!srcok);
+
+  assert((src == VFS_PROC_NR && !unmountdone));
+
+  last_request_transid = TRNS_GET_ID(fs_m_in.m_type);
+  fs_m_in.m_type = TRNS_DEL_ID(fs_m_in.m_type);
+  if (fs_m_in.m_type == 0) {
+         assert(!IS_VFS_FS_TRANSID(last_request_transid));
+         fs_m_in.m_type = last_request_transid;  /* Backwards compat. */
+         last_request_transid = 0;
+  } else
+         assert(IS_VFS_FS_TRANSID(last_request_transid));
+}
+
+
+/*===========================================================================*
+ *                             reply                                        *
+ *===========================================================================*/
+PRIVATE void reply(
+  endpoint_t who,
+  message *m_out                               /* report result */
+)
+{
+  if (OK != send(who, m_out))    /* send the message */
+       lpuffs_debug("libpuffs(%d) was unable to send reply\n", SELF_E);
+
+  last_request_transid = 0;
+}
+
diff --git a/lib/libpuffs/puffs.h b/lib/libpuffs/puffs.h
new file mode 100644 (file)
index 0000000..09d177b
--- /dev/null
@@ -0,0 +1,747 @@
+/*     $NetBSD: puffs.h,v 1.108.4.4 2009/10/27 20:37:38 bouyer 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/stat.h>
+#include <sys/statvfs.h>
+#include <sys/time.h>
+#include <sys/queue.h>
+
+#include "puffs_msgif.h"
+
+#include <mntopts.h>
+#include <time.h>
+
+#include <stdbool.h>
+#include <string.h>
+#include <stdint.h>
+
+#include <dirent.h>
+
+
+/* MINIX forwards */
+struct flock;
+struct dirent;
+
+/* XXX: from sys/fstypes.h
+ * Flags for various system call interfaces.
+ *
+ * waitfor flags to vfs_sync() and getvfsstat()
+ */
+#define MNT_WAIT        1       /* synchronously wait for I/O to complete */
+#define MNT_NOWAIT      2       /* start all I/O, but do not wait for it */
+#define MNT_LAZY        3       /* push data not written by filesystem syncer */
+
+#define MNT_RDONLY      0x00000001      /* read only filesystem */
+#define MNT_SYNCHRONOUS 0x00000002      /* file system written synchronously */
+#define MNT_NOEXEC      0x00000004      /* can't exec from filesystem */
+#define MNT_NOSUID      0x00000008      /* don't honor setuid bits on fs */
+#define MNT_NODEV       0x00000010      /* don't interpret special files */
+#define MNT_UNION       0x00000020      /* union with underlying filesystem */
+#define MNT_ASYNC       0x00000040      /* file system written asynchronously */
+#define MNT_NOCOREDUMP  0x00008000      /* don't write core dumps to this FS */
+#define MNT_IGNORE      0x00100000      /* don't show entry in df */
+#define MNT_LOG         0x02000000      /* Use logging */
+#define MNT_NOATIME     0x04000000      /* Never update access times in fs */
+#define MNT_SYMPERM     0x20000000      /* recognize symlink permission */
+#define MNT_NODEVMTIME  0x40000000      /* Never update mod times for devs */
+#define MNT_SOFTDEP     0x80000000      /* Use soft dependencies */
+
+/* XXX from machine/param.h */
+#define DEV_BSHIFT      9               /* log2(DEV_BSIZE) */
+#define DEV_BSIZE       (1 << DEV_BSHIFT)
+
+
+/* FIXME: move?
+ * some stuff from sys/dirent.h.
+ */
+#define DT_UNKNOWN       0
+#define DT_FIFO          1
+#define DT_CHR           2
+#define DT_DIR           4
+#define DT_BLK           6
+#define DT_REG           8
+#define DT_LNK          10
+#define DT_SOCK         12
+#define DT_WHT          14
+/*
+ * The _DIRENT_ALIGN macro returns the alignment of struct dirent.
+ * struct direct and struct dirent12 used 4 byte alignment but
+ * struct dirent uses 8.
+ */
+/* FIXME: in mfs code sizeof(long) and not d_ino */
+#undef _DIRENT_ALIGN
+#define _DIRENT_ALIGN(dp) (sizeof((dp)->d_ino) - 1)
+/*
+ * The _DIRENT_NAMEOFF macro returns the offset of the d_name field in
+ * struct dirent
+ */
+#define _DIRENT_NAMEOFF(dp) \
+    ((char *)(void *)&(dp)->d_name - (char *)(void *)dp)
+/*
+ * The _DIRENT_RECLEN macro gives the minimum record length which will hold
+ * a name of size "namlen".  This requires the amount of space in struct dirent
+ * without the d_name field, plus enough space for the name with a terminating
+ * null byte (namlen+1), rounded up to a the appropriate byte boundary.
+ */
+/* FIXME */
+#undef _DIRENT_RECLEN
+#define _DIRENT_RECLEN(dp, namlen) \
+    ((_DIRENT_NAMEOFF(dp) + (namlen) + 1 + _DIRENT_ALIGN(dp)) & \
+    ~_DIRENT_ALIGN(dp))
+/*
+ * The _DIRENT_NEXT macro advances to the next dirent record.
+ */
+#define _DIRENT_NEXT(dp) ((void *)((char *)(void *)(dp) + (dp)->d_reclen))
+
+
+
+
+/* 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    */
+
+       /* MINIX fields */
+       char                    pn_mountpoint; /* true if mounted on */
+       int                     pn_count;          /* # times inode used */
+
+};
+#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
+
+/*
+ * 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_nameiop    pcn_pkcnp->pkcn_nameiop
+#define pcn_flags      pcn_pkcnp->pkcn_flags
+#define pcn_name       pcn_pkcnp->pkcn_name
+#define pcn_namelen    pcn_pkcnp->pkcn_namelen
+#define pcn_consume    pcn_pkcnp->pkcn_consume
+
+/*
+ * 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_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_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,
+           const struct puffs_cred *);
+       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 *);
+       void (*puffs_fs_suspend)(struct puffs_usermount *, int);
+
+       int (*puffs_node_lookup)(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, const struct puffs_cred *);
+       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, const struct puffs_cred *, int, off_t, off_t);
+       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, int *);
+       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 *);
+
+#if 0
+       /* enable next time this structure is changed */
+       void *puffs_ops_spare[32];
+#endif
+};
+
+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 *);                          \
+       void fsname##_fs_suspend(struct puffs_usermount *, int);        \
+                                                                       \
+       int fsname##_node_lookup(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, const struct puffs_cred *, int,             \
+           off_t, off_t);                                              \
+       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, int *);                                \
+       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 *);
+
+#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 PUFFS_DEVEL_LIBVERSION 34
+#define puffs_init(a,b,c,d,e) \
+    _puffs_init(PUFFS_DEVEL_LIBVERSION,a,b,c,d,e)
+
+
+#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 PCNISDOTDOT(pcnode) \
+       ((pcnode)->pcn_namelen == 2 && strcmp((pcnode)->pcn_name, "..") == 0)
+
+#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(int, 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_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 *);
+
+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_newinfo_setrdev(struct puffs_newinfo *, dev_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_pn_nodeprint(struct puffs_usermount *pu,
+                                          struct puffs_node *pn, void *arg);
+void                   puffs_pn_nodeprintall(struct puffs_usermount *pu);
+
+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,
+                     const struct puffs_cred *);
+
+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_set_prepost(struct puffs_usermount *,
+                         pu_prepost_fn, pu_prepost_fn);
+void   puffs_set_cmap(struct puffs_usermount *, pu_cmap_fn);
+
+/*
+ * Suspension
+ */
+
+int    puffs_fs_suspend(struct puffs_usermount *);
+
+/*
+ * Frame buffering
+ */
+
+void   puffs_framev_init(struct puffs_usermount *,
+                         puffs_framev_readframe_fn,
+                         puffs_framev_writeframe_fn,
+                         puffs_framev_cmpframe_fn,
+                         puffs_framev_gotframe_fn,
+                         puffs_framev_fdnotify_fn);
+
+struct puffs_framebuf  *puffs_framebuf_make(void);
+void                   puffs_framebuf_destroy(struct puffs_framebuf *);
+int                    puffs_framebuf_dup(struct puffs_framebuf *,
+                                          struct puffs_framebuf **);
+void                   puffs_framebuf_recycle(struct puffs_framebuf *);
+int                    puffs_framebuf_reserve_space(struct puffs_framebuf *,
+                                                    size_t);
+
+int    puffs_framebuf_putdata(struct puffs_framebuf *, const void *, size_t);
+int    puffs_framebuf_putdata_atoff(struct puffs_framebuf *, size_t,
+                                    const void *, size_t);
+int    puffs_framebuf_getdata(struct puffs_framebuf *, void *, size_t);
+int    puffs_framebuf_getdata_atoff(struct puffs_framebuf *, size_t,
+                                    void *, size_t);
+
+size_t puffs_framebuf_telloff(struct puffs_framebuf *);
+size_t puffs_framebuf_tellsize(struct puffs_framebuf *);
+size_t puffs_framebuf_remaining(struct puffs_framebuf *);
+int    puffs_framebuf_seekset(struct puffs_framebuf *, size_t);
+int    puffs_framebuf_getwindow(struct puffs_framebuf *, size_t,
+                                void **, size_t *);
+
+int    puffs_framev_enqueue_cc(struct puffs_cc *, int,
+                               struct puffs_framebuf *, int);
+int    puffs_framev_enqueue_cb(struct puffs_usermount *, int,
+                               struct puffs_framebuf *,
+                               puffs_framev_cb, void *, int);
+int    puffs_framev_enqueue_justsend(struct puffs_usermount *, int,
+                                     struct puffs_framebuf *, int, int);
+int    puffs_framev_enqueue_directreceive(struct puffs_cc *, int,
+                                          struct puffs_framebuf *, int);
+int    puffs_framev_enqueue_directsend(struct puffs_cc *, int,
+                                          struct puffs_framebuf *, int);
+int    puffs_framev_enqueue_waitevent(struct puffs_cc *, int, int *);
+int    puffs_framev_framebuf_ccpromote(struct puffs_framebuf *,
+                                       struct puffs_cc *);
+
+int    puffs_framev_addfd(struct puffs_usermount *, int, int);
+int    puffs_framev_enablefd(struct puffs_usermount *, int, int);
+int    puffs_framev_disablefd(struct puffs_usermount *, int, int);
+int    puffs_framev_removefd(struct puffs_usermount *, int, int);
+void   puffs_framev_removeonclose(struct puffs_usermount *, int, int);
+void   puffs_framev_unmountonclose(struct puffs_usermount *, int, int);
+
+__END_DECLS
+
+#endif /* _PUFFS_H_ */
diff --git a/lib/libpuffs/puffs_cc.3 b/lib/libpuffs/puffs_cc.3
new file mode 100644 (file)
index 0000000..dd3d7ff
--- /dev/null
@@ -0,0 +1,93 @@
+.\"    $NetBSD: puffs_cc.3,v 1.12.4.1 2009/02/24 03:45:56 snj 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 January 28, 2008
+.Dt PUFFS_CC 3
+.Os
+.Sh NAME
+.Nm puffs_cc
+.Nd puffs continuation routines
+.Sh LIBRARY
+.Lb libpuffs
+.Sh SYNOPSIS
+.In puffs.h
+.Ft void
+.Fn puffs_cc_yield "struct puffs_cc *pcc"
+.Ft void
+.Fn puffs_cc_continue "struct puffs_cc *pcc"
+.Ft void
+.Fn puffs_cc_schedule "struct puffs_cc *pcc"
+.Ft struct puffs_cc *
+.Fn puffs_cc_getcc "struct puffs_usermount *pu"
+.Sh DESCRIPTION
+These routines are used for the cooperative multitasking suite present
+in puffs.
+.Pp
+.Bl -tag -width xxxx
+.It Fn puffs_cc_yield "pcc"
+Suspend and save the current execution context and return control
+to the previous point.
+In practice, from the file system author perspective, control returns
+back to where either the mainloop or where
+.Fn puffs_dispatch_exec
+was called from.
+.It Fn puffs_cc_continue pcc
+Will suspend current execution and return control to where it was
+before before calling
+.Fn puffs_cc_yield .
+This is rarely called directly but rather through
+.Fn puffs_dispatch_exec .
+.It Fn puffs_cc_schedule "pcc"
+Schedule a continuation.
+As opposed to
+.Fn puffs_cc_continue
+this call returns immediately.
+.Fa pcc
+will be scheduled sometime in the future.
+.It Fn puffs_cc_getcc "pu"
+Returns the current pcc or
+.Dv NULL
+if this is the main thread.
+.Em NOTE :
+The argument
+.Ar pu
+will most likely disappear at some point.
+.El
+.Pp
+Before calling
+.Fn puffs_cc_yield
+a file system will typically want to record some cookie value into its
+own internal bookkeeping.
+This cookie should be hooked to the
+.Va pcc
+so that the correct continuation can be continued when the event it
+was waiting for triggers.  Alternatively, the
+.Xr puffs_framebuf 3
+framework and
+.Fn puffs_mainloop
+can be used for handling this automatically.
+.Sh SEE ALSO
+.Xr puffs 3 ,
+.Xr puffs_framebuf 3
diff --git a/lib/libpuffs/puffs_cred.3 b/lib/libpuffs/puffs_cred.3
new file mode 100644 (file)
index 0000000..e4264e4
--- /dev/null
@@ -0,0 +1,167 @@
+.\"    $NetBSD: puffs_cred.3,v 1.3.12.1 2009/02/24 03:45:56 snj 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 October 18, 2007
+.Dt PUFFS_CRED 3
+.Os
+.Sh NAME
+.Nm puffs_cred
+.Nd puffs credential and access control routines
+.Sh LIBRARY
+.Lb libpuffs
+.Sh SYNOPSIS
+.In puffs.h
+.Ft int
+.Fn puffs_cred_getuid "const struct puffs_cred *pcr" "uid_t *uid
+.Ft int
+.Fn puffs_cred_getgid "const struct puffs_cred *pcr" "gid_t *gid"
+.Ft int
+.Fo puffs_cred_getgroups
+.Fa "const struct puffs_cred *pcr" "gid_t *gids" "short *ngids"
+.Fc
+.Pp
+.Ft bool
+.Fn puffs_cred_isuid "const struct puffs_cred *pcr" "uid_t uid"
+.Ft bool
+.Fn puffs_cred_hasgroup "const struct puffs_cred *pcr" "gid_t gid"
+.Ft bool
+.Fn puffs_cred_iskernel "const struct puffs_cred *pcr"
+.Ft bool
+.Fn puffs_cred_isfs "const struct puffs_cred *pcr"
+.Ft bool
+.Fn puffs_cred_isjuggernaut "const struct puffs_cred *pcr"
+.Pp
+.Ft int
+.Fo puffs_access
+.Fa "enum vtype type" "mode_t file_mode" "uid_t owner" "gid_t group"
+.Fa "mode_t access_mode" "const struct puffs_cred *pcr"
+.Fc
+.Ft int
+.Fo puffs_access_chown
+.Fa "uid_t owner" "gid_t group" "uid_t newowner" "gid_t newgroup"
+.Fa "const struct puffs_cred *pcr"
+.Fc
+.Ft int
+.Fo puffs_access_chmod
+.Fa "uid_t owner" "gid_t group" "enum vtype type" "mode_t newmode"
+.Fa "const struct puffs_cred *pcr"
+.Fc
+.Ft int
+.Fo puffs_access_times
+.Fa "uid_t owner" "gid_t group" "mode_t file_mode" "int va_utimes_null"
+.Fa "const struct puffs_cred *pcr"
+.Fc
+.Sh DESCRIPTION
+These functions can be used to check operation credentials and perform
+access control.
+The structure
+.Vt struct puffs_cred
+can contain two types of credentials: ones belonging to users and
+ones belonging to the kernel.
+The latter is further divided into generic kernel credentials and
+file system credentials.
+The general rule is that these should be treated as more powerful
+than superuser credentials, but the file system is free to treat
+them as it sees fit.
+.Ss Credentials
+The
+.Fn puffs_cred_get
+family of functions fetch the uid or gid(s) from the given credential
+cookie.
+They return 0 for success or \-1 for an error and set
+.Va errno .
+An error happens when the credentials represent kernel or file system
+credentials and do not contain an uid or gid(s).
+.Pp
+For
+.Fn puffs_cred_getgroups ,
+the argument
+.Fa gids
+should point to an array with room for
+.Fa *ngids
+elements.
+.Pp
+The
+.Fn puffs_cred_is
+family of functions return 1 if the truth value of the function for
+.Fa pcr
+is true and 0 if it is false.
+The function
+.Fn puffs_cred_isjuggernaut
+is true if
+.Fa pcr
+describes superuser, kernel or file system credentials.
+.Ss Access Control
+To help the programmers task of emulating normal kernel file system
+access control semantics, several helper functions are provided to
+check if access is allowed.
+They return 0 if access should be permitted or an errno value to
+indicate that access should be denied with the returned value.
+.Pp
+.Fn puffs_access
+is equivalent to the kernel
+.Fn vaccess
+function.
+The arguments specify current information of the file to be
+tested with the exception of
+.Fa access_mode ,
+which is a combination of
+.Dv PUFFS_VREAD ,
+.Dv PUFFS_VWRITE
+and
+.Dv PUFFS_VEXEC
+indicating the desired file access mode.
+.Pp
+The rest of the functions provide UFS semantics for operations.
+.Fn puffs_access_chown
+checks if it is permissible to chown a file with the current uid
+and gid to the new uid and gid with the credentials of
+.Fa pcr .
+.Pp
+.Fn puffs_access_chmod
+checks against permission to chmod a file of type
+.Fa type
+to the mode
+.Fa newmode .
+.Pp
+Finally,
+.Fn puffs_access_times
+checks if it is allowable to update the timestamps of a file.
+The argument
+.Fa va_utimes_null
+signals if the flags
+.Dv VA_UTIMES_NULL
+was set in
+.Fa va_vaflags
+of
+.Va struct vattr .
+If coming from a path where this information is unavailable, passing
+0 as this argument restricts the permission of modification to the
+owner and superuser.
+Otherwise the function checks for write permissions to the node and
+returns success if the caller has write permissions.
+.Sh SEE ALSO
+.Xr puffs 3 ,
+.Xr vnode 9
diff --git a/lib/libpuffs/puffs_msgif.h b/lib/libpuffs/puffs_msgif.h
new file mode 100644 (file)
index 0000000..092e1dd
--- /dev/null
@@ -0,0 +1,261 @@
+/*     $NetBSD: puffs_msgif.h,v 1.65.20.2 2009/12/14 19:36:57 sborrill 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 _FS_PUFFS_PUFFS_MSGIF_H_
+#define _FS_PUFFS_PUFFS_MSGIF_H_
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/uio.h>
+#include <sys/statvfs.h>
+#include <sys/ucred.h>
+
+#include <time.h>
+
+
+#define PUFFSOP_VFS            0x01    /* kernel-> */
+#define PUFFSOP_VN             0x02    /* kernel-> */
+#define PUFFSOP_CACHE          0x03    /* only kernel-> */
+#define PUFFSOP_ERROR          0x04    /* only kernel-> */
+#define PUFFSOP_FLUSH          0x05    /* ->kernel */
+#define PUFFSOP_SUSPEND                0x06    /* ->kernel */
+
+#define PUFFSOPFLAG_FAF                0x10    /* fire-and-forget */
+#define PUFFSOPFLAG_ISRESPONSE 0x20    /* req is actually a resp */
+
+#define PUFFSOP_OPCMASK                0x07
+#define PUFFSOP_OPCLASS(a)     ((a) & PUFFSOP_OPCMASK)
+#define PUFFSOP_WANTREPLY(a)   (((a) & PUFFSOPFLAG_FAF) == 0)
+
+/* XXX: we don't need everything */
+enum {
+       PUFFS_VFS_MOUNT,        PUFFS_VFS_START,        PUFFS_VFS_UNMOUNT,
+       PUFFS_VFS_ROOT,         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_EXTATTCTL,    PUFFS_VFS_SUSPEND
+};
+#define PUFFS_VFS_MAX PUFFS_VFS_EXTATTCTL
+
+/* moreXXX: we don't need everything here either */
+enum {
+       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
+};
+#define PUFFS_VN_MAX PUFFS_VN_SETEXTATTR
+
+/*
+ * These signal invalid parameters the file system returned.
+ */
+enum {
+       PUFFS_ERR_MAKENODE,     PUFFS_ERR_LOOKUP,       PUFFS_ERR_READDIR,
+       PUFFS_ERR_READLINK,     PUFFS_ERR_READ,         PUFFS_ERR_WRITE,
+       PUFFS_ERR_VPTOFH,       PUFFS_ERR_ERROR
+};
+#define PUFFS_ERR_MAX PUFFS_ERR_VPTOFH
+
+#define PUFFSDEVELVERS 0x80000000
+#define PUFFSVERSION   26
+#define PUFFSNAMESIZE  32
+
+#define PUFFS_TYPEPREFIX "puffs|"
+
+#if 0
+#define PUFFS_TYPELEN (_VFS_NAMELEN - (sizeof(PUFFS_TYPEPREFIX)+1))
+#define PUFFS_NAMELEN (_VFS_MNAMELEN-1)
+#endif
+
+/* 
+ * Just a weak typedef for code clarity.  Additionally, we have a
+ * more appropriate vanity type for puffs:
+ * <uep> it should be croissant, not cookie.
+ */
+typedef void *puffs_cookie_t;
+typedef puffs_cookie_t puffs_croissant_t;
+
+/* FIXME: move? */
+typedef off_t voff_t;
+
+/* other netbsd stuff
+ * FIXME: move to some other place?
+ */
+typedef int32_t lwpid_t;        /* LWP id */
+typedef unsigned long vsize_t;
+typedef int vm_prot_t;
+typedef uint64_t u_quad_t;       /* quads */
+
+
+/* FIXME: from sys/vnode.h, which is commented. */
+/*
+ * Vnode attributes.  A field value of VNOVAL represents a field whose value
+ * is unavailable (getattr) or which is not to be changed (setattr).
+ */
+enum vtype      { VNON, VREG, VDIR, VBLK, VCHR, VLNK, VSOCK, VFIFO, VBAD };
+struct vattr {
+        enum vtype      va_type;        /* vnode type (for create) */
+        mode_t          va_mode;        /* files access mode and type */
+        nlink_t         va_nlink;       /* number of references to file */
+        uid_t           va_uid;         /* owner user id */
+        gid_t           va_gid;         /* owner group id */
+        long            va_fsid;        /* file system id (dev for now) */
+        ino_t           va_fileid;      /* file id */
+        u_quad_t        va_size;        /* file size in bytes */
+        long            va_blocksize;   /* blocksize preferred for i/o */
+        struct timespec va_atime;       /* time of last access */
+        struct timespec va_mtime;       /* time of last modification */
+        struct timespec va_ctime;       /* time file changed */
+        struct timespec va_birthtime;   /* time file created */
+        u_long          va_gen;         /* generation number of file */
+        u_long          va_flags;       /* flags defined for file */
+        dev_t           va_rdev;        /* device the special file represents */
+        u_quad_t        va_bytes;       /* bytes of disk space held by file */
+        u_quad_t        va_filerev;     /* file modification number */
+        u_int           va_vaflags;     /* operations flags, see below */
+        long            va_spare;       /* remain quad aligned */
+};
+
+struct puffs_kargs {
+        unsigned int    pa_vers;
+        int             pa_fd;
+
+        uint32_t        pa_flags;
+
+        size_t          pa_maxmsglen;
+        int             pa_nhashbuckets;
+
+        size_t          pa_fhsize;
+        int             pa_fhflags;
+
+        puffs_cookie_t  pa_root_cookie;
+        enum vtype      pa_root_vtype;
+        voff_t          pa_root_vsize;
+        dev_t           pa_root_rdev;
+
+        struct statvfs  pa_svfsb;
+
+        char            pa_typename[NAME_MAX + 1];
+        char            pa_mntfromname[NAME_MAX + 1];
+
+        uint8_t         pa_vnopmask[PUFFS_VN_MAX];
+};
+
+
+
+#define PUFFS_KFLAG_NOCACHE_NAME       0x01    /* don't use name cache     */
+#define PUFFS_KFLAG_NOCACHE_PAGE       0x02    /* don't use page cache     */
+#define PUFFS_KFLAG_NOCACHE            0x03    /* no cache whatsoever      */
+#define PUFFS_KFLAG_ALLOPS             0x04    /* ignore pa_vnopmask       */
+#define PUFFS_KFLAG_WTCACHE            0x08    /* write-through page cache */
+#define PUFFS_KFLAG_IAONDEMAND         0x10    /* inactive only on demand  */
+#define PUFFS_KFLAG_LOOKUP_FULLPNBUF   0x20    /* full pnbuf in lookup     */
+#define PUFFS_KFLAG_MASK               0x3f
+
+#define PUFFS_FHFLAG_DYNAMIC           0x01
+#define PUFFS_FHFLAG_NFSV2             0x02
+#define PUFFS_FHFLAG_NFSV3             0x04
+#define PUFFS_FHFLAG_PROTOMASK         0x06
+#define PUFFS_FHFLAG_PASSTHROUGH       0x08
+#define PUFFS_FHFLAG_MASK              0x0f
+
+#define PUFFS_FHSIZE_MAX       1020    /* XXX: FHANDLE_SIZE_MAX - 4 */
+
+#define PUFFS_SETBACK_INACT_N1 0x01    /* set VOP_INACTIVE for node 1 */
+#define PUFFS_SETBACK_INACT_N2 0x02    /* set VOP_INACTIVE for node 2 */
+#define PUFFS_SETBACK_NOREF_N1 0x04    /* set pn PN_NOREFS for node 1 */
+#define PUFFS_SETBACK_NOREF_N2 0x08    /* set pn PN_NOREFS for node 2 */
+#define PUFFS_SETBACK_MASK     0x0f
+
+#define PUFFS_INVAL_NAMECACHE_NODE             0
+#define PUFFS_INVAL_NAMECACHE_DIR              1
+#define PUFFS_INVAL_NAMECACHE_ALL              2
+#define PUFFS_INVAL_PAGECACHE_NODE_RANGE       3
+#define PUFFS_FLUSH_PAGECACHE_NODE_RANGE       4
+
+/* keep this for now */
+#define PUFFSREQSIZEOP         _IOR ('p', 1, size_t)
+
+#define PUFFCRED_TYPE_UUC      1
+#define PUFFCRED_TYPE_INTERNAL 2
+#define PUFFCRED_CRED_NOCRED   1
+#define PUFFCRED_CRED_FSCRED   2
+
+/*
+ * 2*MAXPHYS is the max size the system will attempt to copy,
+ * else treated as garbage
+ */
+#define PUFFS_MSG_MAXSIZE      2*MAXPHYS
+#define PUFFS_MSGSTRUCT_MAX    4096 /* XXX: approxkludge */
+
+#define PUFFS_TOMOVE(a,b) (MIN((a), b->pmp_msg_maxsize - PUFFS_MSGSTRUCT_MAX))
+
+/* puffs struct componentname built by kernel */
+struct puffs_kcn {
+        /* args */
+        uint32_t                pkcn_nameiop;   /* namei operation      */
+        uint32_t                pkcn_flags;     /* flags                */
+
+        char pkcn_name[MAXPATHLEN];     /* nulterminated path component */
+        size_t pkcn_namelen;            /* current component length     */
+        size_t pkcn_consume;            /* IN: extra chars server ate   */
+};
+
+/*
+ * Credentials for an operation.  Can be either struct uucred for
+ * ops called from a credential context or NOCRED/FSCRED for ops
+ * called from within the kernel.  It is up to the implementation
+ * if it makes a difference between these two and the super-user.
+ */
+struct puffs_kcred {
+       struct uucred   pkcr_uuc;
+       uint8_t         pkcr_type;
+       uint8_t         pkcr_internal;
+};
+#define PUFFCRED_TYPE_UUC      1
+#define PUFFCRED_TYPE_INTERNAL 2
+#define PUFFCRED_CRED_NOCRED   1
+#define PUFFCRED_CRED_FSCRED   2
+
+
+#endif /* _FS_PUFFS_PUFFS_MSGIF_H_ */
diff --git a/lib/libpuffs/puffs_node.3 b/lib/libpuffs/puffs_node.3
new file mode 100644 (file)
index 0000000..783e82f
--- /dev/null
@@ -0,0 +1,102 @@
+.\"    $NetBSD: puffs_node.3,v 1.3.12.1 2009/02/24 03:45:56 snj 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 June 24, 2007
+.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 .
+.Pp
+.El
+.Sh SEE ALSO
+.Xr puffs 3
diff --git a/lib/libpuffs/puffs_ops.3 b/lib/libpuffs/puffs_ops.3
new file mode 100644 (file)
index 0000000..43ceecc
--- /dev/null
@@ -0,0 +1,763 @@
+.\"    $NetBSD: puffs_ops.3,v 1.21.4.2 2009/04/12 02:24:16 snj 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 December 16, 2007
+.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_cooie_t cookie" "void *fid"
+.Fa "size_t *fidsize"
+.Fc
+.Ft void
+.Fn puffs_fs_suspend "struct puffs_usermount *pu" "int status"
+.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 "const struct puffs_cred *pcr" "int flags" "off_t offlo" "off_t offhi"
+.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
+.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"
+.Ft void
+.Fn puffs_newinfo_setrdev "struct puffs_newinfo *pni" "dev_t rdev"
+.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_suspend "pu" "status"
+Called when file system suspension reaches various phases.
+See
+.Xr puffs_suspend 3
+for more information.
+.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.
+If the located entry is a block device or character device file,
+the dev_t for the entry should be set using
+.Fn puffs_newinfo_setrdev .
+.Pp
+The type of operation is found from
+.Va pcn-\*[Gt]pcn_nameiop :
+.Bl -tag -width XPUFFSLOOKUP_LOOKUPX
+.It Dv PUFFSLOOKUP_LOOKUP
+Normal lookup operation.
+.It Dv PUFFSLOOKUP_CREATE
+Lookup to create a node.
+.It Dv PUFFSLOOKUP_DELETE
+Lookup for node deletion.
+.It Dv PUFFSLOOKUP_RENAME
+Lookup for the target of a rename operation (source will be looked
+up using
+.Dv PUFFSLOOKUP_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 PUFFSLOOKUP_CREATE
+or
+.Dv PUFFSLOOKUP_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_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 .
+.It Fn puffs_newinfo_setrdev pni rdev
+Set the type of the newly located node to
+.Ar vtype .
+This call is valid only for
+.Fn lookup
+and
+.Fn fhtovp
+producing device type nodes.
+.El
+.Sh SEE ALSO
+.Xr puffs 3 ,
+.Xr vfsops 9 ,
+.Xr vnodeops 9
diff --git a/lib/libpuffs/puffs_path.3 b/lib/libpuffs/puffs_path.3
new file mode 100644 (file)
index 0000000..04c1981
--- /dev/null
@@ -0,0 +1,125 @@
+.\"    $NetBSD: puffs_path.3,v 1.3.10.1 2009/02/24 03:45:56 snj 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 December 27, 2007
+.Dt PUFFS_PATH 3
+.Os
+.Sh NAME
+.Nm puffs_path
+.Nd puffs pathbuilding routines
+.Sh LIBRARY
+.Lb libpuffs
+.Sh SYNOPSIS
+.In puffs.h
+.Ft int
+.Fo pu_pathbuild_fn
+.Fa "struct puffs_usermount *pu" "const struct puffs_pathobj *po_dir"
+.Fa "const struct puffs_pathobj *po_comp" "size_t offset"
+.Fa "struct puffs_pathobj *po_new"
+.Fc
+.Ft int
+.Fo pu_pathtransform_fn
+.Fa "struct puffs_usermount *pu" "const struct puffs_pathobj *po_base"
+.Fa "const struct puffs_cn *pcn" "struct puffs_pathobj *po_new"
+.Fc
+.Ft int
+.Fo pu_pathcmp_fn
+.Fa "struct puffs_usermount *pu" "struct puffs_pathobj *po1"
+.Fa "struct puffs_pathobj *po2" "size_t checklen" "int checkprefix"
+.Fc
+.Ft void
+.Fn pu_pathfree_fn "struct puffs_usermount *pu" "struct puffs_pathobj *po"
+.Ft int
+.Fo pu_namemod_fn
+.Fa "struct puffs_usermount *pu" "struct puffs_pathobj *po_dir"
+.Fa "struct puffs_cn *pcn"
+.Fc
+.Ft struct puffs_pathobj *
+.Fn puffs_getrootpathobj "struct puffs_usermount *pu"
+.Sh DESCRIPTION
+The puffs library has the ability to provide full pathnames for backends
+which require them.
+Normal file systems should be constructed without the file system
+node tied to a file name and should not used routines described herein.
+An example of a file system where the backend requires filenames is
+.Xr mount_psshfs 8 .
+.Pp
+The features described here are enabled by passing
+.Dv PUFFS_FLAG_BUILDPATH
+to
+.Fn puffs_init .
+This facility requires to use puffs nodes to store the contents of the
+pathname.
+Either the address of the operation cookie must directly be that of the
+puffs node, or
+.Fn puffs_set_cmap
+must be used to set a mapping function from the cookie to the puffs
+node associated with the cookie.
+Finally, the root node for the file system must be set using
+.Fn puffs_setroot
+and the root path object retrieved using
+.Fn puffs_getrootpathobj
+and initialized.
+.Pp
+There are two different places a filename can be retrieved from.
+It is available for each puffs node after the node has been registered
+with the framework, i.e.
+.Em after
+the routine creating the node returns.
+In other words, there is a window between the node is created and
+when the pathname is available and multithreaded file systems must
+take this into account.
+The second place where a pathname is available is from the componentname
+.Vt struct puffs_pcn
+in operations which are passed one.
+These can be retrieved using the convenience macros
+.Fn PNPATH
+and
+.Fn PCNPATH
+for node and componentname, respectively.
+The type of object they return is
+.Vt void * .
+.Pp
+By default the framework manages "regular" filenames, which consist
+of directory names separated by "/" and a final component.
+If the file system wishes to use pathnames of this format, all it
+has to do it enable the feature.
+Everything else, including bookkeeping for node and directory renames,
+is done by the library.
+The callback routines described next provide the ability to build
+non-standard pathnames.
+A
+.Fn pu_foo_fn
+callback is set using the
+.Fn puffs_set_foo
+routine.
+.Pp
+This manual page is still unfinished.
+Please take a number and wait in line.
+.Sh SEE ALSO
+.Xr puffs 3 ,
+.Xr puffs_node 3 ,
+.Xr mount_psshfs 8 ,
+.Xr mount_sysctlfs 8
diff --git a/lib/libpuffs/puffs_priv.h b/lib/libpuffs/puffs_priv.h
new file mode 100644 (file)
index 0000000..b168bf8
--- /dev/null
@@ -0,0 +1,276 @@
+/*     $NetBSD: puffs_priv.h,v 1.41 2008/08/11 16:23:37 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 ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _PUFFS_PRIVATE_H_
+#define _PUFFS_PRIVATE_H_
+
+#include <sys/types.h>
+#include <ucontext.h>
+
+/* FIXME: fs/puffs/puffs_msgif.h? */
+#include "puffs_msgif.h"
+#include "puffs.h"
+
+/* XXX: MINIX */
+#define IGN_PERM            0
+#define CHK_PERM            1
+#define SU_UID          ((uid_t) 0)     /* super_user's uid_t */
+
+/* XXX: MINIX */
+#define ATIME            002    /* set if atime field needs updating */
+#define CTIME            004    /* set if ctime field needs updating */
+#define MTIME            010    /* set if mtime field needs updating */
+
+#define REQ_READ_SUPER   28
+
+#ifdef PUFFS_WITH_THREADS
+#include <pthread.h>
+extern pthread_mutex_t pu_lock;
+#define PU_LOCK() pthread_mutex_lock(&pu_lock)
+#define PU_UNLOCK() pthread_mutex_unlock(&pu_lock)
+#else
+#define PU_LOCK()
+#define PU_UNLOCK()
+#endif
+
+#define PU_CMAP(pu, c) (pu->pu_cmap ? pu->pu_cmap(pu,c) : (struct puffs_node*)c)
+
+struct puffs_framectrl {
+       puffs_framev_readframe_fn rfb;
+       puffs_framev_writeframe_fn wfb;
+       puffs_framev_cmpframe_fn cmpfb;
+       puffs_framev_gotframe_fn gotfb;
+       puffs_framev_fdnotify_fn fdnotfn;
+};
+
+struct puffs_fctrl_io {
+       struct puffs_framectrl *fctrl;
+
+       int io_fd;
+       int stat;
+
+       int rwait;
+       int wwait;
+
+       struct puffs_framebuf *cur_in;
+
+       TAILQ_HEAD(, puffs_framebuf) snd_qing;  /* queueing to be sent */
+       TAILQ_HEAD(, puffs_framebuf) res_qing;  /* q'ing for rescue */
+       LIST_HEAD(, puffs_fbevent) ev_qing;     /* q'ing for events */
+
+       LIST_ENTRY(puffs_fctrl_io) fio_entries;
+};
+#define FIO_WR         0x01
+#define FIO_WRGONE     0x02
+#define FIO_RDGONE     0x04
+#define FIO_DEAD       0x08
+#define FIO_ENABLE_R   0x10
+#define FIO_ENABLE_W   0x20
+
+#define FIO_EN_WRITE(fio)                              \
+    (!(fio->stat & FIO_WR)                             \
+      && ((!TAILQ_EMPTY(&fio->snd_qing)                        \
+            && (fio->stat & FIO_ENABLE_W))             \
+         || fio->wwait))
+
+#define FIO_RM_WRITE(fio)                      \
+    ((fio->stat & FIO_WR)                      \
+      && (((TAILQ_EMPTY(&fio->snd_qing)                \
+        || (fio->stat & FIO_ENABLE_W) == 0))   \
+       && (fio->wwait == 0)))
+
+
+/*
+ * usermount: describes one file system instance
+ */
+struct puffs_usermount {
+       struct puffs_ops        pu_ops;
+
+       int                     pu_fd;
+       size_t                  pu_maxreqlen;
+
+       uint32_t                pu_flags;
+       int                     pu_cc_stackshift;
+
+       ucontext_t              pu_mainctx;
+#define PUFFS_CCMAXSTORE 32
+       int                     pu_cc_nstored;
+
+       int                     pu_kq;
+       int                     pu_state;
+#define PU_STATEMASK   0x00ff
+#define PU_INLOOP      0x0100
+#define PU_ASYNCFD     0x0200
+#define PU_HASKQ       0x0400
+#define PU_PUFFSDAEMON 0x0800
+#define PU_MAINRESTORE 0x1000
+#define PU_SETSTATE(pu, s) (pu->pu_state = (s) | (pu->pu_state & ~PU_STATEMASK))
+#define PU_SETSFLAG(pu, s) (pu->pu_state |= (s))
+#define PU_CLRSFLAG(pu, s) \
+    (pu->pu_state = ((pu->pu_state &= ~(s)) | (pu->pu_state & PU_STATEMASK)))
+       int                     pu_dpipe[2];
+
+       struct puffs_node       *pu_pn_root;
+
+       LIST_HEAD(, puffs_node) pu_pnodelst;
+       LIST_HEAD(, puffs_node) pu_pnode_removed_lst;
+
+       LIST_HEAD(, puffs_cc)   pu_ccmagazin;
+       TAILQ_HEAD(, puffs_cc)  pu_lazyctx;
+       TAILQ_HEAD(, puffs_cc)  pu_sched;
+
+       pu_cmap_fn              pu_cmap;
+
+       pu_pathbuild_fn         pu_pathbuild;
+       pu_pathtransform_fn     pu_pathtransform;
+       pu_pathcmp_fn           pu_pathcmp;
+       pu_pathfree_fn          pu_pathfree;
+       pu_namemod_fn           pu_namemod;
+
+       pu_errnotify_fn         pu_errnotify;
+
+       pu_prepost_fn           pu_oppre;
+       pu_prepost_fn           pu_oppost;
+
+       struct puffs_framectrl  pu_framectrl[2];
+#define PU_FRAMECTRL_FS   0
+#define PU_FRAMECTRL_USER 1
+       LIST_HEAD(, puffs_fctrl_io) pu_ios;
+       LIST_HEAD(, puffs_fctrl_io) pu_ios_rmlist;
+       struct kevent           *pu_evs;
+       size_t                  pu_nfds;
+
+       puffs_ml_loop_fn        pu_ml_lfn;
+       struct timespec         pu_ml_timeout;
+       struct timespec         *pu_ml_timep;
+
+       struct puffs_kargs      *pu_kargp;
+
+       uint64_t                pu_nextreq;
+       void                    *pu_privdata;
+};
+
+/* call context */
+
+struct puffs_cc;
+typedef void (*puffs_ccfunc)(struct puffs_cc *);
+
+struct puffs_cc {
+       struct puffs_usermount  *pcc_pu;
+       struct puffs_framebuf   *pcc_pb;
+
+       /* real cc */
+       union {
+               struct {
+                       ucontext_t      uc;             /* "continue"   */
+                       ucontext_t      uc_ret;         /* "yield"      */
+               } real;
+               struct {
+                       puffs_ccfunc    func;
+                       void            *farg;
+               } fake;
+       } pcc_u;
+
+       pid_t                   pcc_pid;
+       lwpid_t                 pcc_lid;
+
+       int                     pcc_flags;
+
+       TAILQ_ENTRY(puffs_cc)   pcc_schedent;
+       LIST_ENTRY(puffs_cc)    pcc_rope;
+};
+#define pcc_uc         pcc_u.real.uc
+#define pcc_uc_ret     pcc_u.real.uc_ret
+#define pcc_func       pcc_u.fake.func
+#define pcc_farg       pcc_u.fake.farg
+#define PCC_DONE       0x01
+#define PCC_BORROWED   0x02
+#define PCC_HASCALLER  0x04
+#define PCC_MLCONT     0x08
+
+struct puffs_newinfo {
+       void            **pni_cookie;
+       enum vtype      *pni_vtype;
+       voff_t          *pni_size;
+       dev_t           *pni_rdev;
+};
+
+#define PUFFS_MAKEKCRED(to, from)                                      \
+       /*LINTED: tnilxnaht, the cast is ok */                          \
+       const struct puffs_kcred *to = (const void *)from
+#define PUFFS_MAKECRED(to, from)                                       \
+       /*LINTED: tnilxnaht, the cast is ok */                          \
+       const struct puffs_cred *to = (const void *)from
+#define PUFFS_KCREDTOCRED(to, from)                                    \
+       /*LINTED: tnilxnaht, the cast is ok */                          \
+       to = (void *)from
+
+__BEGIN_DECLS
+
+void   puffs__framev_input(struct puffs_usermount *, struct puffs_framectrl *,
+                          struct puffs_fctrl_io *);
+int    puffs__framev_output(struct puffs_usermount *, struct puffs_framectrl*,
+                           struct puffs_fctrl_io *);
+void   puffs__framev_exit(struct puffs_usermount *);
+void   puffs__framev_readclose(struct puffs_usermount *,
+                              struct puffs_fctrl_io *, int);
+void   puffs__framev_writeclose(struct puffs_usermount *,
+                               struct puffs_fctrl_io *, int);
+void   puffs__framev_notify(struct puffs_fctrl_io *, int);
+void   *puffs__framebuf_getdataptr(struct puffs_framebuf *);
+int    puffs__framev_addfd_ctrl(struct puffs_usermount *, int, int,
+                                struct puffs_framectrl *);
+void   puffs__framebuf_moveinfo(struct puffs_framebuf *,
+                                struct puffs_framebuf *);
+
+void   puffs__theloop(struct puffs_cc *);
+void   puffs__ml_dispatch(struct puffs_usermount *, struct puffs_framebuf *);
+
+int    puffs__cc_create(struct puffs_usermount *, puffs_ccfunc,
+                        struct puffs_cc **);
+void   puffs__cc_cont(struct puffs_cc *);
+void   puffs__cc_destroy(struct puffs_cc *, int);
+void   puffs__cc_setcaller(struct puffs_cc *, pid_t, lwpid_t);
+void   puffs__goto(struct puffs_cc *);
+int    puffs__cc_savemain(struct puffs_usermount *);
+int    puffs__cc_restoremain(struct puffs_usermount *);
+void   puffs__cc_exit(struct puffs_usermount *);
+
+int    puffs__fsframe_read(struct puffs_usermount *, struct puffs_framebuf *,
+                           int, int *);
+int    puffs__fsframe_write(struct puffs_usermount *, struct puffs_framebuf *,
+                           int, int *);
+int    puffs__fsframe_cmp(struct puffs_usermount *, struct puffs_framebuf *,
+                          struct puffs_framebuf *, int *);
+void   puffs__fsframe_gotframe(struct puffs_usermount *,
+                               struct puffs_framebuf *);
+
+__END_DECLS
+
+#define NUL(str,l,m) mfs_nul_f(__FILE__,__LINE__,(str), (l), (m))
+
+#endif /* _PUFFS_PRIVATE_H_ */
diff --git a/lib/libpuffs/read.c b/lib/libpuffs/read.c
new file mode 100644 (file)
index 0000000..3e260b1
--- /dev/null
@@ -0,0 +1,178 @@
+/* Created (MFS based):
+ *   February 2010 (Evgeniy Ivanov)
+ */
+
+#include "fs.h"
+#include <stddef.h>
+#include <string.h>
+#include <stdlib.h>
+#include <dirent.h>
+#include <minix/com.h>
+#include <minix/u64.h>
+#include <minix/vfsif.h>
+#include <assert.h>
+#include <sys/param.h>
+
+#include "puffs.h"
+#include "puffs_priv.h"
+
+
+#define GETDENTS_BUFSIZ  4096
+PRIVATE char getdents_buf[GETDENTS_BUFSIZ];
+
+#define RW_BUFSIZ      (128 << 10)
+PRIVATE char rw_buf[RW_BUFSIZ];
+
+
+/*===========================================================================*
+ *                             fs_readwrite                                 *
+ *===========================================================================*/
+PUBLIC int fs_readwrite(void)
+{
+  int r = OK, rw_flag;
+  cp_grant_id_t gid;
+  off_t pos;
+  size_t nrbytes, bytes_left, bytes_done;
+  struct puffs_node *pn;
+  struct vattr va;
+  PUFFS_MAKECRED(pcr, &global_kcred);
+
+  if ((pn = puffs_pn_nodewalk(global_pu, 0, &fs_m_in.REQ_INODE_NR)) == NULL) {
+       lpuffs_debug("walk failed...\n");
+        return(EINVAL);
+  }
+
+  /* Get the values from the request message */
+  rw_flag = (fs_m_in.m_type == REQ_READ ? READING : WRITING);
+  gid = (cp_grant_id_t) fs_m_in.REQ_GRANT;
+  pos = (off_t) fs_m_in.REQ_SEEK_POS_LO;
+  nrbytes = bytes_left = (size_t) fs_m_in.REQ_NBYTES;
+
+  if (nrbytes > RW_BUFSIZ)
+       nrbytes = bytes_left = RW_BUFSIZ;
+
+  memset(getdents_buf, '\0', RW_BUFSIZ);  /* Avoid leaking any data */
+
+  if (rw_flag == READING) {
+       if (global_pu->pu_ops.puffs_node_read == NULL)
+               return(EINVAL);
+
+       r = global_pu->pu_ops.puffs_node_read(global_pu, pn, (uint8_t *)rw_buf,
+                                               pos, &bytes_left, pcr, 0);
+       if (r) {
+               lpuffs_debug("puffs_node_read failed\n");
+               return(EINVAL);
+       }
+
+       bytes_done = nrbytes - bytes_left;
+       if (bytes_done) {
+               r = sys_safecopyto(VFS_PROC_NR, gid, (vir_bytes) 0,
+                                  (vir_bytes) rw_buf, bytes_done, D);
+               update_times(pn, ATIME, 0);
+       }
+  } else if (rw_flag == WRITING) {
+       /* At first try to change vattr */
+       if (global_pu->pu_ops.puffs_node_setattr == NULL)
+               return(EINVAL);
+
+       puffs_vattr_null(&va);
+       if ( (pos + bytes_left) > pn->pn_va.va_size)
+               va.va_size = bytes_left + pos;
+       va.va_ctime.tv_sec = va.va_mtime.tv_sec = clock_time();
+       va.va_atime.tv_sec = pn->pn_va.va_atime.tv_sec;
+
+       r = global_pu->pu_ops.puffs_node_setattr(global_pu, pn, &va, pcr);
+       if (r) return(EINVAL);
+
+       r = sys_safecopyfrom(VFS_PROC_NR, gid, (vir_bytes) 0,
+                            (vir_bytes) rw_buf, nrbytes, D);
+       if (r != OK) return(EINVAL);
+
+       if (global_pu->pu_ops.puffs_node_write == NULL)
+               return(EINVAL);
+
+       r = global_pu->pu_ops.puffs_node_write(global_pu, pn, (uint8_t *)rw_buf,
+                                               pos, &bytes_left, pcr, 0);
+       bytes_done = nrbytes - bytes_left;
+  }
+
+  if (r != OK) return(EINVAL);
+
+  fs_m_out.RES_SEEK_POS_LO = pos + bytes_done;
+  fs_m_out.RES_NBYTES = bytes_done;
+
+  return(r);
+}
+
+
+/*===========================================================================*
+ *                             fs_breadwrite                                *
+ *===========================================================================*/
+PUBLIC int fs_breadwrite(void)
+{
+  /* We do not support breads/writes */
+  panic("bread write requested, but FS doesn't support it!\n");
+  return(OK);
+}
+
+
+/*===========================================================================*
+ *                             fs_getdents                                  *
+ *===========================================================================*/
+PUBLIC int fs_getdents(void)
+{
+  int r;
+  register struct puffs_node *pn;
+  ino_t ino;
+  cp_grant_id_t gid;
+  size_t size, buf_left;
+  off_t pos;
+  struct dirent *dent;
+  int eofflag = 0;
+  size_t written;
+  PUFFS_MAKECRED(pcr, &global_kcred);
+
+  ino = (ino_t) fs_m_in.REQ_INODE_NR;
+  gid = (gid_t) fs_m_in.REQ_GRANT;
+  size = buf_left = (size_t) fs_m_in.REQ_MEM_SIZE;
+  pos = (off_t) fs_m_in.REQ_SEEK_POS_LO;
+
+  if ((pn = puffs_pn_nodewalk(global_pu, 0, &ino)) == NULL) {
+       lpuffs_debug("walk failed...\n");
+        return(EINVAL);
+  }
+
+  if (GETDENTS_BUFSIZ < size)
+         size = buf_left = GETDENTS_BUFSIZ;
+  memset(getdents_buf, '\0', GETDENTS_BUFSIZ);  /* Avoid leaking any data */
+
+  dent = (struct dirent*) getdents_buf;
+
+  r = global_pu->pu_ops.puffs_node_readdir(global_pu, pn, dent, &pos,
+                                               &buf_left, pcr, &eofflag, 0, 0);
+  if (r) {
+       lpuffs_debug("puffs_node_readdir returned error\n");
+       return(EINVAL);
+  }
+
+  assert(buf_left <= size);
+  written = size - buf_left;
+
+  if (written == 0 && !eofflag) {
+       lpuffs_debug("The user's buffer is too small\n");
+       return(EINVAL);
+  }
+
+  if (written) {
+       r = sys_safecopyto(VFS_PROC_NR, gid, (vir_bytes) 0,
+                            (vir_bytes) getdents_buf, written, D);
+       if (r != OK) return(r);
+  }
+
+  update_times(pn, ATIME, 0);
+
+  fs_m_out.RES_NBYTES = written;
+  fs_m_out.RES_SEEK_POS_LO = pos;
+
+  return(OK);
+}
diff --git a/lib/libpuffs/stadir.c b/lib/libpuffs/stadir.c
new file mode 100644 (file)
index 0000000..ecea24c
--- /dev/null
@@ -0,0 +1,121 @@
+/* Created (MFS based):
+ *   February 2010 (Evgeniy Ivanov)
+ */
+
+#include "fs.h"
+#include <sys/stat.h>
+#include <sys/statfs.h>
+#include <sys/statvfs.h>
+#include <minix/vfsif.h>
+
+#include "puffs.h"
+#include "puffs_priv.h"
+
+
+/*===========================================================================*
+ *                             fs_fstatfs                                   *
+ *===========================================================================*/
+PUBLIC int fs_fstatfs()
+{
+  int r;
+  struct statvfs st_vfs;
+  struct statfs st;
+
+  if (global_pu->pu_ops.puffs_fs_statvfs(global_pu, &st_vfs) != 0) {
+       lpuffs_debug("statfs failed\n");
+       return(EINVAL);
+  }
+
+  st.f_bsize = st_vfs.f_bsize;
+
+  /* Copy the struct to user space. */
+  r = sys_safecopyto(fs_m_in.m_source, (cp_grant_id_t) fs_m_in.REQ_GRANT,
+                    (vir_bytes) 0, (vir_bytes) &st, (size_t) sizeof(st), D);
+
+  return(r);
+}
+
+
+/*===========================================================================*
+ *                             fs_stat                                      *
+ *===========================================================================*/
+PUBLIC int fs_stat()
+{
+  register int r;              /* return value */
+  register struct puffs_node *pn;  /* target pnode */
+  struct vattr va;
+  struct stat statbuf;
+  mode_t mo;
+  int s;
+  PUFFS_MAKECRED(pcr, &global_kcred);
+
+  if (global_pu->pu_ops.puffs_node_getattr == NULL) {
+       lpuffs_debug("fs_stat: puffs_node_getattr is missing\n");
+       return(EINVAL);
+  }
+
+  if ((pn = puffs_pn_nodewalk(global_pu, 0, &fs_m_in.REQ_INODE_NR)) == NULL) {
+       lpuffs_debug("walk failed...\n");
+        return(EINVAL);
+  }
+
+  if (global_pu->pu_ops.puffs_node_getattr(global_pu, pn, &va, pcr) != 0) {
+       if (errno) {
+               if (errno > 0) errno = -errno;
+               return(errno);
+       }
+       return(EINVAL);
+  }
+
+  /* Fill in the statbuf struct. */
+  mo = va.va_mode & I_TYPE;
+
+  /* true iff special */
+  s = (mo == I_CHAR_SPECIAL || mo == I_BLOCK_SPECIAL);
+
+  statbuf.st_dev = fs_dev;
+  statbuf.st_ino = va.va_fileid;
+  statbuf.st_mode = va.va_mode;
+  statbuf.st_nlink = va.va_nlink;
+  statbuf.st_uid = va.va_uid;
+  statbuf.st_gid = va.va_gid;
+  statbuf.st_rdev = (s ? va.va_rdev : NO_DEV);
+  statbuf.st_size = va.va_size;
+  statbuf.st_atime = va.va_atime.tv_sec;
+  statbuf.st_mtime = va.va_mtime.tv_sec;
+  statbuf.st_ctime = va.va_ctime.tv_sec;
+
+  /* Copy the struct to user space. */
+  r = sys_safecopyto(fs_m_in.m_source, (cp_grant_id_t) fs_m_in.REQ_GRANT,
+                    (vir_bytes) 0, (vir_bytes) &statbuf,
+                    (size_t) sizeof(statbuf), D);
+
+  return(r);
+}
+
+
+/*===========================================================================*
+ *                             fs_statvfs                                    *
+ *===========================================================================*/
+PUBLIC int fs_statvfs()
+{
+  int r;
+  struct statvfs st;
+
+  if (global_pu->pu_ops.puffs_fs_statvfs(global_pu, &st) != 0) {
+       lpuffs_debug("statvfs failed\n");
+       return(EINVAL);
+  }
+
+  /* XXX libpuffs doesn't truncate filenames and returns ENAMETOOLONG,
+   * though some servers would like to behave differently.
+   * See subtest 2.18-19 of test23 and test/common.c:does_fs_truncate().
+   */
+  st.f_flag |= ST_NOTRUNC;
+
+  /* Copy the struct to user space. */
+  r = sys_safecopyto(fs_m_in.m_source, fs_m_in.REQ_GRANT, 0, (vir_bytes) &st,
+                    (phys_bytes) sizeof(st), D);
+
+  return(r);
+}
diff --git a/lib/libpuffs/subr.c b/lib/libpuffs/subr.c
new file mode 100644 (file)
index 0000000..c48b2fe
--- /dev/null
@@ -0,0 +1,325 @@
+/*     $NetBSD: subr.c,v 1.23 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/cdefs.h>
+#if !defined(lint)
+__RCSID("$NetBSD: subr.c,v 1.23 2008/08/12 19:44:39 pooka Exp $");
+#endif /* !lint */
+
+#include <sys/types.h>
+#include <sys/dirent.h>
+#include <sys/time.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stddef.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "puffs.h"
+#include "puffs_priv.h"
+
+
+int
+puffs_gendotdent(struct dirent **dent, ino_t id, int dotdot, size_t *reslen)
+{
+       const char *name;
+
+       assert(dotdot == 0 || dotdot == 1);
+       name = dotdot == 0 ? "." : "..";
+
+       return puffs_nextdent(dent, name, id, DT_DIR, reslen);
+}
+
+int
+puffs_nextdent(struct dirent **dent, const char *name, ino_t id, uint8_t dtype,
+       size_t *reslen)
+{
+       struct dirent *d = *dent;
+        unsigned int len, reclen;
+        int o;
+       char *cp;
+
+       /* check if we have enough room for our dent-aligned dirent */
+       if (_DIRENT_RECLEN(d, strlen(name)) > *reslen)
+               return 0;
+
+       d->d_ino = id;
+
+       /* Compute the length of the name */
+       cp = memchr(name, '\0', NAME_MAX);
+       if (cp == NULL)
+               len = NAME_MAX;
+       else
+               len = cp - (name);
+
+       /* Compute record length */
+       reclen = offsetof(struct dirent, d_name) + len + 1;
+       o = (reclen % sizeof(long));
+       if (o != 0)
+               reclen += sizeof(long) - o;
+
+        /* FIXME: Set d_off?
+         * dep->d_off = 
+         */
+       d->d_reclen = (unsigned short) reclen;
+        (void)memcpy(d->d_name, name, (size_t)len);
+        d->d_name[len] = '\0';
+
+       *reslen -= d->d_reclen;
+       *dent = _DIRENT_NEXT(d);
+
+       return 1;
+}
+
+/*ARGSUSED*/
+int
+puffs_fsnop_unmount(struct puffs_usermount *dontuse1, int dontuse2)
+{
+
+       /* would you like to see puffs rule again, my friend? */
+       return 0;
+}
+
+/*ARGSUSED*/
+int
+puffs_fsnop_sync(struct puffs_usermount *dontuse1, int dontuse2,
+       const struct puffs_cred *dontuse3)
+{
+
+       return 0;
+}
+
+/*ARGSUSED*/
+int
+puffs_fsnop_statvfs(struct puffs_usermount *dontuse1, struct statvfs *sbp)
+{
+
+       /* FIXME: implement me
+       sbp->f_bsize = sbp->f_frsize = sbp->f_iosize = 512;
+
+       sbp->f_bfree=sbp->f_bavail=sbp->f_bresvd=sbp->f_blocks = (fsblkcnt_t)0;
+       sbp->f_ffree=sbp->f_favail=sbp->f_fresvd=sbp->f_files = (fsfilcnt_t)0;
+       */
+       return 0;
+}
+
+/*ARGSUSED3*/
+int
+puffs_genfs_node_getattr(struct puffs_usermount *pu, puffs_cookie_t opc,
+       struct vattr *va, const struct puffs_cred *pcr)
+{
+       struct puffs_node *pn = PU_CMAP(pu, opc);
+
+       memcpy(va, &pn->pn_va, sizeof(struct vattr));
+       return 0;
+}
+
+/*
+ * Just put the node, don't do anything else.  Don't use this if
+ * your fs needs more cleanup.
+ */
+/*ARGSUSED2*/
+int
+puffs_genfs_node_reclaim(struct puffs_usermount *pu, puffs_cookie_t opc)
+{
+
+       puffs_pn_put(PU_CMAP(pu, opc));
+
+       return 0;
+}
+
+/*
+ * Just a wrapper to make calling the above nicer without having to pass
+ * NULLs from application code
+ */
+void
+puffs_zerostatvfs(struct statvfs *sbp)
+{
+
+       puffs_fsnop_statvfs(NULL, sbp);
+}
+
+/*
+ * Set vattr values for those applicable (i.e. not PUFFS_VNOVAL).
+ */
+void
+puffs_setvattr(struct vattr *vap, const struct vattr *sva)
+{
+
+#define SETIFVAL(a, t) if (sva->a != (t)PUFFS_VNOVAL) vap->a = sva->a
+       if (sva->va_type != VNON)
+               vap->va_type = sva->va_type;
+       SETIFVAL(va_mode, mode_t);
+       SETIFVAL(va_nlink, nlink_t);
+       SETIFVAL(va_uid, uid_t);
+       SETIFVAL(va_gid, gid_t);
+       SETIFVAL(va_fsid, long);
+       SETIFVAL(va_size, u_quad_t);
+       SETIFVAL(va_fileid, ino_t);
+       SETIFVAL(va_blocksize, long);
+       SETIFVAL(va_atime.tv_sec, time_t);
+       SETIFVAL(va_ctime.tv_sec, time_t);
+       SETIFVAL(va_mtime.tv_sec, time_t);
+       SETIFVAL(va_birthtime.tv_sec, time_t);
+       SETIFVAL(va_atime.tv_nsec, long);
+       SETIFVAL(va_ctime.tv_nsec, long);
+       SETIFVAL(va_mtime.tv_nsec, long);
+       SETIFVAL(va_birthtime.tv_nsec, long);
+       SETIFVAL(va_gen, u_long);
+       SETIFVAL(va_flags, u_long);
+       SETIFVAL(va_rdev, dev_t);
+       SETIFVAL(va_bytes, u_quad_t);
+#undef SETIFVAL
+       /* ignore va->va_vaflags */
+}
+
+void
+puffs_vattr_null(struct vattr *vap)
+{
+
+       vap->va_type = VNON;
+
+       /*
+        * Assign individually so that it is safe even if size and
+        * sign of each member are varied.
+        */
+       vap->va_mode = (mode_t)PUFFS_VNOVAL;
+       vap->va_nlink = (nlink_t)PUFFS_VNOVAL;
+       vap->va_uid = (uid_t)PUFFS_VNOVAL;
+       vap->va_gid = (gid_t)PUFFS_VNOVAL;
+       vap->va_fsid = PUFFS_VNOVAL;
+       vap->va_fileid = (ino_t)PUFFS_VNOVAL;
+       vap->va_size = (u_quad_t)PUFFS_VNOVAL;
+       vap->va_blocksize = sysconf(_SC_PAGESIZE);
+           vap->va_atime.tv_sec =
+           vap->va_mtime.tv_sec =
+           vap->va_ctime.tv_sec =
+       vap->va_birthtime.tv_sec = PUFFS_VNOVAL;
+           vap->va_atime.tv_nsec =
+           vap->va_mtime.tv_nsec =
+           vap->va_ctime.tv_nsec =
+       vap->va_birthtime.tv_nsec = PUFFS_VNOVAL;
+       vap->va_rdev = (dev_t)PUFFS_VNOVAL;
+       vap->va_bytes = (u_quad_t)PUFFS_VNOVAL;
+
+       vap->va_flags = 0;
+       vap->va_gen = 0;
+       vap->va_vaflags = 0;
+}
+
+static int vdmap[] = {
+       DT_UNKNOWN,     /* VNON */
+       DT_REG,         /* VREG */
+       DT_DIR,         /* VDIR */
+       DT_BLK,         /* VBLK */
+       DT_CHR,         /* VCHR */
+       DT_LNK,         /* VLNK */
+       DT_SOCK,        /* VSUCK*/
+       DT_FIFO,        /* VFIFO*/
+       DT_UNKNOWN      /* VBAD */
+};
+/* XXX: DT_WHT ? */
+int
+puffs_vtype2dt(enum vtype vt)
+{
+
+       if ((int)vt >= VNON && vt < (sizeof(vdmap)/sizeof(vdmap[0])))
+               return vdmap[vt];
+
+       return DT_UNKNOWN;
+}
+
+enum vtype
+puffs_mode2vt(mode_t mode)
+{
+
+       switch (mode & S_IFMT) {
+       case S_IFIFO:
+               return VFIFO;
+       case S_IFCHR:
+               return VCHR;
+       case S_IFDIR:
+               return VDIR;
+       case S_IFBLK:
+               return VBLK;
+       case S_IFREG:
+               return VREG;
+       case S_IFLNK:
+               return VLNK;
+       case S_IFSOCK:
+               return VSOCK;
+       default:
+               return VBAD; /* XXX: not really true, but ... */
+       }
+}
+
+void
+puffs_stat2vattr(struct vattr *va, const struct stat *sb)
+{
+
+       va->va_type = puffs_mode2vt(sb->st_mode);
+       va->va_mode = sb->st_mode;
+       va->va_nlink = sb->st_nlink;
+       va->va_uid = sb->st_uid;
+       va->va_gid = sb->st_gid;
+       va->va_fsid = sb->st_dev;
+       va->va_fileid = sb->st_ino;
+       va->va_size = sb->st_size;
+        va->va_atime.tv_nsec = va->va_mtime.tv_nsec = va->va_ctime.tv_nsec = 0;
+       va->va_atime.tv_sec = sb->st_atime;
+       va->va_ctime.tv_sec = sb->st_ctime;
+       va->va_mtime.tv_sec = sb->st_mtime;
+       va->va_blocksize = sb->st_blksize;
+       va->va_birthtime = sb->st_birthtimespec;
+       va->va_gen = sb->st_gen;
+       va->va_flags = sb->st_flags;
+       va->va_rdev = sb->st_rdev;
+       va->va_bytes = sb->st_blocks * sb->st_blksize;
+       va->va_filerev = 0;
+       va->va_vaflags = 0;
+}
+
+mode_t
+puffs_addvtype2mode(mode_t mode, enum vtype type)
+{
+
+       switch (type) {
+       case VCHR:
+               mode |= S_IFCHR;
+               break;
+       case VBLK:
+               mode |= S_IFBLK;
+               break;
+       default:
+               break;
+       }
+
+       return mode;
+}
diff --git a/lib/libpuffs/table.c b/lib/libpuffs/table.c
new file mode 100644 (file)
index 0000000..815fa83
--- /dev/null
@@ -0,0 +1,46 @@
+/* This file contains the table used to map system call numbers onto the
+ * routines that perform them.
+ *
+ * Created (MFS based):
+ *   February 2010 (Evgeniy Ivanov)
+ */
+
+#define _TABLE
+
+#include "fs.h"
+
+PUBLIC _PROTOTYPE (int (*fs_call_vec[]), (void) ) = {
+    no_sys,             /* 0   not used */
+    no_sys,             /* 1   */       /* Was: fs_getnode */
+    fs_putnode,         /* 2   */
+    fs_slink,           /* 3   */
+    fs_ftrunc,          /* 4   */
+    fs_chown,           /* 5   */
+    fs_chmod,           /* 6   */
+    fs_inhibread,       /* 7   */
+    fs_stat,            /* 8   */
+    fs_utime,           /* 9   */
+    fs_fstatfs,         /* 10  */
+    fs_breadwrite,      /* 11  */
+    fs_breadwrite,      /* 12  */
+    fs_unlink,          /* 13  */
+    fs_unlink,          /* 14  */
+    fs_unmount,         /* 15  */
+    fs_sync,            /* 16  */
+    fs_new_driver,      /* 17  */
+    fs_flush,           /* 18  */
+    fs_readwrite,       /* 19  */
+    fs_readwrite,       /* 20  */
+    fs_mknod,           /* 21  */
+    fs_mkdir,           /* 22  */
+    fs_create,          /* 23  */
+    fs_link,            /* 24  */
+    fs_rename,          /* 25  */
+    fs_lookup,          /* 26  */
+    fs_mountpoint,      /* 27  */
+    fs_readsuper,       /* 28  */
+    no_sys,            /* 29  */       /* Was: fs_newnode */
+    fs_rdlink,          /* 30  */
+    fs_getdents,        /* 31  */
+    fs_statvfs,                /* 32  */
+};
diff --git a/lib/libpuffs/time.c b/lib/libpuffs/time.c
new file mode 100644 (file)
index 0000000..09eec3d
--- /dev/null
@@ -0,0 +1,43 @@
+/* Created (MFS based):
+ *   February 2010 (Evgeniy Ivanov)
+ */
+
+#include "fs.h"
+#include <minix/callnr.h>
+#include <minix/com.h>
+#include <minix/vfsif.h>
+
+#include "puffs.h"
+#include "puffs_priv.h"
+
+
+/*===========================================================================*
+ *                             fs_utime                                     *
+ *===========================================================================*/
+PUBLIC int fs_utime()
+{
+  int r;
+  struct puffs_node *pn;
+  struct vattr va;
+  PUFFS_MAKECRED(pcr, &global_kcred);
+
+  if (is_readonly_fs)
+       return(EROFS);
+
+  if (global_pu->pu_ops.puffs_node_setattr == NULL)
+       return(EINVAL);
+
+  if( (pn = puffs_pn_nodewalk(global_pu, 0, &fs_m_in.REQ_INODE_NR)) == NULL)
+        return(EINVAL);
+  
+  puffs_vattr_null(&va);
+  va.va_atime.tv_nsec = va.va_mtime.tv_nsec = va.va_ctime.tv_nsec = 0;
+  va.va_atime.tv_sec = fs_m_in.REQ_ACTIME;
+  va.va_mtime.tv_sec = fs_m_in.REQ_MODTIME;
+  va.va_ctime.tv_sec = clock_time();
+
+  if (global_pu->pu_ops.puffs_node_setattr(global_pu, pn, &va, pcr) != 0)
+       return(EINVAL);
+
+  return(OK);
+}
diff --git a/lib/libpuffs/utility.c b/lib/libpuffs/utility.c
new file mode 100644 (file)
index 0000000..a1e262d
--- /dev/null
@@ -0,0 +1,114 @@
+/* Created (MFS based):
+ *   February 2010 (Evgeniy Ivanov)
+ */
+
+#include "fs.h"
+
+#include <stdarg.h>
+
+#include "puffs.h"
+#include "puffs_priv.h"
+
+
+/*===========================================================================*
+ *                             no_sys                                       *
+ *===========================================================================*/
+PUBLIC int no_sys()
+{
+/* Somebody has used an illegal system call number */
+  lpuffs_debug("no_sys: invalid call %d\n", req_nr);
+  return(EINVAL);
+}
+
+
+/*===========================================================================*
+ *                              mfs_nul                                      *
+ *===========================================================================*/
+PUBLIC void mfs_nul_f(char *file, int line, char *str, unsigned int len,
+                      unsigned int maxlen)
+{
+  if (len < maxlen && str[len-1] != '\0') {
+       lpuffs_debug("%s:%d string (length %d,maxlen %d) not null-terminated\n",
+                file, line, len, maxlen);
+  }
+}
+
+
+/*===========================================================================*
+ *                             clock_time                                   *
+ *===========================================================================*/
+PUBLIC time_t clock_time()
+{
+/* This routine returns the time in seconds since 1.1.1970.  MINIX is an
+ * astrophysically naive system that assumes the earth rotates at a constant
+ * rate and that such things as leap seconds do not exist.
+ */
+
+  register int k;
+  clock_t uptime;
+  time_t boottime;
+
+  if ((k=getuptime2(&uptime, &boottime)) != OK)
+       panic("clock_time: getuptme2 failed: %d", k);
+
+  return( (time_t) (boottime + (uptime/sys_hz())));
+}
+
+
+/*===========================================================================*
+ *                             update_times                                 *
+ *===========================================================================*/
+PUBLIC int update_times(struct puffs_node *pn, int flags, time_t t)
+{
+  int r;
+  struct vattr va;
+  time_t new_time;
+  PUFFS_MAKECRED(pcr, &global_kcred);
+
+  if (!flags)
+       return 0;
+
+  if (global_pu->pu_ops.puffs_node_setattr == NULL)
+       return EINVAL;
+
+  new_time = t != 0 ? t : clock_time();
+  
+  puffs_vattr_null(&va);
+  /* librefuse modifies atime and mtime together,
+   * so set old values to avoid setting either one
+   * to PUFFS_VNOVAL (set by puffs_vattr_null).
+   */
+  va.va_atime.tv_sec = pn->pn_va.va_atime.tv_sec;
+  va.va_mtime.tv_sec = pn->pn_va.va_mtime.tv_sec;
+
+  if (flags & ATIME) {
+       va.va_atime.tv_sec = new_time;
+       va.va_atime.tv_nsec = 0;
+  }
+  if (flags & MTIME) {
+       va.va_mtime.tv_sec = new_time;
+       va.va_mtime.tv_nsec = 0;
+  }
+  if (flags & CTIME) {
+       va.va_ctime.tv_sec = new_time;
+       va.va_ctime.tv_nsec = 0;
+  }
+
+  r = global_pu->pu_ops.puffs_node_setattr(global_pu, pn, &va, pcr);
+
+  return(r);
+}
+
+
+/*===========================================================================*
+ *                             lpuffs_debug                                 *
+ *===========================================================================*/
+PUBLIC void lpuffs_debug(char *format, ...)
+{   
+  char buffer[256];
+  va_list args;
+  va_start (args, format);
+  vsprintf (buffer,format, args);
+  printf("%s: %s", fs_name, buffer);
+  va_end (args);
+}
diff --git a/lib/librefuse/Makefile b/lib/librefuse/Makefile
new file mode 100644 (file)
index 0000000..d01b198
--- /dev/null
@@ -0,0 +1,19 @@
+# $NetBSD: Makefile,v 1.8 2007/11/05 13:41:52 pooka Exp $
+
+USE_FORT?=     yes     # data driven bugs?
+
+LIB=           refuse
+LIBDPLIBS+=    puffs   ${.CURDIR}/../libpuffs
+
+.ifdef DEBUG
+FUSE_OPT_DEBUG_FLAGS=  -g -DFUSE_OPT_DEBUG
+.endif
+
+CFLAGS+=       ${FUSE_OPT_DEBUG_FLAGS}
+SRCS=          refuse.c refuse_opt.c
+MAN=           refuse.3
+WARNS=         4
+INCS=           fuse.h fuse_opt.h
+INCSDIR=        /usr/include
+
+.include <bsd.lib.mk>
diff --git a/lib/librefuse/TODO b/lib/librefuse/TODO
new file mode 100644 (file)
index 0000000..918d38b
--- /dev/null
@@ -0,0 +1,25 @@
+       $NetBSD: TODO,v 1.3 2007/05/03 21:02:54 agc Exp $
+
+To Do
+=====
+address all XXX
+implement all fuse_opt
+implement proper lookup (pending some libpuffs stuff)
+support fuse_mt (i.e. worker threads, but that'll probably be smarter
+                to do inside of libpuffs)
+support fuse_ll (i.e. "raw" vfs/vnode export)
+implement all sorts of compat tweaks to appease various file systems
+do proper implementations of dirfillers
+statfs - some fuse file systems want struct statfs and we only have
+         statvfs available natively
+
+Done
+====
+statvfs
+sync
+WARNS=4
+address lint
+special directory handling in open()
+Finish off manual page
+fuse_setup
+fuse_teardown
diff --git a/lib/librefuse/fuse.h b/lib/librefuse/fuse.h
new file mode 100644 (file)
index 0000000..483fea3
--- /dev/null
@@ -0,0 +1,193 @@
+/* $NetBSD: fuse.h,v 1.21 2008/08/01 15:54:09 dillo Exp $ */
+
+/*
+ * Copyright Â© 2007 Alistair Crooks.  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.
+ * 3. The name of the author may not be used to endorse or promote
+ *    products derived from this software without specific prior written
+ *    permission.
+ *
+ * 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 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 FUSE_H_
+#define FUSE_H_        20070123
+
+/* set the default version to use for the fuse interface */
+/* this value determines the API to be used */
+#ifndef FUSE_USE_VERSION
+#define FUSE_USE_VERSION       26
+#endif
+
+#include <sys/types.h>
+
+#include <puffs.h>
+#include <utime.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct fuse;
+struct fuse_args; /* XXXsupportme */
+
+struct fuse_file_info {
+       int32_t         flags;
+       uint32_t        fh_old;
+       int32_t         writepage;
+       uint32_t        direct_io:1;
+       uint32_t        keep_cache:1;
+       uint32_t        flush:1;
+       uint32_t        padding:29;
+       uint64_t        fh;
+       uint64_t        lock_owner;
+};
+
+struct fuse_conn_info {
+       uint32_t proto_major;
+       uint32_t proto_minor;
+       uint32_t async_read;
+       uint32_t max_write;
+       uint32_t max_readahead;
+       uint32_t reserved[27];
+};
+
+/* equivalent'ish of puffs_cc */
+struct fuse_context {
+       struct fuse     *fuse;
+       uid_t           uid;
+       gid_t           gid;
+       pid_t           pid;
+       void            *private_data;
+};
+
+/**
+ * Argument list
+ */
+struct fuse_args {
+       int     argc;
+       char    **argv;
+       int     allocated;
+};
+
+/**
+ * Initializer for 'struct fuse_args'
+ */
+#define FUSE_ARGS_INIT(argc, argv) { argc, argv, 0 }
+
+typedef struct puffs_fuse_dirh *fuse_dirh_t;
+
+typedef int (*fuse_fill_dir_t)(void *, const char *, const struct stat *, off_t);
+typedef int (*fuse_dirfil_t)(fuse_dirh_t, const char *, int, ino_t);
+
+#define FUSE_VERSION   26
+#define FUSE_MAJOR_VERSION     2
+#define FUSE_MINOR_VERSION     6
+
+/*
+ * These operations shadow those in puffs_usermount, and are used
+ * as a table of callbacks to make when file system requests come
+ * in.
+ *
+ * NOTE: keep same order as fuse
+ */
+struct fuse_operations {
+       int     (*getattr)(const char *, struct stat *);
+       int     (*readlink)(const char *, char *, size_t);
+       int     (*getdir)(const char *, fuse_dirh_t, fuse_dirfil_t);
+       int     (*mknod)(const char *, mode_t, dev_t);
+       int     (*mkdir)(const char *, mode_t);
+       int     (*unlink)(const char *);
+       int     (*rmdir)(const char *);
+       int     (*symlink)(const char *, const char *);
+       int     (*rename)(const char *, const char *);
+       int     (*link)(const char *, const char *);
+       int     (*chmod)(const char *, mode_t);
+       int     (*chown)(const char *, uid_t, gid_t);
+       int     (*truncate)(const char *, off_t);
+       int     (*utime)(const char *, struct utimbuf *);
+       int     (*open)(const char *, struct fuse_file_info *);
+       int     (*read)(const char *, char *, size_t, off_t, struct fuse_file_info *);
+       int     (*write)(const char *, const char *, size_t, off_t, struct fuse_file_info *);
+       int     (*statfs)(const char *, struct statvfs *);
+       int     (*flush)(const char *, struct fuse_file_info *);
+       int     (*release)(const char *, struct fuse_file_info *);
+       int     (*fsync)(const char *, int, struct fuse_file_info *);
+       int     (*setxattr)(const char *, const char *, const char *, size_t, int);
+       int     (*getxattr)(const char *, const char *, char *, size_t);
+       int     (*listxattr)(const char *, char *, size_t);
+       int     (*removexattr)(const char *, const char *);
+       int     (*opendir)(const char *, struct fuse_file_info *);
+       int     (*readdir)(const char *, void *, fuse_fill_dir_t, off_t, struct fuse_file_info *);
+       int     (*releasedir)(const char *, struct fuse_file_info *);
+       int     (*fsyncdir)(const char *, int, struct fuse_file_info *);
+       void    *(*init)(struct fuse_conn_info *);
+       void    (*destroy)(void *);
+       int     (*access)(const char *, int);
+       int     (*create)(const char *, mode_t, struct fuse_file_info *);
+       int     (*ftruncate)(const char *, off_t, struct fuse_file_info *);
+       int     (*fgetattr)(const char *, struct stat *, struct fuse_file_info *);
+       int     (*lock)(const char *, struct fuse_file_info *, int, struct flock *);
+       int     (*utimens)(const char *, const struct timespec *);
+       int     (*bmap)(const char *, size_t , uint64_t *);
+};
+
+
+struct fuse_chan *fuse_mount(const char *, struct fuse_args *);
+struct fuse *fuse_new(struct fuse_chan *, struct fuse_args *,
+       const struct fuse_operations *, size_t, void *);
+
+int fuse_main_real(int, char **, const struct fuse_operations *, size_t, void *);
+int fuse_loop(struct fuse *);
+struct fuse_context *fuse_get_context(void);
+void fuse_exit(struct fuse *);
+void fuse_destroy(struct fuse *);
+
+void fuse_unmount(const char *, struct fuse_chan *);
+
+struct fuse *fuse_setup(int, char **, const struct fuse_operations *,
+       size_t, char **, int *, int *);
+void fuse_teardown(struct fuse *, char *);
+
+#if FUSE_USE_VERSION == 22
+#define fuse_unmount fuse_unmount_compat22
+#endif
+
+void fuse_unmount_compat22(const char *);
+
+#if FUSE_USE_VERSION >= 26
+#define fuse_main(argc, argv, op, arg) \
+            fuse_main_real(argc, argv, op, sizeof(*(op)), arg)
+#define fuse_setup     fuse_setup26
+#else
+#define fuse_main(argc, argv, op) \
+            fuse_main_real(argc, argv, op, sizeof(*(op)), NULL)
+#endif
+
+struct fuse *fuse_setup26(int, char **, const struct fuse_operations *,
+       size_t, char **, int *, void *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#include <fuse_opt.h>
+
+#endif
diff --git a/lib/librefuse/fuse_opt.h b/lib/librefuse/fuse_opt.h
new file mode 100644 (file)
index 0000000..b4d958b
--- /dev/null
@@ -0,0 +1,70 @@
+/*     $NetBSD: fuse_opt.h,v 1.5 2009/04/19 22:25:29 christos Exp $    */
+
+/*
+ * Copyright (c) 2007 Alistair Crooks.  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.
+ * 3. The name of the author may not be used to endorse or promote
+ *    products derived from this software without specific prior written
+ *    permission.
+ *
+ * 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 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 _FUSE_OPT_H_
+#define _FUSE_OPT_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif  
+
+enum {
+       FUSE_OPT_KEY_OPT = -1,
+       FUSE_OPT_KEY_NONOPT = -2,
+       FUSE_OPT_KEY_KEEP = -3,
+       FUSE_OPT_KEY_DISCARD = -4
+};
+
+struct fuse_opt {
+       const char      *templ;
+       int32_t         offset;
+       int32_t         value;
+};
+
+#define FUSE_OPT_KEY(templ, key) { templ, -1U, key }
+#define FUSE_OPT_END { .templ = NULL }
+
+typedef int (*fuse_opt_proc_t)(void *, const char *, int, struct fuse_args *);
+
+
+int fuse_opt_add_arg(struct fuse_args *, const char *);
+struct fuse_args *fuse_opt_deep_copy_args(int, char **);
+void fuse_opt_free_args(struct fuse_args *);
+int fuse_opt_insert_arg(struct fuse_args *, int, const char *);
+int fuse_opt_add_opt(char **, const char *);
+int fuse_opt_parse(struct fuse_args *, void *,
+                  const struct fuse_opt *, fuse_opt_proc_t);
+int fuse_opt_match(const struct fuse_opt *, const char *);
+
+#ifdef __cplusplus
+}
+#endif 
+
+#endif /* _FUSE_OPT_H_ */
diff --git a/lib/librefuse/refuse.3 b/lib/librefuse/refuse.3
new file mode 100644 (file)
index 0000000..f2686f6
--- /dev/null
@@ -0,0 +1,243 @@
+.\"    $NetBSD: refuse.3,v 1.7 2007/11/08 17:08:46 pooka Exp $
+.\"
+.\" Copyright Â© 2007 Alistair Crooks.  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.
+.\" 3. The name of the author may not be used to endorse or promote
+.\"    products derived from this software without specific prior written
+.\"    permission.
+.\"
+.\" 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 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 April 30, 2007
+.Dt REFUSE 3
+.Os
+.Sh NAME
+.Nm refuse
+.Nd Re-implementation of a file system in userspace system
+.Sh LIBRARY
+.Lb librefuse
+.Sh SYNOPSIS
+.In fuse.h
+.Ft int
+.Fo fuse_main
+.Fa "int argc" "char **argv" "const struct fuse_operations *"
+.Fc
+.Ft int
+.Fo fuse_opt_add_arg
+.Fa "struct fuse_args *args" "const char *arg"
+.Fc
+.Ft int
+.Fo fuse_opt_parse
+.Fa "struct fuse_args *args" "void *userdata"
+.Fa "const struct fuse_opt *descriptions" "fuse_opt_proc_t processingfunc"
+.Fc
+.Ft int
+.Fo fuse_teardown
+.Fa "struct fuse *fuse" "char *mountpoint"
+.Fc
+.Ft struct fuse *
+.Fo fuse_setup
+.Fa "int argc" "char **argv" "const struct fuse_operations *ops"
+.Fa "size_t opssize" "char **mountpoint" "int *multithreaded" "int *fd"
+.Fc
+.Ft int
+.Fo puffs_fuse_node_getattr
+.Fa "const char *path" "struct stat *attrs"
+.Fc
+.Ft int
+.Fo puffs_fuse_node_readlink
+.Fa "const char *path" "char *output" "size_t outlen"
+.Fc
+.Ft int
+.Fo puffs_fuse_node_mknod
+.Fa "const char *path" "mode_t permissions" "dev_t devicenumber"
+.Fc
+.Ft int
+.Fo puffs_fuse_node_mkdir
+.Fa "const char *path" "mode_t permissions"
+.Fc
+.Ft int
+.Fo puffs_fuse_unlink
+.Fa "const char *path"
+.Fc
+.Ft int
+.Fo puffs_fuse_node_rmdir
+.Fa "const char *path"
+.Fc
+.Ft int
+.Fo puffs_fuse_node_symlink
+.Fa "const char *path" "const char *target"
+.Fc
+.Ft int
+.Fo puffs_fuse_node_rename
+.Fa "const char *from" "const char *to"
+.Fc
+.Ft int
+.Fo puffs_fuse_node_link
+.Fa "const char *from" "const char *to"
+.Fc
+.Ft int
+.Fo puffs_fuse_node_chmod
+.Fa "const char *path" "mode_t permissions"
+.Fc
+.Ft int
+.Fo puffs_fuse_node_own
+.Fa "const char *path" "uid_t owner" "gid_t group"
+.Fc
+.Ft int
+.Fo puffs_fuse_node_truncate
+.Fa "const char *path" "off_t newsize"
+.Fc
+.Ft int
+.Fo puffs_fuse_node_utime
+.Fa "const char *path" "struct utimbuf *newtimes"
+.Fc
+.Ft int
+.Fo puffs_fuse_node_open
+.Fa "const char *path" "struct fuse_file_info *fileinfo"
+.Fc
+.Ft int
+.Fo puffs_fuse_node_read
+.Fa "const char *path" "char *buffer" "size_t bufferlen" "off_t startoffset"
+.Fa "struct fuse_file_info *fileinfo"
+.Fc
+.Ft int
+.Fo puffs_fuse_node_write
+.Fa "const char *path" "char *buffer" "size_t bufferlen" "off_t startoffset"
+.Fa "struct fuse_file_info *fileinfo"
+.Fc
+.Ft int
+.Fo puffs_fuse_fs_statfs
+.Fa "const char *path" "struct statvfs *vfsinfo"
+.Fc
+.Ft int
+.Fo puffs_fuse_node_flush
+.Fa "const char *path" "struct fuse_file_info *fileinfo"
+.Fc
+.Ft int
+.Fo puffs_fuse_node_fsync
+.Fa "const char *path" "int flags" "struct fuse_file_info *fileinfo"
+.Fc
+.Ft int
+.Fo puffs_fuse_node_setxattr
+.Fa "const char *path" "const char *attrname" "const char *attrvalue"
+.Fa "size_t attrsize" "int flags"
+.Fc
+.Ft int
+.Fo puffs_fuse_node_getxattr
+.Fa "const char *path" "const char *attrname" "const char *attrvalue"
+.Fa "size_t attrvaluesize"
+.Fc
+.Ft int
+.Fo puffs_fuse_node_listxattr
+.Fa "const char *path" "const char *attrname"
+.Fa "size_t attrvaluesize"
+.Fc
+.Ft int
+.Fo puffs_fuse_node_removexattr
+.Fa "const char *path" "const char *attrname"
+.Fc
+.Ft int
+.Fo puffs_fuse_node_opendir
+.Fa "const char *path" "struct fuse_file_info *fileinfo"
+.Fc
+.Ft int
+.Fo puffs_fuse_node_readdir
+.Fa "const char *path" "void *data" "fuse_fill_dir_t fillinfo"
+.Fa "off_t offset" "struct fuse_file_info *fileinfo"
+.Fc
+.Ft int
+.Fo puffs_fuse_node_releasedir
+.Fa "const char *path" "struct fuse_file_info *fileinfo"
+.Fc
+.Ft int
+.Fo puffs_fuse_node_fsyncdir
+.Fa "const char *path" "int flags" "struct fuse_file_info *fileinfo"
+.Fc
+.Ft void *
+.Fo puffs_fuse_fs_init
+.Fa "struct fuse_conn_info *connectioninfo"
+.Fc
+.Ft void
+.Fo puffs_fuse_node_destroy
+.Fa "void *connection"
+.Fc
+.Ft int
+.Fo puffs_fuse_node_access
+.Fa "const char *path" "int accesstype"
+.Fc
+.Ft int
+.Fo puffs_fuse_node_create
+.Fa "const char *path" "mode_t permissions" "struct fuse_file_info *fileinfo"
+.Fc
+.Ft int
+.Fo puffs_fuse_node_ftruncate
+.Fa "const char *path" "off_t offset" "struct fuse_file_info *fileinfo"
+.Fc
+.Ft int
+.Fo puffs_fuse_node_fgetattr
+.Fa "const char *path" "struct stat *attrs" "struct fuse_file_info *fileinfo"
+.Fc
+.Ft int
+.Fo puffs_fuse_node_lock
+.Fa "const char *path" "struct fuse_file_info *fileinfo"
+.Fa "int locktype" "struct flock *lockinfo"
+.Fc
+.Ft int
+.Fo puffs_fuse_node_utimens
+.Fa "const char *path" "const struct timespec *newtimes"
+.Fc
+.Ft int
+.Fo puffs_fuse_node_bmap
+.Fa "const char *path" "size_t mapsize" "uint64_t offset"
+.Fc
+.Sh DESCRIPTION
+.Nm
+is a reimplementation of the file system in user space subsystem.
+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 uses the framework provided by the
+.Xr puffs 3
+subsystem, and, through that, the kernel interface provided by
+.Xr puffs 4 .
+.Sh SEE ALSO
+.Xr puffs 3 ,
+.Xr puffs 4
+.Rs
+.%A Antti Kantee
+.%A Alistair Crooks
+.%D September 2007
+.%J EuroBSDCon 2007
+.%T ReFUSE: Userspace FUSE Reimplementation Using puffs
+.Re
+.Sh HISTORY
+An unsupported experimental version of
+.Nm
+first appeared in
+.Nx 5.0 .
+.Sh AUTHORS
+.An Alistair Crooks Aq agc@NetBSD.org ,
+.An Antti Kantee Aq pooka@NetBSD.org
+.Sh BUGS
+Many, legion, but well-loved.
diff --git a/lib/librefuse/refuse.c b/lib/librefuse/refuse.c
new file mode 100644 (file)
index 0000000..9c37550
--- /dev/null
@@ -0,0 +1,1446 @@
+/*     $NetBSD: refuse.c,v 1.92 2009/03/05 01:21:57 msaitoh Exp $      */
+
+/*
+ * Copyright Â© 2007 Alistair Crooks.  All rights reserved.
+ * Copyright Â© 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.
+ * 3. The name of the author may not be used to endorse or promote
+ *    products derived from this software without specific prior written
+ *    permission.
+ *
+ * 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 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/cdefs.h>
+#if !defined(lint)
+__RCSID("$NetBSD: refuse.c,v 1.92 2009/03/05 01:21:57 msaitoh Exp $");
+#endif /* !lint */
+
+#include <sys/types.h>
+
+#include <assert.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <fuse.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#ifdef MULTITHREADED_REFUSE
+#include <pthread.h>
+#endif
+
+typedef uint64_t        fuse_ino_t;
+
+struct fuse_config {
+       uid_t           uid;
+       gid_t           gid;
+       mode_t          umask;
+       double          entry_timeout;
+       double          negative_timeout;
+       double          attr_timeout;
+       double          ac_attr_timeout;
+       int             ac_attr_timeout_set;
+       int             debug;
+       int             hard_remove;
+       int             use_ino;
+       int             readdir_ino;
+       int             set_mode;
+       int             set_uid;
+       int             set_gid;
+       int             direct_io;
+       int             kernel_cache;
+       int             auto_cache;
+       int             intr;
+       int             intr_signal;
+};
+
+struct fuse_chan {
+       const char              *dir;
+       struct fuse_args        *args;
+       struct puffs_usermount  *pu;
+       int                     dead;
+};
+
+/* this is the private fuse structure */
+struct fuse {
+       struct fuse_chan        *fc;            /* fuse channel pointer */
+       struct fuse_operations  op;             /* switch table of operations */
+       int                     compat;         /* compat level -
+                                                * not used in puffs_fuse */
+       struct node             **name_table;
+       size_t                  name_table_size;
+       struct node             **id_table;
+       size_t                  id_table_size;
+       fuse_ino_t              ctr;
+       unsigned int            generation;
+       unsigned int            hidectr;
+#ifdef MULTITHREADED_REFUSE
+       pthread_mutex_t         lock;
+       pthread_rwlock_t        tree_lock;
+#endif
+       void                    *user_data;
+       struct fuse_config      conf;
+       int                     intr_installed;
+};
+
+struct puffs_fuse_dirh {
+       void *dbuf;
+       struct dirent *d;
+
+       size_t reslen;
+       size_t bufsize;
+};
+
+struct refusenode {
+       struct fuse_file_info   file_info;
+       struct puffs_fuse_dirh  dirh;
+       int opencount;
+       int flags;
+};
+#define RN_ROOT                0x01
+#define RN_OPEN                0x02    /* XXX: could just use opencount */
+
+static int fuse_setattr(struct fuse *, struct puffs_node *,
+                       const char *, const struct vattr *);
+
+static struct puffs_node *
+newrn(struct puffs_usermount *pu)
+{
+       struct puffs_node *pn;
+       struct refusenode *rn;
+
+       if ((rn = calloc(1, sizeof(*rn))) == NULL) {
+               err(EXIT_FAILURE, "newrn");
+       }
+       pn = puffs_pn_new(pu, rn);
+
+       return pn;
+}
+
+static void
+nukern(struct puffs_node *pn)
+{
+       struct refusenode *rn = pn->pn_data;
+
+       free(rn->dirh.dbuf);
+       free(rn);
+       puffs_pn_put(pn);
+}
+
+/* XXX - not threadsafe */
+static ino_t fakeino = 3;
+
+/***************** start of pthread context routines ************************/
+
+/*
+ * Notes on fuse_context:
+ * we follow fuse's lead and use the pthread specific information to hold
+ * a reference to the fuse_context structure for this thread.
+ */
+#ifdef MULTITHREADED_REFUSE
+static pthread_mutex_t         context_mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_key_t           context_key;
+static unsigned long           context_refc;
+#endif
+
+/* return the fuse_context struct related to this thread */
+struct fuse_context *
+fuse_get_context(void)
+{
+#ifdef MULTITHREADED_REFUSE
+       struct fuse_context     *ctxt;
+
+       if ((ctxt = pthread_getspecific(context_key)) == NULL) {
+               if ((ctxt = calloc(1, sizeof(struct fuse_context))) == NULL) {
+                       abort();
+               }
+               pthread_setspecific(context_key, ctxt);
+       }
+       return ctxt;
+#else
+       static struct fuse_context      fcon;
+
+       return &fcon;
+#endif
+}
+
+/* used as a callback function */
+#ifdef MULTITHREADED_REFUSE
+static void
+free_context(void *ctxt)
+{   
+       free(ctxt);
+}
+#endif
+
+/*
+ * Create the pthread key.  The reason for the complexity is to
+ * enable use of multiple fuse instances within a single process.
+ */
+static int
+create_context_key(void)
+{   
+#ifdef MULTITHREADED_REFUSE
+       int rv;
+
+       rv = pthread_mutex_lock(&context_mutex);
+       assert(rv == 0);
+
+       if (context_refc == 0) {
+               if (pthread_key_create(&context_key, free_context) != 0) {
+                       warnx("create_context_key: pthread_key_create failed");
+                       pthread_mutex_unlock(&context_mutex);
+                       return 0;
+               }
+       }
+       context_refc += 1;
+       pthread_mutex_unlock(&context_mutex);
+       return 1;
+#else
+       return 1;
+#endif
+}
+
+static void
+delete_context_key(void)
+{   
+#ifdef MULTITHREADED_REFUSE
+       pthread_mutex_lock(&context_mutex);
+       /* If we are the last fuse instances using the key, delete it */
+       if (--context_refc == 0) {
+               free(pthread_getspecific(context_key));
+               pthread_key_delete(context_key);
+       }
+       pthread_mutex_unlock(&context_mutex);
+#endif
+}
+
+/* set the uid and gid of the calling process in the current fuse context */
+static void
+set_fuse_context_uid_gid(const struct puffs_cred *cred)
+{
+       struct fuse_context     *fusectx;
+       uid_t                    uid;
+       gid_t                    gid;
+
+       fusectx = fuse_get_context();
+       if (puffs_cred_getuid(cred, &uid) == 0) {
+               fusectx->uid = uid;
+       }
+       if (puffs_cred_getgid(cred, &gid) == 0) {
+               fusectx->gid = gid;
+       }
+}
+
+/* set the pid of the calling process in the current fuse context */
+static void
+set_fuse_context_pid(struct puffs_usermount *pu)
+{
+       struct puffs_cc         *pcc = puffs_cc_getcc(pu);
+       struct fuse_context     *fusectx;
+
+       fusectx = fuse_get_context();
+       puffs_cc_getcaller(pcc, &fusectx->pid, NULL);
+}
+
+/***************** end of pthread context routines ************************/
+
+#define DIR_CHUNKSIZE 4096
+static int
+fill_dirbuf(struct puffs_fuse_dirh *dh, const char *name, ino_t dino,
+       uint8_t dtype)
+{
+
+       /* initial? */
+       if (dh->bufsize == 0) {
+               if ((dh->dbuf = calloc(1, DIR_CHUNKSIZE)) == NULL) {
+                       abort();
+               }
+               dh->d = dh->dbuf;
+               dh->reslen = dh->bufsize = DIR_CHUNKSIZE;
+       }
+
+       if (puffs_nextdent(&dh->d, name, dino, dtype, &dh->reslen)) {
+               return 0;
+       }
+
+       /* try to increase buffer space */
+       dh->dbuf = realloc(dh->dbuf, dh->bufsize + DIR_CHUNKSIZE);
+       if (dh->dbuf == NULL) {
+               abort();
+       }
+       dh->d = (void *)((uint8_t *)dh->dbuf + (dh->bufsize - dh->reslen));
+       dh->reslen += DIR_CHUNKSIZE;
+       dh->bufsize += DIR_CHUNKSIZE;
+
+       return !puffs_nextdent(&dh->d, name, dino, dtype, &dh->reslen);
+}
+
+/* ARGSUSED3 */
+/* XXX: I have no idea how "off" is supposed to be used */
+static int
+puffs_fuse_fill_dir(void *buf, const char *name,
+       const struct stat *stbuf, off_t off)
+{
+       struct puffs_fuse_dirh *deh = buf;
+       ino_t dino;
+       uint8_t dtype;
+
+       if (stbuf == NULL) {
+               dtype = DT_UNKNOWN;
+               dino = fakeino++;
+       } else {
+               dtype = puffs_vtype2dt(puffs_mode2vt(stbuf->st_mode));
+               dino = stbuf->st_ino;
+
+               /*
+                * Some FUSE file systems like to always use 0 as the
+                * inode number.   Our readdir() doesn't like to show
+                * directory entries with inode number 0 ==> workaround.
+                */
+               if (dino == 0) {
+                       dino = fakeino++;
+               }
+       }
+
+       return fill_dirbuf(deh, name, dino, dtype);
+}
+
+static int
+puffs_fuse_dirfil(fuse_dirh_t h, const char *name, int type, ino_t ino)
+{
+       ino_t dino;
+       int dtype;
+
+       if ((dtype = type) == 0) {
+               dtype = DT_UNKNOWN;
+       }
+
+       dino = (ino) ? ino : fakeino++;
+
+       return fill_dirbuf(h, name, dino, dtype);
+}
+
+/* place the refuse file system name into `name' */
+static void
+set_refuse_mount_name(char **argv, char *name, size_t size)
+{
+       char    *slash;
+
+       if (argv == NULL || *argv == NULL) {
+               (void) strlcpy(name, "refuse", size);
+       } else {
+               if ((slash = strrchr(*argv, '/')) == NULL) {
+                       slash = *argv;
+               } else {
+                       slash += 1;
+               }
+               if (strncmp(*argv, "refuse:", 7) == 0) {
+                       /* we've already done this */
+                       (void) strlcpy(name, *argv, size);
+               } else {
+                       (void) snprintf(name, size, "refuse:%s", slash);
+               }
+       }
+}
+
+
+/* this function exposes struct fuse to userland */
+static struct fuse *
+fuse_setup_real(int argc, char **argv, const struct fuse_operations *ops,
+       size_t size, char **mountpoint, int *multithreaded, int *fd,
+        void *user_data)
+{
+       struct fuse_chan        *fc;
+       struct fuse_args        *args;
+       struct fuse             *fuse;
+       char                     name[64];
+       int                      i;
+
+       /* set up the proper name */
+       set_refuse_mount_name(argv, name, sizeof(name));
+
+       /* grab the pthread context key */
+       if (!create_context_key()) {
+               return NULL;
+       }
+
+       /* stuff name into fuse_args */
+       args = fuse_opt_deep_copy_args(argc, argv);
+       if (args->argc > 0) {
+               free(args->argv[0]);
+       }
+       if ((args->argv[0] = strdup(name)) == NULL) {
+               fuse_opt_free_args(args);
+               return NULL;
+       }
+
+       /* count back from the end over arguments starting with '-' */
+       for (i = argc - 1 ; i > 0 && *argv[i] == '-' ; --i) {
+       }
+
+       fc = fuse_mount(*mountpoint = argv[i], args);
+       fuse = fuse_new(fc, args, ops, size, user_data);
+
+       fuse_opt_free_args(args);
+       free(args);
+
+       /* XXX - wait for puffs to become multi-threaded */
+       if (multithreaded) {
+               *multithreaded = 0;
+       }
+
+       /* XXX - this is unused */
+       if (fd) {
+               *fd = 0;
+       }
+
+       return fuse;
+}
+
+#ifdef fuse_setup
+#undef fuse_setup
+#endif
+
+struct fuse *
+fuse_setup(int argc, char **argv, const struct fuse_operations *ops,
+       size_t size, char **mountpoint, int *multithreaded, int *fd)
+{
+    return fuse_setup_real(argc, argv, ops, size, mountpoint,
+       multithreaded, fd, NULL);
+}
+
+struct fuse *
+fuse_setup26(int argc, char **argv, const struct fuse_operations *ops,
+       size_t size, char **mountpoint, int *multithreaded, void *user_data)
+{
+    return fuse_setup_real(argc, argv, ops, size, mountpoint,
+       multithreaded, NULL, user_data);
+}
+
+#define FUSE_ERR_UNLINK(fuse, file) if (fuse->op.unlink) fuse->op.unlink(file)
+#define FUSE_ERR_RMDIR(fuse, dir) if (fuse->op.rmdir) fuse->op.rmdir(dir)
+
+/* ARGSUSED1 */
+static int
+fuse_getattr(struct fuse *fuse, struct puffs_node *pn, const char *path,
+       struct vattr *va)
+{
+       struct stat              st;
+       int                     ret;
+
+       if (fuse->op.getattr == NULL) {
+               return ENOSYS;
+       }
+
+       /* wrap up return code */
+       memset(&st, 0, sizeof(st));
+       ret = (*fuse->op.getattr)(path, &st);
+
+       if (ret == 0) {
+               if (st.st_blksize == 0)
+                       st.st_blksize = DEV_BSIZE;
+               puffs_stat2vattr(va, &st);
+       }
+
+       return -ret;
+}
+
+/* utility function to set various elements of the attribute */
+static int
+fuse_setattr(struct fuse *fuse, struct puffs_node *pn, const char *path,
+       const struct vattr *va)
+{
+       struct refusenode       *rn = pn->pn_data;
+       mode_t                  mode;
+       uid_t                   uid;
+       gid_t                   gid;
+       int                     error, ret;
+
+       error = 0;
+
+       mode = va->va_mode;
+       uid = va->va_uid;
+       gid = va->va_gid;
+
+       if (mode != (mode_t)PUFFS_VNOVAL) {
+               ret = 0;
+
+               if (fuse->op.chmod == NULL) {
+                       error = -ENOSYS;
+               } else {
+                       ret = fuse->op.chmod(path, mode);
+                       if (ret)
+                               error = ret;
+               }
+       }
+       if (uid != (uid_t)PUFFS_VNOVAL || gid != (gid_t)PUFFS_VNOVAL) {
+               ret = 0;
+
+               if (fuse->op.chown == NULL) {
+                       error = -ENOSYS;
+               } else {
+                       ret = fuse->op.chown(path, uid, gid);
+                       if (ret)
+                               error = ret;
+               }
+       }
+       if (va->va_atime.tv_sec != (time_t)PUFFS_VNOVAL
+           || va->va_mtime.tv_sec != (long)PUFFS_VNOVAL) {
+               ret = 0;
+
+               if (fuse->op.utimens) {
+                       struct timespec tv[2];
+
+                       tv[0].tv_sec = va->va_atime.tv_sec;
+                       tv[0].tv_nsec = va->va_atime.tv_nsec;
+                       tv[1].tv_sec = va->va_mtime.tv_sec;
+                       tv[1].tv_nsec = va->va_mtime.tv_nsec;
+
+                       ret = fuse->op.utimens(path, tv);
+               } else if (fuse->op.utime) {
+                       struct utimbuf timbuf;
+
+                       timbuf.actime = va->va_atime.tv_sec;
+                       timbuf.modtime = va->va_mtime.tv_sec;
+
+                       ret = fuse->op.utime(path, &timbuf);
+               } else {
+                       error = -ENOSYS;
+               }
+
+               if (ret)
+                       error = ret;
+       }
+       if (va->va_size != (u_quad_t)PUFFS_VNOVAL) {
+               ret = 0;
+
+               if (fuse->op.truncate) {
+                       ret = fuse->op.truncate(path, (off_t)va->va_size);
+               } else if (fuse->op.ftruncate) {
+                       ret = fuse->op.ftruncate(path, (off_t)va->va_size,
+                           &rn->file_info);
+               } else {
+                       error = -ENOSYS;
+               }
+
+               if (ret)
+                       error = ret;
+       }
+       /* XXX: no reflection with reality */
+       puffs_setvattr(&pn->pn_va, va);
+
+       return -error;
+
+}
+
+static int
+fuse_newnode(struct puffs_usermount *pu, const char *path,
+       const struct vattr *va, struct fuse_file_info *fi,
+       struct puffs_newinfo *pni, struct puffs_node **pn_new)
+{
+       struct puffs_node       *pn;
+       struct refusenode       *rn;
+       struct vattr             newva;
+       struct fuse             *fuse;
+
+       fuse = puffs_getspecific(pu);
+
+       /* fix up nodes */
+       pn = newrn(pu);
+       if (pn == NULL) {
+               if (va->va_type == VDIR) {
+                       FUSE_ERR_RMDIR(fuse, path);
+               } else {
+                       FUSE_ERR_UNLINK(fuse, path);
+               }
+               return ENOMEM;
+       }
+       fuse_setattr(fuse, pn, path, va);
+       if (fuse_getattr(fuse, pn, path, &newva) == 0)
+               puffs_setvattr(&pn->pn_va, &newva);
+
+       rn = pn->pn_data;
+       if (fi)
+               memcpy(&rn->file_info, fi, sizeof(struct fuse_file_info));
+
+       puffs_newinfo_setcookie(pni, pn);
+       if (pn_new)
+               *pn_new = pn;
+
+       return 0;
+}
+
+
+/* operation wrappers start here */
+
+/* lookup the path */
+/* ARGSUSED1 */
+static int
+puffs_fuse_node_lookup(struct puffs_usermount *pu, void *opc,
+       struct puffs_newinfo *pni, const struct puffs_cn *pcn)
+{
+       struct puffs_node       *pn_res;
+       struct stat              st;
+       struct fuse             *fuse;
+       const char              *path = PCNPATH(pcn);
+       int                      ret;
+
+       fuse = puffs_getspecific(pu);
+
+       set_fuse_context_uid_gid(pcn->pcn_cred);
+
+       ret = fuse->op.getattr(path, &st);
+
+       if (ret != 0) {
+               return -ret;
+       }
+
+       /* XXX: fiXXXme unconst */
+       pn_res = puffs_pn_nodewalk(pu, puffs_path_walkcmp,
+           __UNCONST(&pcn->pcn_po_full));
+       if (pn_res == NULL) {
+               pn_res = newrn(pu);
+               if (pn_res == NULL)
+                       return errno;
+               puffs_stat2vattr(&pn_res->pn_va, &st);
+       }
+
+       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);
+       puffs_newinfo_setrdev(pni, pn_res->pn_va.va_rdev);
+
+       return 0;
+}
+
+/* get attributes for the path name */
+/* ARGSUSED3 */
+static int
+puffs_fuse_node_getattr(struct puffs_usermount *pu, void *opc, struct vattr *va,
+       const struct puffs_cred *pcr) 
+{
+       struct puffs_node       *pn = opc;
+       struct fuse             *fuse;
+       const char              *path = PNPATH(pn);
+
+       fuse = puffs_getspecific(pu);
+
+       set_fuse_context_uid_gid(pcr);
+
+       return fuse_getattr(fuse, pn, path, va);
+}
+
+/* read the contents of the symbolic link */
+/* ARGSUSED2 */
+static int
+puffs_fuse_node_readlink(struct puffs_usermount *pu, void *opc,
+       const struct puffs_cred *cred, char *linkname, size_t *linklen)
+{
+       struct puffs_node       *pn = opc;
+       struct fuse             *fuse;
+       const char              *path = PNPATH(pn), *p;
+       int                     ret;
+
+       fuse = puffs_getspecific(pu);
+       if (fuse->op.readlink == NULL) {
+               return ENOSYS;
+       }
+
+       set_fuse_context_uid_gid(cred);
+
+       /* wrap up return code */
+       ret = (*fuse->op.readlink)(path, linkname, *linklen);
+
+       if (ret == 0) {
+               p = memchr(linkname, '\0', *linklen);
+               if (!p)
+                       return EINVAL;
+
+               *linklen = p - linkname;
+       }
+
+       return -ret;
+}
+
+/* make the special node */
+/* ARGSUSED1 */
+static int
+puffs_fuse_node_mknod(struct puffs_usermount *pu, void *opc,
+       struct puffs_newinfo *pni, const struct puffs_cn *pcn,
+       const struct vattr *va)
+{
+       struct fuse             *fuse;
+       mode_t                   mode;
+       const char              *path = PCNPATH(pcn);
+       int                     ret;
+
+       fuse = puffs_getspecific(pu);
+       if (fuse->op.mknod == NULL) {
+               return ENOSYS;
+       }
+
+       set_fuse_context_uid_gid(pcn->pcn_cred);
+
+       /* wrap up return code */
+       mode = puffs_addvtype2mode(va->va_mode, va->va_type);
+       ret = (*fuse->op.mknod)(path, mode, va->va_rdev);
+
+       if (ret == 0) {
+               ret = fuse_newnode(pu, path, va, NULL, pni, NULL);
+       }
+
+       return -ret;
+}
+
+/* make a directory */
+/* ARGSUSED1 */
+static int
+puffs_fuse_node_mkdir(struct puffs_usermount *pu, void *opc,
+       struct puffs_newinfo *pni, const struct puffs_cn *pcn,
+       const struct vattr *va)
+{
+       struct fuse             *fuse;
+       mode_t                   mode = va->va_mode;
+       const char              *path = PCNPATH(pcn);
+       int                     ret;
+
+       fuse = puffs_getspecific(pu);
+
+       set_fuse_context_uid_gid(pcn->pcn_cred);
+
+       if (fuse->op.mkdir == NULL) {
+               return ENOSYS;
+       }
+
+       /* wrap up return code */
+       ret = (*fuse->op.mkdir)(path, mode);
+
+       if (ret == 0) {
+               ret = fuse_newnode(pu, path, va, NULL, pni, NULL);
+       }
+
+       return -ret;
+}
+
+/*
+ * create a regular file
+ *
+ * since linux/fuse sports using mknod for creating regular files
+ * instead of having a separate call for it in some versions, if
+ * we don't have create, just jump to op->mknod.
+ */
+/*ARGSUSED1*/
+static int
+puffs_fuse_node_create(struct puffs_usermount *pu, void *opc,
+       struct puffs_newinfo *pni, const struct puffs_cn *pcn,
+       const struct vattr *va)
+{
+       struct fuse             *fuse;
+       struct fuse_file_info   fi;
+       struct puffs_node       *pn;
+       mode_t                  mode = va->va_mode;
+       const char              *path = PCNPATH(pcn);
+       int                     ret, created;
+
+       fuse = puffs_getspecific(pu);
+
+       set_fuse_context_uid_gid(pcn->pcn_cred);
+
+       created = 0;
+       if (fuse->op.create) {
+               ret = fuse->op.create(path, mode | S_IFREG, &fi);
+               if (ret == 0)
+                       created = 1;
+
+       } else if (fuse->op.mknod) {
+               ret = fuse->op.mknod(path, mode | S_IFREG, 0);
+
+       } else {
+               ret = -ENOSYS;
+       }
+
+       if (ret == 0) {
+               ret = fuse_newnode(pu, path, va, &fi, pni, &pn);
+
+               /* sweet..  create also open the file */
+               if (created) {
+                       struct refusenode *rn;
+
+                       rn = pn->pn_data;
+                       rn->flags |= RN_OPEN;
+                       rn->opencount++;
+               }
+       }
+
+       return -ret;
+}
+
+/* remove the directory entry */
+/* ARGSUSED1 */
+static int
+puffs_fuse_node_remove(struct puffs_usermount *pu, void *opc, void *targ,
+       const struct puffs_cn *pcn)
+{
+       struct puffs_node       *pn_targ = targ;
+       struct fuse             *fuse;
+       const char              *path = PNPATH(pn_targ);
+       int                     ret;
+
+       fuse = puffs_getspecific(pu);
+
+       set_fuse_context_uid_gid(pcn->pcn_cred);
+
+       if (fuse->op.unlink == NULL) {
+               return ENOSYS;
+       }
+
+       /* wrap up return code */
+       ret = (*fuse->op.unlink)(path);
+
+       return -ret;
+}
+
+/* remove the directory */
+/* ARGSUSED1 */
+static int
+puffs_fuse_node_rmdir(struct puffs_usermount *pu, void *opc, void *targ,
+       const struct puffs_cn *pcn)
+{
+       struct puffs_node       *pn_targ = targ;
+       struct fuse             *fuse;
+       const char              *path = PNPATH(pn_targ);
+       int                     ret;
+
+       fuse = puffs_getspecific(pu);
+
+       set_fuse_context_uid_gid(pcn->pcn_cred);
+
+       if (fuse->op.rmdir == NULL) {
+               return ENOSYS;
+       }
+
+       /* wrap up return code */
+       ret = (*fuse->op.rmdir)(path);
+
+       return -ret;
+}
+
+/* create a symbolic link */
+/* ARGSUSED1 */
+static int
+puffs_fuse_node_symlink(struct puffs_usermount *pu, void *opc,
+       struct puffs_newinfo *pni, const struct puffs_cn *pcn_src,
+       const struct vattr *va, const char *link_target)
+{
+       struct fuse             *fuse;
+       const char              *path = PCNPATH(pcn_src);
+       int                     ret;
+
+       fuse = puffs_getspecific(pu);
+
+       set_fuse_context_uid_gid(pcn_src->pcn_cred);
+
+       if (fuse->op.symlink == NULL) {
+               return ENOSYS;
+       }
+
+       /* wrap up return code */
+       ret = fuse->op.symlink(link_target, path);
+
+       if (ret == 0) {
+               ret = fuse_newnode(pu, path, va, NULL, pni, NULL);
+       }
+
+       return -ret;
+}
+
+/* rename a directory entry */
+/* ARGSUSED1 */
+static int
+puffs_fuse_node_rename(struct puffs_usermount *pu, void *opc, void *src,
+       const struct puffs_cn *pcn_src, void *targ_dir, void *targ,
+       const struct puffs_cn *pcn_targ)
+{
+       struct fuse             *fuse;
+       const char              *path_src = PCNPATH(pcn_src);
+       const char              *path_dest = PCNPATH(pcn_targ);
+       int                     ret;
+
+       fuse = puffs_getspecific(pu);
+
+       set_fuse_context_uid_gid(pcn_targ->pcn_cred);
+
+       if (fuse->op.rename == NULL) {
+               return ENOSYS;
+       }
+
+       ret = fuse->op.rename(path_src, path_dest);
+
+       if (ret == 0) {
+       }
+
+       return -ret;
+}
+
+/* create a link in the file system */
+/* ARGSUSED1 */
+static int
+puffs_fuse_node_link(struct puffs_usermount *pu, void *opc, void *targ,
+       const struct puffs_cn *pcn)
+{
+       struct puffs_node       *pn = targ;
+       struct fuse             *fuse;
+       int                     ret;
+
+       fuse = puffs_getspecific(pu);
+
+       set_fuse_context_uid_gid(pcn->pcn_cred);
+
+       if (fuse->op.link == NULL) {
+               return ENOSYS;
+       }
+
+       /* wrap up return code */
+       ret = (*fuse->op.link)(PNPATH(pn), PCNPATH(pcn));
+
+       return -ret;
+}
+
+/*
+ * fuse's regular interface provides chmod(), chown(), utimes()
+ * and truncate() + some variations, so try to fit the square block
+ * in the circle hole and the circle block .... something like that
+ */
+/* ARGSUSED3 */
+static int
+puffs_fuse_node_setattr(struct puffs_usermount *pu, void *opc,
+       const struct vattr *va, const struct puffs_cred *pcr)
+{
+       struct puffs_node       *pn = opc;
+       struct fuse             *fuse;
+       const char              *path = PNPATH(pn);
+
+       fuse = puffs_getspecific(pu);
+
+       set_fuse_context_uid_gid(pcr);
+
+       return fuse_setattr(fuse, pn, path, va);
+}
+
+/* ARGSUSED2 */
+static int
+puffs_fuse_node_open(struct puffs_usermount *pu, void *opc, int mode,
+       const struct puffs_cred *cred)
+{
+       struct puffs_node       *pn = opc;
+       struct refusenode       *rn = pn->pn_data;
+       struct fuse_file_info   *fi = &rn->file_info;
+       struct fuse             *fuse;
+       const char              *path = PNPATH(pn);
+
+       fuse = puffs_getspecific(pu);
+
+       set_fuse_context_uid_gid(cred);
+
+       /* if open, don't open again, lest risk nuking file private info */
+       if (rn->flags & RN_OPEN) {
+               rn->opencount++;
+               return 0;
+       }
+
+       /* OFLAGS(), need to convert FREAD/FWRITE to O_RD/WR */
+       fi->flags = (mode & ~(O_CREAT | O_EXCL | O_TRUNC)) - 1;
+
+       if (pn->pn_va.va_type == VDIR) {
+               if (fuse->op.opendir)
+                       fuse->op.opendir(path, fi);
+       } else {
+               if (fuse->op.open)
+                       fuse->op.open(path, fi);
+       }
+
+       rn->flags |= RN_OPEN;
+       rn->opencount++;
+
+       return 0;
+}
+
+/* ARGSUSED2 */
+static int
+puffs_fuse_node_close(struct puffs_usermount *pu, void *opc, int fflag,
+       const struct puffs_cred *pcr)
+{
+       struct puffs_node       *pn = opc;
+       struct refusenode       *rn = pn->pn_data;
+       struct fuse             *fuse;
+       struct fuse_file_info   *fi;
+       const char              *path = PNPATH(pn);
+       int                     ret;
+
+       fuse = puffs_getspecific(pu);
+       fi = &rn->file_info;
+       ret = 0;
+
+       set_fuse_context_uid_gid(pcr);
+
+       if (rn->flags & RN_OPEN) {
+               if (pn->pn_va.va_type == VDIR) {
+                       if (fuse->op.releasedir)
+                               ret = fuse->op.releasedir(path, fi);
+               } else {
+                       if (fuse->op.release)
+                               ret = fuse->op.release(path, fi);
+               }
+       }
+       rn->flags &= ~RN_OPEN;
+       rn->opencount--;
+
+       return ret;
+}
+
+/* read some more from the file */
+/* ARGSUSED5 */
+static int
+puffs_fuse_node_read(struct puffs_usermount *pu, void *opc, uint8_t *buf,
+       off_t offset, size_t *resid, const struct puffs_cred *pcr,
+       int ioflag)
+{
+       struct puffs_node       *pn = opc;
+       struct refusenode       *rn = pn->pn_data;
+       struct fuse             *fuse;
+       const char              *path = PNPATH(pn);
+       size_t                  maxread;
+       int                     ret;
+
+       fuse = puffs_getspecific(pu);
+       if (fuse->op.read == NULL) {
+               return ENOSYS;
+       }
+
+       set_fuse_context_uid_gid(pcr);
+
+       maxread = *resid;
+       if (maxread > pn->pn_va.va_size - offset) {
+               /*LINTED*/
+               maxread = pn->pn_va.va_size - offset;
+       }
+       if (maxread == 0)
+               return 0;
+
+       ret = (*fuse->op.read)(path, (char *)buf, maxread, offset,
+           &rn->file_info);
+
+       if (ret > 0) {
+               *resid -= ret;
+               ret = 0;
+       }
+
+       return -ret;
+}
+
+/* write to the file */
+/* ARGSUSED0 */
+static int
+puffs_fuse_node_write(struct puffs_usermount *pu, void *opc, uint8_t *buf,
+       off_t offset, size_t *resid, const struct puffs_cred *pcr,
+       int ioflag)
+{
+       struct puffs_node       *pn = opc;
+       struct refusenode       *rn = pn->pn_data;
+       struct fuse             *fuse;
+       const char              *path = PNPATH(pn);
+       int                     ret;
+
+       fuse = puffs_getspecific(pu);
+       if (fuse->op.write == NULL) {
+               return ENOSYS;
+       }
+
+       set_fuse_context_uid_gid(pcr);
+
+       if (ioflag & PUFFS_IO_APPEND)
+               offset = pn->pn_va.va_size;
+
+       ret = (*fuse->op.write)(path, (char *)buf, *resid, offset,
+           &rn->file_info);
+
+       if (ret > 0) {
+               if ((uint64_t)(offset + ret) > pn->pn_va.va_size)
+                       pn->pn_va.va_size = offset + ret;
+               *resid -= ret;
+               ret = 0;
+       }
+
+       return -ret;
+}
+
+
+/* ARGSUSED3 */
+static int
+puffs_fuse_node_readdir(struct puffs_usermount *pu, void *opc,
+       struct dirent *dent, off_t *readoff, size_t *reslen,
+       const struct puffs_cred *pcr, int *eofflag,
+       off_t *cookies, size_t *ncookies)
+{
+       struct puffs_node       *pn = opc;
+       struct refusenode       *rn = pn->pn_data;
+       struct puffs_fuse_dirh  *dirh;
+       struct fuse             *fuse;
+       struct dirent           *fromdent;
+       const char              *path = PNPATH(pn);
+       int                     ret;
+
+       fuse = puffs_getspecific(pu);
+       if (fuse->op.readdir == NULL && fuse->op.getdir == NULL) {
+               return ENOSYS;
+       }
+
+       set_fuse_context_uid_gid(pcr);
+
+       if (pn->pn_va.va_type != VDIR)
+               return ENOTDIR;
+
+       dirh = &rn->dirh;
+
+       /*
+        * if we are starting from the beginning, slurp entire directory
+        * into our buffers
+        */
+       if (*readoff == 0) {
+               /* free old buffers */
+               free(dirh->dbuf);
+               memset(dirh, 0, sizeof(struct puffs_fuse_dirh));
+
+               if (fuse->op.readdir)
+                       ret = fuse->op.readdir(path, dirh, puffs_fuse_fill_dir,
+                           0, &rn->file_info);
+               else
+                       ret = fuse->op.getdir(path, dirh, puffs_fuse_dirfil);
+               if (ret)
+                       return -ret;
+       }
+
+        /* Both op.readdir and op.getdir read full directory */
+        *eofflag = 1;
+
+       /* now, stuff results into the kernel buffers */
+       while (*readoff < (off_t)(dirh->bufsize - dirh->reslen)) {
+               /*LINTED*/
+               fromdent = (struct dirent *)((uint8_t *)dirh->dbuf + *readoff);
+
+               if (*reslen < _DIRENT_SIZE(fromdent))
+                       break;
+
+               memcpy(dent, fromdent, _DIRENT_SIZE(fromdent));
+               *readoff += _DIRENT_SIZE(fromdent);
+               *reslen -= _DIRENT_SIZE(fromdent);
+
+               dent = _DIRENT_NEXT(dent);
+       }
+
+       return 0;
+}
+
+/* ARGSUSED */
+static int
+puffs_fuse_node_reclaim(struct puffs_usermount *pu, void *opc)
+{
+       struct puffs_node       *pn = opc;
+
+       nukern(pn);
+       return 0;
+}
+
+/* ARGSUSED1 */
+static int
+puffs_fuse_fs_unmount(struct puffs_usermount *pu, int flags)
+{
+       struct fuse             *fuse;
+
+       fuse = puffs_getspecific(pu);
+       if (fuse->op.destroy == NULL) {
+               return 0;
+       }
+       (*fuse->op.destroy)(fuse);
+        return 0;
+}
+
+/* ARGSUSED0 */
+static int
+puffs_fuse_fs_sync(struct puffs_usermount *pu, int flags,
+            const struct puffs_cred *cr)
+{
+       set_fuse_context_uid_gid(cr);
+        return 0;
+}
+
+/* ARGSUSED2 */
+static int
+puffs_fuse_fs_statvfs(struct puffs_usermount *pu, struct statvfs *svfsb)
+{
+       struct fuse             *fuse;
+       int                     ret;
+
+       fuse = puffs_getspecific(pu);
+       if (fuse->op.statfs == NULL) {
+               if ((ret = statvfs(PNPATH(puffs_getroot(pu)), svfsb)) == -1) {
+                       return errno;
+               }
+       } else {
+               ret = fuse->op.statfs(PNPATH(puffs_getroot(pu)), svfsb);
+       }
+
+        return -ret;
+}
+
+
+/* End of puffs_fuse operations */
+/* ARGSUSED3 */
+int
+fuse_main_real(int argc, char **argv, const struct fuse_operations *ops,
+       size_t size, void *userdata)
+{
+       struct fuse     *fuse;
+       char            *mountpoint;
+       int              multithreaded;
+       int              fd;
+
+       fuse = fuse_setup(argc, argv, ops, size, &mountpoint, &multithreaded,
+                       &fd);
+
+       return fuse_loop(fuse);
+}
+
+/*
+ * XXX: just defer the operation until fuse_new() when we have more
+ * info on our hands.  The real beef is why's this separate in fuse in
+ * the first place?
+ */
+/* ARGSUSED1 */
+struct fuse_chan *
+fuse_mount(const char *dir, struct fuse_args *args)
+{
+       struct fuse_chan        *fc;
+       char                     name[64];
+
+       if ((fc = calloc(1, sizeof(*fc))) == NULL) {
+               err(EXIT_FAILURE, "fuse_mount");
+       }
+       fc->dead = 0;
+
+       if ((fc->dir = strdup(dir)) == NULL) {
+               err(EXIT_FAILURE, "fuse_mount");
+       }
+
+       /*
+        * we need to deep copy the args struct - some fuse file
+        * systems "clean up" the argument vector for "security
+        * reasons"
+        */
+       fc->args = fuse_opt_deep_copy_args(args->argc, args->argv);
+
+       if (args->argc > 0) {
+               set_refuse_mount_name(args->argv, name, sizeof(name));
+               if ((args->argv[0] = strdup(name)) == NULL)
+                       err(1, "fuse_mount");
+       }
+       
+       return fc;
+}
+
+/* ARGSUSED1 */
+struct fuse *
+fuse_new(struct fuse_chan *fc, struct fuse_args *args,
+       const struct fuse_operations *ops, size_t size, void *userdata)
+{
+       struct puffs_usermount  *pu;
+       struct fuse_context     *fusectx;
+       struct puffs_pathobj    *po_root;
+       struct puffs_node       *pn_root;
+       struct puffs_ops        *pops;
+       struct refusenode       *rn_root;
+       struct statvfs           svfsb;
+       struct stat              st;
+       struct fuse             *fuse;
+       extern int               puffs_fakecc;
+       char                     name[64];
+       char                    *argv0;
+
+       if ((fuse = calloc(1, sizeof(*fuse))) == NULL) {
+               err(EXIT_FAILURE, "fuse_new");
+       }
+
+       /* copy fuse ops to their own structure */
+       (void) memcpy(&fuse->op, ops, sizeof(fuse->op));
+
+       fusectx = fuse_get_context();
+       fusectx->fuse = fuse;
+       fusectx->uid = 0;
+       fusectx->gid = 0;
+       fusectx->pid = 0;
+       fusectx->private_data = userdata;
+
+       fuse->fc = fc;
+
+       /* initialise the puffs operations structure */
+        PUFFSOP_INIT(pops);
+
+        PUFFSOP_SET(pops, puffs_fuse, fs, sync);
+        PUFFSOP_SET(pops, puffs_fuse, fs, statvfs);
+        PUFFSOP_SET(pops, puffs_fuse, fs, unmount);
+
+       /*
+        * XXX: all of these don't possibly need to be
+        * unconditionally set
+        */
+        PUFFSOP_SET(pops, puffs_fuse, node, lookup);
+        PUFFSOP_SET(pops, puffs_fuse, node, getattr);
+        PUFFSOP_SET(pops, puffs_fuse, node, setattr);
+        PUFFSOP_SET(pops, puffs_fuse, node, readdir);
+        PUFFSOP_SET(pops, puffs_fuse, node, readlink);
+        PUFFSOP_SET(pops, puffs_fuse, node, mknod);
+        PUFFSOP_SET(pops, puffs_fuse, node, create);
+        PUFFSOP_SET(pops, puffs_fuse, node, remove);
+        PUFFSOP_SET(pops, puffs_fuse, node, mkdir);
+        PUFFSOP_SET(pops, puffs_fuse, node, rmdir);
+        PUFFSOP_SET(pops, puffs_fuse, node, symlink);
+        PUFFSOP_SET(pops, puffs_fuse, node, rename);
+        PUFFSOP_SET(pops, puffs_fuse, node, link);
+        PUFFSOP_SET(pops, puffs_fuse, node, open);
+        PUFFSOP_SET(pops, puffs_fuse, node, close);
+        PUFFSOP_SET(pops, puffs_fuse, node, read);
+        PUFFSOP_SET(pops, puffs_fuse, node, write);
+        PUFFSOP_SET(pops, puffs_fuse, node, reclaim);
+
+       argv0 = (*args->argv[0] == 0x0) ? fc->args->argv[0] : args->argv[0];
+       set_refuse_mount_name(&argv0, name, sizeof(name));
+
+       puffs_fakecc = 1; /* XXX */
+       pu = puffs_init(pops, _PATH_PUFFS, name, fuse,
+                        PUFFS_FLAG_BUILDPATH
+                          | PUFFS_FLAG_HASHPATH
+                          | PUFFS_KFLAG_NOCACHE);
+       if (pu == NULL) {
+               err(EXIT_FAILURE, "puffs_init");
+       }
+       fc->pu = pu;
+
+       pn_root = newrn(pu);
+       puffs_setroot(pu, pn_root);
+       rn_root = pn_root->pn_data;
+       rn_root->flags |= RN_ROOT;
+
+       po_root = puffs_getrootpathobj(pu);
+       if ((po_root->po_path = strdup("/")) == NULL)
+               err(1, "fuse_new");
+       po_root->po_len = 1;
+       puffs_path_buildhash(pu, po_root);
+
+       /* sane defaults */
+       puffs_vattr_null(&pn_root->pn_va);
+       pn_root->pn_va.va_type = VDIR;
+       pn_root->pn_va.va_mode = 0755;
+       if (fuse->op.getattr)
+               if (fuse->op.getattr(po_root->po_path, &st) == 0)
+                       puffs_stat2vattr(&pn_root->pn_va, &st);
+       assert(pn_root->pn_va.va_type == VDIR);
+
+       if (fuse->op.init)
+               fusectx->private_data = fuse->op.init(NULL); /* XXX */
+
+       puffs_set_prepost(pu, set_fuse_context_pid, NULL);
+
+       puffs_zerostatvfs(&svfsb);
+       if (puffs_mount(pu, fc->dir, MNT_NODEV | MNT_NOSUID, pn_root) == -1) {
+               err(EXIT_FAILURE, "puffs_mount: directory \"%s\"", fc->dir);
+       }
+
+       return fuse;
+}
+
+int
+fuse_loop(struct fuse *fuse)
+{
+
+       return puffs_mainloop(fuse->fc->pu);
+}
+
+void
+fuse_destroy(struct fuse *fuse)
+{
+
+       /*
+        * TODO: needs to assert the fs is quiescent, i.e. no other
+        * threads exist
+        */
+
+       delete_context_key();
+       /* XXXXXX: missing stuff */
+       free(fuse);
+}
+
+void
+fuse_exit(struct fuse *fuse)
+{
+       
+       /* XXX: puffs_exit() is WRONG */
+       if (fuse->fc->dead == 0)
+               puffs_exit(fuse->fc->pu, 1);
+       fuse->fc->dead = 1;
+}
+
+/*
+ * XXX: obviously not the most perfect of functions, but needs some
+ * puffs tweaking for a better tomorrow
+ */
+/*ARGSUSED*/
+void
+fuse_unmount(const char *mp, struct fuse_chan *fc)
+{
+
+       /* XXX: puffs_exit() is WRONG */
+       if (fc->dead == 0)
+               puffs_exit(fc->pu, 1);
+       fc->dead = 1;
+}
+
+/*ARGSUSED*/
+void
+fuse_unmount_compat22(const char *mp)
+{
+
+       return;
+}
+
+/* The next function "exposes" struct fuse to userland.  Not much
+* that we can do about this, as we're conforming to a defined
+* interface.  */
+
+void
+fuse_teardown(struct fuse *fuse, char *mountpoint)
+{
+       fuse_unmount(mountpoint, fuse->fc);
+       fuse_destroy(fuse);
+}
diff --git a/lib/librefuse/refuse_opt.c b/lib/librefuse/refuse_opt.c
new file mode 100644 (file)
index 0000000..f44bdc2
--- /dev/null
@@ -0,0 +1,368 @@
+/*     $NetBSD: refuse_opt.c,v 1.14 2009/01/19 09:56:06 lukem Exp $    */
+
+/*-
+ * Copyright (c) 2007 Juan Romero Pardines.
+ * 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 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.
+ */
+
+/*
+ * TODO:
+ *     * -oblah,foo... works, but the options are not enabled.
+ *     * -ofoo=%s (accepts a string) or -ofoo=%u (int) is not
+ *       supported for now.
+ *     * void *data: how is it used? I think it's used to enable
+ *       options or pass values for the matching options.
+ */
+
+#include <sys/types.h>
+
+#include <err.h>
+#include <fuse.h>
+#include <fuse_opt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef FUSE_OPT_DEBUG
+#define DPRINTF(x)     do { printf x; } while ( /* CONSTCOND */ 0)
+#else
+#define DPRINTF(x)
+#endif
+
+enum {
+       KEY_HELP,
+       KEY_VERBOSE,
+       KEY_VERSION
+};
+
+struct fuse_opt_option {
+       const struct fuse_opt *fop;
+       char *option;
+       int key;
+       void *data;
+};
+
+static int fuse_opt_popt(struct fuse_opt_option *, const struct fuse_opt *);
+
+/* 
+ * Public API.
+ *
+ * The following functions always return 0:
+ *
+ * int fuse_opt_add_opt(char **, const char *);
+ *
+ * We implement the next ones:
+ *
+ * int fuse_opt_add_arg(struct fuse_args *, const char *);
+ * void        fuse_opt_free_args(struct fuse_args *);
+ * int fuse_opt_insert_arg(struct fuse_args *, const char *);
+ * int fuse_opt_match(const struct fuse_opt *, const char *);
+ * int fuse_opt_parse(struct fuse_args *, void *,
+ *                    const struct fuse_opt *, fuse_opt_proc_t);
+ *
+ */
+
+/* ARGSUSED */
+int
+fuse_opt_add_arg(struct fuse_args *args, const char *arg)
+{
+       struct fuse_args        *ap;
+
+       if (args->allocated == 0) {
+               ap = fuse_opt_deep_copy_args(args->argc, args->argv);
+               args->argv = ap->argv;
+               args->argc = ap->argc;
+               args->allocated = ap->allocated;
+               (void) free(ap);
+       } else if (args->allocated == args->argc) {
+               void *a;
+               int na = args->allocated + 10;
+
+               if ((a = realloc(args->argv, na * sizeof(*args->argv))) == NULL)
+                       return -1;
+
+               args->argv = a;
+               args->allocated = na;
+       }
+       DPRINTF(("%s: arguments passed: [arg:%s]\n", __func__, arg));
+       if ((args->argv[args->argc++] = strdup(arg)) == NULL)
+               err(1, "fuse_opt_add_arg");
+       args->argv[args->argc] = NULL;
+        return 0;
+}
+
+struct fuse_args *
+fuse_opt_deep_copy_args(int argc, char **argv)
+{
+       struct fuse_args        *ap;
+       int                      i;
+
+       if ((ap = malloc(sizeof(*ap))) == NULL)
+               err(1, "_fuse_deep_copy_args");
+       /* deep copy args structure into channel args */
+       ap->allocated = ((argc / 10) + 1) * 10;
+
+       if ((ap->argv = calloc((size_t)ap->allocated,
+           sizeof(*ap->argv))) == NULL)
+               err(1, "_fuse_deep_copy_args");
+
+       for (i = 0; i < argc; i++) {
+               if ((ap->argv[i] = strdup(argv[i])) == NULL)
+                       err(1, "_fuse_deep_copy_args");
+       }
+       ap->argv[ap->argc = i] = NULL;
+       return ap;
+}
+
+void
+fuse_opt_free_args(struct fuse_args *ap)
+{
+       int     i;
+
+       for (i = 0; i < ap->argc; i++) {
+               free(ap->argv[i]);
+       }
+       free(ap->argv);
+       ap->argv = NULL;
+       ap->allocated = ap->argc = 0;
+}
+
+/* ARGSUSED */
+int
+fuse_opt_insert_arg(struct fuse_args *args, int pos, const char *arg)
+{
+       int     i;
+       int     na;
+       void   *a;
+
+       DPRINTF(("%s: arguments passed: [pos=%d] [arg=%s]\n",
+           __func__, pos, arg));
+       if (args->argv == NULL) {
+               na = 10;
+               a = malloc(na * sizeof(*args->argv));
+       } else {
+               na = args->allocated + 10;
+               a = realloc(args->argv, na * sizeof(*args->argv));
+       }
+       if (a == NULL) {
+               warn("fuse_opt_insert_arg");
+               return -1;
+       }
+       args->argv = a;
+       args->allocated = na;
+
+       for (i = args->argc++; i > pos; --i) {
+               args->argv[i] = args->argv[i - 1];
+       }
+       if ((args->argv[pos] = strdup(arg)) == NULL)
+               err(1, "fuse_opt_insert_arg");
+       args->argv[args->argc] = NULL;
+       return 0;
+}
+
+/* ARGSUSED */
+int fuse_opt_add_opt(char **opts, const char *opt)
+{
+       DPRINTF(("%s: arguments passed: [opts=%s] [opt=%s]\n",
+           __func__, *opts, opt));
+       return 0;
+}
+
+/*
+ * Returns 0 if opt was matched with any option from opts,
+ * otherwise returns 1.
+ */
+int
+fuse_opt_match(const struct fuse_opt *opts, const char *opt)
+{
+       while (opts++) {
+               if (strcmp(opt, opts->templ) == 0)
+                       return 0;
+       }
+
+       return 1;
+}
+
+/*
+ * Returns 0 if foo->option was matched with any option from opts,
+ * and sets the following on match:
+ *
+ *     * foo->key is set to the foo->fop->value if offset == -1.
+ *     * foo->fop points to the matched struct opts.
+ *
+ * otherwise returns 1.
+ */
+static int
+fuse_opt_popt(struct fuse_opt_option *foo, const struct fuse_opt *opts)
+{
+       int i, found = 0;
+       char *match;
+       
+       if (!foo->option) {
+               (void)fprintf(stderr, "fuse: missing argument after -o\n");
+               return 1;
+       }
+       /* 
+        * iterate over argv and opts to see
+        * if there's a match with any template.
+        */
+       for (match = strtok(foo->option, ",");
+            match; match = strtok(NULL, ",")) {
+
+               DPRINTF(("%s: specified option='%s'\n", __func__, match));
+               found = 0;
+
+               for (i = 0; opts && opts->templ; opts++, i++) {
+
+                       DPRINTF(("%s: opts->templ='%s' opts->offset=%d "
+                           "opts->value=%d\n", __func__, opts->templ,
+                           opts->offset, opts->value));
+
+                       /* option is ok */
+                       if (strcmp(match, opts->templ) == 0) {
+                               DPRINTF(("%s: option matched='%s'\n",
+                                   __func__, match));
+                               found++;
+                               /*
+                                * our fop pointer now points 
+                                * to the matched struct opts.
+                                */
+                               foo->fop = opts;
+                               /* 
+                                * assign default key value, necessary for
+                                * KEY_HELP, KEY_VERSION and KEY_VERBOSE.
+                                */
+                               if (foo->fop->offset == -1)
+                                       foo->key = foo->fop->value;
+                               /* reset counter */
+                               opts -= i;
+                               break;
+                       }
+               }
+               /* invalid option */
+               if (!found) {
+                       (void)fprintf(stderr, "fuse: '%s' is not a "
+                           "valid option\n", match);
+                       return 1;
+               }
+       }
+
+       return 0;
+}
+
+/* ARGSUSED1 */
+int
+fuse_opt_parse(struct fuse_args *args, void *data,
+        const struct fuse_opt *opts, fuse_opt_proc_t proc)
+{
+       struct fuse_opt_option foo;
+       char *buf;
+       int i, rv = 0;
+
+       if (!args || !args->argv || !args->argc || !proc)
+               return 0;
+
+       if (args->argc == 1)
+               return proc(foo.data, *args->argv, FUSE_OPT_KEY_OPT, args);
+
+       /* the real loop to process the arguments */
+       for (i = 1; i < args->argc; i++) {
+
+               /* assign current argv string */
+               foo.option = buf = args->argv[i];
+
+               /* argvn != -foo... */
+               if (buf[0] != '-') {
+
+                       foo.key = FUSE_OPT_KEY_NONOPT;
+                       rv = proc(foo.data, foo.option, foo.key, args);
+                       if (rv != 0)
+                               break;
+
+               /* -o was specified... */
+               } else if (buf[0] == '-' && buf[1] == 'o') {
+
+                       /* -oblah,foo... */
+                       if (buf[2]) {
+                               /* skip -o */
+                               foo.option = args->argv[i] + 2;
+                       /* -o blah,foo... */
+                       } else {
+                               /* 
+                                * skip current argv and pass to the
+                                * next one to parse the options.
+                                */
+                               ++i;
+                               foo.option = args->argv[i];
+                       }
+
+                       rv = fuse_opt_popt(&foo, opts);
+                       if (rv != 0)
+                               break;
+
+               /* help/version/verbose argument */
+               } else if (buf[0] == '-' && buf[1] != 'o') {
+                       /* 
+                        * check if the argument matches
+                        * with any template in opts.
+                        */
+                       rv = fuse_opt_popt(&foo, opts);
+                       if (rv != 0) {
+                               break;
+                       } else {
+                               DPRINTF(("%s: foo.fop->templ='%s' "
+                                   "foo.fop->offset: %d "
+                                   "foo.fop->value: %d\n",
+                                   __func__, foo.fop->templ,
+                                   foo.fop->offset, foo.fop->value));
+
+                               /* argument needs to be discarded */
+                               if (foo.key == FUSE_OPT_KEY_DISCARD) {
+                                       rv = 1;
+                                       break;
+                               }
+
+                               /* process help/version argument */
+                               if (foo.key != KEY_VERBOSE &&
+                                   foo.key != FUSE_OPT_KEY_KEEP) {
+                                       rv = proc(foo.data, foo.option,
+                                                 foo.key, args);
+                                       break;
+                               } else {
+                                       /* process verbose argument */
+                                       rv = proc(foo.data, foo.option,
+                                                      foo.key, args);
+                                       if (rv != 0)
+                                               break;
+                               }
+                       }
+               /* unknown option, how could that happen? */
+               } else {
+                       DPRINTF(("%s: unknown option\n", __func__));
+                       rv = 1;
+                       break;
+               }
+       }
+
+       return rv;
+}
diff --git a/lib/librefuse/shlib_version b/lib/librefuse/shlib_version
new file mode 100644 (file)
index 0000000..b82cf93
--- /dev/null
@@ -0,0 +1,4 @@
+#      $NetBSD: shlib_version,v 1.3 2010/05/21 10:53:41 pooka Exp $
+#
+major=2
+minor=0
index 8a24160af695a30f389bd7dcb3296593d57683b4..46a8bca85e636a259bac7badc4f4fb22ca447262 100644 (file)
@@ -10,6 +10,7 @@
 #include <minix/syslib.h>
 #include <minix/rs.h>
 #include <paths.h>
+#include <unistd.h>
 #define OK     0
 
 #ifdef __weak_alias
@@ -18,10 +19,10 @@ __weak_alias(umount, _umount)
 __weak_alias(umount2, _umount2)
 #endif
 
-
-#define FSPATH "/sbin/"
 #define FSDEFAULT "mfs"
 
+static char fspath[] = "/sbin/:/usr/pkg/bin/"; /* Must include trailing '/' */
+
 PRIVATE int rs_down(char *label)
 {
        char cmd[200];
@@ -39,7 +40,7 @@ int mountflags;
   message m;
   struct stat statbuf;
   char label[16];
-  char path[60];
+  char path[PATH_MAX];
   char cmd[200];
   char *p;
   int reuse = 0;
@@ -49,6 +50,7 @@ int mountflags;
   if (type == NULL) type = __UNCONST(FSDEFAULT);
   if (args == NULL) args = __UNCONST("");
   reuse = 0;
+  memset(path, '\0', sizeof(path));
 
   /* Check mount flags */
   if(mountflags & MS_REUSE) {
@@ -94,19 +96,6 @@ int mountflags;
   /* Tell VFS that we are passing in a 16-byte label. */
   mountflags |= MS_LABEL16;
 
-  /* See if the given type is even remotely valid. */
-  if(strlen(FSPATH)+strlen(type) >= sizeof(path)) {
-       errno = E2BIG;
-       return -1;
-  }
-  strcpy(path, FSPATH);
-  strcat(path, type);
-  
-  if(stat(path, &statbuf) != 0) {
-       errno = EINVAL;
-       return -1;
-  }
-
   /* Sanity check on user input. */
   if(strchr(args, '\'')) {
        errno = EINVAL;
@@ -114,16 +103,41 @@ int mountflags;
   }
   /* start the fs-server if not using existing one */
   if (!use_existing) {
-       if(strlen(_PATH_SERVICE)+strlen(path)+strlen(label)+
-               strlen(args)+50 >= sizeof(cmd)) {
+       /* See if the given type is even remotely valid. */
+
+       char *testpath;
+       testpath = strtok(fspath, ":");
+
+       do {
+               if (strlen(testpath) + strlen(type) >= sizeof(path)) {
+                       errno = E2BIG;
+                       return(-1);
+               }
+
+               strcpy(path, testpath);
+               strcat(path, type);
+
+               if (access(path, F_OK) == 0) break;
+
+       } while ((testpath = strtok(NULL, ":")) != NULL);
+
+       if (testpath == NULL) {
+               /* We were not able to find type somewhere in "fspath" */
+               errno = EINVAL;
+               return(-1);
+       }
+
+       if (strlen(_PATH_SERVICE) + strlen(path) + strlen(label) +
+           strlen(args) + 50 >= sizeof(cmd)) {
                errno = E2BIG;
                return -1;
        }
 
-       sprintf(cmd, _PATH_SERVICE " %sup %s -label '%s' -args '%s%s'",
-               reuse ? "-r ": "", path, label, args[0] ? "-o " : "", args);
+       sprintf(cmd, _PATH_SERVICE " %sup %s -label '%s' -args '%s %s %s%s'",
+               reuse ? "-r ": "", path, label, special, name,
+               args[0] ? "-o " : "", args);
 
-       if((r = system(cmd)) != 0) {
+       if ((r = system(cmd)) != 0) {
                fprintf(stderr, "mount: couldn't run %s\n", cmd);
                errno = r;
                return -1;
@@ -139,8 +153,8 @@ int mountflags;
   m.m1_p3 = label;
   r = _syscall(VFS_PROC_NR, MOUNT, &m);
 
-  if(r != OK) {
-       /* If mount() failed, tell RS to shutdown MFS process.
+  if (r != OK && !use_existing) {
+       /* If mount() failed, tell RS to shutdown FS process.
         * No error check - won't do anything with this error anyway.
         */
        rs_down(label);
index 566b071bc1c693416d478b21af11008bd95a0656..5da76253626f2d5f7eb44583e649a3e9438e2a46 100644 (file)
@@ -10,6 +10,26 @@ __weak_alias(pread, _pread)
 
 #include <lib.h>
 #include <unistd.h>
+#include <minix/u64.h>
+
+ssize_t pread64(int fd, void *buffer, size_t nbytes, u64_t where)
+{
+       u64_t here;
+       ssize_t r;
+
+       if (lseek64(fd, make64(0,0), SEEK_CUR, &here) < 0) return(-1);
+       if (lseek64(fd, where, SEEK_SET, NULL) < 0) return(-1);
+       if ((r = read(fd, buffer, nbytes)) < 0) {
+               int e ; errno;
+               lseek64(fd, here, SEEK_SET, NULL);
+               errno = e;
+               return(-1);
+       }
+
+       if (lseek64(fd, here, SEEK_SET, NULL) < 0) return(-1);
+
+       return(r);
+}
 
 ssize_t pread(int fd, void *buffer, size_t nbytes, off_t where)
 {
index 6783804c87af30888ceac8d8f741c2c42883b8fd..f2c07b3a42bc0a32bc9e3f278bb00e674999ceb4 100644 (file)
@@ -8,6 +8,27 @@
 __weak_alias(pwrite, _pwrite)
 #endif
 
+#include <minix/u64.h>
+
+ssize_t pwrite64(int fd, const void *buffer, size_t nbytes, u64_t where)
+{
+       u64_t here;
+       ssize_t w;
+
+       if (lseek64(fd, make64(0,0), SEEK_CUR, &here) < 0) return(-1);
+       if (lseek64(fd, where, SEEK_SET, NULL) < 0) return(-1);
+       if ((w = write(fd, buffer, nbytes)) < 0) {
+               int e = errno;
+               lseek64(fd, here, SEEK_SET, NULL);
+               errno = e;
+               return(-1);
+       }
+
+       if (lseek64(fd, here, SEEK_SET, NULL) < 0) return(-1);
+
+       return(w);
+}
+
 ssize_t pwrite(int fd, const void *buffer, size_t nbytes, off_t where)
 {
        off_t here;
index 5babf6fb5b93d95088bbfab9928d3aaf3708a812..3dfe8c1e2f09bcbdf8375e988b4f860c8c87cc6e 100644 (file)
@@ -5,14 +5,23 @@ struct ucred_old
 {
        pid_t   pid;
        short   uid;
-       char    gid;
+       char   gid;
 };
 
 struct ucred
 {
-       pid_t   pid;
-       uid_t   uid;
-       gid_t   gid;
+       pid_t   pid;
+       uid_t   uid;
+       gid_t   gid;
+};
+
+/* Userland's view of credentials. This should not change */
+struct uucred {
+        unsigned short  cr_unused;              /* not used, compat */
+        uid_t           cr_uid;                 /* effective user id */
+        gid_t           cr_gid;                 /* effective group id */
+        short           cr_ngroups;             /* number of groups */
+        gid_t           cr_groups[NGROUPS_MAX];     /* groups */
 };
 
 #endif
index 4457754246665564696df54b068ced707d99dce7..eef4fffd38ee7206935c22ba9f12335e667e7330 100644 (file)
@@ -327,6 +327,8 @@ pid_t getnpid(endpoint_t proc_ep);
 uid_t getnuid(endpoint_t proc_ep);
 gid_t getngid(endpoint_t proc_ep);
 int getnucred(endpoint_t proc_ep, struct ucred *ucred);
+ssize_t pread64(int fd, void *buf, size_t count, u64_t where);
+ssize_t pwrite64(int fd, const void *buf, size_t count, u64_t where);
 #endif /* __MINIX */
 #endif /* __minix */