Module Name:    src
Committed By:   pooka
Date:           Tue Jul  6 14:16:45 UTC 2010

Added Files:
        src/share/examples/puffs/dtfs: README
        src/tests/fs/puffs/h_dtfs: Makefile dtfs.c dtfs.h dtfs_subr.c
            dtfs_vfsops.c dtfs_vnops.c
Removed Files:
        src/share/examples/puffs/dtfs: Makefile dtfs.c dtfs.h dtfs_subr.c
            dtfs_vfsops.c dtfs_vnops.c

Log Message:
Move the Delectable Test File System from share/examples/puffs/dtfs
to tests/fs/puffs/h_dtfs.

No functional change (apart from adjusting the Makefile for test builds).


To generate a diff of this commit:
cvs rdiff -u -r1.1 -r0 src/share/examples/puffs/dtfs/Makefile
cvs rdiff -u -r0 -r1.1 src/share/examples/puffs/dtfs/README
cvs rdiff -u -r1.42 -r0 src/share/examples/puffs/dtfs/dtfs.c
cvs rdiff -u -r1.21 -r0 src/share/examples/puffs/dtfs/dtfs.h \
    src/share/examples/puffs/dtfs/dtfs_subr.c
cvs rdiff -u -r1.24 -r0 src/share/examples/puffs/dtfs/dtfs_vfsops.c
cvs rdiff -u -r1.43 -r0 src/share/examples/puffs/dtfs/dtfs_vnops.c
cvs rdiff -u -r0 -r1.1 src/tests/fs/puffs/h_dtfs/Makefile \
    src/tests/fs/puffs/h_dtfs/dtfs.c src/tests/fs/puffs/h_dtfs/dtfs.h \
    src/tests/fs/puffs/h_dtfs/dtfs_subr.c \
    src/tests/fs/puffs/h_dtfs/dtfs_vfsops.c \
    src/tests/fs/puffs/h_dtfs/dtfs_vnops.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Added files:

Index: src/share/examples/puffs/dtfs/README
diff -u /dev/null src/share/examples/puffs/dtfs/README:1.1
--- /dev/null	Tue Jul  6 14:16:45 2010
+++ src/share/examples/puffs/dtfs/README	Tue Jul  6 14:16:45 2010
@@ -0,0 +1,3 @@
+	$NetBSD: README,v 1.1 2010/07/06 14:16:45 pooka Exp $
+
+dtfs moved to src/tests/fs/puffs/h_dtfs

