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