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;
+}