DragonFly kernel List (threaded) for 2009-03
DragonFly BSD
DragonFly kernel List (threaded) for 2009-03
[Date Prev][Date Next]  [Thread Prev][Thread Next]  [Date Index][Thread Index]

Porting tmpfs


From: Nikita Glukhov <a63fvb48@xxxxxxxxx>
Date: Sat, 7 Mar 2009 16:21:05 +0300

I'm currently porting tmpfs from FreeBSD. There are some working functions:
mount/unmount, file/directory creation, deletion, symlinks. You can see
how it works now (git diff included):

cd /usr/src/sys/vfs/tmpfs
make
kldload ./tmpfs.ko
cd /usr/src/sbin/mount_tmpfs
make
. /mount_tmpfs /tmp

cp /boot/kernel /tmp
cmp /boot/kernel /tmp/kernel
rm /tmp/kernel

cpdup -r /etc /tmp
rm -r /tmp/*

umount /tmp

But there is one problem with functions that need memory mapping. Usually
filesystems for dragonfly use vop_stdgetpages() and vop_stdputpages() with
buffer cache (except NFS). I think there is no need in my case to use buffer
cache, because it's memory fs. Here is a problem to properly implement this.

All my conclusions I've made from source code, so there may be some errors.
If I'm wrong, please correct me.

NetBSD uses uvm_bio* functions for read and write operations, FreeBSD uses
 it's own tmpfs_mappedread() and tmpfs_mappedwrite(). Try to use FreeBSD
variant causes an error, because these functions doesn't change vnode object,
 but vnode_pager_generic_getpages() intends some changes after VOP_READ().

I think I must use generic getpages/putpages-functions with some changes in
 my read/write-functions to get correct memory mapping. Can anyone direct me
 in the right way?
diff --git a/sbin/mount_tmpfs/Makefile b/sbin/mount_tmpfs/Makefile
new file mode 100644
index 0000000..eb5d258
--- /dev/null
+++ b/sbin/mount_tmpfs/Makefile
@@ -0,0 +1,12 @@
+#
+# $DragonFly: src/sbin/mount_hammer/Makefile,v 1.1 2007/10/10 19:35:19 dillon Exp $
+
+PROG=	mount_tmpfs
+SRCS=	mount_tmpfs.c getmntopts.c
+MAN=	
+
+MOUNT=	${.CURDIR}/../mount
+CFLAGS+= -I${.CURDIR}/../../sys -I${MOUNT}
+.PATH:	${MOUNT}
+
+.include <bsd.prog.mk>
diff --git a/sbin/mount_tmpfs/mount_tmpfs.c b/sbin/mount_tmpfs/mount_tmpfs.c
new file mode 100755
index 0000000..2091870
--- /dev/null
+++ b/sbin/mount_tmpfs/mount_tmpfs.c
@@ -0,0 +1,94 @@
+
+#include <sys/param.h>
+#include <sys/mount.h>
+#include "tmpfs_args.h"
+//#include <vfs/tmpfs/tmpfs.h>
+
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <unistd.h>
+
+#include "/usr/src/sbin/mount/mntopts.h"
+
+struct mntopt mopts[] = {
+	MOPT_STDOPTS,
+	MOPT_NULL
+};
+
+static void	usage(void) __dead2;
+
+int
+main(int argc, char **argv)
+{
+	struct tmpfs_args args;
+	int ch, mntflags = 0;
+	char target[MAXPATHLEN];
+	struct vfsconf vfc;
+	int error;
+
+	/*
+	mntflags = 0;
+	while ((ch = getopt(argc, argv, "o:")) != -1)
+		switch(ch) {
+		case 'o':
+			getmntopts(optarg, mopts, &mntflags, 0);
+			break;
+		case '?':
+		default:
+			usage();
+		}
+	argc -= optind;
+	argv += optind;
+	*/
+	argv++;
+	argc--;
+	if (argc < 1)
+		usage();
+
+	/* resolve target and source with realpath(3) */
+	checkpath(argv[0], target);
+
+	/*
+	 * Mount points that did not use distinct paths (e.g. / on /mnt)
+	 * used to be disallowed because mount linkages were stored in
+	 * vnodes and would lead to endlessly recursive trees.  DragonFly
+	 * stores mount linkages in the namecache topology and does not
+	 * have this problem, so paths no longer need to be distinct.
+	 */
+
+//	args.target = target;
+	bzero(&args, sizeof(struct tmpfs_args));
+	args.ta_version = TMPFS_ARGS_VERSION;
+	args.ta_nodes_max = 160000;
+	args.ta_size_max = 0xf0000000;
+	if (argc >= 2)
+		args.ta_size_max = atoi(argv[1]);
+	args.ta_root_uid = getuid();
+	args.ta_root_gid = getgid();
+	args.ta_root_mode = 0777;
+
+	error = getvfsbyname("tmpfs", &vfc);
+	if (error && vfsisloadable("tmpfs")) {
+		if(vfsload("tmpfs"))
+			err(EX_OSERR, "vfsload(tmpfs)");
+		endvfsent();
+		error = getvfsbyname("tmpfs", &vfc);
+	}
+	if (error)
+		errx(EX_OSERR, "tmpfs filesystem is not available");
+
+	if (mount(vfc.vfc_name, target, mntflags, &args))
+		err(1, NULL);
+	exit(0);
+}
+
+static void
+usage(void)
+{
+	fprintf(stderr,
+		"usage: mount_tmpfs [-o options] mount_point\n");
+	exit(1);
+}
diff --git a/sys/vfs/tmpfs/Makefile b/sys/vfs/tmpfs/Makefile
new file mode 100755
index 0000000..2917513
--- /dev/null
+++ b/sys/vfs/tmpfs/Makefile
@@ -0,0 +1,8 @@
+
+KMOD=	tmpfs
+SRCS=	tmpfs_vnops.c tmpfs_vfsops.c tmpfs_subr.c \
+#	tmpfs_specops.c tmpfs_pool.c tmpfsr_fifops.c
+NOMAN=
+
+.include <bsd.kmod.mk>
+
diff --git a/sys/vfs/tmpfs/tmpfs.h b/sys/vfs/tmpfs/tmpfs.h
new file mode 100755
index 0000000..567b6a7
--- /dev/null
+++ b/sys/vfs/tmpfs/tmpfs.h
@@ -0,0 +1,580 @@
+/*	$NetBSD: tmpfs.h,v 1.37 2008/07/29 09:10:09 pooka Exp $	*/
+
+/*
+ * Copyright (c) 2005, 2006, 2007 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Julio M. Merino Vidal, developed as part of Google's Summer of Code
+ * 2005 program.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _FS_TMPFS_TMPFS_H_
+#define _FS_TMPFS_TMPFS_H_
+
+#include <sys/cdefs.h>
+//#include "opt_posix.h"
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/proc.h>
+#include <sys/lock.h>
+#include <sys/module.h>
+#include <sys/malloc.h>
+
+#include <sys/dirent.h>
+#include <sys/mount.h>
+#include <sys/queue.h>
+#include <sys/vnode.h>
+#include <sys/lockf.h>
+
+#include <vm/vm_object.h>
+#include <vm/vm_pager.h>
+#include <vm/swap_pager.h>
+
+#define TMPFS_DEBUG 0
+
+#if TMPFS_DEBUG > 0
+#define DP(format, args...) kprintf(format, ## args)
+#else
+#define DP(format, args...)
+#endif
+
+MALLOC_DECLARE(M_TMPFS);
+
+#define kmutex_t		struct lock
+
+#define mutex_init(mtx, a, b)	lockinit(mtx, "mutex", 0, 0)
+#define mutex_destroy(mtx)		lockuninit(mtx)
+#define mutex_enter(mtx)		lockmgr(mtx, LK_EXCLUSIVE)
+#define mutex_exit(mtx)			lockmgr(mtx, LK_RELEASE)
+
+#define MNT_GETARGS 0
+#define INT_MAX		0xffffffff
+#define MAXNAMLEN	MNAMELEN
+#define IMNT_MPSAFE	0
+
+#define v_interlock	v_lock
+
+#define UPDATE_CLOSE		0
+
+#define TMPFS_LOCK(tmp)		mutex_enter(&tmp->tm_lock)
+#define TMPFS_UNLOCK(tmp)	mutex_exit(&tmp->tm_lock)
+
+/* XXX */
+#define VM_OBJECT_LOCK(obj)
+#define VM_OBJECT_UNLOCK(obj)
+#define vm_page_lock_queues()
+#define vm_page_unlock_queues()
+
+
+/* --------------------------------------------------------------------- */
+/* For the kernel and anyone who likes peeking into kernel memory        */
+/* --------------------------------------------------------------------- */
+
+#if defined(_KERNEL)
+#include <vfs/tmpfs/tmpfs_pool.h>
+#endif /* defined(_KERNEL) */
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Internal representation of a tmpfs directory entry.
+ */
+struct tmpfs_dirent {
+	TAILQ_ENTRY(tmpfs_dirent)	td_entries;
+
+	/* Length of the name stored in this directory entry.  This avoids
+	 * the need to recalculate it every time the name is used. */
+	uint16_t			td_namelen;
+
+	/* The name of the entry, allocated from a string pool.  This
+	* string is not required to be zero-terminated; therefore, the
+	* td_namelen field must always be used when accessing its value. */
+	char *				td_name;
+
+	/* Pointer to the node this entry refers to. */
+	struct tmpfs_node *		td_node;
+};
+
+/* A directory in tmpfs holds a sorted list of directory entries, which in
+ * turn point to other files (which can be directories themselves).
+ *
+ * In tmpfs, this list is managed by a tail queue, whose head is defined by
+ * the struct tmpfs_dir type.
+ *
+ * It is imporant to notice that directories do not have entries for . and
+ * .. as other file systems do.  These can be generated when requested
+ * based on information available by other means, such as the pointer to
+ * the node itself in the former case or the pointer to the parent directory
+ * in the latter case.  This is done to simplify tmpfs's code and, more
+ * importantly, to remove redundancy. */
+TAILQ_HEAD(tmpfs_dir, tmpfs_dirent);
+
+/* Each entry in a directory has a cookie that identifies it.  Cookies
+ * supersede offsets within directories because, given how tmpfs stores
+ * directories in memory, there is no such thing as an offset.  (Emulating
+ * a real offset could be very difficult.)
+ *
+ * The '.', '..' and the end of directory markers have fixed cookies which
+ * cannot collide with the cookies generated by other entries.  The cookies
+ * fot the other entries are generated based on the memory address on which
+ * stores their information is stored.
+ *
+ * Ideally, using the entry's memory pointer as the cookie would be enough
+ * to represent it and it wouldn't cause collisions in any system.
+ * Unfortunately, this results in "offsets" with very large values which
+ * later raise problems in the Linux compatibility layer (and maybe in other
+ * places) as described in PR kern/32034.  Hence we need to workaround this
+ * with a rather ugly hack.
+ *
+ * Linux 32-bit binaries, unless built with _FILE_OFFSET_BITS=64, have off_t
+ * set to 'long', which is a 32-bit *signed* long integer.  Regardless of
+ * the macro value, GLIBC (2.3 at least) always uses the getdents64
+ * system call (when calling readdir) which internally returns off64_t
+ * offsets.  In order to make 32-bit binaries work, *GLIBC* converts the
+ * 64-bit values returned by the kernel to 32-bit ones and aborts with
+ * EOVERFLOW if the conversion results in values that won't fit in 32-bit
+ * integers (which it assumes is because the directory is extremely large).
+ * This wouldn't cause problems if we were dealing with unsigned integers,
+ * but as we have signed integers, this check fails due to sign expansion.
+ *
+ * For example, consider that the kernel returns the 0xc1234567 cookie to
+ * userspace in a off64_t integer.  Later on, GLIBC casts this value to
+ * off_t (remember, signed) with code similar to:
+ *     system call returns the offset in kernel_value;
+ *     off_t casted_value = kernel_value;
+ *     if (sizeof(off_t) != sizeof(off64_t) &&
+ *         kernel_value != casted_value)
+ *             error!
+ * In this case, casted_value still has 0xc1234567, but when it is compared
+ * for equality against kernel_value, it is promoted to a 64-bit integer and
+ * becomes 0xffffffffc1234567, which is different than 0x00000000c1234567.
+ * Then, GLIBC assumes this is because the directory is very large.
+ *
+ * Given that all the above happens in user-space, we have no control over
+ * it; therefore we must workaround the issue here.  We do this by
+ * truncating the pointer value to a 32-bit integer and hope that there
+ * won't be collisions.  In fact, this will not cause any problems in
+ * 32-bit platforms but some might arise in 64-bit machines (I'm not sure
+ * if they can happen at all in practice).
+ *
+ * XXX A nicer solution shall be attempted. */
+#if defined(_KERNEL)
+#define	TMPFS_DIRCOOKIE_DOT	0
+#define	TMPFS_DIRCOOKIE_DOTDOT	1
+#define	TMPFS_DIRCOOKIE_EOF	2
+static __inline
+off_t
+tmpfs_dircookie(struct tmpfs_dirent *de)
+{
+	off_t cookie;
+
+	cookie = ((off_t)(uintptr_t)de >> 1) & 0x7FFFFFFF;
+	KKASSERT(cookie != TMPFS_DIRCOOKIE_DOT);
+	KKASSERT(cookie != TMPFS_DIRCOOKIE_DOTDOT);
+	KKASSERT(cookie != TMPFS_DIRCOOKIE_EOF);
+
+	return cookie;
+}
+#endif /* defined(_KERNEL) */
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Internal representation of a tmpfs file system node.
+ *
+ * This structure is splitted in two parts: one holds attributes common
+ * to all file types and the other holds data that is only applicable to
+ * a particular type.  The code must be careful to only access those
+ * attributes that are actually allowed by the node's type.
+ */
+struct tmpfs_node {
+	/* Doubly-linked list entry which links all existing nodes for a
+	 * single file system.  This is provided to ease the removal of
+	 * all nodes during the unmount operation. */
+	LIST_ENTRY(tmpfs_node)	tn_entries;
+
+	/* The node's type.  Any of 'VBLK', 'VCHR', 'VDIR', 'VFIFO',
+	 * 'VLNK', 'VREG' and 'VSOCK' is allowed.  The usage of vnode
+	 * types instead of a custom enumeration is to make things simpler
+	 * and faster, as we do not need to convert between two types. */
+	enum vtype		tn_type;
+
+	/* Node identifier. */
+	ino_t			tn_id;
+
+	/* Node's internal status.  This is used by several file system
+	 * operations to do modifications to the node in a delayed
+	 * fashion. */
+	int			tn_status;
+#define	TMPFS_NODE_ACCESSED	(1 << 1)
+#define	TMPFS_NODE_MODIFIED	(1 << 2)
+#define	TMPFS_NODE_CHANGED	(1 << 3)
+
+	/* The node size.  It does not necessarily match the real amount
+	 * of memory consumed by it. */
+	off_t			tn_size;
+
+	/* Generic node attributes. */
+	uid_t			tn_uid;
+	gid_t			tn_gid;
+	mode_t			tn_mode;
+	int			tn_flags;
+	nlink_t			tn_links;
+	struct timespec		tn_atime;
+	struct timespec		tn_mtime;
+	struct timespec		tn_ctime;
+	struct timespec		tn_birthtime;
+	unsigned long		tn_gen;
+
+	/* Head of byte-level lock list (used by tmpfs_advlock). */
+	struct lockf		tn_lockf;
+
+	/* As there is a single vnode for each active file within the
+	 * system, care has to be taken to avoid allocating more than one
+	 * vnode per file.  In order to do this, a bidirectional association
+	 * is kept between vnodes and nodes.
+	 *
+	 * Whenever a vnode is allocated, its v_data field is updated to
+	 * point to the node it references.  At the same time, the node's
+	 * tn_vnode field is modified to point to the new vnode representing
+	 * it.  Further attempts to allocate a vnode for this same node will
+	 * result in returning a new reference to the value stored in
+	 * tn_vnode.
+	 *
+	 * May be NULL when the node is unused (that is, no vnode has been
+	 * allocated for it or it has been reclaimed). */
+	kmutex_t		tn_vlock;
+	struct vnode *		tn_vnode;
+
+	union {
+		/* Valid when tn_type == VBLK || tn_type == VCHR. */
+		struct {
+			int			tn_rmajor;
+			int			tn_rminor;
+		} tn_dev;
+
+		/* Valid when tn_type == VDIR. */
+		struct {
+			/* Pointer to the parent directory.  The root
+			 * directory has a pointer to itself in this field;
+			 * this property identifies the root node. */
+			struct tmpfs_node *	tn_parent;
+
+			/* Head of a tail-queue that links the contents of
+			 * the directory together.  See above for a
+			 * description of its contents. */
+			struct tmpfs_dir	tn_dir;
+
+			/* Number and pointer of the first directory entry
+			 * returned by the readdir operation if it were
+			 * called again to continue reading data from the
+			 * same directory as before.  This is used to speed
+			 * up reads of long directories, assuming that no
+			 * more than one read is in progress at a given time.
+			 * Otherwise, these values are discarded and a linear
+			 * scan is performed from the beginning up to the
+			 * point where readdir starts returning values. */
+			off_t			tn_readdir_lastn;
+			struct tmpfs_dirent *	tn_readdir_lastp;
+		} tn_dir;
+
+		/* Valid when tn_type == VLNK. */
+		struct tn_lnk {
+			/* The link's target, allocated from a string pool. */
+			char *			tn_link;
+		} tn_lnk;
+
+		/* Valid when tn_type == VREG. */
+		struct tn_reg {
+			/* The contents of regular files stored in a tmpfs
+			 * file system are represented by a single anonymous
+			 * memory object (aobj, for short).  The aobj provides
+			 * direct access to any position within the file,
+			 * because its contents are always mapped in a
+			 * contiguous region of virtual memory.  It is a task
+			 * of the memory management subsystem (see uvm(9)) to
+			 * issue the required page ins or page outs whenever
+			 * a position within the file is accessed. */
+			//struct uvm_object *	tn_aobj;
+			vm_object_t		tn_aobj;
+			size_t			tn_aobj_pages;
+		} tn_reg;
+	} tn_spec;
+};
+
+#if defined(_KERNEL)
+LIST_HEAD(tmpfs_node_list, tmpfs_node);
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Internal representation of a tmpfs mount point.
+ */
+struct tmpfs_mount {
+	/* Maximum number of memory pages available for use by the file
+	 * system, set during mount time.  This variable must never be
+	 * used directly as it may be bigger than the current amount of
+	 * free memory; in the extreme case, it will hold the SIZE_MAX
+	 * value.  Instead, use the TMPFS_PAGES_MAX macro. */
+	unsigned int		tm_pages_max;
+
+	/* Number of pages in use by the file system.  Cannot be bigger
+	 * than the value returned by TMPFS_PAGES_MAX in any case. */
+	unsigned int		tm_pages_used;
+
+	/* Pointer to the node representing the root directory of this
+	 * file system. */
+	struct tmpfs_node *	tm_root;
+
+	/* Maximum number of possible nodes for this file system; set
+	 * during mount time.  We need a hard limit on the maximum number
+	 * of nodes to avoid allocating too much of them; their objects
+	 * cannot be released until the file system is unmounted.
+	 * Otherwise, we could easily run out of memory by creating lots
+	 * of empty files and then simply removing them. */
+	unsigned int		tm_nodes_max;
+
+	/* Number of nodes currently allocated.  This number only grows.
+	 * When it reaches tm_nodes_max, no more new nodes can be allocated.
+	 * Of course, the old, unused ones can be reused. */
+	unsigned int		tm_nodes_cnt;
+
+	/* Node list. */
+	kmutex_t		tm_lock;
+	struct tmpfs_node_list	tm_nodes;
+
+	/* Pools used to store file system meta data.  These are not shared
+	 * across several instances of tmpfs for the reasons described in
+	 * tmpfs_pool.c. */
+	struct tmpfs_pool	tm_dirent_pool;
+	struct tmpfs_pool	tm_node_pool;
+	struct tmpfs_str_pool	tm_str_pool;
+};
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * This structure maps a file identifier to a tmpfs node.  Used by the
+ * NFS code.
+ */
+struct tmpfs_fid {
+	uint16_t		tf_len;
+	uint16_t		tf_pad;
+	uint32_t		tf_gen;
+	ino_t			tf_id;
+};
+
+/* --------------------------------------------------------------------- */
+
+
+void print_links(struct vnode *, struct tmpfs_node *);
+
+/*
+ * Prototypes for tmpfs_subr.c.
+ */
+
+int	tmpfs_alloc_node(struct tmpfs_mount *, enum vtype,
+	    uid_t uid, gid_t gid, mode_t mode, struct tmpfs_node *,
+	    char *, int, int, struct tmpfs_node **);
+void	tmpfs_free_node(struct tmpfs_mount *, struct tmpfs_node *);
+int	tmpfs_alloc_dirent(struct tmpfs_mount *, struct tmpfs_node *,
+	    const char *, uint16_t, struct tmpfs_dirent **);
+void	tmpfs_free_dirent(struct tmpfs_mount *, struct tmpfs_dirent *,
+	    boolean_t);
+int	tmpfs_alloc_vp(struct mount *, struct tmpfs_node *, struct vnode **);
+void	tmpfs_free_vp(struct vnode *);
+int	tmpfs_alloc_file(struct vnode *, struct vnode **, struct vattr *,
+	    struct namecache *, struct ucred *, char *);
+void	tmpfs_dir_attach(struct vnode *, struct tmpfs_dirent *);
+void	tmpfs_dir_detach(struct vnode *, struct tmpfs_dirent *);
+struct tmpfs_dirent *	tmpfs_dir_lookup(struct tmpfs_node *,
+			    struct namecache *);
+int	tmpfs_dir_getdotdent(struct tmpfs_node *, struct uio *);
+int	tmpfs_dir_getdotdotdent(struct tmpfs_node *, struct uio *);
+struct tmpfs_dirent *tmpfs_dir_lookupbycookie(struct tmpfs_node *, off_t);
+int	tmpfs_dir_getdents(struct tmpfs_node *, struct uio *, off_t *);
+int	tmpfs_reg_resize(struct vnode *, off_t);
+//size_t	tmpfs_mem_info(bool);
+int	tmpfs_chflags(struct vnode *, int, struct ucred *);
+int	tmpfs_chmod(struct vnode *, mode_t, struct ucred *);
+int	tmpfs_chown(struct vnode *, uid_t, gid_t, struct ucred *);
+int	tmpfs_chsize(struct vnode *, u_quad_t, struct ucred *);
+int	tmpfs_chtimes(struct vnode *, const struct timespec *,
+    const struct timespec *, const struct timespec *, int, struct ucred *);
+void	tmpfs_itimes(struct vnode *, const struct timespec *,
+	    const struct timespec *, const struct timespec *);
+
+void	tmpfs_update(struct vnode *, const struct timespec *,
+	    const struct timespec *, const struct timespec *, int);
+int	tmpfs_truncate(struct vnode *, off_t);
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Convenience macros to simplify some logical expressions.
+ */
+#define IMPLIES(a, b) (!(a) || (b))
+#define IFF(a, b) (IMPLIES(a, b) && IMPLIES(b, a))
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Checks that the directory entry pointed by 'de' matches the name 'name'
+ * with a length of 'len'.
+ */
+#define TMPFS_DIRENT_MATCHES(de, name, len) \
+    (de->td_namelen == (uint16_t)len && \
+    memcmp((de)->td_name, (name), (de)->td_namelen) == 0)
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Ensures that the node pointed by 'node' is a directory and that its
+ * contents are consistent with respect to directories.
+ */
+#define TMPFS_VALIDATE_DIR(node) \
+    KKASSERT((node)->tn_type == VDIR); \
+    KKASSERT((node)->tn_size % sizeof(struct tmpfs_dirent) == 0); \
+    KKASSERT((node)->tn_spec.tn_dir.tn_readdir_lastp == NULL || \
+        tmpfs_dircookie((node)->tn_spec.tn_dir.tn_readdir_lastp) == \
+        (node)->tn_spec.tn_dir.tn_readdir_lastn);
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Memory management stuff.
+ */
+
+/* Amount of memory pages to reserve for the system (e.g., to not use by
+ * tmpfs).
+ * XXX: Should this be tunable through sysctl, for instance? */
+#define TMPFS_PAGES_RESERVED (4 * 1024 * 1024 / PAGE_SIZE)
+
+/*
+ * Returns information about the number of available memory pages,
+ * including physical and virtual ones.
+ *
+ * If 'total' is TRUE, the value returned is the total amount of memory
+ * pages configured for the system (either in use or free).
+ * If it is FALSE, the value returned is the amount of free memory pages.
+ *
+ * Remember to remove TMPFS_PAGES_RESERVED from the returned value to avoid
+ * excessive memory usage.
+ *
+ */
+
+#define swap_pager_avail swap_pager_full
+
+static __inline size_t
+tmpfs_mem_info(void)
+{
+	size_t size;
+
+	size = swap_pager_avail + vmstats.v_free_count + vmstats.v_inactive_count;
+	size -= size > vmstats.v_wire_count ? vmstats.v_wire_count : size;
+	return size;
+}
+
+/* Returns the maximum size allowed for a tmpfs file system.  This macro
+ * must be used instead of directly retrieving the value from tm_pages_max.
+ * The reason is that the size of a tmpfs file system is dynamic: it lets
+ * the user store files as long as there is enough free memory (including
+ * physical memory and swap space).  Therefore, the amount of memory to be
+ * used is either the limit imposed by the user during mount time or the
+ * amount of available memory, whichever is lower.  To avoid consuming all
+ * the memory for a given mount point, the system will always reserve a
+ * minimum of TMPFS_PAGES_RESERVED pages, which is also taken into account
+ * by this macro (see above). */
+static __inline size_t
+TMPFS_PAGES_MAX(struct tmpfs_mount *tmp)
+{
+	size_t freepages;
+
+	freepages = tmpfs_mem_info();//false);
+	if (freepages < TMPFS_PAGES_RESERVED)
+		freepages = 0;
+	else
+		freepages -= TMPFS_PAGES_RESERVED;
+
+	return MIN(tmp->tm_pages_max, freepages + tmp->tm_pages_used);
+}
+
+/* Returns the available space for the given file system. */
+#define TMPFS_PAGES_AVAIL(tmp)		\
+    ((ssize_t)(TMPFS_PAGES_MAX(tmp) - (tmp)->tm_pages_used))
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Macros/functions to convert from generic data structures to tmpfs
+ * specific ones.
+ */
+
+static __inline
+struct tmpfs_mount *
+VFS_TO_TMPFS(struct mount *mp)
+{
+	struct tmpfs_mount *tmp;
+
+#ifdef KKASSERT
+	KKASSERT((mp) != NULL && (mp)->mnt_data != NULL);
+#endif
+	tmp = (struct tmpfs_mount *)(mp)->mnt_data;
+	return tmp;
+}
+
+#endif /* defined(_KERNEL) */
+
+static __inline
+struct tmpfs_node *
+VP_TO_TMPFS_NODE(struct vnode *vp)
+{
+	struct tmpfs_node *node;
+
+#ifdef KKASSERT
+	KKASSERT((vp) != NULL && (vp)->v_data != NULL);
+#endif
+	node = (struct tmpfs_node *)vp->v_data;
+	return node;
+}
+
+#if defined(_KERNEL)
+
+static __inline
+struct tmpfs_node *
+VP_TO_TMPFS_DIR(struct vnode *vp)
+{
+	struct tmpfs_node *node;
+
+	node = VP_TO_TMPFS_NODE(vp);
+#ifdef KKASSERT
+	TMPFS_VALIDATE_DIR(node);
+#endif
+	return node;
+}
+
+#endif /* defined(_KERNEL) */
+#endif /* _FS_TMPFS_TMPFS_H_ */
diff --git a/sys/vfs/tmpfs/tmpfs_args.h b/sys/vfs/tmpfs/tmpfs_args.h
new file mode 100755
index 0000000..ba3e9b9
--- /dev/null
+++ b/sys/vfs/tmpfs/tmpfs_args.h
@@ -0,0 +1,54 @@
+/*	$NetBSD: tmpfs_args.h,v 1.3 2008/07/29 09:10:09 pooka Exp $	*/
+
+/*
+ * Copyright (c) 2005, 2006, 2007 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Julio M. Merino Vidal, developed as part of Google's Summer of Code
+ * 2005 program.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _FS_TMPFS_TMPFS_ARGS_H_
+#define _FS_TMPFS_TMPFS_ARGS_H_
+
+/*
+ * This structure is used to communicate mount parameters between userland
+ * and kernel space.
+ */
+#define TMPFS_ARGS_VERSION	1
+struct tmpfs_args {
+	int			ta_version;
+
+	/* Size counters. */
+	ino_t			ta_nodes_max;
+	off_t			ta_size_max;
+
+	/* Root node attributes. */
+	uid_t			ta_root_uid;
+	gid_t			ta_root_gid;
+	mode_t			ta_root_mode;
+};
+
+#endif /* _FS_TMPFS_TMPFS_ARGS_H_ */
diff --git a/sys/vfs/tmpfs/tmpfs_pool.h b/sys/vfs/tmpfs/tmpfs_pool.h
new file mode 100755
index 0000000..017c7d8
--- /dev/null
+++ b/sys/vfs/tmpfs/tmpfs_pool.h
@@ -0,0 +1,121 @@
+/*	$NetBSD: tmpfs_pool.h,v 1.7 2008/04/28 20:24:02 martin Exp $	*/
+
+/*
+ * Copyright (c) 2005, 2006 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Julio M. Merino Vidal, developed as part of Google's Summer of Code
+ * 2005 program.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _FS_TMPFS_TMPFS_POOL_H_
+#define _FS_TMPFS_TMPFS_POOL_H_
+
+struct pool
+{
+	int p_size;
+	int p_nallocs;
+	int p_nfrees;
+};
+
+void *pool_get(struct pool* pp, int flags);
+void  pool_put(struct pool* pp, void *p);
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * tmpfs_pool is an extension of regular system pools to also hold data
+ * specific to tmpfs.  More specifically, we want a pointer to the
+ * tmpfs_mount structure using the pool so that we can update its memory
+ * usage statistics.
+ */
+struct tmpfs_pool {
+	struct pool		tp_pool;
+
+	/* Reference to the mount point that holds the pool.  This is used
+	 * by the tmpfs_pool_allocator to access and modify the memory
+	 * accounting variables for the mount point. */
+	struct tmpfs_mount *	tp_mount;
+
+	/* The pool's name.  Used as the wait channel. */
+	char			tp_name[64];
+};
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * tmpfs uses variable-length strings to store file names and to store
+ * link targets.  Reserving a fixed-size buffer for each of them is
+ * inefficient because it will consume a lot more memory than is really
+ * necessary.  However, managing variable-sized buffers is difficult as
+ * regards memory allocation and very inefficient in computation time.
+ * This is why tmpfs provides an hybrid scheme to store strings: string
+ * pools.
+ *
+ * A string pool is a collection of memory pools, each one with elements
+ * of a fixed size.  In tmpfs's case, a string pool contains independent
+ * memory pools for 16-byte, 32-byte, 64-byte, 128-byte, 256-byte,
+ * 512-byte and 1024-byte long objects.  Whenever an object is requested
+ * from the pool, the new object's size is rounded to the closest upper
+ * match and an item from the corresponding pool is returned.
+ */
+struct tmpfs_str_pool {
+	struct tmpfs_pool	tsp_pool_16;
+	struct tmpfs_pool	tsp_pool_32;
+	struct tmpfs_pool	tsp_pool_64;
+	struct tmpfs_pool	tsp_pool_128;
+	struct tmpfs_pool	tsp_pool_256;
+	struct tmpfs_pool	tsp_pool_512;
+	struct tmpfs_pool	tsp_pool_1024;
+};
+
+/* --------------------------------------------------------------------- */
+#ifdef _KERNEL
+
+/*
+ * Convenience functions and macros to manipulate a tmpfs_pool.
+ */
+
+void	tmpfs_pool_init(struct tmpfs_pool *tpp, size_t size,
+	    const char *what, struct tmpfs_mount *tmp);
+void	tmpfs_pool_destroy(struct tmpfs_pool *tpp);
+
+#define	TMPFS_POOL_GET(tpp, flags) pool_get((struct pool *)(tpp), flags)
+#define	TMPFS_POOL_PUT(tpp, v) pool_put((struct pool *)(tpp), v)
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Functions to manipulate a tmpfs_str_pool.
+ */
+
+void	tmpfs_str_pool_init(struct tmpfs_str_pool *, struct tmpfs_mount *);
+void	tmpfs_str_pool_destroy(struct tmpfs_str_pool *);
+char *	tmpfs_str_pool_get(struct tmpfs_str_pool *, size_t, int);
+void	tmpfs_str_pool_put(struct tmpfs_str_pool *, char *, size_t);
+
+#endif
+
+#endif /* _FS_TMPFS_TMPFS_POOL_H_ */
diff --git a/sys/vfs/tmpfs/tmpfs_subr.c b/sys/vfs/tmpfs/tmpfs_subr.c
new file mode 100755
index 0000000..820c1b0
--- /dev/null
+++ b/sys/vfs/tmpfs/tmpfs_subr.c
@@ -0,0 +1,1375 @@
+/*	$NetBSD: tmpfs_subr.c,v 1.48 2008/06/19 19:03:44 christos Exp $	*/
+
+/*
+ * Copyright (c) 2005, 2006, 2007 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Julio M. Merino Vidal, developed as part of Google's Summer of Code
+ * 2005 program.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Efficient memory file system supporting functions.
+ */
+
+#include <sys/cdefs.h>
+//__KERNEL_RCSID(0, "$NetBSD: tmpfs_subr.c,v 1.48 2008/06/19 19:03:44 christos Exp $");
+
+#include <sys/param.h>
+#include <sys/dirent.h>
+#include <sys/event.h>
+#include <sys/malloc.h>
+#include <sys/mount.h>
+#include <sys/namei.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/systm.h>
+#include <sys/vnode.h>
+#include <sys/proc.h>
+
+#include <machine/atomic.h>
+
+#include <vm/vm_extern.h>
+#include <vm/swap_pager.h>
+
+#include <vfs/tmpfs/tmpfs.h>
+#include <vfs/tmpfs/tmpfs_vnops.h>
+
+#ifndef VT_TMPFS
+#define VT_TMPFS (VT_HAMMER + 1)
+#endif
+
+
+MALLOC_DEFINE(M_TMPFS, "tmpfs", "tmpfs data");
+
+#define atomic_inc_uint_nv(p)	atomic_add_int(p, 1)
+#define atomic_dec_uint(p)	atomic_subtract_int(p, 1)
+
+#define uvm_vnp_setsize(vp, size)
+
+static __inline void
+VN_KNOTE(struct vnode *vp, long hint)
+{
+	//mutex_enter(&vp->v_interlock);
+	//KNOTE(&vp->v_klist, hint);
+	//mutex_exit(&vp->v_interlock);
+}
+
+void
+print_links(struct vnode *vp, struct tmpfs_node *node)
+{
+	DP("l v%x n%x f", vp->v_sysref.refcnt, node->tn_links);
+	if (vp->v_flag & VCACHED) DP("VCACHED ");	/* No active references but has cache value */
+	if (vp->v_flag & VOBJBUF) DP("VOBJBUF ");	/* Allocate buffers in VM object */
+	if (vp->v_flag & VINACTIVE) DP("VINACTIVE ");	/* The vnode is inactive (did VOP_INACTIVE) */
+	if (vp->v_flag & VAGE) DP("VAGE ");		/* Insert vnode at head of free list */
+	if (vp->v_flag & VOLOCK) DP("VOLOCK ");		/* vnode is locked waiting for an object */
+	if (vp->v_flag & VOWANT) DP("VOWANT ");	/* a process is waiting for VOLOCK */
+	if (vp->v_flag & VRECLAIMED) DP("VRECLAIMED ");	/* This vnode has been destroyed */
+	if (vp->v_flag & VFREE) DP("VFREE ");		/* This vnode is on the freelist */
+							/* open for business    0x100000 */
+	if (vp->v_flag & VONWORKLST) DP("VONWORKLST ");	 /* On syncer work-list */
+	if (vp->v_flag & VMOUNT) DP("VMOUNT ");		/* Mount in progress */
+	if (vp->v_flag & VOBJDIRTY) DP("VOBJDIRTY ");
+	DP("\n");
+}
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Allocates a new node of type 'type' inside the 'tmp' mount point, with
+ * its owner set to 'uid', its group to 'gid' and its mode set to 'mode',
+ * using the credentials of the process 'p'.
+ *
+ * If the node type is set to 'VDIR', then the parent parameter must point
+ * to the parent directory of the node being created.  It may only be NULL
+ * while allocating the root node.
+ *
+ * If the node type is set to 'VBLK' or 'VCHR', then the rdev parameter
+ * specifies the device the node represents.
+ *
+ * If the node type is set to 'VLNK', then the parameter target specifies
+ * the file name of the target file for the symbolic link that is being
+ * created.
+ *
+ * Note that new nodes are retrieved from the available list if it has
+ * items or, if it is empty, from the node pool as long as there is enough
+ * space to create them.
+ *
+ * Returns zero on success or an appropriate error code on failure.
+ */
+
+int
+tmpfs_alloc_node(struct tmpfs_mount *tmp, enum vtype type,
+    uid_t uid, gid_t gid, mode_t mode, struct tmpfs_node *parent,
+    char *target, int rmajor, int rminor, struct tmpfs_node **node)
+{
+	struct tmpfs_node *nnode;
+
+	DP("tmpfs_alloc_node %d %d parent %p t %s\n", type, mode, parent, target );
+
+	/* If the root directory of the 'tmp' file system is not yet
+	 * allocated, this must be the request to do it. */
+	KKASSERT(IMPLIES(tmp->tm_root == NULL, parent == NULL && type == VDIR));
+	KKASSERT(IFF(type == VLNK, target != NULL));
+	KKASSERT(IFF(type == VBLK || type == VCHR, rmajor != VNOVAL, rminor != VNOVAL));
+	KKASSERT(uid != VNOVAL && gid != VNOVAL && mode != VNOVAL);
+	nnode = NULL;
+
+	atomic_add_int(&tmp->tm_nodes_cnt, 1);
+	if (tmp->tm_nodes_cnt >= tmp->tm_nodes_max) {
+		atomic_subtract_int(&tmp->tm_nodes_cnt, 1);
+		return ENOSPC;
+	}
+
+	nnode = (struct tmpfs_node *)TMPFS_POOL_GET(&tmp->tm_node_pool, 0);
+	if (nnode == NULL) {
+		atomic_subtract_int(&tmp->tm_nodes_cnt, 1);
+		return ENOSPC;
+	}
+
+	/*
+	 * XXX Where the pool is backed by a map larger than (4GB *
+	 * sizeof(*nnode)), this may produce duplicate inode numbers
+	 * for applications that do not understand 64-bit ino_t.
+	 */
+	nnode->tn_id = (ino_t)((uintptr_t)nnode / sizeof(*nnode));
+	nnode->tn_gen = karc4random();
+
+	/* Generic initialization. */
+	nnode->tn_type = type;
+	nnode->tn_size = 0;
+	nnode->tn_status = 0;
+	nnode->tn_flags = 0;
+	nnode->tn_links = 0;
+	getnanotime(&nnode->tn_atime);
+	nnode->tn_birthtime = nnode->tn_ctime = nnode->tn_mtime =
+	    nnode->tn_atime;
+	nnode->tn_uid = uid;
+	nnode->tn_gid = gid;
+	nnode->tn_mode = mode;
+	nnode->tn_vnode = NULL;
+
+	/* Type-specific initialization. */
+	switch (nnode->tn_type) {
+	case VBLK:
+	case VCHR:
+		nnode->tn_spec.tn_dev.tn_rmajor = rmajor;
+		nnode->tn_spec.tn_dev.tn_rminor = rminor;
+		break;
+
+	case VDIR:
+		TAILQ_INIT(&nnode->tn_spec.tn_dir.tn_dir);
+		nnode->tn_spec.tn_dir.tn_parent =
+		    (parent == NULL) ? nnode : parent;
+		nnode->tn_spec.tn_dir.tn_readdir_lastn = 0;
+		nnode->tn_spec.tn_dir.tn_readdir_lastp = NULL;
+		nnode->tn_links++;
+		break;
+
+	case VFIFO: /* FALLTHROUGH */
+	case VSOCK:
+		break;
+
+	case VLNK:
+		KKASSERT(strlen(target) < MAXPATHLEN);
+		nnode->tn_size = strlen(target);
+		nnode->tn_spec.tn_lnk.tn_link =
+		    tmpfs_str_pool_get(&tmp->tm_str_pool, nnode->tn_size, 0);
+		if (nnode->tn_spec.tn_lnk.tn_link == NULL) {
+			atomic_subtract_int(&tmp->tm_nodes_cnt, 1);
+			TMPFS_POOL_PUT(&tmp->tm_node_pool, nnode);
+			return ENOSPC;
+		}
+		memcpy(nnode->tn_spec.tn_lnk.tn_link, target, nnode->tn_size);
+		break;
+
+	case VREG:
+		nnode->tn_spec.tn_reg.tn_aobj =
+		    vm_pager_allocate(OBJT_SWAP, NULL, 0, VM_PROT_DEFAULT, 0);
+		nnode->tn_spec.tn_reg.tn_aobj_pages = 0;
+		//nnode->tn_spec.tn_reg.tn_aobj =
+		//    uao_create(INT32_MAX - PAGE_SIZE, 0);
+		//nnode->tn_spec.tn_reg.tn_aobj_pages = 0;
+		break;
+
+	default:
+		KKASSERT(0);
+	}
+
+	mutex_init(&nnode->tn_vlock, MUTEX_DEFAULT, IPL_NONE);
+
+	mutex_enter(&tmp->tm_lock);
+	LIST_INSERT_HEAD(&tmp->tm_nodes, nnode, tn_entries);
+	mutex_exit(&tmp->tm_lock);
+
+	*node = nnode;
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Destroys the node pointed to by node from the file system 'tmp'.
+ * If the node does not belong to the given mount point, the results are
+ * unpredicted.
+ *
+ * If the node references a directory; no entries are allowed because
+ * their removal could need a recursive algorithm, something forbidden in
+ * kernel space.  Furthermore, there is not need to provide such
+ * functionality (recursive removal) because the only primitives offered
+ * to the user are the removal of empty directories and the deletion of
+ * individual files.
+ *
+ * Note that nodes are not really deleted; in fact, when a node has been
+ * allocated, it cannot be deleted during the whole life of the file
+ * system.  Instead, they are moved to the available list and remain there
+ * until reused.
+ */
+void
+tmpfs_free_node(struct tmpfs_mount *tmp, struct tmpfs_node *node)
+{
+	size_t pages = 0;
+	DP("tmpfs_free_node tmp %p node %p\n", tmp, node );
+
+	atomic_dec_uint(&tmp->tm_nodes_cnt);
+	mutex_enter(&tmp->tm_lock);
+	LIST_REMOVE(node, tn_entries);
+	mutex_exit(&tmp->tm_lock);
+
+	switch (node->tn_type) {
+	case VLNK:
+		tmpfs_str_pool_put(&tmp->tm_str_pool,
+		    node->tn_spec.tn_lnk.tn_link, node->tn_size);
+		break;
+
+	case VREG:
+		vm_object_deallocate(node->tn_spec.tn_reg.tn_aobj);
+		pages = node->tn_spec.tn_reg.tn_aobj_pages;
+		// XXXX if (node->tn_spec.tn_reg.tn_aobj != NULL)
+			//uao_detach(node->tn_spec.tn_reg.tn_aobj);
+		break;
+
+	default:
+		break;
+	}
+	mutex_destroy(&node->tn_vlock);
+	TMPFS_POOL_PUT(&tmp->tm_node_pool, node);
+
+	atomic_subtract_int(&tmp->tm_pages_used, pages);
+	DP("tmpfs_free_node used %d\n", tmp->tm_pages_used);
+}
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Allocates a new directory entry for the node node with a name of name.
+ * The new directory entry is returned in *de.
+ *
+ * The link count of node is increased by one to reflect the new object
+ * referencing it.  This takes care of notifying kqueue listeners about
+ * this change.
+ *
+ * Returns zero on success or an appropriate error code on failure.
+ */
+int
+tmpfs_alloc_dirent(struct tmpfs_mount *tmp, struct tmpfs_node *node,
+    const char *name, uint16_t len, struct tmpfs_dirent **de)
+{
+	struct tmpfs_dirent *nde;
+	DP("tmpfs_alloc_dirent tmp %p n %p %*.*s\n",tmp,node,len,len,name);
+	nde = (struct tmpfs_dirent *) TMPFS_POOL_GET(&tmp->tm_dirent_pool, 0);
+	if (nde == NULL)
+		return ENOSPC;
+
+	nde->td_name = tmpfs_str_pool_get(&tmp->tm_str_pool, len, 0);
+	if (nde->td_name == NULL) {
+		TMPFS_POOL_PUT(&tmp->tm_dirent_pool, nde);
+		return ENOSPC;
+	}
+	nde->td_namelen = len;
+	memcpy(nde->td_name, name, len);
+	nde->td_node = node;
+
+	node->tn_links++;
+	if (node->tn_links > 1 && node->tn_vnode != NULL)
+		VN_KNOTE(node->tn_vnode, NOTE_LINK);
+	*de = nde;
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Frees a directory entry.  It is the caller's responsibility to destroy
+ * the node referenced by it if needed.
+ *
+ * The link count of node is decreased by one to reflect the removal of an
+ * object that referenced it.  This only happens if 'node_exists' is true;
+ * otherwise the function will not access the node referred to by the
+ * directory entry, as it may already have been released from the outside.
+ *
+ * Interested parties (kqueue) are notified of the link count change; note
+ * that this can include both the node pointed to by the directory entry
+ * as well as its parent.
+ */
+void
+tmpfs_free_dirent(struct tmpfs_mount *tmp, struct tmpfs_dirent *de,
+    boolean_t node_exists)
+{
+	DP("tmpfs_free_dirent tmp %p dirent %p \n", tmp, de );
+	if (node_exists) {
+		struct tmpfs_node *node;
+		node = de->td_node;
+
+		KKASSERT(node->tn_links > 0);
+		node->tn_links--;
+		if (node->tn_vnode != NULL)
+			VN_KNOTE(node->tn_vnode, node->tn_links == 0 ?
+			    NOTE_DELETE : NOTE_LINK);
+		if (node->tn_type == VDIR)
+			VN_KNOTE(node->tn_spec.tn_dir.tn_parent->tn_vnode,
+			    NOTE_LINK);
+	}
+	tmpfs_str_pool_put(&tmp->tm_str_pool, de->td_name, de->td_namelen);
+	TMPFS_POOL_PUT(&tmp->tm_dirent_pool, de);
+}
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Allocates a new vnode for the node node or returns a new reference to
+ * an existing one if the node had already a vnode referencing it.  The
+ * resulting locked vnode is returned in *vpp.
+ *
+ * Returns zero on success or an appropriate error code on failure.
+ */
+int
+tmpfs_alloc_vp(struct mount *mp, struct tmpfs_node *node, struct vnode **vpp)
+{
+	int error;
+	struct vnode *vp;
+	DP("tmpfs_alloc_vp tmp %p node %p\n", mp, node );
+	/* If there is already a vnode, then lock it. */
+	for (;;) {
+		mutex_enter(&node->tn_vlock);
+		if ((vp = node->tn_vnode) != NULL) {
+			/*XXX*///mutex_enter(&vp->v_interlock);
+			error = vget(vp, LK_EXCLUSIVE);// | LK_INTERLOCK);
+			mutex_exit(&node->tn_vlock);
+			if (error == ENOENT) {
+				/* vnode was reclaimed. */
+				continue;
+			}
+			*vpp = vp;
+			DP("tmpfs_alloc_vp ret2 %p\n", vp);
+			return error;
+		}
+		break;
+	}
+	/* Get a new vnode and associate it with our node. */
+	error = getnewvnode(VT_TMPFS, mp, &vp, 0, 0);
+	if (error != 0) {
+		mutex_exit(&node->tn_vlock);
+		return error;
+	}
+	vp->v_type = node->tn_type;
+	/* Type-specific initialization. */
+	switch (node->tn_type) {
+	case VBLK: /* FALLTHROUGH */
+	case VCHR:
+		vp->v_ops = &mp->mnt_vn_spec_ops;
+		addaliasu(vp, node->tn_spec.tn_dev.tn_rmajor,
+		    node->tn_spec.tn_dev.tn_rminor);
+		break;
+	case VDIR:
+		/*XXX  vp->v_flag |= node->tn_spec.tn_dir.tn_parent == node
+		    ? V_ROOT : 0; */
+		break;
+	case VFIFO:
+		vp->v_ops = &mp->mnt_vn_fifo_ops;
+		break;
+	case VREG:
+		vinitvmio(vp, node->tn_size);
+		break;
+	case VLNK: /* FALLTHROUGH */
+	case VSOCK:
+		break;
+	default:
+		KKASSERT(0);
+	}
+	/* XXX uvm_vnp_setsize(vp, node->tn_size); */
+	vp->v_data = node;
+	node->tn_vnode = vp;
+	mutex_exit(&node->tn_vlock);
+	*vpp = vp;
+	KKASSERT(IFF(error == 0, *vpp != NULL && vn_islocked(*vpp)));
+	KKASSERT(*vpp == node->tn_vnode);
+	DP("tmpfs_alloc_vp ret %p\t", vp);
+	print_links(vp, node);
+	return error;
+}
+/* --------------------------------------------------------------------- */
+
+/*
+ * Destroys the association between the vnode vp and the node it
+ * references.
+ */
+void
+tmpfs_free_vp(struct vnode *vp)
+{
+	struct tmpfs_node *node;
+	DP("tmpfs_free_vp vp %p\n", vp);
+
+	node = VP_TO_TMPFS_NODE(vp);
+
+	mutex_enter(&node->tn_vlock);
+	node->tn_vnode = NULL;
+	mutex_exit(&node->tn_vlock);
+	vp->v_data = NULL;
+}
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Allocates a new file of type 'type' and adds it to the parent directory
+ * 'dvp'; this addition is done using the component name given in 'cnp'.
+ * The ownership of the new file is automatically assigned based on the
+ * credentials of the caller (through 'cnp'), the group is set based on
+ * the parent directory and the mode is determined from the 'vap' argument.
+ * If successful, *vpp holds a vnode to the newly created file and zero
+ * is returned.  Otherwise *vpp is NULL and the function returns an
+ * appropriate error code.
+ */
+int
+tmpfs_alloc_file(struct vnode *dvp, struct vnode **vpp, struct vattr *vap,
+		struct namecache *ncp, struct ucred *cred, char *target)
+{
+	int error;
+	struct tmpfs_dirent *de;
+	struct tmpfs_mount *tmp;
+	struct tmpfs_node *dnode;
+	struct tmpfs_node *node;
+	struct tmpfs_node *parent;
+
+	DP("tmpfs_alloc_file dvp %p vtype %d\n", dvp, vap->va_type);
+	KKASSERT(vn_islocked(dvp));
+
+	tmp = VFS_TO_TMPFS(dvp->v_mount);
+	dnode = VP_TO_TMPFS_DIR(dvp);
+	*vpp = NULL;
+	/* If the entry we are creating is a directory, we cannot overflow
+	 * the number of links of its parent, because it will get a new
+	 * link. */
+	if (vap->va_type == VDIR) {
+		/* Ensure that we do not overflow the maximum number of links
+		 * imposed by the system. */
+		KKASSERT(dnode->tn_links <= LINK_MAX);
+		if (dnode->tn_links == LINK_MAX) {
+			error = EMLINK;
+			goto out;
+		}
+
+		parent = dnode;
+	} else
+		parent = NULL;
+
+	/* Allocate a node that represents the new file. */
+	error = tmpfs_alloc_node(tmp, vap->va_type,
+	    /* XXX kauth_cred_geteuid(cred), */
+	    cred->cr_uid, dnode->tn_gid,
+	    vap->va_mode, parent, target,
+	    vap->va_rmajor, vap->va_rminor,
+	    &node);
+	if (error != 0)
+		goto out;
+
+	/* Allocate a directory entry that points to the new file. */
+	error = tmpfs_alloc_dirent(tmp, node, ncp->nc_name, ncp->nc_nlen, &de);
+	if (error != 0) {
+		tmpfs_free_node(tmp, node);
+		goto out;
+	}
+
+	/* Allocate a vnode for the new file. */
+	error = tmpfs_alloc_vp(dvp->v_mount, node, vpp);
+	if (error != 0) {
+		tmpfs_free_dirent(tmp, de, TRUE);
+		tmpfs_free_node(tmp, node);
+		goto out;
+	}
+
+	/* Now that all required items are allocated, we can proceed to
+	 * insert the new node into the directory, an operation that
+	 * cannot fail. */
+	tmpfs_dir_attach(dvp, de);
+	if (vap->va_type == VDIR) {
+		VN_KNOTE(dvp, NOTE_LINK);
+		dnode->tn_links++;
+		KKASSERT(dnode->tn_links <= LINK_MAX);
+	}
+out:
+
+	KKASSERT(IFF(error == 0, *vpp != NULL));
+	return error;
+}
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Attaches the directory entry de to the directory represented by vp.
+ * Note that this does not change the link count of the node pointed by
+ * the directory entry, as this is done by tmpfs_alloc_dirent.
+ *
+ * As the "parent" directory changes, interested parties are notified of
+ * a write to it.
+ */
+void
+tmpfs_dir_attach(struct vnode *vp, struct tmpfs_dirent *de)
+{
+	struct tmpfs_node *dnode;
+	DP("tmpfs_dir_attach dvp %p dirent %p\n", vp, de);
+	dnode = VP_TO_TMPFS_DIR(vp);
+	TAILQ_INSERT_TAIL(&dnode->tn_spec.tn_dir.tn_dir, de, td_entries);
+	dnode->tn_size += sizeof(struct tmpfs_dirent);
+	dnode->tn_status |= TMPFS_NODE_ACCESSED | TMPFS_NODE_CHANGED | \
+	    TMPFS_NODE_MODIFIED;
+	uvm_vnp_setsize(vp, dnode->tn_size);
+	VN_KNOTE(vp, NOTE_WRITE);
+}
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Detaches the directory entry de from the directory represented by vp.
+ * Note that this does not change the link count of the node pointed by
+ * the directory entry, as this is done by tmpfs_free_dirent.
+ *
+ * As the "parent" directory changes, interested parties are notified of
+ * a write to it.
+ */
+void
+tmpfs_dir_detach(struct vnode *vp, struct tmpfs_dirent *de)
+{
+	struct tmpfs_node *dnode;
+	DP("tmpfs_dir_detach vp %p dirent %p\n", vp, de);
+	KKASSERT(vn_islocked(vp));
+	dnode = VP_TO_TMPFS_DIR(vp);
+	if (dnode->tn_spec.tn_dir.tn_readdir_lastp == de) {
+		dnode->tn_spec.tn_dir.tn_readdir_lastn = 0;
+		dnode->tn_spec.tn_dir.tn_readdir_lastp = NULL;
+	}
+	TAILQ_REMOVE(&dnode->tn_spec.tn_dir.tn_dir, de, td_entries);
+	dnode->tn_size -= sizeof(struct tmpfs_dirent);
+	dnode->tn_status |= TMPFS_NODE_ACCESSED | TMPFS_NODE_CHANGED | \
+	    TMPFS_NODE_MODIFIED;
+	uvm_vnp_setsize(vp, dnode->tn_size);
+	VN_KNOTE(vp, NOTE_WRITE);
+}
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Looks for a directory entry in the directory represented by node.
+ * 'cnp' describes the name of the entry to look for.  Note that the .
+ * and .. components are not allowed as they do not physically exist
+ * within directories.
+ *
+ * Returns a pointer to the entry when found, otherwise NULL.
+ */
+struct tmpfs_dirent *
+tmpfs_dir_lookup(struct tmpfs_node *node, struct namecache *ncp)
+{
+	boolean_t found;
+	struct tmpfs_dirent *de;
+	DP("tmpfs_dir_lookup node %p ncp %p\n", node, ncp);
+	KKASSERT(IMPLIES(ncp->nc_name == 1, ncp->nc_name[0] != '.'));
+	KKASSERT(IMPLIES(ncp->nc_nlen == 2, !(ncp->nc_name[0] == '.' &&
+	    ncp->nc_name[1] == '.')));
+	TMPFS_VALIDATE_DIR(node);
+	node->tn_status |= TMPFS_NODE_ACCESSED;
+	found = 0;
+	TAILQ_FOREACH(de, &node->tn_spec.tn_dir.tn_dir, td_entries) {
+		KKASSERT(ncp->nc_nlen < 0xffff);
+		if (de->td_namelen == (uint16_t)ncp->nc_nlen &&
+		    memcmp(de->td_name, ncp->nc_name, de->td_namelen) == 0) {
+			found = 1;
+			break;
+		}
+	}
+	return found ? de : NULL;
+}
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Helper function for tmpfs_readdir.  Creates a '.' entry for the given
+ * directory and returns it in the uio space.  The function returns 0
+ * on success, -1 if there was not enough space in the uio structure to
+ * hold the directory entry or an appropriate error code if another
+ * error happens.
+ */
+int
+tmpfs_dir_getdotdent(struct tmpfs_node *node, struct uio *uio)
+{
+	int error;
+	struct dirent dent;
+	int reclen;
+
+	DP("tmpfALIDATE_DIR(node);
+	KKASSERT(uio->uio_offset == TMPFS_DIRCOOKIE_DOT);
+
+	dent.d_ino = node->tn_id;
+	dent.d_type = DT_DIR;
+	dent.d_namlen = 1;
+	dent.d_name[0] = '.';
+	dent.d_name[1] = '\0';
+	/* XXX dentp->d_reclen */
+	reclen = _DIRENT_DIRSIZ(&dent);
+
+	if (/*dent.d_*/reclen > uio->uio_resid)
+		error = -1;
+	else {
+		error = uiomove((caddr_t)&dent, /*dent.d_*/reclen, uio);
+		if (error == 0)
+			uio->uio_offset = TMPFS_DIRCOOKIE_DOTDOT;
+	}
+
+	node->tn_status |= TMPFS_NODE_ACCESSED;
+
+	return error;
+}
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Helper function for tmpfs_readdir.  Creates a '..' entry for the given
+ * directory and returns it in the uio space.  The function returns 0
+ * on success, -1 if there was not enough space in the uio structure to
+ * hold the directory entry or an appropriate error code if another
+ * error happens.
+ */
+int
+tmpfs_dir_getdotdotdent(struct tmpfs_node *node, struct uio *uio)
+{
+	int error, reclen;
+	struct dirent dent;
+
+	DP("tmpfs_dir_getdotdotdent node %p uio %p\n", node, uio);
+	TMPFS_VALIDATE_DIR(node);
+	KKASSERT(uio->uio_offset == TMPFS_DIRCOOKIE_DOTDOT);
+
+	dent.d_ino = node->tn_spec.tn_dir.tn_parent->tn_id;
+	dent.d_type = DT_DIR;
+	dent.d_namlen = 2;
+	dent.d_name[0] = '.';
+	dent.d_name[1] = '.';
+	dent.d_name[2] = '\0';
+	/*XXX dent.d_reclen = _DIRENT_SIZE(dentp);*/
+	reclen = _DIRENT_DIRSIZ(&dent);
+
+	if (/*dent.d_*/reclen > uio->uio_resid)
+		error = -1;
+	else {
+		error = uiomove((caddr_t)&dent, /*dent.d_*/reclen, uio);
+		if (error == 0) {
+			struct tmpfs_dirent *de;
+
+			de = TAILQ_FIRST(&node->tn_spec.tn_dir.tn_dir);
+			if (de == NULL)
+				uio->uio_offset = TMPFS_DIRCOOKIE_EOF;
+			else
+				uio->uio_offset = tmpfs_dircookie(de);
+		}
+	}
+	node->tn_status |= TMPFS_NODE_ACCESSED;
+
+	return error;
+}
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Lookup a directory entry by its associated cookie.
+ */
+struct tmpfs_dirent *
+tmpfs_dir_lookupbycookie(struct tmpfs_node *node, off_t cookie)
+{
+	struct tmpfs_dirent *de;
+	DP("tmpfs_dir_lookupbycookie node %p cookie %d\n", node, (int)cookie);
+	if (cookie == node->tn_spec.tn_dir.tn_readdir_lastn &&
+	    node->tn_spec.tn_dir.tn_readdir_lastp != NULL) {
+		return node->tn_spec.tn_dir.tn_readdir_lastp;
+	}
+	TAILQ_FOREACH(de, &node->tn_spec.tn_dir.tn_dir, td_entries) {
+		if (tmpfs_dircookie(de) == cookie) {
+			break;
+		}
+	}
+	return de;
+}
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Helper function for tmpfs_readdir.  Returns as much directory entries
+ * as can fit in the uio space.  The read starts at uio->uio_offset.
+ * The function returns 0 on success, -1 if there was not enough space
+ * in the uio structure to hold the directory entry or an appropriate
+ * error code if another error happens.
+ */
+int
+tmpfs_dir_getdents(struct tmpfs_node *node, struct uio *uio, off_t *cntp)
+{
+
+	int error;
+	off_t startcookie;
+	struct dirent dent;
+	struct tmpfs_dirent *de;
+	int reclen;
+
+	DP("tmpfs_dir_getdents node %p uio %p\n", node, uio);
+	TMPFS_VALIDATE_DIR(node);
+
+	/* Locate the first directory entry we have to return.  We have cached
+	 * the last readdir in the node, so use those values if appropriate.
+	 * Otherwise do a linear scan to find the requested entry. */
+	startcookie = uio->uio_offset;
+	KKASSERT(startcookie != TMPFS_DIRCOOKIE_DOT);
+	KKASSERT(startcookie != TMPFS_DIRCOOKIE_DOTDOT);
+	if (startcookie == TMPFS_DIRCOOKIE_EOF) {
+		return 0;
+	} else {
+		de = tmpfs_dir_lookupbycookie(node, startcookie);
+	}
+	if (de == NULL) {
+		return EINVAL;
+	}
+
+	/* Read as much entries as possible; i.e., until we reach the end of
+	 * the directory or we exhaust uio space. */
+	do {
+		/* Create a dirent structure representing the current
+		 * tmpfs_node and fill it. */
+		dent.d_ino = de->td_node->tn_id;
+		switch (de->td_node->tn_type) {
+		case VBLK:
+			dent.d_type = DT_BLK;
+			break;
+
+		case VCHR:
+			dent.d_type = DT_CHR;
+			break;
+
+		case VDIR:
+			dent.d_type = DT_DIR;
+			break;
+
+		case VFIFO:
+			dent.d_type = DT_FIFO;
+			break;
+
+		case VLNK:
+			dent.d_type = DT_LNK;
+			break;
+
+		case VREG:
+			dent.d_type = DT_REG;
+			break;
+
+		case VSOCK:
+			dent.d_type = DT_SOCK;
+			break;
+
+		default:
+			KKASSERT(0);
+		}
+		dent.d_namlen = de->td_namelen;
+		KKASSERT(de->td_namelen < sizeof(dent.d_name));
+		(void)memcpy(dent.d_name, de->td_name, de->td_namelen);
+		dent.d_name[de->td_namelen] = '\0';
+		/* XXX dentp->d_reclen */
+		reclen = _DIRENT_DIRSIZ(&dent);
+
+		/* Stop reading if the directory entry we are treating is
+		 * bigger than the amount of data that can be returned. */
+		if (/*dent.d_*/reclen > uio->uio_resid) {
+			error = -1;
+			break;
+		}
+
+		/* Copy the new dirent structure into the output buffer and
+		 * advance pointers. */
+		error = uiomove((caddr_t)&dent, /*dent.d_*/reclen, uio);
+
+		(*cntp)++;
+		de = TAILQ_NEXT(de, td_entries);
+	} while (error == 0 && uio->uio_resid > 0 && de != NULL);
+
+	/* Update the offset and cache. */
+	if (de == NULL) {
+		uio->uio_offset = TMPFS_DIRCOOKIE_EOF;
+		node->tn_spec.tn_dir.tn_readdir_lastn = 0;
+		node->tn_spec.tn_dir.tn_readdir_lastp = NULL;
+	} else {
+		node->tn_spec.tn_dir.tn_readdir_lastn = uio->uio_offset =
+		    tmpfs_dircookie(de);
+		node->tn_spec.tn_dir.tn_readdir_lastp = de;
+	}
+
+	node->tn_status |= TMPFS_NODE_ACCESSED;
+
+	return error;
+}
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Resizes the aobj associated to the regular file pointed to by vp to
+ * the size newsize.  'vp' must point to a vnode that represents a regular
+ * file.  'newsize' must be positive.
+ *
+ * If the file is extended, the appropriate kevent is raised.  This does
+ * not rise a write event though because resizing is not the same as
+ * writing.
+ *
+ * Returns zero on success or an appropriate error code on failure.
+ */
+int
+tmpfs_reg_resize(struct vnode *vp, off_t newsize)
+{
+	int error;
+	size_t newpages, oldpages;
+	struct tmpfs_mount *tmp;
+	struct tmpfs_node *node;
+	off_t oldsize;
+
+	DP("tmpfs_reg_resize vp %p newsize %d\n", vp, (int)newsize);
+	KKASSERT(vp->v_type == VREG);
+	KKASSERT(newsize >= 0);
+
+	node = VP_TO_TMPFS_NODE(vp);
+	tmp = VFS_TO_TMPFS(vp->v_mount);
+
+	/* Convert the old and new sizes to the number of pages needed to
+	 * store them.  It may happen that we do not need to do anything
+	 * because the last allocated page can accommodate the change on
+	 * its own. */
+	oldsize = node->tn_size;
+	oldpages = round_page(oldsize) / PAGE_SIZE;
+	KKASSERT(oldpages == node->tn_spec.tn_reg.tn_aobj_pages);
+	newpages = round_page(newsize) / PAGE_SIZE;
+
+	if (newpages > oldpages &&
+	    newpages - oldpages > TMPFS_PAGES_AVAIL(tmp)) {
+		error = ENOSPC;
+		goto out;
+	}
+
+	node->tn_spec.tn_reg.tn_aobj_pages = newpages;
+
+	TMPFS_LOCK(tmp);
+	tmp->tm_pages_used += (newpages - oldpages);
+	TMPFS_UNLOCK(tmp);
+
+	node->tn_size = newsize;
+	vnode_pager_setsize(vp, newsize);
+	if (newsize < oldsize) {
+		size_t zerolen = round_page(newsize) - newsize;
+		vm_object_t uobj = node->tn_spec.tn_reg.tn_aobj;
+		vm_page_t m;
+
+		/*
+		 * free "backing store"
+		 */
+		/* XXX */
+		VM_OBJECT_LOCK(uobj);
+		if (newpages < oldpages) {
+			swap_pager_freespace(uobj,
+				newpages, oldpages - newpages);
+			vm_object_page_remove(uobj,
+				OFF_TO_IDX(newsize + PAGE_MASK), 0, FALSE);
+		}
+
+		/*
+		 * zero out the truncated part of the last page.
+		 */
+
+		if (zerolen > 0) {
+			m = vm_page_grab(uobj, OFF_TO_IDX(newsize),
+					VM_ALLOC_NORMAL | VM_ALLOC_RETRY);
+			pmap_zero_page_area((vm_paddr_t)(vm_offset_t)m,
+			    PAGE_SIZE - zerolen, zerolen);
+			vm_page_wakeup(m);
+		}
+		VM_OBJECT_UNLOCK(uobj);
+
+	}
+	error = 0;
+out:
+	return error;
+}
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Returns information about the number of available memory pages,
+ * including physical and virtual ones.
+ *
+ * If 'total' is true, the value returned is the total amount of memory
+ * pages configured for the system (either in use or free).
+ * If it is FALSE, the value returned is the amount of free memory pages.
+ *
+ * Remember to remove TMPFS_PAGES_RESERVED from the returned value to avoid
+ * excessive memory usage.
+ *
+ */
+/*
+size_t
+tmpfs_mem_info(boolean_t total)
+{
+	size_t size;
+
+	DP("tmpfs_mem_info \n");
+	size = 0;
+	vmmeter
+	size += uvmexp.swpgavail;
+	if (!total) {
+		size -= uvmexp.swpgonly;
+	}
+	size += uvmexp.free;
+	size += uvmexp.filepages;
+	if (size > uvmexp.wired) {
+		size -= uvmexp.wired;
+	} else {
+		size = 0;
+	}
+
+	return size;
+}
+*/
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Change flags of the given vnode.
+ * Caller should execute tmpfs_update on vp after a successful execution.
+ * The vnode must be locked on entry and remain locked on exit.
+ */
+int
+tmpfs_chflags(struct vnode *vp, int flags, struct ucred *cred)
+{
+	int error;
+	struct tmpfs_node *node;
+
+	DP("tmpfs_chflags vp %p\n", vp);
+	KKASSERT(vn_islocked(vp));
+	node = VP_TO_TMPFS_NODE(vp);
+
+	/* Disallow this operation if the file system is mounted read-only. */
+	if (vp->v_mount->mnt_flag & MNT_RDONLY)
+		return EROFS;
+
+	/* XXX: The following comes from UFS code, and can be found in
+	 * several other file systems.  Shouldn't this be centralized
+	 * somewhere? */
+	//if (kauth_cred_geteuid(cred) != node->tn_uid &&
+	//    (error = kauth_authorize_generic(cred, KAUTH_GENERIC_ISSUSER,
+	//    NULL)))
+	//	return error;
+	//if (kauth_authorize_generic(cred, KAUTH_GENERIC_ISSUSER, NULL) == 0) {
+		/* The super-user is only allowed to change flags if the file
+		 * wasn't protected before and the securelevel is zero. */
+	//	if ((node->tn_flags & (SF_IMMUTABLE | SF_APPEND)) &&
+	//	    kauth_authorize_system(l->l_cred, KAUTH_SYSTEM_CHSYSFLAGS,
+	//	     0, NULL, NULL, NULL))
+	//		return EPERM;
+	//	node->tn_flags = flags;
+	//} else {
+		/* Regular users can change flags provided they only want to
+		 * change user-specific ones, not those reserved for the
+		 * super-user. */
+		if ((node->tn_flags & (SF_IMMUTABLE | SF_APPEND)) ||
+		    (flags & UF_SETTABLE) != flags)
+			return EPERM;
+		if ((node->tn_flags & SF_SETTABLE) != (flags & SF_SETTABLE))
+			return EPERM;
+		node->tn_flags &= SF_SETTABLE;
+		node->tn_flags |= (flags & UF_SETTABLE);
+	//}
+
+	node->tn_status |= TMPFS_NODE_CHANGED;
+	VN_KNOTE(vp, NOTE_ATTRIB);
+
+	KKASSERT(VOP_ISLOCKED(vp));
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Change access mode on the given vnode.
+ * Caller should execute tmpfs_update on vp after a successful execution.
+ * The vnode must be locked on entry and remain locked on exit.
+ */
+int
+tmpfs_chmod(struct vnode *vp, mode_t mode, struct ucred *cred)
+{
+	int error, ismember = 0;
+	struct tmpfs_node *node;
+
+	DP("tmpfs_chmod vp %p\n", vp);
+	KKASSERT(VOP_ISLOCKED(vp));
+	node = VP_TO_TMPFS_NODE(vp);
+
+	/* Disallow this operation if the file system is mounted read-only. */
+	if (vp->v_mount->mnt_flag & MNT_RDONLY)
+		return EROFS;
+
+	/* Immutable or append-only files cannot be modified, either. */
+	if (node->tn_flags & (IMMUTABLE | APPEND))
+		return EPERM;
+
+	/* XXX: The following comes from UFS code, and can be found in
+	 * several other file systems.  Shouldn't this be centralized
+	 * somewhere? */
+	//if (kauth_cred_geteuid(cred) != node->tn_uid &&
+	//    (error = kauth_authorize_generic(cred, KAUTH_GENERIC_ISSUSER,
+	//    NULL)))
+	//	return error;
+	//if (kauth_authorize_generic(cred, KAUTH_GENERIC_ISSUSER, NULL) != 0) {
+	//	if (vp->v_type != VDIR && (mode & S_ISTXT))
+	//		return EFTYPE;
+
+	//	if ((kauth_cred_ismember_gid(cred, node->tn_gid,
+	//	    &ismember) != 0 || !ismember) && (mode & S_ISGID))
+	//		return EPERM;
+	//}
+
+	node->tn_mode = (mode & ALLPERMS);
+	node->tn_status |= TMPFS_NODE_CHANGED;
+	VN_KNOTE(vp, NOTE_ATTRIB);
+	KKASSERT(VOP_ISLOCKED(vp));
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Change ownership of the given vnode.  At least one of uid or gid must
+ * be different than VNOVAL.  If one is set to that value, the attribute
+ * is unchanged.
+ * Caller should execute tmpfs_update on vp after a successful execution.
+ * The vnode must be locked on entry and remain locked on exit.
+ */
+int
+tmpfs_chown(struct vnode *vp, uid_t uid, gid_t gid, struct ucred *cred)
+{
+	int error, ismember = 0;
+	struct tmpfs_node *node;
+
+	DP("tmpfs_chown vp %p\n", vp);
+	KKASSERT(VOP_ISLOCKED(vp));
+	node = VP_TO_TMPFS_NODE(vp);
+	/* Assign default values if they are unknown. */
+	KKASSERT(uid != VNOVAL || gid != VNOVAL);
+	if (uid == VNOVAL)
+		uid = node->tn_uid;
+	if (gid == VNOVAL)
+		gid = node->tn_gid;
+	KKASSERT(uid != VNOVAL && gid != VNOVAL);
+
+	/* Disallow this operation if the file system is mounted read-only. */
+	if (vp->v_mount->mnt_flag & MNT_RDONLY)
+		return EROFS;
+
+	/* Immutable or append-only files cannot be modified, either. */
+	if (node->tn_flags & (IMMUTABLE | APPEND))
+		return EPERM;
+
+	/* XXX: The following comes from UFS code, and can be found in
+	 * several other file systems.  Shouldn't this be centralized
+	 * somewhere? */
+	//if ((kauth_cred_geteuid(cred) != node->tn_uid || uid != node->tn_uid ||
+	//    (gid != node->tn_gid && !(kauth_cred_getegid(cred) == node->tn_gid ||
+	//    (kauth_cred_ismember_gid(cred, gid, &ismember) == 0 && ismember)))) &&
+	//    ((error = kauth_authorize_generic(cred, KAUTH_GENERIC_ISSUSER,
+	//    NULL)) != 0))
+	//	return error;
+	node->tn_uid = uid;
+	node->tn_gid = gid;
+
+	node->tn_status |= TMPFS_NODE_CHANGED;
+	VN_KNOTE(vp, NOTE_ATTRIB);
+	KKASSERT(VOP_ISLOCKED(vp));
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Change size of the given vnode.
+ * Caller should execute tmpfs_update on vp after a successful execution.
+ * The vnode must be locked on entry and remain locked on exit.
+ */
+int
+tmpfs_chsize(struct vnode *vp, u_quad_t size, struct ucred *cred)
+{
+	int error;
+	struct tmpfs_node *node;
+
+	DP("tmpfs_chsize vp %p size %d\n", vp, (int)size);
+	KKASSERT(VOP_ISLOCKED(vp));
+	node = VP_TO_TMPFS_NODE(vp);
+	/* Decide whether this is a valid operation based on the file type. */
+	error = 0;
+	switch (vp->v_type) {
+	case VDIR:
+		return EISDIR;
+	case VREG:
+		if (vp->v_mount->mnt_flag & MNT_RDONLY)
+			return EROFS;
+		break;
+	case VBLK: /* FALLTHROUGH */
+	case VCHR: /* FALLTHROUGH */
+	case VFIFO:
+		/* Allow modifications of special files even if in the file
+		 * system is mounted read-only (we are not modifying the
+		 * files themselves, but the objects they represent). */
+		return 0;
+	default:
+		/* Anything else is unsupported. */
+		return EOPNOTSUPP;
+	}
+
+	/* Immutable or append-only files cannot be modified, either. */
+	if (node->tn_flags & (IMMUTABLE | APPEND))
+		return EPERM;
+
+	error = tmpfs_truncate(vp, size);
+	/* tmpfs_truncate will raise the NOTE_EXTEND and NOTE_ATTRIB kevents
+	 * for us, as will update tn_status; no need to do that here. */
+	KKASSERT(VOP_ISLOCKED(vp));
+	return error;
+}
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Change access and modification times of the given vnode.
+ * Caller should execute tmpfs_update on vp after a successful execution.
+ * The vnode must be locked on entry and remain locked on exit.
+ */
+int
+tmpfs_chtimes(struct vnode *vp, const struct timespec *atime,
+    const struct timespec *mtime, const struct timespec *btime,
+    int vaflags, struct ucred *cred)
+{
+	int error;
+	struct tmpfs_node *node;
+
+	DP("tmpfs_chtimes vp %p\n", vp);
+	KKASSERT(VOP_ISLOCKED(vp));
+	node = VP_TO_TMPFS_NODE(vp);
+	/* Disallow this operation if the file system is mounted read-only. */
+	if (vp->v_mount->mnt_flag & MNT_RDONLY)
+		return EROFS;
+
+	/* Immutable or append-only files cannot be modified, either. */
+	if (node->tn_flags & (IMMUTABLE | APPEND))
+		return EPERM;
+
+	/* XXX: The following comes from UFS code, and can be found in
+	 * several other file systems.  Shouldn't this be centralized
+	 * somewhere? */
+	//if (kauth_cred_geteuid(cred) != node->tn_uid &&
+	//    (error = kauth_authorize_generic(cred, KAUTH_GENERIC_ISSUSER,
+	//    NULL)) && ((vaflags & VA_UTIMES_NULL) == 0 ||
+	//    (error = VOP_ACCESS(vp, VWRITE, cred))))
+	//	return error;
+
+	if (atime->tv_sec != VNOVAL && atime->tv_nsec != VNOVAL)
+		node->tn_status |= TMPFS_NODE_ACCESSED;
+
+	if (mtime->tv_sec != VNOVAL && mtime->tv_nsec != VNOVAL)
+		node->tn_status |= TMPFS_NODE_MODIFIED;
+
+	if (btime->tv_sec == VNOVAL && btime->tv_nsec == VNOVAL)
+		btime = NULL;
+
+	tmpfs_update(vp, atime, mtime, btime, 0);
+	VN_KNOTE(vp, NOTE_ATTRIB);
+	KKASSERT(VOP_ISLOCKED(vp));
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+/* Sync timestamps */
+void
+tmpfs_itimes(struct vnode *vp, const struct timespec *acc,
+    const struct timespec *mod, const struct timespec *birth)
+{
+	struct timespec now, *nowp = NULL;
+	struct tmpfs_node *node;
+
+	DP("tmpfs_itimes vp %p\n", vp);
+	node = VP_TO_TMPFS_NODE(vp);
+
+	if ((node->tn_status & (TMPFS_NODE_ACCESSED | TMPFS_NODE_MODIFIED |
+	    TMPFS_NODE_CHANGED)) == 0)
+		return;
+
+	if (birth != NULL)
+		node->tn_birthtime = *birth;
+
+	if (node->tn_status & TMPFS_NODE_ACCESSED) {
+		if (acc == NULL) {
+			if (nowp == NULL)
+				getnanotime(nowp = &now);
+			acc = nowp;
+		}
+		node->tn_atime = *acc;
+	}
+	if (node->tn_status & TMPFS_NODE_MODIFIED) {
+		if (mod == NULL) {
+			if (nowp == NULL)
+				getnanotime(nowp = &now);
+			mod = nowp;
+		}
+		node->tn_mtime = *mod;
+	}
+	if (node->tn_status & TMPFS_NODE_CHANGED) {
+		if (nowp == NULL)
+			getnanotime(nowp = &now);
+		node->tn_ctime = *nowp;
+	}
+
+	node->tn_status &=
+	    ~(TMPFS_NODE_ACCESSED | TMPFS_NODE_MODIFIED | TMPFS_NODE_CHANGED);
+}
+
+/* --------------------------------------------------------------------- */
+
+void
+tmpfs_update(struct vnode *vp, const struct timespec *acc,
+    const struct timespec *mod, const struct timespec *birth, int flags)
+{
+	struct tmpfs_node *node;
+	DP("tmpfs_update vp %p\n", vp);
+	KKASSERT(VOP_ISLOCKED(vp));
+	node = VP_TO_TMPFS_NODE(vp);
+#if 0
+	if (flags & UPDATE_CLOSE)
+		; /* XXX Need to do anything special? */
+#endif
+	tmpfs_itimes(vp, acc, mod, birth);
+	KKASSERT(VOP_ISLOCKED(vp));
+}
+
+/* --------------------------------------------------------------------- */
+
+int
+tmpfs_truncate(struct vnode *vp, off_t length)
+{
+	boolean_t extended;
+	int error;
+	struct tmpfs_node *node;
+
+	DP("tmpfs_truncate vp %p l %d\n", vp, (int)length);
+	node = VP_TO_TMPFS_NODE(vp);
+	extended = length > node->tn_size;
+
+	if (length < 0) {
+		error = EINVAL;
+		goto out;
+	}
+	if (node->tn_size == length) {
+		error = 0;
+		goto out;
+	}
+	error = tmpfs_reg_resize(vp, length);
+	if (error == 0)
+		node->tn_status |= TMPFS_NODE_CHANGED | TMPFS_NODE_MODIFIED;
+
+out:
+	tmpfs_update(vp, NULL, NULL, NULL, 0);
+	return error;
+}
+
+
+/* --------------------------------------------------------------------- */
+
+void
+tmpfs_pool_init(struct tmpfs_pool *tpp, size_t size,
+	    const char *what, struct tmpfs_mount *tmp)
+{
+	DP("tmpfs_pool_init pool %p size %d\n", tpp, size);
+	tpp->tp_pool.p_size = size;
+	tpp->tp_pool.p_nallocs = 0;
+	tpp->tp_pool.p_nfrees = 0;
+}
+
+void
+tmpfs_pool_destroy(struct tmpfs_pool *tpp)
+{
+	DP("tmpfs_pool_destroy pool %p allocs %d frees %d\n", tpp,
+		tpp->tp_pool.p_nallocs,
+		tpp->tp_pool.p_nfrees);
+}
+
+void *
+pool_get(struct pool* pp, int flags)
+{
+	pp->p_nallocs++;
+	return kmalloc(pp->p_size, M_TMPFS, M_WAITOK | M_ZERO);
+}
+
+void
+pool_put(struct pool* pp, void *p)
+{
+	pp->p_nfrees++;
+	kfree(p, M_TMPFS);
+}
+
+void tmpfs_str_pool_init(struct tmpfs_str_pool *a, struct tmpfs_mount *b)
+{
+}
+
+void tmpfs_str_pool_destroy(struct tmpfs_str_pool *a)
+{
+}
+
+char *tmpfs_str_pool_get(struct tmpfs_str_pool *pool, size_t size, int flags)
+{
+	return kmalloc(size, M_TMPFS, M_WAITOK | M_ZERO);
+		//flags);
+}
+
+void tmpfs_str_pool_put(struct tmpfs_str_pool *pool, char *p, size_t size)
+{
+	kfree(p, M_TMPFS);
+}
diff --git a/sys/vfs/tmpfs/tmpfs_vfsops.c b/sys/vfs/tmpfs/tmpfs_vfsops.c
new file mode 100755
index 0000000..e89dcef
--- /dev/null
+++ b/sys/vfs/tmpfs/tmpfs_vfsops.c
@@ -0,0 +1,477 @@
+/*	$NetBSD: tmpfs_vfsops.c,v 1.44 2008/07/29 09:10:09 pooka Exp $	*/
+
+/*
+ * Copyright (c) 2005, 2006, 2007 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Julio M. Merino Vidal, developed as part of Google's Summer of Code
+ * 2005 program.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Efficient memory file system.
+ *
+ * tmpfs is a file system that uses NetBSD's virtual memory sub-system
+ * (the well-known UVM) to store file data and metadata in an efficient
+ * way.  This means that it does not follow the structure of an on-disk
+ * file system because it simply does not need to.  Instead, it uses
+ * memory-specific data structures and algorithms to automatically
+ * allocate and release resources.
+ */
+
+#include <sys/cdefs.h>
+//__KERNEL_RCSID(0, "$NetBSD: tmpfs_vfsops.c,v 1.44 2008/07/29 09:10:09 pooka Exp $");
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/malloc.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/systm.h>
+#include <sys/vnode.h>
+#include <sys/proc.h>
+#include <sys/module.h>
+#include <sys/file.h>
+#include <machine/atomic.h>
+
+#include <vfs/tmpfs/tmpfs.h>
+#include <vfs/tmpfs/tmpfs_args.h>
+#include <vfs/tmpfs/tmpfs_vnops.h>
+//#include <vfs/tmpfs/tmpfs_pool.h>
+
+/* --------------------------------------------------------------------- */
+
+static int	tmpfs_mount(struct mount *,  char *, caddr_t, struct ucred *);
+static int	tmpfs_start(struct mount *, int);
+static int	tmpfs_unmount(struct mount *, int);
+static int	tmpfs_root(struct mount *, struct vnode **);
+static int	tmpfs_vget(struct mount *, ino_t, struct vnode **);
+static int	tmpfs_fhtovp(struct mount *, struct fid *, struct vnode **);
+static int	tmpfs_vptofh(struct vnode *, struct fid *);//, size_t *);
+static int	tmpfs_statfs(struct mount *, struct statfs *, struct ucred *);
+static int	tmpfs_statvfs(struct mount *, struct statvfs *, struct ucred *cred);
+static int	tmpfs_sync(struct mount *, int);//, kauth_cred_t);
+static int	tmpfs_init(struct vfsconf *conf);
+
+/* --------------------------------------------------------------------- */
+
+static int
+tmpfs_mount(struct mount *mp, char *path, caddr_t data, struct ucred *cred)
+{
+	int error;
+	ino_t nodes;
+	size_t pages;
+	struct tmpfs_mount *tmp;
+	struct tmpfs_node *root;
+	struct tmpfs_args args;
+
+	DP("tmpfs_mount %s mnt=%p fl=%x\n", path, mp, mp->mnt_flag);
+
+	error = copyin(data, (caddr_t)&args, sizeof(struct tmpfs_args));
+	if (error)
+		return (error);
+
+	DP("tmpfs_mount %d %d %d \t\t %d %d %x\n",
+		args.ta_version, (int)args.ta_nodes_max, (int)args.ta_size_max,
+		args.ta_root_uid, args.ta_root_gid, args.ta_root_mode);
+
+	/* Handle retrieval of mount point arguments. */
+	if (mp->mnt_flag & MNT_GETARGS) {
+		DP("tmpfs_mount MNT_GETARGS\n");
+		if (mp->mnt_data == NULL)
+			return EIO;
+		tmp = VFS_TO_TMPFS(mp);
+
+		args.ta_version = TMPFS_ARGS_VERSION;
+		args.ta_nodes_max = tmp->tm_nodes_max;
+		args.ta_size_max = tmp->tm_pages_max * PAGE_SIZE;
+
+		root = tmp->tm_root;
+		args.ta_root_uid = root->tn_uid;
+		args.ta_root_gid = root->tn_gid;
+		args.ta_root_mode = root->tn_mode;
+
+		return 0;
+	}
+
+	if (mp->mnt_flag & MNT_UPDATE) {
+		/* XXX: There is no support yet to update file system
+		 * settings.  Should be added. */
+		return EOPNOTSUPP;
+	}
+
+	if (args.ta_version != TMPFS_ARGS_VERSION)
+		return EINVAL;
+
+	DP("tmpfs_mount 2\n");
+	/* Do not allow mounts if we do not have enough memory to preserve
+	 * the minimum reserved pages. */
+
+	/* XXX */
+	//if (tmpfs_mem_info(TRUE) < TMPFS_PAGES_RESERVED)
+	//	return EINVAL;
+
+	/* Get the maximum number of memory pages this file system is
+	 * allowed to use, based on the maximum size the user passed in
+	 * the mount structure.  A value of zero is treated as if the
+	 * maximum available space was requested. */
+	if (args.ta_size_max < PAGE_SIZE || args.ta_size_max >= SIZE_MAX)
+		pages = SIZE_MAX;
+	else
+		pages = args.ta_size_max / PAGE_SIZE +
+		    (args.ta_size_max % PAGE_SIZE == 0 ? 0 : 1);
+	if (pages > INT_MAX)
+		pages = INT_MAX;
+	KKASSERT(pages > 0);
+
+	if (args.ta_nodes_max <= 3)
+		nodes = 3 + pages * PAGE_SIZE / 1024;
+	else
+		nodes = args.ta_nodes_max;
+	if (nodes > INT_MAX)
+		nodes = INT_MAX;
+	KKASSERT(nodes >= 3);
+
+	/* Allocate the tmpfs mount structure and fill it. */
+	tmp = kmalloc(sizeof(struct tmpfs_mount), M_TMPFS, M_WAITOK | M_ZERO);
+	if (tmp == NULL)
+		return ENOMEM;
+
+	DP("tmp mount = %p\n", tmp);
+
+	tmp->tm_nodes_max = nodes;
+	tmp->tm_nodes_cnt = 0;
+	LIST_INIT(&tmp->tm_nodes);
+
+	mutex_init(&tmp->tm_lock, MUTEX_DEFAULT, IPL_NONE);
+
+	tmp->tm_pages_max = pages;
+	tmp->tm_pages_used = 0;
+	tmpfs_pool_init(&tmp->tm_dirent_pool, sizeof(struct tmpfs_dirent),
+	    "dirent", tmp);
+	tmpfs_pool_init(&tmp->tm_node_pool, sizeof(struct tmpfs_node),
+	    "node", tmp);
+	tmpfs_str_pool_init(&tmp->tm_str_pool, tmp);
+
+	vfs_add_vnodeops(mp, &tmpfs_vnode_vops, &mp->mnt_vn_norm_ops);
+	vfs_add_vnodeops(mp, &tmpfs_spec_vops, &mp->mnt_vn_spec_ops);
+	vfs_add_vnodeops(mp, &tmpfs_fifo_vops, &mp->mnt_vn_fifo_ops);
+
+	DP("tmpfs_mount alloc_node\n");
+	/* Allocate the root node. */
+	error = tmpfs_alloc_node(tmp, VDIR, args.ta_root_uid,
+	    args.ta_root_gid, args.ta_root_mode & ALLPERMS, NULL, NULL,
+	    VNOVAL, VNOVAL, &root);
+	DP("tmpfs_mount root = %p\n", root);
+	KKASSERT(error == 0 && root != NULL);
+	root->tn_links++;
+	tmp->tm_root = root;
+
+	mp->mnt_data = (qaddr_t) tmp;
+	mp->mnt_flag |= MNT_LOCAL;
+	/* XXX */
+	//mp->mnt_stat.f_namemax = MAXNAMLEN;
+	//mp->mnt_fs_bshift = PAGE_SHIFT;
+	//mp->mnt_dev_bshift = DEV_BSHIFT;
+	//mp->mnt_iflag |= IMNT_MPSAFE;
+	vfs_getnewfsid(mp);
+
+	/* XXX */
+	strcpy(mp->mnt_stat.f_mntfromname, "tmpfs");
+	//copyinstr(args.target, mp->mnt_stat.f_mntfromname, MNAMELEN - 1, &size);
+	//bzero(mp->mnt_stat.f_mntfromname + size, MNAMELEN - size);
+
+	tmpfs_statfs(mp, &mp->mnt_stat, cred);
+
+	//TMPFSDEBUG("nullfs_mount: lower %s, alias at %s\n",
+	//	mp->mnt_stat.f_mntfromname, mp->mnt_stat.f_mntfromname);
+	//return set_statvfs_info(path, UIO_USERSPACE, "tmpfs", UIO_SYSSPACE,
+	//    mp->mnt_op->vfs_name, mp, l);
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int
+tmpfs_start(struct mount *mp, int flags)
+{
+
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+/* ARGSUSED2 */
+static int
+tmpfs_unmount(struct mount *mp, int mntflags)
+{
+	int error;
+	int flags = 0;
+	struct tmpfs_mount *tmp;
+	struct tmpfs_node *node;
+
+	DP("tmpfs_unmount %p %d\n", mp, mntflags);
+
+	/* Handle forced unmounts. */
+	if (mntflags & MNT_FORCE)
+		flags |= FORCECLOSE;
+
+	/* Finalize all pending I/O. XXX */
+	error = vflush(mp, 0, flags);
+	if (error != 0)
+		return error;
+
+	tmp = VFS_TO_TMPFS(mp);
+
+	/* Free all associated data.  The loop iterates over the linked list
+	 * we have containing all used nodes.  For each of them that is
+	 * a directory, we free all its directory entries.  Note that after
+	 * freeing a node, it will automatically go to the available list,
+	 * so we will later have to iterate over it to release its items. */
+	node = LIST_FIRST(&tmp->tm_nodes);
+	while (node != NULL) {
+		struct tmpfs_node *next;
+
+		if (node->tn_type == VDIR) {
+			struct tmpfs_dirent *de;
+
+			de = TAILQ_FIRST(&node->tn_spec.tn_dir.tn_dir);
+			while (de != NULL) {
+				struct tmpfs_dirent *nde;
+
+				nde = TAILQ_NEXT(de, td_entries);
+				tmpfs_free_dirent(tmp, de, FALSE);
+				de = nde;
+				node->tn_size -= sizeof(struct tmpfs_dirent);
+			}
+		}
+
+		next = LIST_NEXT(node, tn_entries);
+		tmpfs_free_node(tmp, node);
+		node = next;
+	}
+
+	tmpfs_pool_destroy(&tmp->tm_dirent_pool);
+	tmpfs_pool_destroy(&tmp->tm_node_pool);
+	tmpfs_str_pool_destroy(&tmp->tm_str_pool);
+
+	KKASSERT(tmp->tm_pages_used == 0);
+
+	/* Throw away the tmpfs_mount structure. */
+	mutex_destroy(&tmp->tm_lock);
+	kfree(tmp, M_TMPFS);
+	mp->mnt_data = NULL;
+
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int
+tmpfs_root(struct mount *mp, struct vnode **vpp)
+{
+	return tmpfs_alloc_vp(mp, VFS_TO_TMPFS(mp)->tm_root, vpp);
+}
+
+/* --------------------------------------------------------------------- */
+
+static int
+tmpfs_vget(struct mount *mp, ino_t ino,
+    struct vnode **vpp)
+{
+	kprintf("tmpfs_vget called; need for it unknown yet\n");
+	return EOPNOTSUPP;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int
+tmpfs_fhtovp(struct mount *mp, struct fid *fhp, struct vnode **vpp)
+{
+	boolean_t found;
+	struct tmpfs_fid tfh;
+	struct tmpfs_mount *tmp;
+	struct tmpfs_node *node;
+
+	DP("tmpfs_fhtovp\n");
+	tmp = VFS_TO_TMPFS(mp);
+
+	if (fhp->fid_len != sizeof(struct tmpfs_fid))
+		return EINVAL;
+
+	memcpy(&tfh, fhp, sizeof(struct tmpfs_fid));
+
+	if (tfh.tf_id >= tmp->tm_nodes_max)
+		return EINVAL;
+
+	found = FALSE;
+	mutex_enter(&tmp->tm_lock);
+	LIST_FOREACH(node, &tmp->tm_nodes, tn_entries) {
+		if (node->tn_id == tfh.tf_id &&
+		    node->tn_gen == tfh.tf_gen) {
+			found = TRUE;
+			break;
+		}
+	}
+	mutex_exit(&tmp->tm_lock);
+
+	/* XXXAD nothing to prevent 'node' from being removed. */
+	return found ? tmpfs_alloc_vp(mp, node, vpp) : EINVAL;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int
+tmpfs_vptofh(struct vnode *vp, struct fid *fhp)
+{
+	struct tmpfs_fid tfh;
+	struct tmpfs_node *node;
+	DP("tmpfs_vptofh\n");
+/*
+	if (*fh_size < sizeof(struct tmpfs_fid)) {
+		*fh_size = sizeof(struct tmpfs_fid);
+		return E2BIG;
+	}
+
+	*fh_size = sizeof(struct tmpfs_fid);
+*/
+	node = VP_TO_TMPFS_NODE(vp);
+
+	memset(&tfh, 0, sizeof(tfh));
+	tfh.tf_len = sizeof(struct tmpfs_fid);
+	tfh.tf_gen = node->tn_gen;
+	tfh.tf_id = node->tn_id;
+	memcpy(fhp, &tfh, sizeof(tfh));
+
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int
+tmpfs_statfs(struct mount *mp, struct statfs *sbp, struct ucred *cred)
+{
+	fsfilcnt_t freenodes;
+	struct tmpfs_mount *tmp;
+	struct statfs *sp = &mp->mnt_stat;
+
+	DP("tmpfs_statfs\n");
+	tmp = VFS_TO_TMPFS(mp);
+
+	sp->f_iosize = sp->f_bsize = PAGE_SIZE;
+
+	sp->f_blocks = TMPFS_PAGES_MAX(tmp);
+	sp->f_bavail = sp->f_bfree = TMPFS_PAGES_AVAIL(tmp);
+
+	freenodes = MIN(tmp->tm_nodes_max - tmp->tm_nodes_cnt,
+	    TMPFS_PAGES_AVAIL(tmp) * PAGE_SIZE / sizeof(struct tmpfs_node));
+
+	sp->f_files = tmp->tm_nodes_cnt + freenodes;
+	sp->f_ffree = freenodes;
+
+	*sbp = mp->mnt_stat;
+	return 0;
+}
+
+/* ARGSUSED2 */
+static int
+tmpfs_statvfs(struct mount *mp, struct statvfs *sbp, struct ucred *cred)
+{
+	fsfilcnt_t freenodes;
+	struct tmpfs_mount *tmp;
+	struct statvfs *sp = &mp->mnt_vstat;
+
+	DP("tmpfs_statvfs\n");
+	tmp = VFS_TO_TMPFS(mp);
+
+	sp->f_frsize = sp->f_bsize = PAGE_SIZE;
+
+	sp->f_blocks = TMPFS_PAGES_MAX(tmp);
+	sp->f_bavail = sp->f_bfree = TMPFS_PAGES_AVAIL(tmp);
+
+	freenodes = MIN(tmp->tm_nodes_max - tmp->tm_nodes_cnt,
+	    TMPFS_PAGES_AVAIL(tmp) * PAGE_SIZE / sizeof(struct tmpfs_node));
+
+	sp->f_files = tmp->tm_nodes_cnt + freenodes;
+	sp->f_favail = sp->f_ffree = freenodes;
+
+	*sbp = mp->mnt_vstat;
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+/* ARGSUSED0 */
+static int
+tmpfs_sync(struct mount *mp, int waitfor)
+{
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int
+tmpfs_init(struct vfsconf *conf)
+{
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * tmpfs vfs operations.
+ */
+
+struct vfsops tmpfs_vfsops = {
+	.vfs_mount =	tmpfs_mount,
+	.vfs_start =	tmpfs_start,
+	.vfs_unmount =	tmpfs_unmount,
+	.vfs_root =	tmpfs_root,
+	.vfs_statvfs = tmpfs_statvfs,
+	.vfs_sync =	tmpfs_sync,
+	.vfs_vget =	tmpfs_vget,
+	.vfs_fhtovp =	tmpfs_fhtovp,
+	.vfs_vptofh =	tmpfs_vptofh,
+	.vfs_init =	tmpfs_init,
+	.vfs_extattrctl = vfs_stdextattrctl
+	/*(void *)eopnotsupp,	vfs_quotactl */
+};
+
+/*
+static int
+tmpfs_modcmd(modcmd_t cmd, void *arg)
+{
+	switch (cmd) {
+	case MODULE_CMD_INIT:
+		return vfs_attach(&tmpfs_vfsops);
+	case MODULE_CMD_FINI:
+		return vfs_detach(&tmpfs_vfsops);
+	default:
+		return ENOTTY;
+	}
+}
+*/
+
+VFS_SET(tmpfs_vfsops, tmpfs, VFCF_SYNTHETIC);
diff --git a/sys/vfs/tmpfs/tmpfs_vnops.c b/sys/vfs/tmpfs/tmpfs_vnops.c
new file mode 100755
index 0000000..7d96952
--- /dev/null
+++ b/sys/vfs/tmpfs/tmpfs_vnops.c
@@ -0,0 +1,1798 @@
+/*	$NetBSD: tmpfs_vnops.c,v 1.52 2008/11/26 20:17:33 pooka Exp $	*/
+
+/*
+ * Copyright (c) 2005, 2006, 2007 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Julio M. Merino Vidal, developed as part of Google's Summer of Code
+ * 2005 program.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * tmpfs vnode interface.
+ */
+
+#include <sys/cdefs.h>
+//__KERNEL_RCSID(0, "$NetBSD: tmpfs_vnops.c,v 1.52 2008/11/26 20:17:33 pooka Exp $");
+
+#include <sys/param.h>
+#include <sys/dirent.h>
+#include <sys/fcntl.h>
+#include <sys/event.h>
+#include <sys/malloc.h>
+#include <sys/namei.h>
+#include <sys/proc.h>
+#include <sys/stat.h>
+#include <sys/uio.h>
+#include <sys/unistd.h>
+#include <sys/namecache.h>
+#include <sys/vnode.h>
+#include <sys/lockf.h>
+#include <sys/ucred.h>
+#include <sys/vfsops.h>
+//#include <sys/kauth.h>
+
+#include <machine/pmap.h>
+
+#include <vm/vm.h>
+#include <vm/vm_object.h>
+#include <vm/vm_page.h>
+#include <vm/vm_pager.h>
+#include <vm/vm_map.h>
+#include <vm/vnode_pager.h>
+#include <vm/vm_extern.h>
+
+#include <sys/sched.h>
+#include <sys/sfbuf.h>
+
+#include <vfs/fifofs/fifo.h>
+#include <vfs/tmpfs/tmpfs_vnops.h>
+#include <vfs/tmpfs/tmpfs.h>
+
+
+int	tmpfs_open		(struct vop_open_args *);
+int	tmpfs_close		(struct vop_close_args *);
+int	tmpfs_access		(struct vop_access_args *);
+int	tmpfs_getattr		(struct vop_getattr_args *);
+int	tmpfs_setattr		(struct vop_setattr_args *);
+int	tmpfs_read		(struct vop_read_args *);
+int	tmpfs_write		(struct vop_write_args *);
+int	tmpfs_fsync		(struct vop_fsync_args *);
+int	tmpfs_readdir		(struct vop_readdir_args *);
+int	tmpfs_readlink		(struct vop_readlink_args *);
+int	tmpfs_inactive		(struct vop_inactive_args *);
+int	tmpfs_reclaim		(struct vop_reclaim_args *);
+int	tmpfs_print		(struct vop_print_args *v);
+int	tmpfs_pathconf		(struct vop_pathconf_args *);
+int	tmpfs_advlock		(struct vop_advlock_args *);
+int	tmpfs_getpages		(struct vop_getpages_args *);
+int	tmpfs_putpages		(struct vop_putpages_args *);
+
+int	tmpfs_nresolve		(struct vop_nresolve_args *);
+int	tmpfs_nlookupdotdot	(struct vop_nlookupdotdot_args *);
+int	tmpfs_ncreate		(struct vop_ncreate_args *);
+int	tmpfs_nmkdir		(struct vop_nmkdir_args *);
+int	tmpfs_nmknod		(struct vop_nmknod_args *);
+int	tmpfs_nrmdir		(struct vop_nrmdir_args *);
+int	tmpfs_nremove		(struct vop_nremove_args *);
+int	tmpfs_nrename		(struct vop_nrename_args *);
+int	tmpfs_nsymlink		(struct vop_nsymlink_args *);
+int	tmpfs_nlink		(struct vop_nlink_args *);
+
+/*
+int	tmpfs_lookup		(struct vop_old_lookup_args *);
+int	tmpfs_create		(struct vop_old_create_args *);
+int	tmpfs_mknod		(struct vop_old_mknod_args *);
+int	tmpfs_remove		(struct vop_old_remove_args *);
+int	tmpfs_link		(struct vop_old_link_args *);
+int	tmpfs_rename		(struct vop_old_rename_args *);
+int	tmpfs_mkdir		(struct vop_old_mkdir_args *);
+int	tmpfs_rmdir		(struct vop_old_rmdir_args *);
+int	tmpfs_symlink		(struct vop_old_symlink_args *);
+*/
+
+struct vop_ops tmpfs_vnode_vops = {
+	.vop_default =	vop_defaultop,
+	.vop_open =	tmpfs_open,
+	.vop_close =	tmpfs_close,
+	.vop_access =	tmpfs_access,
+	.vop_getattr =	tmpfs_getattr,
+	.vop_setattr =	tmpfs_setattr,
+	.vop_read =	tmpfs_read,
+	.vop_write =	tmpfs_write,
+	//.vop_mmap =	vop_eopnotsupp,
+	.vop_fsync =	tmpfs_fsync,
+	.vop_readdir =	tmpfs_readdir,
+	.vop_readlink =	tmpfs_readlink,
+	.vop_inactive =	tmpfs_inactive,
+	.vop_reclaim =	tmpfs_reclaim,
+	//.vop_bmap =	vop_eopnotsupp,
+	.vop_print =	tmpfs_print,
+	.vop_pathconf =	tmpfs_pathconf,
+	.vop_advlock =	tmpfs_advlock,
+	.vop_getpages =	tmpfs_getpages,//vop_stdgetpages,
+	.vop_putpages =	vop_stdputpages,//tmpfs_putpages,
+
+	.vop_nresolve =	tmpfs_nresolve,
+	.vop_nlookupdotdot=tmpfs_nlookupdotdot,
+	.vop_ncreate =	tmpfs_ncreate,
+	.vop_nmknod =	tmpfs_nmknod,
+	.vop_nmkdir =	tmpfs_nmkdir,
+	.vop_nrmdir =	tmpfs_nrmdir,
+	.vop_nremove =	tmpfs_nremove,
+	.vop_nsymlink =	tmpfs_nsymlink,
+	.vop_nrename =	tmpfs_nrename
+
+	/*.vop_old_lookup=tmpfs_lookup,
+	.vop_old_create=tmpfs_create,
+	.vop_old_mknod =tmpfs_mknod,
+	.vop_old_remove=tmpfs_remove,
+	.vop_old_link =	tmpfs_link,
+	.vop_old_rename=tmpfs_rename,
+	.vop_old_mkdir =tmpfs_mkdir,
+	.vop_old_rmdir =tmpfs_rmdir,
+	.vop_old_symlink=tmpfs_symlink, */
+};
+
+int tmpfs_fifo_write(struct vop_write_args *ap);
+int tmpfs_fifo_read(struct vop_read_args *ap);
+int tmpfs_fifo_close(struct vop_close_args *ap);
+
+struct vop_ops tmpfs_fifo_vops = {
+	.vop_default =			fifo_vnoperate,
+	.vop_inactive =			tmpfs_inactive,
+	.vop_reclaim =			tmpfs_reclaim,
+	.vop_access =			tmpfs_access,
+	.vop_getattr =			tmpfs_getattr,
+	.vop_setattr =			tmpfs_setattr,
+	.vop_read =				tmpfs_fifo_read,
+	.vop_write =			tmpfs_fifo_write,
+	.vop_close =			tmpfs_fifo_close,
+	/* XXX .vop_kqfilter =			tmpfs_fifo_kqfilter */
+};
+
+int tmpfs_spec_write(struct vop_write_args *ap);
+int tmpfs_spec_read(struct vop_read_args *ap);
+int tmpfs_spec_close(struct vop_close_args *ap);
+
+struct vop_ops tmpfs_spec_vops = {
+	.vop_default =			spec_vnoperate,
+	.vop_inactive =			tmpfs_inactive,
+	.vop_reclaim =			tmpfs_reclaim,
+	.vop_access =			tmpfs_access,
+	.vop_getattr =			tmpfs_getattr,
+	.vop_setattr =			tmpfs_setattr,
+	.vop_read =				tmpfs_spec_read,
+	.vop_write =			tmpfs_spec_write,
+	.vop_close =			tmpfs_spec_close
+};
+
+int
+tmpfs_nresolve(struct vop_nresolve_args *ap)
+{
+	struct nchandle *nch = ap->a_nch;
+	struct namecache *ncp = nch->ncp;
+	struct vnode *dvp = ap->a_dvp;
+	/*struct ucred *cred = ap->a_cred;*/
+
+	int error;
+	struct tmpfs_dirent *de;
+	struct tmpfs_node *dnode;
+	struct vnode *vp = NULL;
+
+	/*KKASSERT(vn_islocked(dvp));*/
+	dnode = VP_TO_TMPFS_DIR(dvp);
+	DP("tmpfs_nresolve dvp%p n%p %*.*s\n",
+		(void *)dvp, (void *)dnode,
+		ncp->nc_nlen, ncp->nc_nlen, ncp->nc_name);
+
+	if (ncp->nc_nlen == 1 && ncp->nc_name[0] == '.') {
+		/*XXX*///VREF(dvp);
+		vp = dvp;
+		cache_setvp(nch, dvp);
+		error = 0;
+	} else {
+
+		de = tmpfs_dir_lookup(dnode, ncp);
+
+		if (de == NULL) {
+			error = ENOENT;
+			cache_setvp(nch, NULL);
+		} else {
+			/* The entry was found, so get its associated tmpfs_node.
+			 * Allocate a new vnode on the matching entry. */
+			error = tmpfs_alloc_vp(dvp->v_mount, de->td_node, &vp);
+			if (error == 0) {
+				vn_unlock(vp);
+				cache_setvp(nch, vp);
+				vrele(vp);
+			}
+		}
+	}
+	return error;
+}
+
+int
+tmpfs_nlookupdotdot(struct vop_nlookupdotdot_args *ap)
+{
+	struct vnode *dvp = ap->a_dvp;
+	struct vnode **vpp = ap->a_vpp;
+
+	int error;
+	struct tmpfs_node *dnode;
+	int ltype;
+
+	dnode = VP_TO_TMPFS_DIR(dvp);
+	DP("tmpfs_nlookupdotdot dvp%p n%p\n", (void *)dvp, (void *)dnode);
+
+	/* XXX */
+	ltype = vn_islocked(dvp);
+	vhold(dvp);
+	vn_unlock(dvp);
+
+	/* Allocate a new vnode on the matching entry. */
+	error = tmpfs_alloc_vp(dvp->v_mount, dnode->tn_spec.tn_dir.tn_parent, vpp);
+
+	vn_lock(dvp, ltype | LK_RETRY);
+	vdrop(dvp);
+
+	KKASSERT(IFF(error == 0, *vpp != NULLVP  && vn_islocked(*vpp)));
+	return error;
+}
+
+static int
+tmpfs_create_file(struct vnode *dvp, struct vnode **vpp,
+		  struct vattr *vap,  struct nchandle *nch,
+		  struct ucred *cred, char *target)
+{
+	int error;
+
+	*vpp = NULL;
+
+	if ((error = vget(dvp, LK_EXCLUSIVE | LK_RETRY)) != 0)
+		return error;
+
+	error = tmpfs_alloc_file(dvp, vpp, vap, nch->ncp, cred, target);
+
+	vput(dvp);
+
+	if (error == 0) {
+		cache_setunresolved(nch);
+		cache_setvp(nch, *vpp);
+	}
+
+	return error;
+}
+
+int
+tmpfs_ncreate(struct vop_ncreate_args *ap)
+{
+	DP("tmpfs_nmkdir dvp %p\n %*.*s\n", (void *)ap->a_dvp,
+	    ap->a_nch->ncp->nc_nlen, ap->a_nch->ncp->nc_nlen, ap->a_nch->ncp->nc_name);
+
+	KKASSERT(ap->a_vap->va_type == VREG || ap->a_vap->va_type == VSOCK);
+
+	return tmpfs_create_file(ap->a_dvp, ap->a_vpp,
+		ap->a_vap, ap->a_nch, ap->a_cred, NULL);
+}
+
+int
+tmpfs_nmkdir(struct vop_nmkdir_args *ap)
+{
+	DP("tmpfs_nmkdir dvp %p\n %*.*s\n", (void *)ap->a_dvp,
+	    ap->a_nch->ncp->nc_nlen, ap->a_nch->ncp->nc_nlen, ap->a_nch->ncp->nc_name);
+
+	KKASSERT(ap->a_vap->va_type == VDIR);
+
+	return tmpfs_create_file(ap->a_dvp, ap->a_vpp,
+		ap->a_vap, ap->a_nch, ap->a_cred, NULL);
+}
+
+int
+tmpfs_nsymlink(struct vop_nsymlink_args *ap)
+{
+	DP("tmpfs_nsymlink dvp %p\n", (void *)ap->a_dvp);
+
+	ap->a_vap->va_type = VLNK;
+
+	return tmpfs_create_file(ap->a_dvp, ap->a_vpp,
+	    ap->a_vap, ap->a_nch, ap->a_cred, ap->a_target);
+}
+
+
+int
+tmpfs_nmknod(struct vop_nmknod_args *ap)
+{
+	DP("tmpfs_nmknod\n");
+
+	if (ap->a_vap->va_type != VBLK && ap->a_vap->va_type != VCHR &&
+	    ap->a_vap->va_type != VFIFO)
+		return EINVAL;
+
+	return tmpfs_create_file(ap->a_dvp, ap->a_vpp,
+	    ap->a_vap, ap->a_nch, ap->a_cred, NULL);
+}
+
+int
+tmpfs_nrmdir(struct vop_nrmdir_args *ap)
+{
+	struct vnode *dvp = ap->a_dvp;
+	struct vnode *vp;
+
+	int error;
+	struct tmpfs_dirent *de;
+	struct tmpfs_mount *tmp;
+	struct tmpfs_node *dnode;
+	struct tmpfs_node *node;
+
+	DP("tmpfs_nrmdir dvp %p vp %p\n", dvp, vp);
+
+	error = cache_vget(ap->a_nch, ap->a_cred, LK_EXCLUSIVE, &vp);
+	if (error)
+		return error;
+	/* XXX vget ? */
+	error = vget(dvp,  LK_EXCLUSIVE);
+	if (error) {
+		vput(vp);
+		return error;
+	}
+
+	KKASSERT(vn_islocked(dvp));
+	KKASSERT(vn_islocked(vp));
+
+	tmp = VFS_TO_TMPFS(dvp->v_mount);
+	dnode = VP_TO_TMPFS_DIR(dvp);
+	node = VP_TO_TMPFS_DIR(vp);
+	error = 0;
+
+	/* Directories with more than two entries ('.' and '..') cannot be
+	 * removed. */
+	if (node->tn_size > 0) {
+		error = ENOTEMPTY;
+		goto out;
+	}
+
+	/* This invariant holds only if we are not trying to remove "..".
+	 * We checked for that above so this is safe now. */
+	KKASSERT(node->tn_spec.tn_dir.tn_parent == dnode);
+
+	/* Get the directory entry associated with node (vp). */
+	de = tmpfs_dir_lookup(dnode, ap->a_nch->ncp);
+	if (de == NULL) {
+		error = ENOENT;
+		goto out;
+	}
+	KKASSERT(de->td_node == node);
+
+	/* Check flags to see if we are allowed to remove the directory. */
+	if (dnode->tn_flags & APPEND || node->tn_flags & (IMMUTABLE | APPEND)) {
+		error = EPERM;
+		goto out;
+	}
+
+	/* Detach the directory entry from the directory (dnode). */
+	tmpfs_dir_detach(dvp, de);
+
+	node->tn_links--;
+	node->tn_status |= TMPFS_NODE_ACCESSED | TMPFS_NODE_CHANGED | \
+	    TMPFS_NODE_MODIFIED;
+	node->tn_spec.tn_dir.tn_parent->tn_links--;
+	node->tn_spec.tn_dir.tn_parent->tn_status |= TMPFS_NODE_ACCESSED | \
+	    TMPFS_NODE_CHANGED | TMPFS_NODE_MODIFIED;
+
+	/* Free the directory entry we just deleted.  Note that the node
+	 * referred by it will not be removed until the vnode is really
+	 * reclaimed. */
+	tmpfs_free_dirent(tmp, de, TRUE);
+
+	KKASSERT(node->tn_links == 0);
+
+	cache_setunresolved(ap->a_nch);
+	cache_setvp(ap->a_nch, NULL);
+	/* XXX locking */
+	cache_inval_vp(vp, CINV_DESTROY);
+
+ out:
+	/* Release the nodes. */
+	vput(dvp);
+	vput(vp);
+
+	return error;
+}
+
+int
+tmpfs_nremove(struct vop_nremove_args *ap)
+{
+	struct vnode *dvp = ap->a_dvp;
+
+	struct vnode *vp;
+	struct tmpfs_dirent *de;
+	struct tmpfs_mount *tmp;
+	struct tmpfs_node *dnode;
+	struct tmpfs_node *node;
+
+	int error;
+
+	DP("tmpfs_nremove dvp %p ", dvp);
+
+	error = cache_vget(ap->a_nch, ap->a_cred, LK_EXCLUSIVE, &vp);
+	if (error)
+		return error;
+	DP("vp %p\n", vp);
+	/* XXX vget ? */
+	error = vget(dvp,  LK_EXCLUSIVE);
+	if (error) {
+		vput(vp);
+		return error;
+	}
+
+	KKASSERT(vn_islocked(dvp));
+	KKASSERT(vn_islocked(vp));
+
+	if (vp->v_type == VDIR) {
+		error = EISDIR;
+		goto out;
+	}
+
+	dnode = VP_TO_TMPFS_DIR(dvp);
+	node = VP_TO_TMPFS_NODE(vp);
+	tmp = VFS_TO_TMPFS(vp->v_mount);
+
+	de = tmpfs_dir_lookup(dnode, ap->a_nch->ncp);
+	if (de == NULL) {
+		error = ENOENT;
+		goto out;
+	}
+	KKASSERT(de->td_node == node);
+
+	/* Files marked as immutable or append-only cannot be deleted. */
+	if (node->tn_flags & (IMMUTABLE | APPEND)) {
+		error = EPERM;
+		goto out;
+	}
+
+	/* Remove the entry from the directory; as it is a file, we do not
+	 * have to change the number of hard links of the directory. */
+	tmpfs_dir_detach(dvp, de);
+
+	/* Free the directory entry we just deleted.  Note that the node
+	 * referred by it will not be removed until the vnode is really
+	 * reclaimed. */
+	tmpfs_free_dirent(tmp, de, TRUE);
+
+	cache_setunresolved(ap->a_nch);
+	cache_setvp(ap->a_nch, NULL);
+	/* XXX locking */
+	cache_inval_vp(vp, CINV_DESTROY);
+
+	error = 0;
+out:
+	/* Release the nodes. */
+	vput(vp);
+	vput(dvp);
+
+	return error;
+}
+
+int
+tmpfs_nrename(struct vop_nrename_args *ap)
+{
+	struct vnode *fdvp = ap->a_fdvp;
+	struct vnode *fvp;
+
+	struct vnode *tdvp = ap->a_tdvp;
+	struct vnode *tvp = NULL;
+
+	struct nchandle *fnch = ap->a_fnch;
+	struct nchandle *tnch = ap->a_tnch;
+
+	char *newname = NULL;
+	int error;
+	struct tmpfs_dirent *fde, *tde;
+	struct tmpfs_mount *tmp;
+	struct tmpfs_node *fnode;
+	struct tmpfs_node *fdnode;
+	struct tmpfs_node *tnode;
+	struct tmpfs_node *tdnode;
+	size_t namelen;
+
+	DP("tmpfs_nrename\n");
+
+	/* Disallow cross-device renames. */
+	if (ap->a_fdvp->v_mount != ap->a_tdvp->v_mount)
+		return (EXDEV);
+	if (ap->a_fdvp->v_mount != ap->a_fnch->ncp->nc_vp->v_mount)
+		return (EXDEV);
+
+	fdnode = VP_TO_TMPFS_DIR(fdvp);
+	tdnode = VP_TO_TMPFS_DIR(tdvp);
+	tmp = VFS_TO_TMPFS(tdvp->v_mount);
+
+#if 0
+	error = vget(fdvp, LK_EXCLUSIVE | LK_RETRY);
+	if (error)
+		return error;
+
+	if (tdvp != fdvp) {
+		error = vget(tdvp, LK_EXCLUSIVE | LK_RETRY);
+		if (error) {
+			vput(fdvp);
+			return error;
+		}
+	}
+	else
+		vref(tdvp);
+#endif
+
+	/* XXX */
+	//cache_vget(fnch, ap->a_cred, LK_EXCLUSIVE, &fvp);
+	//cache_vget(tnch, ap->a_cred, LK_EXCLUSIVE, &tvp);
+	fvp = fnch->ncp->nc_vp;
+	tvp = tnch->ncp->nc_vp;
+
+	tnode = (tvp == NULL) ? NULL : VP_TO_TMPFS_NODE(tvp);
+	fnode = VP_TO_TMPFS_NODE(fvp);
+
+	/* If we need to move the directory between entries, lock the
+	 * source so that we can safely operate on it. */
+
+	/* XXX: this is a potential locking order violation! */
+	/*if (fdnode != tdnode) {
+		error = vn_lock(fdvp, LK_EXCLUSIVE | LK_RETRY);
+		if (error != 0)
+			goto out_unlocked;
+	}*/
+
+	fde = tmpfs_dir_lookup(fdnode, ap->a_fnch->ncp);
+	KKASSERT(fde == NULL)
+	KKASSERT(fde->td_node == fnode);
+
+	/* If replacing an existing entry, ensure we can do the operation. */
+	if (tvp != NULL) {
+		KKASSERT(tnode != NULL);
+
+		if (fnode->tn_type == VDIR && tnode->tn_type == VDIR) {
+			if (tnode->tn_size > 0) {
+				error = ENOTEMPTY;
+				goto out;
+			}
+		} else if (fnode->tn_type == VDIR && tnode->tn_type != VDIR) {
+			error = ENOTDIR;
+			goto out;
+		} else if (fnode->tn_type != VDIR && tnode->tn_type == VDIR) {
+			error = EISDIR;
+			goto out;
+		}
+	}
+
+	/* Ensure that we have enough memory to hold the new name, if it
+	 * has to be changed. */
+	namelen = tnch->ncp->nc_nlen;
+	if (fnch->ncp->nc_nlen != tnch->ncp->nc_nlen ||
+	    memcmp(fnch->ncp->nc_name, tnch->ncp->nc_name,
+	        fnch->ncp->nc_nlen) != 0) {
+		newname = tmpfs_str_pool_get(&tmp->tm_str_pool, namelen, 0);
+		if (newname == NULL) {
+			error = ENOSPC;
+			goto out;
+		}
+	}
+	else
+		newname = NULL;
+
+	/* If the node is being moved to another directory,
+	 * we have to do the move. */
+	if (fdnode != tdnode) {
+		/* In case we are moving a directory, we have to adjust its
+		 * parent to point to the new parent. */
+		if (fde->td_node->tn_type == VDIR) {
+			struct tmpfs_node *n;
+
+			/* Ensure the target directory is not a child of the
+			 * directory being moved.  Otherwise, we'd end up
+			 * with stale nodes. */
+			n = tdnode;
+			while (n != n->tn_spec.tn_dir.tn_parent) {
+				if (n == fnode) {
+					error = EINVAL;
+					goto out;
+				}
+				n = n->tn_spec.tn_dir.tn_parent;
+			}
+
+			/* Adjust the parent pointer. */
+			TMPFS_VALIDATE_DIR(fnode);
+			fde->td_node->tn_spec.tn_dir.tn_parent = tdnode;
+
+			/* As a result of changing the target of the '..'
+			 * entry, the link count of the source and target
+			 * directories has to be adjusted. */
+			fdnode->tn_links--;
+			tdnode->tn_links++;
+		}
+
+		/* Do the move: just remove the entry from the source directory
+		 * and insert it into the target one. */
+		tmpfs_dir_detach(fdvp, fde);
+		tmpfs_dir_attach(tdvp, fde);
+	}
+
+	/* If we are overwriting an entry, we have to remove the old one
+	 * from the target directory. */
+	if (tvp != NULL) {
+		KKASSERT(tnode != NULL);
+
+		/* Remove the old entry from the target directory.
+		 * Note! This relies on tmpfs_dir_attach() putting the new
+		 * node on the end of the target's node list. */
+		tde = tmpfs_dir_lookup(tdnode, tnch->ncp);
+		KKASSERT(tde != NULL);
+		KKASSERT(tde->td_node == tnode);
+		tmpfs_dir_detach(tdvp, tde);
+
+		/* Free the directory entry we just deleted.  Note that the
+		 * node referred by it will not be removed until the vnode is
+		 * really reclaimed. */
+		tmpfs_free_dirent(VFS_TO_TMPFS(tvp->v_mount), tde, TRUE);
+	}
+
+	/* If the name has changed, we need to make it effective by changing
+	 * it in the directory entry. */
+	if (newname != NULL) {
+		KKASSERT(tnch->ncp->nc_nlen < MAXNAMLEN);
+		KKASSERT(tnch->ncp->nc_nlen < 0xffff);
+
+		tmpfs_str_pool_put(&tmp->tm_str_pool, fde->td_name,  fde->td_namelen);
+		fde->td_namelen = (uint16_t)namelen;
+		memcpy(newname, tnch->ncp->nc_name, namelen);
+		fde->td_name = newname;
+		newname = NULL;
+
+		fnode->tn_status |= TMPFS_NODE_CHANGED;
+		tdnode->tn_status |= TMPFS_NODE_MODIFIED;
+	}
+
+	cache_rename(fnch, tnch);
+	error = 0;
+
+out:
+	/* if (fdnode != tdnode)
+		vn_unlock(fdvp, 0); */
+
+out_unlocked:
+	/* Release target nodes. */
+	//if (tdvp == tvp)
+	//	vrele(tdvp);
+	//else
+	//	vput(tdvp);
+	//if (tvp != NULL)
+	//	vput(tvp);
+
+	/* Release source nodes. */
+	////vrele(fdvp);
+	////vrele(fvp);
+
+	//vrele(fdvp);
+	//vput(fvp);
+
+	if (newname != NULL)
+		tmpfs_str_pool_put(&tmp->tm_str_pool, newname, namelen);
+
+	return error;
+}
+
+
+int
+tmpfs_nlink(struct vop_nlink_args *ap)
+{
+	struct vnode *dvp = ap->a_dvp;
+	struct vnode *vp = ap->a_vp;
+
+	int error;
+	struct tmpfs_dirent *de;
+	struct tmpfs_node *dnode;
+	struct tmpfs_node *node;
+
+	DP("tmpfs_nlink\n");
+
+	error = vget(dvp,  LK_EXCLUSIVE);
+	if (error)
+		return error;
+
+	KKASSERT(vn_islocked(dvp));
+	/*XXX*///KKASSERT(cnp->cn_flags & HASBUF);
+	KKASSERT(dvp != vp); /* XXX When can this be false? */
+
+	dnode = VP_TO_TMPFS_DIR(dvp);
+	node = VP_TO_TMPFS_NODE(vp);
+
+	/* Lock vp because we will need to run tmpfs_update over it, which
+	 * needs the vnode to be locked. */
+	error = vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
+	if (error != 0)
+		goto out1;
+
+	/* XXX: Why aren't the following two tests done by the caller? */
+
+	/* Hard links of directories are forbidden. */
+	if (vp->v_type == VDIR) {
+		error = EISDIR;
+		goto out;
+	}
+
+	/* Cannot create cross-device links. */
+	if (dvp->v_mount != vp->v_mount) {
+		error = EXDEV;
+		goto out;
+	}
+
+	/* Ensure that we do not overflow the maximum number of links imposed
+	 * by the system. */
+	KKASSERT(node->tn_links <= LINK_MAX);
+	if (node->tn_links == LINK_MAX) {
+		error = EMLINK;
+		goto out;
+	}
+
+	/* We cannot create links of files marked immutable or append-only. */
+	if (node->tn_flags & (IMMUTABLE | APPEND)) {
+		error = EPERM;
+		goto out;
+	}
+
+	/* Allocate a new directory entry to represent the node. */
+	error = tmpfs_alloc_dirent(VFS_TO_TMPFS(vp->v_mount), node,
+		ap->a_nch->ncp->nc_name, ap->a_nch->ncp->nc_nlen, &de);
+	if (error != 0)
+		goto out;
+
+	/* Insert the new directory entry into the appropriate directory. */
+	tmpfs_dir_attach(dvp, de);
+
+	/* vp link count has changed, so update node times. */
+	node->tn_status |= TMPFS_NODE_CHANGED;
+	tmpfs_update(vp, NULL, NULL, NULL, 0);
+
+	error = 0;
+
+out:
+	vn_unlock(vp);
+out1:
+	/*XXX*///PNBUF_PUT(cnp->cn_pnbuf);
+	vput(dvp);
+	return error;
+}
+
+int
+tmpfs_open(struct vop_open_args *a)
+{
+	struct vnode *vp = a->a_vp;
+	int mode = a->a_mode;
+	int error;
+	struct tmpfs_node *node;
+
+	DP("tmpfs_open vp %p mode %x ", vp, mode);
+
+	KKASSERT(vn_islocked(vp));
+	node = VP_TO_TMPFS_NODE(vp);
+
+	print_links(vp, node);
+	/* The file is still active but all its names have been removed
+	 * (e.g. by a "rmdir $(pwd)").  It cannot be opened any more as
+	 * it is about to die. */
+	if (node->tn_links < 1) {
+		error = ENOENT;
+		goto out;
+	}
+	/* If the file is marked append-only, deny write requests. */
+/*XXX*///if (node->tn_flags & APPEND && (mode & (FWRITE | O_APPEND)) == FWRITE)
+/*XXX*///	error = EPERM;
+/*XXX*///else
+	error = 0;
+out:
+	KKASSERT(vn_islocked(vp));
+	return (vop_stdopen(a));
+}
+
+int
+tmpfs_close(struct vop_close_args *ap)
+{
+	struct vnode *vp = ap->a_vp;
+	struct tmpfs_node *node;
+
+	DP("tmpfs_close vp %p ", vp);
+	KKASSERT(vn_islocked(vp));
+	node = VP_TO_TMPFS_NODE(vp);
+	print_links(vp, node);
+	if (node->tn_links > 0) {
+		/* Update node times.  No need to do it if the node has
+		 * been deleted, because it will vanish after we return. */
+		tmpfs_update(vp, NULL, NULL, NULL, UPDATE_CLOSE);
+	}
+	return (vop_stdclose(ap));
+}
+
+int
+tmpp = a->a_vp;
+	int mode = a->a_mode;
+	/*XXX*///kauth_cred_t cred = a->a_cred;
+	int error;
+	struct tmpfs_node *node;
+
+	DP("tmpfs_access vp %p\t", vp);
+
+	KKASSERT(vn_islocked(vp));
+	node = VP_TO_TMPFS_NODE(vp);
+	print_links(vp, node);
+	switch (vp->v_type) {
+	case VDIR:	/* FALLTHROUGH */
+	case VLNK:	/* FALLTHROUGH */
+	case VREG:
+		if (mode & VWRITE && vp->v_mount->mnt_flag & MNT_RDONLY) {
+			error = EROFS;
+			goto out;
+		}
+		break;
+	case VBLK:	/* FALLTHROUGH */
+	case VCHR:	/* FALLTHROUGH */
+	case VSOCK:	/* FALLTHROUGH */
+	case VFIFO:
+		break;
+	default:
+		error = EINVAL;
+		goto out;
+	}
+	if (mode & VWRITE && node->tn_flags & IMMUTABLE) {
+		error = EPERM;
+		goto out;
+	}
+	/*XXX*///error = vaccess(vp->v_type, node->tn_mode, node->tn_uid,
+	/*XXX*///    node->tn_gid, mode, cred);
+	error = 0;
+out:
+	KKASSERT(vn_islocked(vp));
+	return error;
+}
+
+int
+tmpfs_getattr(struct vop_getattr_args *a)
+{
+	struct vnode *vp = a->a_vp;
+	struct vattr *vap = a->a_vap;
+	struct tmpfs_node *node;
+	DP("tmpfs_getattr vp %p\t", vp);
+
+	node = VP_TO_TMPFS_NODE(vp);
+
+	VATTR_NULL(vap);
+
+	tmpfs_itimes(vp, NULL, NULL, NULL);
+
+	vap->va_type = vp->v_type;
+	vap->va_mode = node->tn_mode;
+	vap->va_nlink = node->tn_links;
+	vap->va_uid = node->tn_uid;
+	vap->va_gid = node->tn_gid;
+	vap->va_fsid = vp->v_mount->mnt_stat.f_fsid.val[0];
+	vap->va_fileid = node->tn_id;
+	vap->va_size = node->tn_size;
+	vap->va_blocksize = PAGE_SIZE;
+	vap->va_atime = node->tn_atime;
+	vap->va_mtime = node->tn_mtime;
+	vap->va_ctime = node->tn_ctime;
+	/* XXX vap->va_birthtime = node->tn_birthtime; */
+	vap->va_gen = node->tn_gen;
+	vap->va_flags = node->tn_flags;
+	vap->va_rmajor = (vp->v_type == VBLK || vp->v_type == VCHR) ?
+	    node->tn_spec.tn_dev.tn_rmajor : VNOVAL;
+	vap->va_rminor = (vp->v_type == VBLK || vp->v_type == VCHR) ?
+		    node->tn_spec.tn_dev.tn_rminor : VNOVAL;
+	vap->va_bytes = round_page(node->tn_size);
+	vap->va_filerev = VNOVAL;
+	vap->va_vaflags = 0;
+	vap->va_spare = VNOVAL; /* XXX */
+	return 0;
+}
+
+#define GOODTIME(tv)	((tv)->tv_sec != VNOVAL || (tv)->tv_nsec != VNOVAL)
+/* XXX Should this operation be atomic?  I think it should, but code in
+ * other places (e.g., ufs) doesn't seem to be... */
+int
+tmpfs_setattr(struct vop_setattr_args *ap)
+{
+	struct vnode *vp = ap->a_vp;
+	struct vattr *vap = ap->a_vap;
+	struct ucred *cred = ap->a_cred;
+
+	int error;
+
+	DP("tmpfs_setattr\n");
+	KKASSERT(vn_islocked(vp));
+	error = 0;
+
+	/* Abort if any unsettable attribute is given. */
+	if (vap->va_type != VNON ||
+	    vap->va_nlink != VNOVAL ||
+	    vap->va_fsid != VNOVAL ||
+	    vap->va_fileid != VNOVAL ||
+	    vap->va_blocksize != VNOVAL ||
+	    GOODTIME(&vap->va_ctime) ||
+	    vap->va_gen != VNOVAL ||
+	    vap->va_rmajor != VNOVAL ||
+	    vap->va_rminor != VNOVAL ||
+	    vap->va_bytes != VNOVAL)
+		error = EINVAL;
+
+	if (error == 0 && (vap->va_flags != VNOVAL))
+		error = tmpfs_chflags(vp, vap->va_flags, cred);
+
+	if (error == 0 && (vap->va_size != VNOVAL))
+		error = tmpfs_chsize(vp, vap->va_size, cred);
+
+	if (error == 0 && (vap->va_uid != VNOVAL || vap->va_gid != VNOVAL))
+		error = tmpfs_chown(vp, vap->va_uid, vap->va_gid, cred);
+
+	if (error == 0 && (vap->va_mode != (u_short)VNOVAL))
+		error = tmpfs_chmod(vp, vap->va_mode, cred);
+
+	if (error == 0 &&
+	      (GOODTIME(&vap->va_atime)
+	    || GOODTIME(&vap->va_mtime)
+	    || GOODTIME(&vap->va_ctime)
+	    ))
+		if ((error = tmpfs_chtimes(vp,
+		    &vap->va_atime, &vap->va_mtime, &vap->va_ctime,
+		    vap->va_vaflags, cred)) == 0)
+			return 0;
+
+	/* Update the node times.  We give preference to the error codes
+	 * generated by this function rather than the ones that may arise
+	 * from tmpfs_update. */
+	tmpfs_update(vp, NULL, NULL, NULL, 0);
+	KKASSERT(vn_islocked(vp));
+	return error;
+}
+
+
+/* --------------------------------------------------------------------- */
+
+static int
+tmpfs_mappedread(vm_object_t vobj, vm_object_t tobj, size_t len, struct uio *uio)
+{
+	vm_pindex_t	idx;
+	vm_page_t	m;
+	struct sf_buf	*sf;
+	off_t		offset, addr;
+	size_t		tlen;
+	caddr_t		va;
+	int		error;
+
+	DP("tmpfs_mapped_read len %d\n", len);
+	addr = uio->uio_offset;
+	idx = OFF_TO_IDX(addr);
+	offset = addr & PAGE_MASK;
+	tlen = MIN(PAGE_SIZE - offset, len);
+
+	if ((vobj == NULL) || (vobj->resident_page_count == 0))
+		goto nocache;
+
+	VM_OBJECT_LOCK(vobj);
+lookupvpg:
+	if (((m = vm_page_lookup(vobj, idx)) != NULL) &&
+	    vm_page_is_valid(m, offset, tlen)) {
+		if (vm_page_sleep_busy(m, FALSE, "tmfsmr"))
+			goto lookupvpg;
+		vm_page_busy(m);
+		VM_OBJECT_UNLOCK(vobj);
+		/* XXX sched_pin(); */
+		sf = sf_buf_alloc(m, SFB_CPUPRIVATE);
+		va = (caddr_t)sf_buf_kva(sf);
+		error = uiomove(va + offset, tlen, uio);
+		sf_buf_free(sf);
+		/* XXX sched_unpin(); */
+		VM_OBJECT_LOCK(vobj);
+		vm_page_wakeup(m);
+		VM_OBJECT_UNLOCK(vobj);
+		return	(error);
+	}
+	VM_OBJECT_UNLOCK(vobj);
+nocache:
+	VM_OBJECT_LOCK(tobj);
+	vm_object_pip_add(tobj, 1);
+	m = vm_page_grab(tobj, idx,
+			/* XXX VM_ALLOC_WIRED |*/
+	    VM_ALLOC_ZERO | VM_ALLOC_NORMAL | VM_ALLOC_RETRY);
+
+	vm_page_wire(m);
+	if (m->valid != VM_PAGE_BITS_ALL) {
+		int behind, ahead;
+		if (vm_pager_has_page(tobj, idx, &behind, &ahead)) {
+			error = vm_pager_get_pages(tobj, &m, 1, 0);
+			if (error != 0) {
+				kprintf("tmpfs get pages from pager error [read]\n");
+				goto out;
+			}
+		} else
+			vm_page_zero_invalid(m, TRUE);
+	}
+	VM_OBJECT_UNLOCK(tobj);
+	/* XXX sched_pin(); */
+	sf = sf_buf_alloc(m, SFB_CPUPRIVATE);
+	va = (caddr_t)sf_buf_kva(sf);
+	error = uiomove(va + offset, tlen, uio);
+	sf_buf_free(sf);
+	/* XXX sched_unpin(); */
+	VM_OBJECT_LOCK(tobj);
+out:
+	vm_page_lock_queues();
+	vm_page_unwire(m, 0);
+	vm_page_activate(m);
+	vm_page_unlock_queues();
+	vm_page_wakeup(m);
+	vm_object_pip_subtract(tobj, 1);
+	VM_OBJECT_UNLOCK(tobj);
+
+	return	(error);
+}
+
+int
+tmpfs_read(struct vop_read_args *v)
+{
+	struct vnode *vp = v->a_vp;
+	struct uio *uio = v->a_uio;
+
+	struct tmpfs_node *node;
+	vm_object_t uobj;
+	size_t len;
+	int resid;
+
+	int error;
+
+	DP("tmpfs_read  off %d s %d\n", (int)uio->uio_offset, uio->uio_resid);
+	node = VP_TO_TMPFS_NODE(vp);
+
+	if (vp->v_type != VREG) {
+		error = EISDIR;
+		goto out;
+	}
+
+	if (uio->uio_offset < 0) {
+		error = EINVAL;
+		goto out;
+	}
+
+	node->tn_status |= TMPFS_NODE_ACCESSED;
+
+	uobj = node->tn_spec.tn_reg.tn_aobj;
+	while ((resid = uio->uio_resid) > 0) {
+		error = 0;
+		if (node->tn_size <= uio->uio_offset)
+			break;
+		len = MIN(node->tn_size - uio->uio_offset, resid);
+		if (len == 0)
+			break;
+		error = tmpfs_mappedread(vp->v_object, uobj, len, uio);
+		if ((error != 0) || (resid == uio->uio_resid))
+			break;
+	}
+out:
+	return error;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int
+tmpfs_mappedwrite(vm_object_t vobj, vm_object_t tobj, size_t len, struct uio *uio)
+{
+	vm_pindex_t	idx;
+	vm_page_t	vpg, tpg;
+	struct sf_buf	*sf;
+	off_t		offset, addr;
+	size_t		tlen;
+	caddr_t		va;
+	int		error;
+
+	DP("tmpfs_mapped_write len %d\n", len);
+	addr = uio->uio_offset;
+	idx = OFF_TO_IDX(addr);
+	offset = addr & PAGE_MASK;
+	tlen = MIN(PAGE_SIZE - offset, len);
+
+	if ((vobj == NULL) || (vobj->resident_page_count == 0)) {
+		vpg = NULL;
+		goto nocache;
+	}
+
+	VM_OBJECT_LOCK(vobj);
+lookupvpg:
+	if (((vpg = vm_page_lookup(vobj, idx)) != NULL) &&
+	    vm_page_is_valid(vpg, offset, tlen)) {
+		if (vm_page_sleep_busy(vpg, FALSE, "tmfsmw"))
+			goto lookupvpg;
+		vm_page_busy(vpg);
+		vm_page_lock_queues();
+		vm_page_undirty(vpg);
+		vm_page_unlock_queues();
+		VM_OBJECT_UNLOCK(vobj);
+
+		/* XXX sched_pin(); */
+		sf = sf_buf_alloc(vpg, SFB_CPUPRIVATE);
+		va = (caddr_t)sf_buf_kva(sf);
+		error = uiomove(va + offset, tlen, uio);
+		sf_buf_free(sf);
+		/* XXX sched_unpin(); */
+	} else {
+		VM_OBJECT_UNLOCK(vobj);
+		vpg = NULL;
+	}
+nocache:
+	VM_OBJECT_LOCK(tobj);
+	vm_object_pip_add(tobj, 1);
+	tpg = vm_page_grab(tobj, idx,
+		/* XXX  VM_ALLOC_WIRED | */
+	    VM_ALLOC_ZERO | VM_ALLOC_NORMAL | VM_ALLOC_RETRY);
+
+	vm_page_wire(tpg);
+	if (tpg->valid != VM_PAGE_BITS_ALL) {
+		int behind, ahead;
+		if (vm_pager_has_page(tobj, idx, &behind, &ahead)) {
+			error = vm_pager_get_pages(tobj, &tpg, 1, 0);
+			if (error != 0) {
+				kprintf("tmpfs get pages from pager error [write]\n");
+				goto out;
+			}
+		} else
+			vm_page_zero_invalid(tpg, TRUE);
+	}
+	VM_OBJECT_UNLOCK(tobj);
+	if (vpg == NULL) {
+		/* XXX sched_pin(); */
+		sf = sf_buf_alloc(tpg, SFB_CPUPRIVATE);
+		va = (caddr_t)sf_buf_kva(sf);
+		error = uiomove(va + offset, tlen, uio);
+		sf_buf_free(sf);
+		/* XXX sched_unpin(); */
+	} else {
+		KASSERT(vpg->valid == VM_PAGE_BITS_ALL, ("parts of vpg invalid"));
+		pmap_copy_page((vm_paddr_t)(vm_offset_t)vpg, (vm_paddr_t)(vm_offset_t)tpg);
+	}
+	VM_OBJECT_LOCK(tobj);
+out:
+	if (vobj != NULL)
+		VM_OBJECT_LOCK(vobj);
+	vm_page_lock_queues();
+	if (error == 0) {
+		vm_page_set_validclean(tpg, offset, tlen);
+		vm_page_zero_invalid(tpg, TRUE);
+		vm_page_dirty(tpg);
+	}
+	vm_page_unwire(tpg, 0);
+	vm_page_activate(tpg);
+	vm_page_unlock_queues();
+	vm_page_wakeup(tpg);
+	if (vpg != NULL)
+		vm_page_wakeup(vpg);
+	if (vobj != NULL)
+		VM_OBJECT_UNLOCK(vobj);
+	vm_object_pip_subtract(tobj, 1);
+	VM_OBJECT_UNLOCK(tobj);
+
+	return	(error);
+}
+
+/* --------------------------------------------------------------------- */
+
+int
+tmpfs_write(struct vop_write_args *v)
+{
+	struct vnode *vp = v->a_vp;
+	struct uio *uio = v->a_uio;
+	int ioflag = v->a_ioflag;
+
+	boolean_t extended;
+	int error = 0;
+	off_t oldsize;
+	struct tmpfs_node *node;
+	vm_object_t uobj;
+	size_t len;
+	int resid;
+
+	DP("tmpfs_write %p\n", vp);
+	node = VP_TO_TMPFS_NODE(vp);
+	oldsize = node->tn_size;
+
+	if (uio->uio_offset < 0 || vp->v_type != VREG) {
+		error = EINVAL;
+		goto out;
+	}
+
+	if (uio->uio_resid == 0) {
+		error = 0;
+		goto out;
+	}
+
+	if (ioflag & IO_APPEND)
+		uio->uio_offset = node->tn_size;
+
+	/*XXX*/
+	//if (uio->uio_offset + uio->uio_resid >
+	//  VFS_TO_TMPFS(vp->v_mount)->tm_maxfilesize)
+	//	return (EFBIG);
+#if 0
+	if (vp->v_type == VREG && td != NULL) {
+		/*XXX*/PROC_LOCK(uio->uio_td->td_proc);
+		if (uio->uio_offset + uio->uio_resid >
+		  lim_cur(uio->uio_td->td_proc, RLIMIT_FSIZE)) {
+			psignal(uio->uio_td->td_proc, SIGXFSZ);
+			PROC_UNLOCK(uio->uio_td->td_proc);
+			return (EFBIG);
+		}
+		/*XXX*/PROC_UNLOCK(uio->uio_td->td_proc);
+	}
+#endif
+	extended = uio->uio_offset + uio->uio_resid > node->tn_size;
+	if (extended) {
+		error = tmpfs_reg_resize(vp, uio->uio_offset + uio->uio_resid);
+		if (error != 0)
+			goto out;
+	}
+
+	uobj = node->tn_spec.tn_reg.tn_aobj;
+	while ((resid = uio->uio_resid) > 0) {
+		if (node->tn_size <= uio->uio_offset)
+			break;
+		len = MIN(node->tn_size - uio->uio_offset, resid);
+		if (len == 0)
+			break;
+		error = tmpfs_mappedwrite(vp->v_object, uobj, len, uio);
+		if ((error != 0) || (resid == uio->uio_resid))
+			break;
+	}
+
+	node->tn_status |= TMPFS_NODE_ACCESSED | TMPFS_NODE_MODIFIED |
+	    (extended ? TMPFS_NODE_CHANGED : 0);
+
+	/* XXX */
+	//if (node->tn_mode & (S_ISUID | S_ISGID)) {
+	//	if (priv_check_cred(v->a_cred, PRIV_VFS_RETAINSUGID, 0))
+	//		node->tn_mode &= ~(S_ISUID | S_ISGID);
+	//}
+
+	if (error != 0)
+		(void)tmpfs_reg_resize(vp, oldsize);
+
+out:
+	KKASSERT(IMPLIES(error == 0, uio->uio_resid == 0));
+	KKASSERT(IMPLIES(error != 0, oldsize == node->tn_size));
+	return error;
+}
+
+int
+tmpfs_fsync(struct vop_fsync_args *a)
+{
+	struct vnode *vp = a->a_vp;
+
+	DP("tmpfs_fsync\n");
+	KKASSERT(vn_islocked(vp));
+
+	tmpfs_update(vp, NULL, NULL, NULL, 0);
+
+	return 0;
+}
+
+
+int
+tmpfs_readdir(struct vop_readdir_args *a)
+{
+	struct vnode *vp = a->a_vp;
+	struct uio *uio = a->a_uio;
+	int *eofflag = a->a_eofflag;
+	off_t **cookies = a->a_cookies;
+	int *ncookies = a->a_ncookies;
+
+	int error;
+	off_t startoff;
+	off_t cnt;
+	struct tmpfs_node *node;
+
+	DP("tmpfs_readdir\n");
+	/* This operation only makes sense on directory nodes. */
+	if (vp->v_type != VDIR) {
+		error = ENOTDIR;
+		goto out;
+	}
+	node = VP_TO_TMPFS_DIR(vp);
+	startoff = uio->uio_offset;
+
+	cnt = 0;
+	if (uio->uio_offset == TMPFS_DIRCOOKIE_DOT) {
+		error = tmpfs_dir_getdotdent(node, uio);
+		if (error == -1) {
+			error = 0;
+			goto outok;
+		} else if (error != 0)
+			goto outok;
+		cnt++;
+	}
+
+	if (uio->uio_offset == TMPFS_DIRCOOKIE_DOTDOT) {
+		error = tmpfs_dir_getdotdotdent(node, uio);
+		if (error == -1) {
+			error = 0;
+			goto outok;
+		} else if (error != 0)
+			goto outok;
+		cnt++;
+	}
+
+	error = tmpfs_dir_getdents(node, uio, &cnt);
+	if (error == -1)
+		error = 0;
+	KKASSERT(error >= 0);
+
+outok:
+	/* This label assumes that startoff has been
+	 * initialized.  If the compiler didn't spit out warnings, we'd
+	 * simply make this one be 'out' and drop 'outok'. */
+
+	if (eofflag != NULL)
+		*eofflag =
+		    (error == 0 && uio->uio_offset == TMPFS_DIRCOOKIE_EOF);
+
+	/* Update NFS-related variables. */
+	if (error == 0 && cookies != NULL && ncookies != NULL) {
+		off_t i;
+		off_t off = startoff;
+		struct tmpfs_dirent *de = NULL;
+
+		*ncookies = cnt;
+		*cookies = kmalloc(cnt * sizeof(off_t), M_TEMP, M_WAITOK);
+
+		for (i = 0; i < cnt; i++) {
+			KKASSERT(off != TMPFS_DIRCOOKIE_EOF);
+			if (off == TMPFS_DIRCOOKIE_DOT) {
+				off = TMPFS_DIRCOOKIE_DOTDOT;
+			} else {
+				if (off == TMPFS_DIRCOOKIE_DOTDOT) {
+					de = TAILQ_FIRST(&node->tn_spec.
+					    tn_dir.tn_dir);
+				} else if (de != NULL) {
+					de = TAILQ_NEXT(de, td_entries);
+				} else {
+					de = tmpfs_dir_lookupbycookie(node,
+					    off);
+					KKASSERT(de != NULL);
+					de = TAILQ_NEXT(de, td_entries);
+				}
+				if (de == NULL) {
+					off = TMPFS_DIRCOOKIE_EOF;
+				} else {
+					off = tmpfs_dircookie(de);
+				}
+			}
+
+			(*cookies)[i] = off;
+		}
+		KKASSERT(uio->uio_offset == off);
+	}
+
+out:
+	return error;
+}
+
+int
+tmpfs_readlink(struct vop_readlink_args *a)
+{
+	struct vnode *vp = a->a_vp;
+	struct uio *uio = a->a_uio;
+
+	int error;
+	struct tmpfs_node *node;
+
+	DP("tmpfs_readlink\n");
+	KKASSERT(vn_islocked(vp));
+	KKASSERT(uio->uio_offset == 0);
+	KKASSERT(vp->v_type == VLNK);
+
+	node = VP_TO_TMPFS_NODE(vp);
+
+	error = uiomove(node->tn_spec.tn_lnk.tn_link,
+	    MIN(node->tn_size, uio->uio_resid), uio);
+	node->tn_status |= TMPFS_NODE_ACCESSED;
+
+	KKASSERT(vn_islocked(vp));
+
+	return error;
+}
+
+int
+tmpfs_inactive(struct vop_inactive_args *a)
+{
+	struct vnode *vp = a->a_vp;
+	struct tmpfs_node *node;
+	DP("tmpfs_inactive vp %p\n", vp);
+	KKASSERT(vn_islocked(vp));
+	node = VP_TO_TMPFS_NODE(vp);
+
+	if (node->tn_links == 0)
+		vrecycle(vp);
+	vn_unlock(vp);
+	return 0;
+}
+
+int
+tmpfs_reclaim(struct vop_reclaim_args *a)
+{
+	struct vnode *vp = a->a_vp;
+	struct tmpfs_mount *tmp;
+	struct tmpfs_node *node;
+
+	DP("tmpfs_reclaim vp %p\n", vp);
+
+	if (vp->v_data != NULL)
+	{
+		node = VP_TO_TMPFS_NODE(vp);
+		tmp = VFS_TO_TMPFS(vp->v_mount);
+
+		tmpfs_free_vp(vp);
+
+		/* If the node referenced by this vnode was deleted by the user,
+		 * we must free its associated data structures (now that the vnode
+		 * is being reclaimed). */
+		if (node->tn_links == 0)
+			tmpfs_free_node(tmp, node);
+
+		KKASSERT(vp->v_data == NULL);
+	}
+	return 0;
+}
+
+#define PRIdMAX "LX"
+int
+tmpfs_print(struct vop_print_args *ap)
+{
+	struct vnode *vp = ap->a_vp;
+	struct tmpfs_node *node;
+
+	node = VP_TO_TMPFS_NODE(vp);
+
+	kprintf("tag VT_TMPFS, tmpfs_node %p, flags 0x%x, links %d\n",
+	    node, node->tn_flags, node->tn_links);
+	kprintf("\tmode 0%o, owner %d, group %d, size %" PRIdMAX
+	    ", status 0x%x\n",
+	    node->tn_mode, node->tn_uid, node->tn_gid,
+	    (uintmax_t)node->tn_size, (u_int)node->tn_status);
+	if (vp->v_type == VFIFO)
+		fifo_printinfo(vp);
+	kprintf("\n");
+
+	return 0;
+}
+
+int
+tmpfs_pathconf(struct vop_pathconf_args *ap)
+{
+	int name = ap->a_name;
+	register_t *retval = ap->a_retval;
+
+	int error;
+	DP("tmpfs_pathconf\n");
+	error = 0;
+
+	switch (name) {
+	case _PC_LINK_MAX:
+		*retval = LINK_MAX;
+		break;
+
+	case _PC_NAME_MAX:
+		*retval = NAME_MAX;
+		break;
+
+	case _PC_PATH_MAX:
+		*retval = PATH_MAX;
+		break;
+
+	case _PC_PIPE_BUF:
+		*retval = PIPE_BUF;
+		break;
+
+	case _PC_CHOWN_RESTRICTED:
+		*retval = 1;
+		break;
+
+	case _PC_NO_TRUNC:
+		*retval = 1;
+		break;
+#if 0
+	case _PC_SYNC_IO:
+		*retval = 1;
+		break;
+
+	case _PC_FILESIZEBITS:
+		*retval = 0; /* XXX Don't know which value should I return. */
+		break;
+#endif
+	default:
+		error = EINVAL;
+	}
+
+	return error;
+}
+
+int
+tmpfs_advlock(struct vop_advlock_args *ap)
+{
+	struct tmpfs_node *node = VP_TO_TMPFS_NODE(ap->a_vp);
+	DP("tmpfs_advlock\n");
+	return lf_advlock(ap, &node->tn_lockf, (u_quad_t)node->tn_size);
+}
+
+
+
+int
+tmpfs_getpages(struct vop_getpages_args *ap)
+{
+	struct vnode *vp = ap->a_vp;
+	vm_ooffset_t offset = ap->a_offset;
+	struct vm_page **m = ap->a_m;
+	int count = ap->a_count;
+	int reqpage = ap->a_reqpage;
+
+	int error = 0;
+	int i;
+	struct tmpfs_node *node;
+	struct vm_object *tobj;
+	vm_pindex_t	idx;
+	vm_page_t p;
+	vm_page_t ma[ap->a_count];
+	int rv;
+
+	DP("tmpfs_getpages\n");
+	KKASSERT(vp->v_type == VREG);
+	KKASSERT(mutex_owned(&vp->v_interlock));
+
+	node = VP_TO_TMPFS_NODE(vp);
+	tobj = node->tn_spec.tn_reg.tn_aobj;
+
+	idx = m[reqpage]->pindex;
+
+	crit_enter();
+
+	p = vm_page_grab(tobj, idx, VM_ALLOC_NORMAL | VM_ALLOC_RETRY);
+
+	vm_page_wire(p);
+
+	if (p->valid != VM_PAGE_BITS_ALL) {
+		int behind, ahead;
+		if (vm_pager_has_page(tobj, idx, &behind, &ahead)) {
+			error = vm_pager_get_pages(tobj, &p, 1, 0);
+			if (error != 0) {
+				kprintf("tmpfs.gp get pages from pager error [read]\n");
+				goto out;
+			}
+		} else
+			vm_page_zero_invalid(p, TRUE);
+	}
+
+	vm_page_copy(p, m[reqpage]);
+
+	m[reqpage]->valid = VM_PAGE_BITS_ALL;
+	vm_page_undirty(m[reqpage]);
+
+out:
+
+	vm_page_unwire(p, 0);
+	vm_page_activate(p);
+	vm_page_wakeup(p);
+	vm_page_queues[0];
+	crit_exit();
+/*
+	for (i = 0; i < count; i++) {
+		if (i != reqpage) {
+			if (!error) {
+				if (m[i]->flags & PG_WANTED)
+					vm_page_activate(m[i]);
+				else
+					vm_page_deactivate(m[i]);
+				vm_page_wakeup(m[i]);
+			} else {
+				vnode_pager_freepage(m[i]);
+			}
+		}
+	}
+*/
+	//error = vm_pager_get_pages(uobj, m, count, reqpage);
+
+	/* We currently don't rely on PGO_PASTEOF. */
+#if 0
+	if (vp->v_size <= offset + (centeridx << PAGE_SHIFT)) {
+		if ((flags & PGO_LOCKED) == 0)
+			mutex_exit(&vp->v_interlock);
+		return EINVAL;
+	}
+
+	if (vp->v_size < offset + (npages << PAGE_SHIFT)) {
+		npages = (round_page(vp->v_size) - offset) >> PAGE_SHIFT;
+	}
+
+	if ((flags & PGO_LOCKED) != 0)
+		return EBUSY;
+
+	if ((flags & PGO_NOTIMESTAMP) == 0) {
+		if ((vp->v_mount->mnt_flag & MNT_NOATIME) == 0)
+			node->tn_status |= TMPFS_NODE_ACCESSED;
+
+		if ((access_type & VM_PROT_WRITE) != 0)
+			node->tn_status |= TMPFS_NODE_MODIFIED;
+	}
+
+	mutex_exit(&vp->v_interlock);
+
+	/*
+	 * Make sure that the array on which we will store the
+	 * gotten pages is clean.  Otherwise uao_get (pointed to by
+	 * the pgo_get below) gets confused and does not return the
+	 * appropriate pages.
+	 *
+	 * XXX This shall be revisited when kern/32166 is addressed
+	 * because the loop to clean m[i] will most likely be redundant
+	 * as well as the PGO_ALLPAGES flag.
+	 */
+	if (m != NULL)
+		for (i = 0; i < npages; i++)
+			m[i] = NULL;
+	mutex_enter(&uobj->vmobjlock);
+
+	error = (*uobj->pgops->pgo_get)(uobj, offset, m, &npages, centeridx,
+		access_type, advice, flags | PGO_ALLPAGES);
+#endif
+
+#if defined(DEBUG)
+		{
+			/* Make sure that all the pages we return are valid. */
+			int dbgi;
+			if (error == 0 && m != NULL)
+				for (dbgi = 0; dbgi < npages; dbgi++)
+					KKASSERT(m[dbgi] != NULL);
+		}
+	#endif
+
+	return error;
+}
+#if 1
+int
+tmpfs_putpages(struct vop_putpages_args *ap)
+{
+	struct vnode *vp = ap->a_vp;
+	vm_ooffset_t offset = ap->a_offset;
+	struct vm_page **m = ap->a_m;
+	int count = ap->a_count;
+	int *rtvals = ap->a_rtvals;
+	int sync = ap->a_sync;
+
+	struct tmpfs_node *node;
+	struct vm_object *uobj;
+
+	DP("tmpfs_putpages\n");
+	KKASSERT(vp->v_type == VREG);
+	//KKASSERT(mutex_owned(&vp->v_interlock));
+
+	node = VP_TO_TMPFS_NODE(vp);
+	uobj = node->tn_spec.tn_reg.tn_aobj;
+
+	vm_pager_put_pages(uobj, m, count, sync, rtvals);
+	return 0;
+}
+
+#endif
+
+
+
+/* --------------------------------------------------------------------- */
+/* tmpfs_fifo_ops */
+
+
+static int
+tmpfs_fifo_kqfilter(struct vop_kqfilter_args *ap)
+{
+	struct vnode *vp;
+	struct tmpfs_node *node;
+
+	vp = ap->a_vp;
+	node = VP_TO_TMPFS_NODE(vp);
+
+	switch (ap->a_kn->kn_filter){
+	case EVFILT_READ:
+		node->tn_status |= TMPFS_NODE_ACCESSED;
+		break;
+	case EVFILT_WRITE:
+		node->tn_status |= TMPFS_NODE_MODIFIED;
+		break;
+	}
+
+	return (VOCALL(&fifo_vnode_vops, &ap->a_head));
+}
+
+int
+tmpfs_fifo_close(struct vop_close_args *ap)
+{
+	DP("tmpfs_fifo_close\n");
+	tmpfs_update(ap->a_vp, NULL, NULL, NULL, UPDATE_CLOSE);
+	return (VOCALL(&fifo_vnode_vops, &ap->a_head));
+}
+
+int
+tmpfs_fifo_read(struct vop_read_args *ap)
+{
+	DP("tmpfs_fifo_read\n");
+	VP_TO_TMPFS_NODE(ap->a_vp)->tn_status |= TMPFS_NODE_ACCESSED;
+	return (VOCALL(&fifo_vnode_vops, &ap->a_head));
+}
+
+
+int
+tmpfs_fifo_write(struct vop_write_args *ap)
+{
+	DP("tmpfs_fifo_write\n");
+	VP_TO_TMPFS_NODE(ap->a_vp)->tn_status |= TMPFS_NODE_MODIFIED;
+	int error = VOCALL(&fifo_vnode_vops, &ap->a_head);
+	return error;
+}
+
+/* --------------------------------------------------------------------- */
+/* tmpfs_spec_ops */
+
+int
+tmpfs_spec_close(struct vop_close_args *ap)
+{
+	struct vnode *vp = ap->a_vp;
+	int error;
+	DP("tmpfs_spec_close\n");
+	tmpfs_update(vp, NULL, NULL, NULL, UPDATE_CLOSE);
+	error = VOCALL(&spec_vnode_vops, &ap->a_head);
+	return error;
+}
+
+
+int
+tmpfs_spec_read(struct vop_read_args *ap)
+{
+	struct vnode *vp = ap->a_vp;
+	DP("tmpfs_spec_read\n");
+	VP_TO_TMPFS_NODE(vp)->tn_status |= TMPFS_NODE_ACCESSED;
+	return  VOCALL(&spec_vnode_vops, &ap->a_head);
+}
+
+
+int
+tmpfs_spec_write(struct vop_write_args *ap)
+{
+	struct vnode *vp = ap->a_vp;
+	DP("tmpfs_spec_write\n");
+	VP_TO_TMPFS_NODE(vp)->tn_status |= TMPFS_NODE_MODIFIED;
+	return  VOCALL(&spec_vnode_vops, &ap->a_head);
+}
diff --git a/sys/vfs/tmpfs/tmpfs_vnops.h b/sys/vfs/tmpfs/tmpfs_vnops.h
new file mode 100755
index 0000000..4287789
--- /dev/null
+++ b/sys/vfs/tmpfs/tmpfs_vnops.h
@@ -0,0 +1,48 @@
+/*	$NetBSD: tmpfs_vnops.h,v 1.11 2008/04/28 20:24:02 martin Exp $	*/
+
+/*
+ * Copyright (c) 2005, 2006 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Julio M. Merino Vidal, developed as part of Google's Summer of Code
+ * 2005 program.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _VFS_TMPFS_TMPFS_VNOPS_H_
+#define _VFS_TMPFS_TMPFS_VNOPS_H_
+
+#if !defined(_KERNEL)
+#error not supposed to be exposed to userland.
+#endif
+
+/*
+ * Declarations for tmpfs_vnops.c.
+ */
+
+extern struct vop_ops tmpfs_vnode_vops;
+extern struct vop_ops tmpfs_spec_vops;
+extern struct vop_ops tmpfs_fifo_vops;
+
+#endif /* _VFS_TMPFS_TMPFS_VNOPS_H_ */


[Date Prev][Date Next]  [Thread Prev][Thread Next]  [Date Index][Thread Index]