Index: src/tests/fs/puffs/h_dtfs/Makefile
diff -u /dev/null src/tests/fs/puffs/h_dtfs/Makefile:1.1
--- /dev/null	Tue Jul  6 14:16:45 2010
+++ src/tests/fs/puffs/h_dtfs/Makefile	Tue Jul  6 14:16:44 2010
@@ -0,0 +1,11 @@
+#	$NetBSD: Makefile,v 1.1 2010/07/06 14:16:44 pooka Exp $
+
+TESTS_C=	h_dtfs
+
+SRCS.h_dtfs=	dtfs.c dtfs_vfsops.c dtfs_vnops.c dtfs_subr.c
+LDADD+=		-lpuffs -lutil
+
+WARNS=2
+DBG=-g
+
+.include <bsd.test.mk>
Index: src/tests/fs/puffs/h_dtfs/dtfs.c
diff -u /dev/null src/tests/fs/puffs/h_dtfs/dtfs.c:1.1
--- /dev/null	Tue Jul  6 14:16:45 2010
+++ src/tests/fs/puffs/h_dtfs/dtfs.c	Tue Jul  6 14:16:44 2010
@@ -0,0 +1,254 @@
+/*	$NetBSD: dtfs.c,v 1.1 2010/07/06 14:16:44 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.
+ */
+
+/*
+ * Delectable Test File System: a simple in-memory file system which
+ * demonstrates the use of puffs.
+ * (a.k.a. Detrempe FS ...)
+ */
+
+#include <sys/types.h>
+
+#include <err.h>
+#include <mntopts.h>
+#include <paths.h>
+#include <puffs.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "dtfs.h"
+
+#ifdef DEEP_ROOTED_CLUE
+#define FSNAME "detrempe"
+#else
+#define FSNAME "dt"
+#endif
+#define MAXREQMAGIC -37
+
+static struct puffs_usermount *gpu;
+static struct dtfs_mount gdtm;
+int dynamicfh;
+int straightflush;
+
+static void usage(void);
+
+static void
+usage()
+{
+
+	fprintf(stderr, "usage: %s [-bsdftl] [-c hashbuckets] [-m maxreqsize] "
+	    "[-n typename]\n       [-o mntopt] [-o puffsopt] [-p prot] "
+	    "[-r rootnodetype]\n       detrempe /mountpoint\n", getprogname());
+	exit(1);
+}
+
+static void
+wipe_the_sleep_out_of_my_eyes(int v)
+{
+
+	gdtm.dtm_needwakeup++;
+}
+
+static void
+loopfun(struct puffs_usermount *pu)
+{
+	struct dtfs_mount *dtm = puffs_getspecific(pu);
+	struct dtfs_poll *dp;
+
+	while (dtm->dtm_needwakeup) {
+		dtm->dtm_needwakeup--;
+		dp = LIST_FIRST(&dtm->dtm_pollent);
+		if (dp == NULL)
+			return;
+
+		LIST_REMOVE(dp, dp_entries);
+		puffs_cc_continue(dp->dp_pcc);
+	}
+}
+
+int
+main(int argc, char *argv[])
+{
+	extern char *optarg;
+	extern int optind;
+	struct puffs_usermount *pu;
+	struct puffs_pathobj *po_root;
+	struct puffs_ops *pops;
+	struct timespec ts;
+	const char *typename;
+	char *rtstr;
+	mntoptparse_t mp;
+	int pflags, detach, mntflags;
+	int ch;
+	int khashbuckets;
+	int maxreqsize;
+
+	setprogname(argv[0]);
+
+	rtstr = NULL;
+	detach = 1;
+	mntflags = 0;
+	khashbuckets = 256;
+	pflags = PUFFS_KFLAG_IAONDEMAND;
+	typename = FSNAME;
+	maxreqsize = MAXREQMAGIC;
+	gdtm.dtm_allowprot = VM_PROT_ALL;
+	while ((ch = getopt(argc, argv, "bc:dfilm:n:o:p:r:st")) != -1) {
+		switch (ch) {
+		case 'b': /* build paths, for debugging the feature */
+			pflags |= PUFFS_FLAG_BUILDPATH;
+			break;
+		case 'c':
+			khashbuckets = atoi(optarg);
+			break;
+		case 'd':
+			dynamicfh = 1;
+			break;
+		case 'f':
+			pflags |= PUFFS_KFLAG_LOOKUP_FULLPNBUF;
+			break;
+		case 'i':
+			pflags &= ~PUFFS_KFLAG_IAONDEMAND;
+			break;
+		case 'l':
+			straightflush = 1;
+			break;
+		case 'm':
+			maxreqsize = atoi(optarg);
+			break;
+		case 'n':
+			typename = optarg;
+			break;
+		case 'o':
+			mp = getmntopts(optarg, puffsmopts, &mntflags, &pflags);
+			if (mp == NULL)
+				err(1, "getmntopts");
+			freemntopts(mp);
+			break;
+		case 'p':
+			gdtm.dtm_allowprot = atoi(optarg);
+			if ((gdtm.dtm_allowprot | VM_PROT_ALL) != VM_PROT_ALL)
+				usage();
+			break;
+		case 'r':
+			rtstr = optarg;
+			break;
+		case 's': /* stay on top */
+			detach = 0;
+			break;
+		case 't':
+			pflags |= PUFFS_KFLAG_WTCACHE;
+			break;
+		default:
+			usage();
+			/*NOTREACHED*/
+		}
+	}
+	if (pflags & PUFFS_FLAG_OPDUMP)
+		detach = 0;
+	argc -= optind;
+	argv += optind;
+
+	if (argc != 2)
+		usage();
+
+	PUFFSOP_INIT(pops);
+
+	PUFFSOP_SET(pops, dtfs, fs, statvfs);
+	PUFFSOP_SET(pops, dtfs, fs, unmount);
+	PUFFSOP_SETFSNOP(pops, sync);
+	PUFFSOP_SET(pops, dtfs, fs, fhtonode);
+	PUFFSOP_SET(pops, dtfs, fs, nodetofh);
+
+	PUFFSOP_SET(pops, dtfs, node, lookup);
+	PUFFSOP_SET(pops, dtfs, node, access);
+	PUFFSOP_SET(pops, puffs_genfs, node, getattr);
+	PUFFSOP_SET(pops, dtfs, node, setattr);
+	PUFFSOP_SET(pops, dtfs, node, create);
+	PUFFSOP_SET(pops, dtfs, node, remove);
+	PUFFSOP_SET(pops, dtfs, node, readdir);
+	PUFFSOP_SET(pops, dtfs, node, poll);
+	PUFFSOP_SET(pops, dtfs, node, mmap);
+	PUFFSOP_SET(pops, dtfs, node, mkdir);
+	PUFFSOP_SET(pops, dtfs, node, rmdir);
+	PUFFSOP_SET(pops, dtfs, node, rename);
+	PUFFSOP_SET(pops, dtfs, node, read);
+	PUFFSOP_SET(pops, dtfs, node, write);
+	PUFFSOP_SET(pops, dtfs, node, link);
+	PUFFSOP_SET(pops, dtfs, node, symlink);
+	PUFFSOP_SET(pops, dtfs, node, readlink);
+	PUFFSOP_SET(pops, dtfs, node, mknod);
+	PUFFSOP_SET(pops, dtfs, node, inactive);
+	PUFFSOP_SET(pops, dtfs, node, reclaim);
+
+	srandom(time(NULL)); /* for random generation numbers */
+
+	pu = puffs_init(pops, _PATH_PUFFS, typename, &gdtm, pflags);
+	if (pu == NULL)
+		err(1, "init");
+	gpu = pu;
+
+	puffs_setfhsize(pu, sizeof(struct dtfs_fid),
+	    PUFFS_FHFLAG_NFSV2 | PUFFS_FHFLAG_NFSV3
+	    | (dynamicfh ? PUFFS_FHFLAG_DYNAMIC : 0));
+	puffs_setncookiehash(pu, khashbuckets);
+
+	if (signal(SIGALRM, wipe_the_sleep_out_of_my_eyes) == SIG_ERR)
+		warn("cannot set alarm sighandler");
+
+	/* init */
+	if (dtfs_domount(pu, rtstr) != 0)
+		errx(1, "dtfs_domount failed");
+
+	po_root = puffs_getrootpathobj(pu);
+	po_root->po_path = argv[0];
+	po_root->po_len = strlen(argv[0]);
+
+	/* often enough for testing poll */
+	ts.tv_sec = 1;
+	ts.tv_nsec = 0;
+	puffs_ml_setloopfn(pu, loopfun);
+	puffs_ml_settimeout(pu, &ts);
+
+	if (maxreqsize != MAXREQMAGIC)
+		puffs_setmaxreqlen(pu, maxreqsize);
+
+	puffs_set_errnotify(pu, puffs_kernerr_abort);
+	if (detach)
+		if (puffs_daemon(pu, 1, 1) == -1)
+			err(1, "puffs_daemon");
+
+	if (puffs_mount(pu,  argv[1], mntflags, puffs_getroot(pu)) == -1)
+		err(1, "mount");
+	if (puffs_mainloop(pu) == -1)
+		err(1, "mainloop");
+
+	return 0;
+}
Index: src/tests/fs/puffs/h_dtfs/dtfs.h
diff -u /dev/null src/tests/fs/puffs/h_dtfs/dtfs.h:1.1
--- /dev/null	Tue Jul  6 14:16:45 2010
+++ src/tests/fs/puffs/h_dtfs/dtfs.h	Tue Jul  6 14:16:44 2010
@@ -0,0 +1,125 @@
+/*	$NetBSD: dtfs.h,v 1.1 2010/07/06 14:16:44 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.
+ */
+
+#ifndef DTFS_H_
+#define DTFS_H_
+
+#include <sys/types.h>
+
+#include <puffs.h>
+
+PUFFSOP_PROTOS(dtfs);
+int	dtfs_domount(struct puffs_usermount *, const char *);
+
+#define DTFS_BLOCKSHIFT	(12)
+#define DTFS_BLOCKSIZE	(1<<DTFS_BLOCKSHIFT)
+
+#define ROUNDUP(a,b) ((a) & ((b)-1))
+#define BLOCKNUM(a,b) (((a) & ~((1<<(b))-1)) >> (b))
+
+struct dtfs_fid;
+struct dtfs_mount {
+	ino_t		dtm_nextfileid;	/* running number for file id	*/
+
+	size_t		dtm_fsizes;	/* sum of file sizes in bytes	*/
+	fsfilcnt_t	dtm_nfiles;	/* number of files		*/
+
+	LIST_HEAD(, dtfs_poll) dtm_pollent;
+	int		dtm_needwakeup;
+	vm_prot_t	dtm_allowprot;
+};
+
+struct dtfs_file {
+	union {
+		struct {
+			uint8_t **blocks;
+			size_t numblocks;
+			size_t datalen;
+		} reg;
+		struct {
+			struct puffs_node *dotdot;
+			LIST_HEAD(, dtfs_dirent) dirents;
+		} dir; 
+		struct {
+			char *target;
+		} link;
+	} u;
+#define df_blocks u.reg.blocks
+#define df_numblocks u.reg.numblocks
+#define df_datalen u.reg.datalen
+#define df_dotdot u.dir.dotdot
+#define df_dirents u.dir.dirents
+#define df_linktarget u.link.target
+};
+
+struct dtfs_dirent {
+	struct puffs_node *dfd_node;
+	struct puffs_node *dfd_parent;
+	char *dfd_name;
+	size_t dfd_namelen;
+
+	LIST_ENTRY(dtfs_dirent) dfd_entries;
+};
+
+struct dtfs_fid {
+	struct puffs_node	*dfid_addr;
+
+	/* best^Wsome-effort extra sanity check */
+	ino_t			dfid_fileid;
+	u_long			dfid_gen;
+};
+#define DTFS_FIDSIZE (sizeof(struct dtfs_fid))
+
+struct dtfs_poll {
+	struct puffs_cc *dp_pcc;
+	LIST_ENTRY(dtfs_poll) dp_entries;
+};
+
+struct puffs_node *	dtfs_genfile(struct puffs_node *,
+				     const struct puffs_cn *, enum vtype);
+struct dtfs_file *	dtfs_newdir(void);
+struct dtfs_file *	dtfs_newfile(void);
+struct dtfs_dirent *	dtfs_dirgetnth(struct dtfs_file *, int);
+struct dtfs_dirent *	dtfs_dirgetbyname(struct dtfs_file *,
+					  const char *, size_t);
+
+void			dtfs_nukenode(struct puffs_node *, struct puffs_node *,
+				      const char *, size_t);
+void			dtfs_freenode(struct puffs_node *);
+void			dtfs_setsize(struct puffs_node *, off_t);
+
+void	dtfs_adddent(struct puffs_node *, struct dtfs_dirent *);
+void	dtfs_removedent(struct puffs_node *, struct dtfs_dirent *);
+
+void	dtfs_baseattrs(struct vattr *, enum vtype, ino_t);
+void	dtfs_updatetimes(struct puffs_node *, int, int, int);
+
+
+#define DTFS_CTOF(a) ((struct dtfs_file *)(((struct puffs_node *)a)->pn_data))
+#define DTFS_PTOF(a) ((struct dtfs_file *)(a->pn_data))
+
+#endif /* DTFS_H_ */
Index: src/tests/fs/puffs/h_dtfs/dtfs_subr.c
diff -u /dev/null src/tests/fs/puffs/h_dtfs/dtfs_subr.c:1.1
--- /dev/null	Tue Jul  6 14:16:45 2010
+++ src/tests/fs/puffs/h_dtfs/dtfs_subr.c	Tue Jul  6 14:16:44 2010
@@ -0,0 +1,340 @@
+/*	$NetBSD: dtfs_subr.c,v 1.1 2010/07/06 14:16:44 pooka Exp $	*/
+
+/*
+ * Copyright (c) 2006  Antti Kantee.  All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/time.h>
+
+#include <assert.h>
+#include <err.h>
+#include <errno.h>
+#include <puffs.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <util.h>
+
+#include "dtfs.h"
+
+void
+dtfs_baseattrs(struct vattr *vap, enum vtype type, ino_t id)
+{
+	struct timeval tv;
+	struct timespec ts;
+
+	gettimeofday(&tv, NULL);
+	TIMEVAL_TO_TIMESPEC(&tv, &ts);
+
+	vap->va_type = type;
+	if (type == VDIR) {
+		vap->va_mode = 0777;
+		vap->va_nlink = 1;	/* n + 1 after adding dent */
+	} else {
+		vap->va_mode = 0666;
+		vap->va_nlink = 0;	/* n + 1 */
+	}
+	vap->va_uid = 0;
+	vap->va_gid = 0;
+	vap->va_fileid = id;
+	vap->va_size = 0;
+	vap->va_blocksize = getpagesize();
+	vap->va_gen = random();
+	vap->va_flags = 0;
+	vap->va_rdev = PUFFS_VNOVAL;
+	vap->va_bytes = 0;
+	vap->va_filerev = 1;
+	vap->va_vaflags = 0;
+
+	vap->va_atime = vap->va_mtime = vap->va_ctime = vap->va_birthtime = ts;
+}
+
+/*
+ * Well, as you can probably see, this interface has the slight problem
+ * of assuming file creation will always be succesful, or at least not
+ * giving a reason for the failure.  Be sure to do better when you
+ * implement your own fs.
+ */
+struct puffs_node *
+dtfs_genfile(struct puffs_node *dir, const struct puffs_cn *pcn,
+	enum vtype type)
+{
+	struct dtfs_file *df_dir, *dff;
+	struct dtfs_dirent *dfd;
+	struct dtfs_mount *dtm;
+	struct puffs_node *newpn;
+	uid_t uid;
+	int rv;
+
+	assert(dir->pn_va.va_type == VDIR);
+	assert(dir->pn_mnt != NULL);
+
+	uid = 0;
+	rv = puffs_cred_getuid(pcn->pcn_cred, &uid);
+	assert(rv == 0);
+
+	if (type == VDIR) {
+		dff = dtfs_newdir();
+		dff->df_dotdot = dir;
+	} else
+		dff = dtfs_newfile();
+
+	dtm = puffs_pn_getmntspecific(dir);
+	newpn = puffs_pn_new(dir->pn_mnt, dff);
+	if (newpn == NULL)
+		errx(1, "getnewpnode");
+	dtfs_baseattrs(&newpn->pn_va, type, dtm->dtm_nextfileid++);
+
+	df_dir = dir->pn_data;
+	dfd = emalloc(sizeof(struct dtfs_dirent));
+	dfd->dfd_node = newpn;
+	dfd->dfd_name = estrndup(pcn->pcn_name, pcn->pcn_namelen);
+	dfd->dfd_namelen = strlen(dfd->dfd_name);
+	dfd->dfd_parent = dir;
+	dtfs_adddent(dir, dfd);
+
+	newpn->pn_va.va_uid = uid;
+	newpn->pn_va.va_gid = dir->pn_va.va_gid;
+
+	return newpn;
+}
+
+struct dtfs_file *
+dtfs_newdir()
+{
+	struct dtfs_file *dff;
+
+	dff = emalloc(sizeof(struct dtfs_file));
+	memset(dff, 0, sizeof(struct dtfs_file));
+	LIST_INIT(&dff->df_dirents);
+
+	return dff;
+}
+
+struct dtfs_file *
+dtfs_newfile()
+{
+	struct dtfs_file *dff;
+
+	dff = emalloc(sizeof(struct dtfs_file));
+	memset(dff, 0, sizeof(struct dtfs_file));
+
+	return dff;
+}
+
+struct dtfs_dirent *
+dtfs_dirgetnth(struct dtfs_file *searchdir, int n)
+{
+	struct dtfs_dirent *dirent;
+	int i;
+
+	i = 0;
+	LIST_FOREACH(dirent, &searchdir->df_dirents, dfd_entries) {
+		if (i == n)
+			return dirent;
+		i++;
+	}
+
+	return NULL;
+}
+
+struct dtfs_dirent *
+dtfs_dirgetbyname(struct dtfs_file *searchdir, const char *fname, size_t fnlen)
+{
+	struct dtfs_dirent *dirent;
+
+	LIST_FOREACH(dirent, &searchdir->df_dirents, dfd_entries)
+		if (dirent->dfd_namelen == fnlen
+		    && strncmp(dirent->dfd_name, fname, fnlen) == 0)
+			return dirent;
+
+	return NULL;
+}
+
+/*
+ * common nuke, kill dirent from parent node
+ */
+void
+dtfs_nukenode(struct puffs_node *nukeme, struct puffs_node *pn_parent,
+	const char *fname, size_t fnlen)
+{
+	struct dtfs_dirent *dfd;
+	struct dtfs_mount *dtm;
+
+	assert(pn_parent->pn_va.va_type == VDIR);
+
+	dfd = dtfs_dirgetbyname(DTFS_PTOF(pn_parent), fname, fnlen);
+	assert(dfd);
+
+	dtm = puffs_pn_getmntspecific(nukeme);
+	dtm->dtm_nfiles--;
+	assert(dtm->dtm_nfiles >= 1);
+
+	dtfs_removedent(pn_parent, dfd);
+	free(dfd);
+}
+
+/* free lingering information */
+void
+dtfs_freenode(struct puffs_node *pn)
+{
+	struct dtfs_file *df = DTFS_PTOF(pn);
+	struct dtfs_mount *dtm;
+	int i;
+
+	assert(pn->pn_va.va_nlink == 0);
+	dtm = puffs_pn_getmntspecific(pn);
+
+	switch (pn->pn_va.va_type) {
+	case VREG:
+		assert(dtm->dtm_fsizes >= pn->pn_va.va_size);
+		dtm->dtm_fsizes -= pn->pn_va.va_size;
+		for (i = 0; i < BLOCKNUM(df->df_datalen, DTFS_BLOCKSHIFT); i++)
+			free(df->df_blocks[i]);
+		if (df->df_datalen > i << DTFS_BLOCKSHIFT)
+			free(df->df_blocks[i]);
+		break;
+	case VLNK:
+		free(df->df_linktarget);
+		break;
+	case VCHR:
+	case VBLK:
+	case VDIR:
+	case VSOCK:
+	case VFIFO:
+		break;
+	default:
+		assert(0);
+		break;
+	}
+
+	free(df);
+	puffs_pn_put(pn);
+}
+
+void
+dtfs_setsize(struct puffs_node *pn, off_t newsize)
+{
+	struct dtfs_file *df = DTFS_PTOF(pn);
+	struct dtfs_mount *dtm;
+	size_t newblocks;
+	int needalloc, shrinks;
+	int i;
+
+	needalloc = newsize > ROUNDUP(df->df_datalen, DTFS_BLOCKSIZE);
+	shrinks = newsize < pn->pn_va.va_size;
+
+	if (needalloc || shrinks) {
+		newblocks = BLOCKNUM(newsize, DTFS_BLOCKSHIFT) + 1;
+
+		if (shrinks)
+			for (i = newblocks; i < df->df_numblocks; i++)
+				free(df->df_blocks[i]);
+
+		df->df_blocks = erealloc(df->df_blocks,
+		    newblocks * sizeof(uint8_t *));
+		/*
+		 * if extended, set storage to zero
+		 * to match correct behaviour
+		 */ 
+		if (!shrinks) {
+			for (i = df->df_numblocks; i < newblocks; i++) {
+				df->df_blocks[i] = emalloc(DTFS_BLOCKSIZE);
+				memset(df->df_blocks[i], 0, DTFS_BLOCKSIZE);
+			}
+		}
+
+		df->df_datalen = newsize;
+		df->df_numblocks = newblocks;
+	}
+
+	dtm = puffs_pn_getmntspecific(pn);
+	if (!shrinks) {
+		dtm->dtm_fsizes += newsize - pn->pn_va.va_size;
+	} else {
+		dtm->dtm_fsizes -= pn->pn_va.va_size - newsize;
+	}
+
+	pn->pn_va.va_size = newsize;
+	pn->pn_va.va_bytes = BLOCKNUM(newsize,DTFS_BLOCKSHIFT)>>DTFS_BLOCKSHIFT;
+}
+
+/* add & bump link count */
+void
+dtfs_adddent(struct puffs_node *pn_dir, struct dtfs_dirent *dent)
+{
+	struct dtfs_file *dir = DTFS_PTOF(pn_dir);
+	struct puffs_node *pn_file = dent->dfd_node;
+	struct dtfs_file *file = DTFS_PTOF(pn_file);
+	struct dtfs_mount *dtm;
+
+	assert(pn_dir->pn_va.va_type == VDIR);
+	LIST_INSERT_HEAD(&dir->df_dirents, dent, dfd_entries);
+	pn_file->pn_va.va_nlink++;
+
+	dtm = puffs_pn_getmntspecific(pn_file);
+	dtm->dtm_nfiles++;
+
+	dent->dfd_parent = pn_dir;
+	if (dent->dfd_node->pn_va.va_type == VDIR) {
+		file->df_dotdot = pn_dir;
+		pn_dir->pn_va.va_nlink++;
+	}
+
+	dtfs_updatetimes(pn_dir, 0, 1, 1);
+}
+
+/* remove & lower link count */
+void
+dtfs_removedent(struct puffs_node *pn_dir, struct dtfs_dirent *dent)
+{
+	struct puffs_node *pn_file = dent->dfd_node;
+
+	assert(pn_dir->pn_va.va_type == VDIR);
+	LIST_REMOVE(dent, dfd_entries);
+	if (pn_file->pn_va.va_type == VDIR)
+		pn_dir->pn_va.va_nlink--;
+	pn_file->pn_va.va_nlink--;
+	assert(pn_dir->pn_va.va_nlink >= 2);
+
+	dtfs_updatetimes(pn_dir, 0, 1, 1);
+}
+
+void
+dtfs_updatetimes(struct puffs_node *pn, int doatime, int doctime, int domtime)
+{
+	struct timeval tv;
+	struct timespec ts;
+
+	gettimeofday(&tv, NULL);
+	TIMEVAL_TO_TIMESPEC(&tv, &ts);
+
+	if (doatime)
+		pn->pn_va.va_atime = ts;
+	if (doctime)
+		pn->pn_va.va_ctime = ts;
+	if (domtime)
+		pn->pn_va.va_mtime = ts;
+}
Index: src/tests/fs/puffs/h_dtfs/dtfs_vfsops.c
diff -u /dev/null src/tests/fs/puffs/h_dtfs/dtfs_vfsops.c:1.1
--- /dev/null	Tue Jul  6 14:16:45 2010
+++ src/tests/fs/puffs/h_dtfs/dtfs_vfsops.c	Tue Jul  6 14:16:44 2010
@@ -0,0 +1,297 @@
+/*	$NetBSD: dtfs_vfsops.c,v 1.1 2010/07/06 14:16:44 pooka Exp $	*/
+
+/*
+ * Copyright (c) 2006  Antti Kantee.  All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/resource.h>
+
+#include <assert.h>
+#include <err.h>
+#include <errno.h>
+#include <puffs.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <util.h>
+
+#include "dtfs.h"
+
+static int
+rtstr(struct puffs_usermount *pu, const char *str, enum vtype vt)
+{
+	struct puffs_node *pn = puffs_getroot(pu);
+	struct vattr *va = &pn->pn_va;
+	struct dtfs_file *df = DTFS_PTOF(pn);
+	char ltarg[256+1];
+
+	if (sscanf(str, "%*s %256s", ltarg) != 1)
+		return 1;
+
+	dtfs_baseattrs(va, vt, 2);
+	df->df_linktarget = estrdup(ltarg);
+
+	va->va_nlink = 1;
+	va->va_size = strlen(df->df_linktarget);
+
+	puffs_setrootinfo(pu, vt, 0, 0);
+
+	return 0;
+}
+
+static int
+rtdev(struct puffs_usermount *pu, const char *str, enum vtype vt)
+{
+	struct puffs_node *pn = puffs_getroot(pu);
+	struct vattr *va = &pn->pn_va;
+	int major, minor;
+
+	if (sscanf(str, "%*s %d %d", &major, &minor) != 2)
+		return 1;
+
+	dtfs_baseattrs(va, vt, 2);
+	va->va_nlink = 1;
+	va->va_rdev = makedev(major, minor);
+
+	if (vt == VBLK)
+		va->va_mode |= S_IFBLK;
+	else
+		va->va_mode |= S_IFCHR;
+
+	puffs_setrootinfo(pu, vt, 0, va->va_rdev);
+
+	return 0;
+}
+
+static int
+rtnorm(struct puffs_usermount *pu, const char *str, enum vtype vt)
+{
+	struct puffs_node *pn = puffs_getroot(pu);
+	struct vattr *va = &pn->pn_va;
+
+	dtfs_baseattrs(va, vt, 2);
+	if (vt == VDIR)
+		va->va_nlink = 2;
+	else
+		va->va_nlink = 1;
+
+	puffs_setrootinfo(pu, vt, 0, 0);
+
+	return 0;
+}
+
+struct rtype {
+	char *tstr;
+	enum vtype vt;
+	int (*pfunc)(struct puffs_usermount *, const char *, enum vtype);
+} rtypes[] = {
+	{ "reg", VREG, rtnorm },
+	{ "dir", VDIR, rtnorm },
+	{ "blk", VBLK, rtdev },
+	{ "chr", VCHR, rtdev },
+	{ "lnk", VLNK, rtstr },
+	{ "sock", VSOCK, rtnorm },
+	{ "fifo", VFIFO, rtnorm }
+};
+#define NTYPES (sizeof(rtypes) / sizeof(rtypes[0]))
+
+int
+dtfs_domount(struct puffs_usermount *pu, const char *typestr)
+{
+	struct dtfs_mount *dtm;
+	struct dtfs_file *dff;
+	struct puffs_node *pn;
+	int i;
+
+	/* create mount-local thingie */
+	dtm = puffs_getspecific(pu);
+	dtm->dtm_nextfileid = 3;
+	dtm->dtm_nfiles = 1;
+	dtm->dtm_fsizes = 0;
+	LIST_INIT(&dtm->dtm_pollent);
+
+	/*
+	 * create root directory, do it "by hand" to avoid special-casing
+	 * dtfs_genfile()
+	 */
+	dff = dtfs_newdir();
+	dff->df_dotdot = NULL;
+	pn = puffs_pn_new(pu, dff);
+	if (!pn)
+		errx(1, "puffs_newpnode");
+	puffs_setroot(pu, pn);
+
+	if (!typestr) {
+		rtnorm(pu, NULL, VDIR);
+	} else {
+		for (i = 0; i < NTYPES; i++) {
+			if (strncmp(rtypes[i].tstr, typestr,
+			    strlen(rtypes[i].tstr)) == 0) {
+				if (rtypes[i].pfunc(pu, typestr,
+				    rtypes[i].vt) != 0) {
+					fprintf(stderr, "failed to parse "
+					    "\"%s\"\n", typestr);
+					return 1;
+				}
+				break;
+			}
+		}
+		if (i == NTYPES) {
+			fprintf(stderr, "no maching type for %s\n", typestr);
+			return 1;
+		}
+	}
+
+	return 0;
+}
+
+/*
+ * statvfs() should fill in the following members of struct statvfs:
+ * 
+ * unsigned long   f_bsize;         file system block size
+ * unsigned long   f_frsize;        fundamental file system block size
+ * unsigned long   f_iosize;        optimal 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
+ *
+ *
+ * The rest are filled in by the kernel.
+ */
+#define ROUND(a,b) (((a) + ((b)-1)) & ~((b)-1))
+#define NFILES 1024*1024
+int
+dtfs_fs_statvfs(struct puffs_usermount *pu, struct statvfs *sbp)
+{
+	struct rlimit rlim;
+	struct dtfs_mount *dtm;
+	off_t btot, bfree;
+	int pgsize;
+
+	dtm = puffs_getspecific(pu);
+	pgsize = getpagesize();
+	memset(sbp, 0, sizeof(struct statvfs));
+
+	/*
+	 * Use datasize rlimit as an _approximation_ for free size.
+	 * This, of course, is not accurate due to overhead and not
+	 * accounting for metadata.
+	 */
+	if (getrlimit(RLIMIT_DATA, &rlim) == 0)
+		btot = rlim.rlim_cur;
+	else
+		btot = 16*1024*1024;
+	bfree = btot - dtm->dtm_fsizes;
+
+	sbp->f_blocks = ROUND(btot, pgsize) / pgsize;
+	sbp->f_files = NFILES;
+
+	sbp->f_bsize = sbp->f_frsize = sbp->f_iosize = pgsize;
+	sbp->f_bfree = sbp->f_bavail = ROUND(bfree, pgsize) / pgsize;
+	sbp->f_ffree = sbp->f_favail = NFILES - dtm->dtm_nfiles;
+
+	sbp->f_bresvd = sbp->f_fresvd = 0;
+
+	return 0;
+}
+#undef ROUND 
+
+static void *
+addrcmp(struct puffs_usermount *pu, struct puffs_node *pn, void *arg)
+{
+
+	if (pn == arg)
+		return pn;
+
+	return NULL;
+}
+
+int
+dtfs_fs_fhtonode(struct puffs_usermount *pu, void *fid, size_t fidsize,
+	struct puffs_newinfo *pni)
+{
+	struct dtfs_fid *dfid;
+	struct puffs_node *pn;
+
+	assert(fidsize == sizeof(struct dtfs_fid));
+	dfid = fid;
+
+	pn = puffs_pn_nodewalk(pu, addrcmp, dfid->dfid_addr);
+	if (pn == NULL)
+		return EINVAL;
+
+	if (pn->pn_va.va_fileid != dfid->dfid_fileid
+	    || pn->pn_va.va_gen != dfid->dfid_gen)
+		return EINVAL;
+	
+	puffs_newinfo_setcookie(pni, pn);
+	puffs_newinfo_setvtype(pni, pn->pn_va.va_type);
+	puffs_newinfo_setsize(pni, pn->pn_va.va_size);
+	puffs_newinfo_setrdev(pni, pn->pn_va.va_rdev);
+
+	return 0;
+}
+
+int
+dtfs_fs_nodetofh(struct puffs_usermount *pu, void *cookie,
+	void *fid, size_t *fidsize)
+{
+	struct puffs_node *pn = cookie;
+	struct dtfs_fid *dfid;
+	extern int dynamicfh;
+
+	if (dynamicfh == 0) {
+		assert(*fidsize >= sizeof(struct dtfs_fid));
+	} else {
+		if (*fidsize < sizeof(struct dtfs_fid)) {
+			*fidsize = sizeof(struct dtfs_fid);
+			return E2BIG;
+		}
+		*fidsize = sizeof(struct dtfs_fid);
+	}
+
+	dfid = fid;
+
+	dfid->dfid_addr = pn;
+	dfid->dfid_fileid = pn->pn_va.va_fileid;
+	dfid->dfid_gen = pn->pn_va.va_gen;
+
+	return 0;
+}
+
+int
+dtfs_fs_unmount(struct puffs_usermount *pu, int flags)
+{
+
+	return 0;
+}
Index: src/tests/fs/puffs/h_dtfs/dtfs_vnops.c
diff -u /dev/null src/tests/fs/puffs/h_dtfs/dtfs_vnops.c:1.1
--- /dev/null	Tue Jul  6 14:16:45 2010
+++ src/tests/fs/puffs/h_dtfs/dtfs_vnops.c	Tue Jul  6 14:16:44 2010
@@ -0,0 +1,518 @@
+/*	$NetBSD: dtfs_vnops.c,v 1.1 2010/07/06 14:16:44 pooka Exp $	*/
+
+/*
+ * Copyright (c) 2006  Antti Kantee.  All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/poll.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <puffs.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <util.h>
+
+#include "dtfs.h"
+
+int
+dtfs_node_lookup(struct puffs_usermount *pu, void *opc,
+	struct puffs_newinfo *pni, const struct puffs_cn *pcn)
+{
+	struct puffs_node *pn_dir = opc;
+	struct dtfs_file *df = DTFS_CTOF(opc);
+	struct dtfs_dirent *dfd;
+	extern int straightflush;
+	int rv;
+
+	/* parent dir? */
+	if (PCNISDOTDOT(pcn)) {
+		assert(df->df_dotdot->pn_va.va_type == VDIR);
+		puffs_newinfo_setcookie(pni, df->df_dotdot);
+		puffs_newinfo_setvtype(pni, df->df_dotdot->pn_va.va_type);
+
+		return 0;
+	}
+
+	dfd = dtfs_dirgetbyname(df, pcn->pcn_name, pcn->pcn_namelen);
+	if (dfd) {
+		puffs_newinfo_setcookie(pni, dfd->dfd_node);
+		puffs_newinfo_setvtype(pni, dfd->dfd_node->pn_va.va_type);
+		puffs_newinfo_setsize(pni, dfd->dfd_node->pn_va.va_size);
+		puffs_newinfo_setrdev(pni, dfd->dfd_node->pn_va.va_rdev);
+
+		if (straightflush)
+			puffs_flush_pagecache_node(pu, dfd->dfd_node);
+
+		return 0;
+	}
+
+	if ((pcn->pcn_flags & NAMEI_ISLASTCN)
+	    && (pcn->pcn_nameiop == NAMEI_CREATE ||
+	      pcn->pcn_nameiop == NAMEI_RENAME)) {
+		rv = puffs_access(VDIR, pn_dir->pn_va.va_mode,
+		    pn_dir->pn_va.va_uid, pn_dir->pn_va.va_gid,
+		    PUFFS_VWRITE, pcn->pcn_cred);
+		if (rv)
+			return rv;
+	}
+
+	return ENOENT;
+}
+
+int
+dtfs_node_access(struct puffs_usermount *pu, void *opc, int acc_mode,
+	const struct puffs_cred *pcr)
+{
+	struct puffs_node *pn = opc;
+
+	return puffs_access(pn->pn_va.va_type, pn->pn_va.va_mode,
+	    pn->pn_va.va_uid, pn->pn_va.va_gid, acc_mode, pcr);
+}
+
+int
+dtfs_node_setattr(struct puffs_usermount *pu, void *opc,
+	const struct vattr *va, const struct puffs_cred *pcr)
+{
+	struct puffs_node *pn = opc;
+	int rv;
+
+	/* check permissions */
+	if (va->va_flags != PUFFS_VNOVAL)
+		return EOPNOTSUPP;
+
+	if (va->va_uid != PUFFS_VNOVAL || va->va_gid != PUFFS_VNOVAL) {
+		rv = puffs_access_chown(pn->pn_va.va_uid, pn->pn_va.va_gid,
+		    va->va_uid, va->va_gid, pcr);
+		if (rv)
+			return rv;
+	}
+
+	if (va->va_mode != PUFFS_VNOVAL) {
+		rv = puffs_access_chmod(pn->pn_va.va_uid, pn->pn_va.va_gid,
+		    pn->pn_va.va_type, va->va_mode, pcr);
+		if (rv)
+			return rv;
+	}
+
+	if ((va->va_atime.tv_sec != PUFFS_VNOVAL
+	      && va->va_atime.tv_nsec != PUFFS_VNOVAL)
+	    || (va->va_mtime.tv_sec != PUFFS_VNOVAL
+	      && va->va_mtime.tv_nsec != PUFFS_VNOVAL)) {
+		rv = puffs_access_times(pn->pn_va.va_uid, pn->pn_va.va_gid,
+		    pn->pn_va.va_mode, va->va_vaflags & VA_UTIMES_NULL, pcr);
+		if (rv)
+			return rv;
+	}
+
+	if (va->va_size != PUFFS_VNOVAL) {
+		switch (pn->pn_va.va_type) {
+		case VREG:
+			dtfs_setsize(pn, va->va_size);
+			pn->pn_va.va_bytes = va->va_size;
+			break;
+		case VBLK:
+		case VCHR:
+		case VFIFO:
+			break;
+		case VDIR:
+			return EISDIR;
+		default:
+			return EOPNOTSUPP;
+		}
+	}
+
+	puffs_setvattr(&pn->pn_va, va);
+
+	return 0;
+}
+
+/* create a new node in the parent directory specified by opc */
+int
+dtfs_node_create(struct puffs_usermount *pu, void *opc,
+	struct puffs_newinfo *pni, const struct puffs_cn *pcn,
+	const struct vattr *va)
+{
+	struct puffs_node *pn_parent = opc;
+	struct puffs_node *pn_new;
+
+	if (!(va->va_type == VREG || va->va_type == VSOCK))
+		return ENODEV;
+
+	pn_new = dtfs_genfile(pn_parent, pcn, va->va_type);
+	puffs_setvattr(&pn_new->pn_va, va);
+
+	puffs_newinfo_setcookie(pni, pn_new);
+
+	return 0;
+}
+
+int
+dtfs_node_remove(struct puffs_usermount *pu, void *opc, void *targ,
+	const struct puffs_cn *pcn)
+{
+	struct puffs_node *pn_parent = opc;
+	struct puffs_node *pn = targ;
+
+	if (pn->pn_va.va_type == VDIR)
+		return EPERM;
+
+	dtfs_nukenode(targ, pn_parent, pcn->pcn_name, pcn->pcn_namelen);
+
+	if (pn->pn_va.va_nlink == 0)
+		puffs_setback(puffs_cc_getcc(pu), PUFFS_SETBACK_NOREF_N2);
+
+	return 0;
+}
+
+int
+dtfs_node_mkdir(struct puffs_usermount *pu, void *opc,
+	struct puffs_newinfo *pni, const struct puffs_cn *pcn,
+	const struct vattr *va)
+{
+	struct puffs_node *pn_parent = opc;
+	struct puffs_node *pn_new;
+
+	pn_new = dtfs_genfile(pn_parent, pcn, VDIR);
+	puffs_setvattr(&pn_new->pn_va, va);
+
+	puffs_newinfo_setcookie(pni, pn_new);
+
+	return 0;
+}
+
+int
+dtfs_node_rmdir(struct puffs_usermount *pu, void *opc, void *targ,
+	const struct puffs_cn *pcn)
+{
+	struct puffs_node *pn_parent = opc;
+	struct dtfs_file *df = DTFS_CTOF(targ);
+
+	if (!LIST_EMPTY(&df->df_dirents))
+		return ENOTEMPTY;
+
+	dtfs_nukenode(targ, pn_parent, pcn->pcn_name, pcn->pcn_namelen);
+	puffs_setback(puffs_cc_getcc(pu), PUFFS_SETBACK_NOREF_N2);
+
+	return 0;
+}
+
+int
+dtfs_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 puffs_node *pn_nth;
+	struct dtfs_dirent *dfd_nth;
+
+	if (pn->pn_va.va_type != VDIR)
+		return ENOTDIR;
+	
+	dtfs_updatetimes(pn, 1, 0, 0);
+
+	*ncookies = 0;
+ again:
+	if (*readoff == DENT_DOT || *readoff == DENT_DOTDOT) {
+		puffs_gendotdent(&dent, pn->pn_va.va_fileid, *readoff, reslen);
+		(*readoff)++;
+		PUFFS_STORE_DCOOKIE(cookies, ncookies, *readoff);
+		goto again;
+	}
+
+	for (;;) {
+		dfd_nth = dtfs_dirgetnth(pn->pn_data, DENT_ADJ(*readoff));
+		if (!dfd_nth) {
+			*eofflag = 1;
+			break;
+		}
+		pn_nth = dfd_nth->dfd_node;
+
+		if (!puffs_nextdent(&dent, dfd_nth->dfd_name,
+		    pn_nth->pn_va.va_fileid,
+		    puffs_vtype2dt(pn_nth->pn_va.va_type),
+		    reslen))
+			break;
+
+		(*readoff)++;
+		PUFFS_STORE_DCOOKIE(cookies, ncookies, *readoff);
+	}
+
+	return 0;
+}
+
+int
+dtfs_node_poll(struct puffs_usermount *pu, void *opc, int *events)
+{
+	struct dtfs_mount *dtm = puffs_getspecific(pu);
+	struct dtfs_poll dp;
+	struct itimerval it;
+
+	memset(&it, 0, sizeof(struct itimerval));
+	it.it_value.tv_sec = 4;
+	if (setitimer(ITIMER_REAL, &it, NULL) == -1)
+		return errno;
+
+	dp.dp_pcc = puffs_cc_getcc(pu);
+	LIST_INSERT_HEAD(&dtm->dtm_pollent, &dp, dp_entries);
+	puffs_cc_yield(dp.dp_pcc);
+
+	*events = *events & (POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM);
+	return 0;
+}
+
+int
+dtfs_node_mmap(struct puffs_usermount *pu, void *opc, vm_prot_t prot,
+	const struct puffs_cred *pcr)
+{
+	struct dtfs_mount *dtm = puffs_getspecific(pu);
+
+	if ((dtm->dtm_allowprot & prot) != prot)
+		return EACCES;
+
+	return 0;
+}
+
+int
+dtfs_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 dtfs_dirent *dfd_src;
+	struct puffs_node *pn_sdir = opc;
+	struct puffs_node *pn_tdir = targ_dir;
+	struct puffs_node *pn_tfile = targ;
+
+	dfd_src = dtfs_dirgetbyname(DTFS_PTOF(pn_sdir),
+	    pcn_src->pcn_name, pcn_src->pcn_namelen);
+
+	/* asked for "." or ".." XXX: make sure? */
+	if (!dfd_src)
+		return EINVAL;
+
+	/* if there's a target file, nuke it for atomic replacement */
+	if (pn_tfile) {
+		if (pn_tfile->pn_va.va_type == VDIR) {
+			assert(/*CONSTCOND*/0); /* XXX FIXME */
+		}
+		dtfs_nukenode(pn_tfile, pn_sdir,
+		    pcn_targ->pcn_name, pcn_targ->pcn_namelen);
+	}
+
+	/* out with the old */
+	dtfs_removedent(pn_sdir, dfd_src);
+	/* and in with the new */
+	dtfs_adddent(pn_tdir, dfd_src);
+
+	/* update name */
+	free(dfd_src->dfd_name);
+	dfd_src->dfd_name = estrndup(pcn_targ->pcn_name,pcn_targ->pcn_namelen);
+	dfd_src->dfd_namelen = strlen(dfd_src->dfd_name);
+
+	dtfs_updatetimes(src, 0, 1, 0);
+
+	return 0;
+}
+
+int
+dtfs_node_link(struct puffs_usermount *pu, void *opc, void *targ,
+	const struct puffs_cn *pcn)
+{
+	struct puffs_node *pn_dir = opc;
+	struct dtfs_dirent *dfd;
+
+	dfd = emalloc(sizeof(struct dtfs_dirent));
+	dfd->dfd_node = targ;
+	dfd->dfd_name = estrndup(pcn->pcn_name, pcn->pcn_namelen);
+	dfd->dfd_namelen = strlen(dfd->dfd_name);
+	dtfs_adddent(pn_dir, dfd);
+
+	dtfs_updatetimes(targ, 0, 1, 0);
+
+	return 0;
+}
+
+int
+dtfs_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 puffs_node *pn_parent = opc;
+	struct puffs_node *pn_new;
+	struct dtfs_file *df_new;
+
+	if (va->va_type != VLNK)
+		return ENODEV;
+
+	pn_new = dtfs_genfile(pn_parent, pcn_src, VLNK);
+	puffs_setvattr(&pn_new->pn_va, va);
+	df_new = DTFS_PTOF(pn_new);
+	df_new->df_linktarget = estrdup(link_target);
+	pn_new->pn_va.va_size = strlen(df_new->df_linktarget);
+
+	puffs_newinfo_setcookie(pni, pn_new);
+
+	return 0;
+}
+
+int
+dtfs_node_readlink(struct puffs_usermount *pu, void *opc,
+	const struct puffs_cred *cred, char *linkname, size_t *linklen)
+{
+	struct dtfs_file *df = DTFS_CTOF(opc);
+	struct puffs_node *pn = opc;
+
+	assert(pn->pn_va.va_type == VLNK);
+	strlcpy(linkname, df->df_linktarget, *linklen);
+	*linklen = strlen(linkname);
+
+	return 0;
+}
+
+int
+dtfs_node_mknod(struct puffs_usermount *pu, void *opc,
+	struct puffs_newinfo *pni, const struct puffs_cn *pcn,
+	const struct vattr *va)
+{
+	struct puffs_node *pn_parent = opc;
+	struct puffs_node *pn_new;
+	struct dtfs_file *df;
+
+	if (!(va->va_type == VBLK || va->va_type == VCHR
+	    || va->va_type == VFIFO))
+		return EINVAL;
+
+	pn_new = dtfs_genfile(pn_parent, pcn, va->va_type);
+	puffs_setvattr(&pn_new->pn_va, va);
+
+	df = DTFS_PTOF(pn_new);
+	puffs_newinfo_setcookie(pni, pn_new);
+
+	return 0;
+}
+
+#define BLOCKOFF(a,b) ((a) & ((b)-1))
+#define BLOCKLEFT(a,b) ((b) - BLOCKOFF(a,b))
+
+/*
+ * Read operation, used both for VOP_READ and VOP_GETPAGES
+ */
+int
+dtfs_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 dtfs_file *df = DTFS_CTOF(opc);
+	quad_t xfer, origxfer;
+	uint8_t *src, *dest;
+	size_t copylen;
+
+	if (pn->pn_va.va_type != VREG)
+		return EISDIR;
+
+	xfer = MIN(*resid, df->df_datalen - offset);
+	if (xfer < 0)
+		return EINVAL;
+
+	dest = buf;
+	origxfer = xfer;
+	while (xfer > 0) {
+		copylen = MIN(xfer, BLOCKLEFT(offset, DTFS_BLOCKSIZE));
+		src = df->df_blocks[BLOCKNUM(offset, DTFS_BLOCKSHIFT)]
+		    + BLOCKOFF(offset, DTFS_BLOCKSIZE);
+		memcpy(dest, src, copylen);
+		offset += copylen;
+		dest += copylen;
+		xfer -= copylen;
+	}
+	*resid -= origxfer;
+
+	dtfs_updatetimes(pn, 1, 0, 0);
+
+	return 0;
+}
+
+/*
+ * write operation on the wing
+ */
+int
+dtfs_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 dtfs_file *df = DTFS_CTOF(opc);
+	uint8_t *src, *dest;
+	size_t copylen;
+
+	if (pn->pn_va.va_type != VREG)
+		return EISDIR;
+
+	if (ioflag & PUFFS_IO_APPEND)
+		offset = pn->pn_va.va_size;
+
+	if (*resid + offset > pn->pn_va.va_size)
+		dtfs_setsize(pn, *resid + offset);
+
+	src = buf;
+	while (*resid > 0) {
+		int i;
+		copylen = MIN(*resid, BLOCKLEFT(offset, DTFS_BLOCKSIZE));
+		i = BLOCKNUM(offset, DTFS_BLOCKSHIFT);
+		dest = df->df_blocks[i]
+		    + BLOCKOFF(offset, DTFS_BLOCKSIZE);
+		memcpy(dest, src, copylen);
+		offset += copylen;
+		dest += copylen;
+		*resid -= copylen;
+	}
+
+	dtfs_updatetimes(pn, 0, 1, 1);
+
+	return 0;
+}
+
+int
+dtfs_node_inactive(struct puffs_usermount *pu, puffs_cookie_t opc)
+{
+	struct puffs_node *pn = opc;
+
+	if (pn->pn_va.va_nlink == 0)
+		puffs_setback(puffs_cc_getcc(pu), PUFFS_SETBACK_NOREF_N1);
+	return 0;
+}
+
+int
+dtfs_node_reclaim(struct puffs_usermount *pu, void *opc)
+{
+	struct puffs_node *pn = opc;
+
+	if (pn->pn_va.va_nlink == 0)
+		dtfs_freenode(pn);
+
+	return 0;
+}

Reply via email to