kernel - TMPFS - Initial port of NetBSD's tmpfs
authorMatthew Dillon <dillon@apollo.backplane.com>
Fri, 12 Feb 2010 21:46:28 +0000 (13:46 -0800)
committerMatthew Dillon <dillon@apollo.backplane.com>
Fri, 12 Feb 2010 21:46:28 +0000 (13:46 -0800)
* This is the initial pre-stabilization port of NetBSD's tmpfs,
  by Naoya Sugioka.

Submitted-by: Naoya Sugioka <naoya.sugioka@gmail.com>
29 files changed:
sbin/Makefile
sbin/mount/mount.8
sbin/mount_tmpfs/Makefile [new file with mode: 0644]
sbin/mount_tmpfs/mount_tmpfs.8 [new file with mode: 0644]
sbin/mount_tmpfs/mount_tmpfs.c [new file with mode: 0644]
sbin/mount_tmpfs/mount_tmpfs.h [new file with mode: 0644]
share/man/man5/Makefile
share/man/man5/tmpfs.5 [new file with mode: 0644]
sys/conf/files
sys/conf/options
sys/config/GENERIC
sys/config/LINT
sys/config/VKERNEL
sys/config/X86_64_GENERIC
sys/kern/Make.tags.inc
sys/kern/Makefile
sys/kern/Makefile.misc
sys/sys/mount.h
sys/sys/vfscache.h
sys/vfs/Makefile
sys/vfs/tmpfs/Makefile [new file with mode: 0644]
sys/vfs/tmpfs/tmpfs.h [new file with mode: 0644]
sys/vfs/tmpfs/tmpfs_args.h [new file with mode: 0644]
sys/vfs/tmpfs/tmpfs_fifoops.c [new file with mode: 0644]
sys/vfs/tmpfs/tmpfs_fifoops.h [new file with mode: 0644]
sys/vfs/tmpfs/tmpfs_subr.c [new file with mode: 0644]
sys/vfs/tmpfs/tmpfs_vfsops.c [new file with mode: 0644]
sys/vfs/tmpfs/tmpfs_vnops.c [new file with mode: 0644]
sys/vfs/tmpfs/tmpfs_vnops.h [new file with mode: 0644]

index 12b42f3..b380006 100644 (file)
@@ -57,6 +57,7 @@ SUBDIR=       adjkerntz \
        mount_null \
        mount_portal \
        mount_std \
+       mount_tmpfs \
        mount_udf \
        mount_union \
        mountd \
index f5f4a2c..b1a146b 100644 (file)
@@ -398,6 +398,7 @@ have permission to load the module.
 .Xr mount_procfs 8 ,
 .Xr mount_smbfs 8 ,
 .Xr mount_std 8 ,
+.Xr mount_tmpfs 8 ,
 .Xr mount_udf 8 ,
 .Xr mount_union 8 ,
 .Xr sysctl 8 ,
diff --git a/sbin/mount_tmpfs/Makefile b/sbin/mount_tmpfs/Makefile
new file mode 100644 (file)
index 0000000..8c430bc
--- /dev/null
@@ -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_tmpfs.8
+
+MOUNT= ${.CURDIR}/../mount
+CFLAGS+= -I${.CURDIR}/../../sys -I${MOUNT}
+.PATH: ${MOUNT}
+
+.include <bsd.prog.mk>
diff --git a/sbin/mount_tmpfs/mount_tmpfs.8 b/sbin/mount_tmpfs/mount_tmpfs.8
new file mode 100644 (file)
index 0000000..c3a9afc
--- /dev/null
@@ -0,0 +1,143 @@
+.\"    $NetBSD: mount_tmpfs.8,v 1.14 2008/04/30 13:10:53 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.
+.\"
+.Dd February 13, 2008
+.Dt MOUNT_TMPFS 8
+.Os
+.Sh NAME
+.Nm mount_tmpfs
+.Nd mount an efficient memory file system
+.Sh SYNOPSIS
+.Nm
+.Op Fl g Ar group
+.Op Fl m Ar mode
+.Op Fl n Ar nodes
+.Op Fl o Ar options
+.Op Fl s Ar size
+.Op Fl u Ar user
+.Ar tmpfs
+.Ar mount_point
+.Sh DESCRIPTION
+The
+.Nm
+command attaches an instance of the efficient memory file system to the
+global file system namespace.
+The
+.Ar tmpfs
+parameter only exists for compatibility with the other mount commands and
+is ignored.
+The directory specified by
+.Ar mount_point
+is converted to an absolute path before use and its attributes (owner,
+group and mode) are inherited unless explicitly overriden by the options
+described below.
+.Pp
+The following options are supported:
+.Bl -tag -width XoXoptions
+.It Fl g Ar group
+Specifies the group name or GID of the root inode of the file system.
+Defaults to the mount point's GID.
+.It Fl m Ar mode
+Specifies the mode (in octal notation) of the root inode of the file system.
+Defaults to the mount point's mode.
+.It Fl n Ar nodes
+Specifies the maximum number of nodes available to the file system.
+If not specified, the file system chooses a reasonable maximum given its
+size at mount time, which can be limited with
+.Fl s .
+.It Fl o Ar options
+Options are specified with a
+.Fl o
+flag followed by a comma-separated string of options.
+See the
+.Xr mount 8
+man page for possible options and their meanings.
+.It Fl s Ar size
+Specifies the total file system size in bytes.
+If zero is given (the default), the available amount of memory (including
+main memory and swap space) will be used.
+Note that four megabytes are always reserved for the system and cannot
+be assigned to the file system.
+.It Fl u Ar user
+Specifies the user name or UID of the root inode of the file system.
+Defaults to the mount point's UID.
+.El
+.Pp
+Every option that accepts a numerical value as its argument can take a
+trailing
+.Sq b
+to indicate bytes (the default), a trailing
+.Sq k
+to indicate kilobytes, a trailing
+.Sq M
+to indicate megabytes or a trailing
+.Sq G
+to indicate gigabytes.
+Note that both lowercase and uppercase forms of these letters are allowed.
+.Sh EXAMPLES
+The following command mounts a tmpfs instance over the
+.Pa /tmp
+directory, inheriting its owner, group and mode settings:
+.Pp
+.Ic "mount -t tmpfs tmpfs /tmp"
+.Pp
+The following command mounts a tmpfs instance over the
+.Pa /mnt
+directory, setting a 20 megabytes limit in space, owned by the
+.Sq joe
+user and belonging to the
+.Sq users
+group, with a restricted 0700 mode:
+.Pp
+.Ic "mount -t tmpfs -o -s20M -o -ujoe -o -gusers -o -m0700 tmpfs /mnt"
+.Pp
+See
+.Pa /usr/share/examples/fstab/fstab.ramdisk
+for some examples on how to add tmpfs entries to
+.Pa /etc/fstab .
+.Sh SEE ALSO
+.Xr fstab 5 ,
+.Xr mount 8
+.Sh HISTORY
+The
+.Nm
+utility first appeared in
+.Nx 4.0 .
+.Sh BUGS
+File system meta-data is not pageable.
+If there is not enough main memory to hold this information, the system may
+become unstable or very unresponsive because it will not be able to allocate
+required memory.
+A malicious user could trigger this condition if he could create lots of
+files inside a size-unbounded tmpfs file system.
+Limiting the number of nodes per file system
+.Pq Fl n
+will prevent this; the default value for this setting is also often adjusted
+to an adequate value to resolve this.
diff --git a/sbin/mount_tmpfs/mount_tmpfs.c b/sbin/mount_tmpfs/mount_tmpfs.c
new file mode 100644 (file)
index 0000000..24d934a
--- /dev/null
@@ -0,0 +1,263 @@
+/*     $NetBSD: mount_tmpfs.c,v 1.24 2008/08/05 20:57:45 pooka 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.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+
+#include <vfs/tmpfs/tmpfs_args.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <grp.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <unistd.h>
+#include <inttypes.h>
+
+#include "mntopts.h"
+#include "mount_tmpfs.h"
+
+/* --------------------------------------------------------------------- */
+
+static const struct mntopt mopts[] = {
+       MOPT_STDOPTS,
+       MOPT_NULL
+};
+
+/* --------------------------------------------------------------------- */
+
+static gid_t   a_gid(char *);
+static uid_t   a_uid(char *);
+static mode_t  a_mask(char *);
+static void    usage(void) __dead2;
+
+/* --------------------------------------------------------------------- */
+
+void
+mount_tmpfs_parseargs(int argc, char *argv[],
+       struct tmpfs_args *args, int *mntflags,
+       char *canon_dev, char *canon_dir)
+{
+       int gidset, modeset, uidset; /* Ought to be 'bool'. */
+       int ch;
+       gid_t gid;
+       uid_t uid;
+       mode_t mode;
+       int64_t tmpnumber = 0;
+       struct stat sb;
+       char *a;
+
+       /* Set default values for mount point arguments. */
+       memset(args, 0, sizeof(*args));
+       args->ta_version = TMPFS_ARGS_VERSION;
+       args->ta_size_max = 0;
+       args->ta_nodes_max = 0;
+       *mntflags = 0;
+
+       gidset = 0; gid = 0;
+       uidset = 0; uid = 0;
+       modeset = 0; mode = 0;
+
+       optind = optreset = 1;
+       while ((ch = getopt(argc, argv, "g:m:n:o:s:u:")) != -1 ) {
+               switch (ch) {
+               case 'g':
+                       gid = a_gid(optarg);
+                       gidset = 1;
+                       break;
+
+               case 'm':
+                       mode = a_mask(optarg);
+                       modeset = 1;
+                       break;
+
+               case 'n':
+                       for (a = optarg; *optarg && isdigit(*optarg); ++optarg);
+                       if (!*optarg)
+                               tmpnumber = strtoimax(a, NULL, 10);
+
+                       args->ta_nodes_max = tmpnumber;
+                       break;
+
+               case 'o':
+                       getmntopts(optarg, mopts, mntflags, 0);
+                       break;
+
+               case 's':
+                       for (a = optarg; *optarg && isdigit(*optarg); ++optarg);
+                       if (!*optarg)
+                               tmpnumber = strtoimax(a, NULL, 10);
+
+                       args->ta_size_max = tmpnumber;
+                       break;
+
+               case 'u':
+                       uid = a_uid(optarg);
+                       uidset = 1;
+                       break;
+
+               case '?':
+               default:
+                       usage();
+               }
+       }
+       argc -= optind;
+       argv += optind;
+
+       if (argc != 2)
+               usage();
+
+       strlcpy(canon_dev, argv[0], MAXPATHLEN);
+       strlcpy(canon_dir, argv[1], MAXPATHLEN);
+
+       if (stat(canon_dir, &sb) == -1)
+               err(EXIT_FAILURE, "cannot stat `%s'", canon_dir);
+
+       args->ta_root_uid = uidset ? uid : sb.st_uid;
+       args->ta_root_gid = gidset ? gid : sb.st_gid;
+       args->ta_root_mode = modeset ? mode : sb.st_mode;
+}
+
+/* --------------------------------------------------------------------- */
+
+static gid_t
+a_gid(char *s)
+{
+       struct group *gr;
+       char *gname;
+       gid_t gid;
+
+       if ((gr = getgrnam(s)) != NULL)
+               gid = gr->gr_gid;
+       else {
+               for (gname = s; *s && isdigit(*s); ++s);
+               if (!*s)
+                       gid = atoi(gname);
+               else
+                       errx(EX_NOUSER, "unknown group id: %s", gname);
+       }
+       return (gid);
+}
+
+static uid_t
+a_uid(char *s)
+{
+       struct passwd *pw;
+       char *uname;
+       uid_t uid;
+
+       if ((pw = getpwnam(s)) != NULL)
+               uid = pw->pw_uid;
+       else {
+               for (uname = s; *s && isdigit(*s); ++s);
+               if (!*s)
+                       uid = atoi(uname);
+               else
+                       errx(EX_NOUSER, "unknown user id: %s", uname);
+       }
+       return (uid);
+}
+
+static mode_t
+a_mask(char *s)
+{
+       int done, rv=0;
+       char *ep;
+
+       done = 0;
+       if (*s >= '0' && *s <= '7') {
+               done = 1;
+               rv = strtol(optarg, &ep, 8);
+       }
+       if (!done || rv < 0 || *ep)
+               errx(EX_USAGE, "invalid file mode: %s", s);
+       return (rv);
+}
+
+static void
+usage(void)
+{
+       (void)fprintf(stderr,
+           "Usage: %s [-g group] [-m mode] [-n nodes] [-o options] [-s size]\n"
+           "           [-u user] tmpfs mountpoint\n", getprogname());
+       exit(1);
+}
+
+/* --------------------------------------------------------------------- */
+
+int
+mount_tmpfs(int argc, char *argv[])
+{
+       struct tmpfs_args args;
+       char canon_dev[MAXPATHLEN], canon_dir[MAXPATHLEN];
+       int mntflags;
+       struct vfsconf vfc;
+       int error;
+
+       mount_tmpfs_parseargs(argc, argv, &args, &mntflags,
+           canon_dev, canon_dir);
+
+
+       error = getvfsbyname("tmpfs", &vfc);
+       if (error && vfsisloadable("tmpfs")) {
+               if(vfsload("tmpfs"))
+                       err(EX_OSERR, "vfsload(%s)", "tmpfs");
+               endvfsent();
+               error = getvfsbyname("tmpfs", &vfc);
+       }
+       if (error)
+               errx(EX_OSERR, "%s filesystem not available", "tmpfs");
+
+       if (mount(vfc.vfc_name, canon_dir, mntflags, 0) == -1)
+               err(EXIT_FAILURE, "tmpfs on %s", canon_dir);
+
+       return EXIT_SUCCESS;
+}
+
+#ifndef MOUNT_NOMAIN
+int
+main(int argc, char *argv[])
+{
+
+       setprogname(argv[0]);
+       return mount_tmpfs(argc, argv);
+}
+#endif
diff --git a/sbin/mount_tmpfs/mount_tmpfs.h b/sbin/mount_tmpfs/mount_tmpfs.h
new file mode 100644 (file)
index 0000000..96874ef
--- /dev/null
@@ -0,0 +1,37 @@
+/*     $NetBSD: mount_tmpfs.h,v 1.1 2008/08/05 20:57:45 pooka Exp $    */
+
+/*
+ * Copyright (c) 2008 The NetBSD Foundation.  All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _SBIN_MOUNT_TMPFS_MOUNT_TMPFS_H_
+#define _SBIN_MOUNT_TMPFS_MOUNT_TMPFS_H_
+
+#include <vfs/tmpfs/tmpfs_args.h>
+
+int    mount_tmpfs(int, char **);
+void   mount_tmpfs_parseargs(int, char **, struct tmpfs_args *, int *,
+                             char *, char *);
+
+#endif /* _SBIN_MOUNT_TMPFS_MOUNT_TMPFS_H_ */
index ecff5f7..0e858f5 100644 (file)
@@ -53,6 +53,7 @@ MAN=  acct.5 \
        shells.5 \
        stab.5 \
        sysctl.conf.5 \
+       tmpfs.5 \
        utf2.5 \
        utf8.5 \
        utmp.5 \
diff --git a/share/man/man5/tmpfs.5 b/share/man/man5/tmpfs.5
new file mode 100644 (file)
index 0000000..a812636
--- /dev/null
@@ -0,0 +1,120 @@
+.\"
+.\" Copyright (c) 2007 Xin LI
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in the
+.\"    documentation and/or other materials provided with the distribution.
+.\" 3. The name of the author may not be used to endorse or promote products
+.\"    derived from this software without specific prior written permission
+.\"
+.\" THIS DOCUMENTATION IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+.\"
+.\" $FreeBSD: src/share/man/man5/tmpfs.5,v 1.7 2010/01/29 15:11:50 jh Exp $
+.\"
+.Dd February 2, 2010
+.Dt TMPFS 5
+.Os
+.Sh NAME
+.Nm tmpfs
+.Nd "efficient memory file system"
+.Sh SYNOPSIS
+To compile this driver into the kernel,
+place the following line in your
+kernel configuration file:
+.Bd -ragged -offset indent
+.Cd "options TMPFS"
+.Ed
+.Pp
+Alternatively, to load the driver as a
+module at boot time, place the following line in
+.Xr loader.conf 5 :
+.Bd -literal -offset indent
+tmpfs_load="YES"
+.Ed
+.Sh DESCRIPTION
+The
+.Nm
+driver will permit the
+.Dx
+kernel to access
+.Tn tmpfs
+file systems.
+.Sh OPTIONS
+The following options are available when
+mounting
+.Nm
+file systems:
+.Bl -tag -width indent
+.It Cm gid
+root group id.
+.It Cm uid
+root user id.
+.It Cm mode
+permissions in octal format.
+.It Cm inodes
+maximum number of inodes.
+.It Cm size
+maximum size (in bytes) for the file system.
+.It Cm maxfilesize
+maximum file size (in bytes).
+.El
+.Sh EXAMPLES
+To mount a
+.Nm
+memory file system:
+.Pp
+.Dl "mount -t tmpfs tmpfs /tmp"
+.Sh SEE ALSO
+.Xr nmount 2 ,
+.Xr unmount 2 ,
+.Xr fstab 5 ,
+.Xr mount 8
+.Sh HISTORY
+The
+.Nm
+driver first appeared in
+.Dx 2.5.1 .
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm
+kernel implementation was written by
+.An Julio M. Merino Vidal Aq jmmv@NetBSD.org
+as a Google SoC project.
+.Pp
+.An Rohit Jalan
+and others ported it from
+.Nx
+to
+.Fx .
+.Pp
+.An Naoya Sugioka
+and others ported it from
+.Nx
+to
+.Dx .
+.Pp
+This manual page was written by
+.An Xin LI Aq delphij@FreeBSD.org .
+.Sh BUGS
+The
+.Nm
+kernel implementation is currently considered as
+an experimental feature.
+Some file system mount
+time options are not well supported.
index 430873f..4176600 100644 (file)
@@ -1426,6 +1426,12 @@ vfs/hammer/hammer_undo.c optional hammer
 vfs/hammer/hammer_redo.c       optional hammer
 vfs/hammer/hammer_vfsops.c     optional hammer
 vfs/hammer/hammer_vnops.c      optional hammer
+#tmpfs static filesystem for debugging should be marked as optional tmpfs
+vfs/tmpfs/tmpfs_fifoops.c      standard
+vfs/tmpfs/tmpfs_subr.c         standard
+vfs/tmpfs/tmpfs_vfsops.c       standard
+vfs/tmpfs/tmpfs_vnops.c                standard
+#
 vm/default_pager.c             standard
 vm/device_pager.c              standard
 vm/phys_pager.c                        standard
index a78ee65..38b6379 100644 (file)
@@ -150,6 +150,7 @@ PORTAL              opt_dontuse.h
 PROCFS         opt_dontuse.h
 UDF            opt_dontuse.h
 NTFS           opt_dontuse.h
+#TMPFS         opt_dontuse.h
 
 # These static filesystems has one slightly bogus static dependency in
 # sys/platform/.../i386/autoconf.c.  If any of these filesystems are
@@ -161,6 +162,7 @@ FFS
 NFS
 NWFS
 USERFS
+TMPFS
 HAMMER
 
 # If you are following the conditions in the copyright,
index d474b58..385de25 100644 (file)
@@ -27,6 +27,7 @@ options       FFS_ROOT                #FFS usable as root device [keep this!]
 options        SOFTUPDATES             #Enable FFS soft updates support
 options        UFS_DIRHASH             #Improve performance on big directories
 options        MFS                     #Memory Filesystem
+options        TMPFS                   #Temporary Filesystem
 options        MD_ROOT                 #MD is a potential root device
 options        NFS                     #Network Filesystem
 options        NFS_ROOT                #NFS usable as root device, NFS required
index 00653d4..3976dec 100644 (file)
@@ -749,6 +749,7 @@ options     PROCFS                  #Process filesystem
 options        SMBFS                   #SMB/CIFS filesystem
 options                UDF                     #UDF filesystem
 options                HAMMER                  #HAMMER filesystem
+options        TMPFS                   #Temporary filesystem
 
 # YYY-DR Till we rework the VOP methods for this filesystem
 #options       UNION                   #Union filesystem
index 49b0271..ac67400 100644 (file)
@@ -51,6 +51,7 @@ options       FFS_ROOT                #FFS usable as root device [keep this!]
 options        SOFTUPDATES             #Enable FFS soft updates support
 options        UFS_DIRHASH             #Improve performance on big directories
 options        MFS                     #Memory Filesystem
+options        TMPFS                   #Temporary Filesystem
 options        MD_ROOT                 #MD is a potential root device
 options        NFS                     #Network Filesystem
 options        NFS_ROOT                #NFS usable as root device, NFS required
index dcc40e1..8ac2b32 100644 (file)
@@ -22,6 +22,7 @@ options       FFS_ROOT                #FFS usable as root device [keep this!]
 options        SOFTUPDATES             #Enable FFS soft updates support
 options        UFS_DIRHASH             #Improve performance on big directories
 options        MFS                     #Memory Filesystem
+options        TMPFS                   #Temporary Filesystem
 options        MD_ROOT                 #MD is a potential root device
 options        NFS                     #Network Filesystem
 options        NFS_ROOT                #NFS usable as root device, NFS required
index 80fef82..203e8bd 100644 (file)
@@ -52,6 +52,7 @@ COMM= ${SYS}/conf/*.[ch] \
        ${SYS}/emulation/posix4/*.[ch] \
        ${SYS}/vfs/mfs/*.[ch] \
        ${SYS}/vfs/ufs/*.[ch] \
+       ${SYS}/vfs/tmpfs/*.[ch] \
        ${SYS}/vm/*.[ch] \
        ${SYS}/sys/*.[ch]
 
@@ -91,4 +92,5 @@ COMMDIR2= ${SYS}/dev/disk/advansys \
        ${SYS}/vfs/specfs \
        ${SYS}/vfs/union \
        ${SYS}/vfs/ufs \
-       ${SYS}/vfs/mfs
+       ${SYS}/vfs/mfs \
+       ${SYS}/vfs/tmpfs
index 8b59c21..a55d8bd 100644 (file)
@@ -27,7 +27,7 @@ DGEN= conf \
        vfs vfs/deadfs vfs/fdesc vfs/fifofs \
        vfs/nullfs vfs/portal vfs/procfs \
        vfs/specfs vfs/union \
-       vfs/ufs vfs/mfs vfs/nfs \
+       vfs/ufs vfs/mfs vfs/nfs vfs/tmpfs \
        net netinet netns sys \
        vm
 
index 922e1a6..df0563a 100644 (file)
@@ -42,7 +42,7 @@ DGEN= conf \
        vfs vfs/deadfs vfs/fdesc vfs/fifofs \
        vfs/nullfs vfs/portal vfs/procfs \
        vfs/specfs vfs/union \
-       vfs/ufs vfs/mfs vfs/nfs \
+       vfs/ufs vfs/mfs vfs/nfs vfs/tmpfs \
        net netinet netns sys \
        vm
 
index 399a6ea..7d14dbe 100644 (file)
@@ -290,6 +290,7 @@ struct mount {
 #define MNTK_WR_MPSAFE 0x00040000      /* vop_write is MPSAFE */
 #define MNTK_GA_MPSAFE 0x00080000      /* vop_getattr is MPSAFE */
 #define MNTK_IN_MPSAFE 0x00100000      /* vop_inactive is MPSAFE */
+#define MNTK_SG_MPSAFE 0x00200000      /* vop_strategy is MPSAFE */
 #define MNTK_NCALIASED 0x00800000      /* namecached aliased */
 #define MNTK_UNMOUNT   0x01000000      /* unmount in progress */
 #define        MNTK_MWAIT      0x02000000      /* waiting for unmount to finish */
index e554098..bccc26f 100644 (file)
@@ -114,7 +114,7 @@ enum vtagtype       {
        VT_PORTAL, VT_NULL, VT_UNUSED10, VT_KERNFS, VT_PROCFS, VT_AFS,
        VT_ISOFS, VT_UNION, VT_MSDOSFS, VT_TFS, VT_VFS, VT_CODA, VT_NTFS,
        VT_HPFS, VT_NWFS, VT_SMBFS, VT_UDF, VT_EXT2FS, VT_SYNTH,
-       VT_USERFS, VT_HAMMER, VT_DEVFS
+       VT_USERFS, VT_HAMMER, VT_DEVFS, VT_TMPFS
 };
 
 /*
index 4771dbd..7cd1e26 100644 (file)
@@ -4,6 +4,6 @@
 
 SUBDIR=fifofs msdosfs portal gnu nfs procfs \
        hpfs ntfs smbfs isofs fdesc mfs nwfs udf \
-       nullfs hammer
+       nullfs hammer tmpfs
 
 .include <bsd.subdir.mk>
diff --git a/sys/vfs/tmpfs/Makefile b/sys/vfs/tmpfs/Makefile
new file mode 100644 (file)
index 0000000..8f05ea6
--- /dev/null
@@ -0,0 +1,7 @@
+
+KMOD=  tmpfs
+SRCS=  tmpfs_vnops.c tmpfs_subr.c \
+        tmpfs_fifoops.c tmpfs_vfsops.c
+NOMAN=
+
+.include <bsd.kmod.mk>
diff --git a/sys/vfs/tmpfs/tmpfs.h b/sys/vfs/tmpfs/tmpfs.h
new file mode 100644 (file)
index 0000000..73be143
--- /dev/null
@@ -0,0 +1,597 @@
+/*     $NetBSD: tmpfs.h,v 1.26 2007/02/22 06:37:00 thorpej 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.
+ *
+ * $FreeBSD: src/sys/fs/tmpfs/tmpfs.h,v 1.18 2009/10/11 07:03:56 delphij Exp $
+ */
+
+#ifndef _VFS_TMPFS_TMPFS_H_
+#define _VFS_TMPFS_TMPFS_H_
+
+/* ---------------------------------------------------------------------
+ * KERNEL-SPECIFIC DEFINITIONS
+ * --------------------------------------------------------------------- */
+#include <sys/dirent.h>
+#include <sys/mount.h>
+#include <sys/queue.h>
+#include <sys/vnode.h>
+#include <sys/file.h>
+#include <sys/lock.h>
+#include <sys/lockf.h>
+#include <sys/mutex.h>
+#include <sys/objcache.h>
+
+/* --------------------------------------------------------------------- */
+#include <sys/malloc.h>
+#include <sys/systm.h>
+#include <sys/vmmeter.h>
+#include <vm/swap_pager.h>
+
+MALLOC_DECLARE(M_TMPFSMNT);
+MALLOC_DECLARE(M_TMPFSNAME);
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * 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
+ * for 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. */
+#ifdef _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
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * 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.
+ *
+ *
+ * Below is the key of locks used to protected the fields in the following
+ * structures.
+ *
+ */
+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;
+       int32_t                 tn_atime;
+       int32_t                 tn_atimensec;
+       int32_t                 tn_mtime;
+       int32_t                 tn_mtimensec;
+       int32_t                 tn_ctime;
+       int32_t                 tn_ctimensec;
+       unsigned long           tn_gen;
+       struct lockf            tn_advlock;
+
+       /* 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). */
+       struct vnode *          tn_vnode;
+
+       /* interlock to protect tn_vpstate */
+       struct lock             tn_interlock;
+
+       /* Identify if current node has vnode assiocate with
+        * or allocating vnode.
+        */
+       int             tn_vpstate;
+
+       /* misc data field for different tn_type node */
+       union {
+               /* Valid when tn_type == VBLK || tn_type == VCHR. */
+               dev_t                   tn_rdev; /*int32_t ?*/
+
+               /* Valid when tn_type == VDIR. */
+               struct tn_dir{
+                       /* 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_dirhead;
+
+                       /* 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. */
+               /* The link's target, allocated from a string pool. */
+               char *                  tn_link;
+
+               /* 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. */
+                       vm_object_t             tn_aobj;
+                       size_t                  tn_aobj_pages;
+
+               }tn_reg;
+
+               /* Valid when tn_type = VFIFO */
+               struct tn_fifo {
+                       int (*tn_fo_read)  (struct file *fp, struct uio *uio,
+                               struct ucred *cred, int flags);
+                       int (*tn_fo_write) (struct file *fp, struct uio *uio,
+                               struct ucred *cred, int flags);
+               }tn_fifo;
+       }tn_spec;
+};
+LIST_HEAD(tmpfs_node_list, tmpfs_node);
+
+#define tn_rdev tn_spec.tn_rdev
+#define tn_dir tn_spec.tn_dir
+#define tn_link tn_spec.tn_link
+#define tn_reg tn_spec.tn_reg
+#define tn_fifo tn_spec.tn_fifo
+
+#define TMPFS_NODE_LOCK(node) lockmgr(&(node)->tn_interlock, LK_EXCLUSIVE|LK_RETRY)
+#define TMPFS_NODE_UNLOCK(node) lockmgr(&(node)->tn_interlock, LK_RELEASE)
+#define TMPFS_NODE_MTX(node) (&(node)->tn_interlock)
+
+#ifdef INVARIANTS
+#define TMPFS_ASSERT_LOCKED(node) do {                                 \
+               KKASSERT(node != NULL);                                 \
+               KKASSERT(node->tn_vnode != NULL);                       \
+               if (!vn_islocked(node->tn_vnode) &&                     \
+                   (lockstatus(TMPFS_NODE_MTX(node), curthread) == LK_EXCLUSIVE ))             \
+                       panic("tmpfs: node is not locked: %p", node);   \
+       } while (0)
+#define TMPFS_ASSERT_ELOCKED(node) do {                                        \
+               KKASSERT((node) != NULL);                               \
+               KKASSERT((node)->tn_vnode != NULL);                     \
+               KKASSERT(lockstatus(TMPFS_NODE_MTX(node), curthread) == LK_EXCLUSIVE);          \
+       } while (0)
+#else
+#define TMPFS_ASSERT_LOCKED(node) (void)0
+#define TMPFS_ASSERT_ELOCKED(node) (void)0
+#endif
+
+#define TMPFS_VNODE_ALLOCATING 1
+#define TMPFS_VNODE_WANT       2
+#define TMPFS_VNODE_DOOMED     4
+/* --------------------------------------------------------------------- */
+
+/*
+ * 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. */
+       size_t                  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. */
+       size_t                  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. */
+       ino_t                   tm_nodes_max;
+
+       /* Number of nodes currently that are in use. */
+       ino_t                   tm_nodes_inuse;
+
+       /* maximum representable file size */
+       u_int64_t               tm_maxfilesize;
+
+       /* Nodes are organized in two different lists.  The used list
+        * contains all nodes that are currently used by the file system;
+        * i.e., they refer to existing files.  The available list contains
+        * all nodes that are currently available for use by new files.
+        * Nodes must be kept in this list (instead of deleting them)
+        * because we need to keep track of their generation number (tn_gen
+        * field).
+        *
+        * Note that nodes are lazily allocated: if the available list is
+        * empty and we have enough space to create more nodes, they will be
+        * created and inserted in the used list.  Once these are released,
+        * they will go into the available list, remaining alive until the
+        * file system is unmounted. */
+       struct tmpfs_node_list  tm_nodes_used;
+
+       /* All node lock to protect the node list and tmp_pages_used */
+       struct lock              allnode_lock;
+
+       /* 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 objcache         *tm_dirent_pool;
+       struct objcache         *tm_node_pool;
+};
+#define TMPFS_LOCK(tm) lockmgr(&(tm)->allnode_lock, LK_EXCLUSIVE|LK_RETRY)
+#define TMPFS_UNLOCK(tm) lockmgr(&(tm)->allnode_lock, LK_RELEASE)
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * 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;
+       ino_t                   tf_id;
+       unsigned long           tf_gen;
+};
+
+/* --------------------------------------------------------------------- */
+
+#ifdef _KERNEL
+/*
+ * 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 *, int,
+           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 *node,
+                           struct tmpfs_node *f,
+                           struct namecache *ncp);
+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, int);
+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 *, struct timespec *, struct timespec *,
+           int, struct ucred *);
+void   tmpfs_itimes(struct vnode *, const struct timespec *,
+           const struct timespec *);
+
+void   tmpfs_update(struct vnode *);
+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 && \
+    bcmp((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_dir.tn_readdir_lastp == NULL || \
+       tmpfs_dircookie((node)->tn_dir.tn_readdir_lastp) == (node)->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.
+ *
+ */
+static __inline size_t
+tmpfs_mem_info(void)
+{
+       size_t size;
+
+       size = vm_swap_size + 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();
+       freepages -= freepages < TMPFS_PAGES_RESERVED ?
+           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_META_PAGES(tmp) (howmany((tmp)->tm_nodes_inuse * (sizeof(struct tmpfs_node) \
+                               + sizeof(struct tmpfs_dirent)), PAGE_SIZE))
+#define TMPFS_FILE_PAGES(tmp) ((tmp)->tm_pages_used)
+
+#define TMPFS_PAGES_AVAIL(tmp) (TMPFS_PAGES_MAX(tmp) > \
+                       TMPFS_META_PAGES(tmp)+TMPFS_FILE_PAGES(tmp)? \
+                       TMPFS_PAGES_MAX(tmp) - TMPFS_META_PAGES(tmp) \
+                       - TMPFS_FILE_PAGES(tmp):0)
+
+#endif
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * 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;
+
+       KKASSERT((mp) != NULL && (mp)->mnt_data != NULL);
+       tmp = (struct tmpfs_mount *)(mp)->mnt_data;
+       return tmp;
+}
+
+static inline
+struct tmpfs_node *
+VP_TO_TMPFS_NODE(struct vnode *vp)
+{
+       struct tmpfs_node *node;
+
+       KKASSERT((vp) != NULL && (vp)->v_data != NULL);
+       node = (struct tmpfs_node *)vp->v_data;
+       return node;
+}
+
+static inline
+struct tmpfs_node *
+VP_TO_TMPFS_DIR(struct vnode *vp)
+{
+       struct tmpfs_node *node;
+
+       node = VP_TO_TMPFS_NODE(vp);
+       TMPFS_VALIDATE_DIR(node);
+       return node;
+}
+
+/* --------------------------------------------------------------------- */
+/*
+ * buffer cache size
+ */
+#define BSIZE (off_t)16384          /* buffer cache size*/
+#define BMASK (off_t)(BSIZE - 1)
+
+#endif /* _VFS_TMPFS_TMPFS_H_ */
diff --git a/sys/vfs/tmpfs/tmpfs_args.h b/sys/vfs/tmpfs/tmpfs_args.h
new file mode 100644 (file)
index 0000000..6601ebd
--- /dev/null
@@ -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 _VFS_TMPFS_TMPFS_ARGS_H_
+#define _VFS_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 /* _VFS_TMPFS_TMPFS_ARGS_H_ */
diff --git a/sys/vfs/tmpfs/tmpfs_fifoops.c b/sys/vfs/tmpfs/tmpfs_fifoops.c
new file mode 100644 (file)
index 0000000..0abdb26
--- /dev/null
@@ -0,0 +1,99 @@
+/*     $NetBSD: tmpfs_fifoops.c,v 1.5 2005/12/11 12:24:29 christos Exp $       */
+
+/*-
+ * Copyright (c) 2005 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 for named pipes.
+ */
+#include <sys/cdefs.h>
+
+#include <sys/kernel.h>
+#include <sys/param.h>
+#include <sys/filedesc.h>
+#include <sys/proc.h>
+#include <sys/vnode.h>
+
+#include <vm/vm.h>
+#include <vm/vm_object.h>
+
+#include <vfs/fifofs/fifo.h>
+#include <vfs/tmpfs/tmpfs.h>
+#include <vfs/tmpfs/tmpfs_fifoops.h>
+#include <vfs/tmpfs/tmpfs_vnops.h>
+
+/* --------------------------------------------------------------------- */
+
+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 fifo_vnode_vops.vop_kqfilter(ap);
+}
+
+/* --------------------------------------------------------------------- */
+
+static int
+tmpfs_fifo_close(struct vop_close_args *v)
+{
+       struct tmpfs_node *node;
+       node = VP_TO_TMPFS_NODE(v->a_vp);
+       node->tn_status |= TMPFS_NODE_ACCESSED;
+
+       tmpfs_update(v->a_vp);
+       return fifo_vnode_vops.vop_close(v);
+}
+
+/*
+ * vnode operations vector used for fifos stored in a tmpfs file system.
+ */
+struct vop_ops tmpfs_fifo_vops = {
+       .vop_default =                  fifo_vnoperate,
+       .vop_close =                    tmpfs_fifo_close,
+       .vop_reclaim =                  tmpfs_reclaim,
+       .vop_access =                   tmpfs_access,
+       .vop_getattr =                  tmpfs_getattr,
+       .vop_setattr =                  tmpfs_setattr,
+       .vop_kqfilter =                 tmpfs_fifo_kqfilter,
+};
diff --git a/sys/vfs/tmpfs/tmpfs_fifoops.h b/sys/vfs/tmpfs/tmpfs_fifoops.h
new file mode 100644 (file)
index 0000000..767dd72
--- /dev/null
@@ -0,0 +1,53 @@
+/*     $NetBSD: tmpfs_fifoops.h,v 1.4 2005/12/03 17:34:44 christos Exp $       */
+
+/*-
+ * Copyright (c) 2005 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.
+ *
+ * $FreeBSD: src/sys/fs/tmpfs/tmpfs_fifoops.h,v 1.4 2008/09/03 18:53:48 delphij Exp $
+ */
+
+#ifndef _VFS_TMPFS_TMPFS_FIFOOPS_H_
+#define _VFS_TMPFS_TMPFS_FIFOOPS_H_
+
+#if !defined(_KERNEL)
+#error not supposed to be exposed to userland.
+#endif
+
+#include <vfs/tmpfs/tmpfs_vnops.h>
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Declarations for tmpfs_fifoops.c.
+ */
+
+extern struct vop_vector tmpfs_fifoop_entries;
+
+/* --------------------------------------------------------------------- */
+#endif /* _VFS_TMPFS_TMPFS_FIFOOPS_H_ */
diff --git a/sys/vfs/tmpfs/tmpfs_subr.c b/sys/vfs/tmpfs/tmpfs_subr.c
new file mode 100644 (file)
index 0000000..371c103
--- /dev/null
@@ -0,0 +1,1414 @@
+/*     $NetBSD: tmpfs_subr.c,v 1.35 2007/07/09 21:10:50 ad Exp $       */
+
+/*-
+ * Copyright (c) 2005 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>
+
+#include <sys/kernel.h>
+#include <sys/param.h>
+#include <sys/namei.h>
+#include <sys/priv.h>
+#include <sys/proc.h>
+#include <sys/spinlock2.h>
+#include <sys/stat.h>
+#include <sys/systm.h>
+#include <sys/vnode.h>
+#include <sys/vmmeter.h>
+
+#include <sys/mplock2.h>
+
+#include <vm/vm.h>
+#include <vm/vm_object.h>
+#include <vm/vm_page.h>
+#include <vm/vm_pager.h>
+#include <vm/vm_extern.h>
+
+#include <vfs/tmpfs/tmpfs.h>
+#include <vfs/tmpfs/tmpfs_fifoops.h>
+#include <vfs/tmpfs/tmpfs_vnops.h>
+
+static ino_t t_ino = 2;
+static struct spinlock ino_lock;
+static ino_t tmpfs_fetch_ino(void);
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * 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;
+       struct timespec ts;
+       udev_t rdev;
+
+       /* 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));
+
+       if (tmp->tm_nodes_inuse > tmp->tm_nodes_max)
+               return (ENOSPC);
+
+       nnode = (struct tmpfs_node *)objcache_get(tmp->tm_node_pool, M_WAITOK);
+
+       /* Generic initialization. */
+       nnode->tn_type = type;
+       vfs_timestamp(&ts);
+       nnode->tn_ctime = nnode->tn_mtime = nnode->tn_atime
+               = ts.tv_sec;
+       nnode->tn_ctimensec = nnode->tn_mtimensec = nnode->tn_atimensec
+               = ts.tv_nsec;
+       nnode->tn_uid = uid;
+       nnode->tn_gid = gid;
+       nnode->tn_mode = mode;
+       nnode->tn_id = tmpfs_fetch_ino();
+
+       /* Type-specific initialization. */
+       switch (nnode->tn_type) {
+       case VBLK:
+       case VCHR:
+               rdev = makeudev(rmajor, rminor);
+               if (rdev == NOUDEV) {
+                       return(EINVAL);
+               }
+               nnode->tn_rdev = rdev;
+               break;
+
+       case VDIR:
+               TAILQ_INIT(&nnode->tn_dir.tn_dirhead);
+               KKASSERT(parent != nnode);
+               KKASSERT(IMPLIES(parent == NULL, tmp->tm_root == NULL));
+               nnode->tn_dir.tn_parent = (parent == NULL) ? nnode : parent;
+               nnode->tn_dir.tn_readdir_lastn = 0;
+               nnode->tn_dir.tn_readdir_lastp = NULL;
+               nnode->tn_links++;
+               nnode->tn_size = 0;
+               TMPFS_NODE_LOCK(nnode->tn_dir.tn_parent);
+               nnode->tn_dir.tn_parent->tn_links++;
+               TMPFS_NODE_UNLOCK(nnode->tn_dir.tn_parent);
+               break;
+
+       case VFIFO:
+               /* FALLTHROUGH */
+       case VSOCK:
+               break;
+
+       case VLNK:
+               KKASSERT((strlen(target) +1) < MAXPATHLEN);
+               nnode->tn_size = strlen(target) +1;
+               nnode->tn_link = kmalloc(nnode->tn_size, M_TMPFSNAME,
+                   M_WAITOK);
+               bcopy(target, nnode->tn_link, nnode->tn_size);
+               nnode->tn_link[nnode->tn_size] = '\0';
+               break;
+
+       case VREG:
+               nnode->tn_reg.tn_aobj =
+                   vm_pager_allocate(OBJT_SWAP, NULL, 0, VM_PROT_DEFAULT, 0);
+               nnode->tn_reg.tn_aobj_pages = 0;
+               nnode->tn_size = 0;
+               break;
+
+       default:
+               panic("tmpfs_alloc_node: type %p %d", nnode, (int)nnode->tn_type);
+       }
+
+       TMPFS_NODE_LOCK(nnode);
+       LIST_INSERT_HEAD(&tmp->tm_nodes_used, nnode, tn_entries);
+       TMPFS_LOCK(tmp);
+       tmp->tm_nodes_inuse++;
+       TMPFS_UNLOCK(tmp);
+       TMPFS_NODE_UNLOCK(nnode);
+
+       *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;
+
+       TMPFS_NODE_LOCK(node);
+
+#ifdef INVARIANTS
+       TMPFS_ASSERT_ELOCKED(node);
+       KKASSERT(node->tn_vnode == NULL);
+       KKASSERT((node->tn_vpstate & TMPFS_VNODE_ALLOCATING) == 0);
+#endif
+
+       LIST_REMOVE(node, tn_entries);
+       TMPFS_LOCK(tmp);
+       tmp->tm_nodes_inuse--;
+       TMPFS_UNLOCK(tmp);
+
+       switch (node->tn_type) {
+       case VNON:
+               /* Do not do anything.  VNON is provided to let the
+                * allocation routine clean itself easily by avoiding
+                * duplicating code in it. */
+               /* FALLTHROUGH */
+       case VBLK:
+               /* FALLTHROUGH */
+       case VCHR:
+               /* FALLTHROUGH */
+               break;
+       case VDIR:
+               node->tn_links--;
+               node->tn_size = 0;
+               TMPFS_NODE_LOCK(node->tn_dir.tn_parent);
+               node->tn_dir.tn_parent->tn_links--;
+               TMPFS_NODE_UNLOCK(node->tn_dir.tn_parent);
+               break;
+       case VFIFO:
+               /* FALLTHROUGH */
+       case VSOCK:
+               break;
+
+       case VLNK:
+               kfree(node->tn_link, M_TMPFSNAME);
+               break;
+
+       case VREG:
+               if (node->tn_reg.tn_aobj != NULL)
+                       vm_pager_deallocate(node->tn_reg.tn_aobj);
+               node->tn_reg.tn_aobj = NULL;
+               pages = node->tn_reg.tn_aobj_pages;
+               break;
+
+       default:
+               panic("tmpfs_free_node: type %p %d", node, (int)node->tn_type);
+       }
+
+       objcache_put(tmp->tm_node_pool, node);
+
+       TMPFS_LOCK(tmp);
+       tmp->tm_pages_used -= pages;
+       TMPFS_UNLOCK(tmp);
+       TMPFS_NODE_UNLOCK(node);
+}
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * 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.
+ *
+ * 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;
+
+
+       nde = (struct tmpfs_dirent *)objcache_get(tmp->tm_dirent_pool, M_WAITOK);
+       nde->td_name = kmalloc(len, M_TMPFSNAME, M_WAITOK);
+       nde->td_namelen = len;
+       bcopy(name, nde->td_name, len);
+       nde->td_name[len] = '\0';
+
+       nde->td_node = node;
+
+       TMPFS_NODE_LOCK(node);
+       node->tn_links++;
+       TMPFS_NODE_UNLOCK(node);
+
+       *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.
+ */
+void
+tmpfs_free_dirent(struct tmpfs_mount *tmp, struct tmpfs_dirent *de,
+    boolean_t node_exists)
+{
+       if (node_exists) {
+               struct tmpfs_node *node;
+
+               node = de->td_node;
+
+               TMPFS_NODE_LOCK(node);
+               TMPFS_ASSERT_ELOCKED(node);
+               KKASSERT(node->tn_links > 0);
+               node->tn_links--;
+               TMPFS_NODE_UNLOCK(node);
+       }
+
+       kfree(de->td_name, M_TMPFSNAME);
+       objcache_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, int lkflag,
+    struct vnode **vpp)
+{
+       int error = 0;
+       struct vnode *vp;
+
+loop:
+       TMPFS_NODE_LOCK(node);
+       if ((vp = node->tn_vnode) != NULL) {
+               KKASSERT((node->tn_vpstate & TMPFS_VNODE_DOOMED) == 0);
+               TMPFS_NODE_UNLOCK(node);
+               (void) vget(vp, lkflag | LK_EXCLUSIVE | LK_RETRY);
+
+               /*
+                * Make sure the vnode is still there after
+                * getting the interlock to avoid racing a free.
+                */
+               if (node->tn_vnode == NULL || node->tn_vnode != vp) {
+                       vput(vp);
+                       goto loop;
+               }
+
+               goto out;
+       }
+
+       if ((node->tn_vpstate & TMPFS_VNODE_DOOMED) ||
+           (node->tn_type == VDIR && node->tn_dir.tn_parent == NULL)) {
+               TMPFS_NODE_UNLOCK(node);
+               error = ENOENT;
+               vp = NULL;
+               goto out;
+       }
+
+       /*
+        * otherwise lock the vp list while we call getnewvnode
+        * since that can block.
+        */
+       if (node->tn_vpstate & TMPFS_VNODE_ALLOCATING) {
+               node->tn_vpstate |= TMPFS_VNODE_WANT;
+               error = tsleep((caddr_t) &node->tn_vpstate,
+                   PINTERLOCKED | PCATCH, "tmpfs_alloc_vp", 0);
+               TMPFS_NODE_UNLOCK(node);
+               if (error)
+                       return error;
+
+               goto loop;
+       } else
+               node->tn_vpstate |= TMPFS_VNODE_ALLOCATING;
+
+       TMPFS_NODE_UNLOCK(node);
+
+       /* Get a new vnode and associate it with our node. */
+       error = getnewvnode(VT_TMPFS, mp, &vp, VLKTIMEOUT, LK_CANRECURSE);
+       if (error != 0)
+               goto unlock;
+       KKASSERT(vp != NULL);
+
+       vp->v_data = node;
+       vp->v_type = node->tn_type;
+
+       /* Type-specific initialization. */
+       switch (node->tn_type) {
+       case VBLK:
+               /* FALLTHROUGH */
+       case VCHR:
+               /* FALLTHROUGH */
+       case VSOCK:
+               break;
+       case VREG:
+               vinitvmio(vp, node->tn_size);
+               break;
+       case VLNK:
+               if (node->tn_size >= vp->v_mount->mnt_maxsymlinklen)
+                       vinitvmio(vp, node->tn_size);
+               break;
+       case VFIFO:
+               vp->v_ops = &mp->mnt_vn_fifo_ops;
+               break;
+       case VDIR:
+               vinitvmio(vp, node->tn_size);
+               KKASSERT(node->tn_dir.tn_parent != NULL);
+               break;
+
+       default:
+               panic("tmpfs_alloc_vp: type %p %d", node, (int)node->tn_type);
+       }
+
+       insmntque(vp, mp);
+
+unlock:
+       TMPFS_NODE_LOCK(node);
+
+       KKASSERT(node->tn_vpstate & TMPFS_VNODE_ALLOCATING);
+       node->tn_vpstate &= ~TMPFS_VNODE_ALLOCATING;
+       node->tn_vnode = vp;
+
+       if (node->tn_vpstate & TMPFS_VNODE_WANT) {
+               node->tn_vpstate &= ~TMPFS_VNODE_WANT;
+               TMPFS_NODE_UNLOCK(node);
+               wakeup((caddr_t) &node->tn_vpstate);
+       } else
+               TMPFS_NODE_UNLOCK(node);
+
+out:
+       *vpp = vp;
+
+       KKASSERT(IFF(error == 0, *vpp != NULL && vn_islocked(*vpp)));
+#ifdef INVARIANTS
+       TMPFS_NODE_LOCK(node);
+       KKASSERT(*vpp == node->tn_vnode);
+       TMPFS_NODE_UNLOCK(node);
+#endif
+
+       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;
+
+       node = VP_TO_TMPFS_NODE(vp);
+
+       TMPFS_NODE_LOCK(node);
+       KKASSERT(lockcount(TMPFS_NODE_MTX(node)) > 0);
+       node->tn_vnode = NULL;
+       TMPFS_NODE_UNLOCK(node);
+       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;
+
+       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) {
+                       return EMLINK;
+               }
+
+               parent = dnode;
+               KKASSERT(parent != NULL);
+       } else
+               parent = NULL;
+
+       /* Allocate a node that represents the new file. */
+       error = tmpfs_alloc_node(tmp, vap->va_type, cred->cr_uid,
+           dnode->tn_gid, vap->va_mode, parent, target, vap->va_rmajor, vap->va_rminor, &node);
+       if (error != 0)
+               return error;
+
+       /* 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);
+               return error;
+       }
+
+       /* Allocate a vnode for the new file. */
+       error = tmpfs_alloc_vp(dvp->v_mount, node, LK_EXCLUSIVE, vpp);
+       if (error != 0) {
+               tmpfs_free_dirent(tmp, de, TRUE);
+               tmpfs_free_node(tmp, node);
+               return error;
+       }
+
+       /* 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);
+
+       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.
+ */
+void
+tmpfs_dir_attach(struct vnode *vp, struct tmpfs_dirent *de)
+{
+       struct tmpfs_node *dnode;
+
+       dnode = VP_TO_TMPFS_DIR(vp);
+
+       crit_enter();
+       TAILQ_INSERT_TAIL(&dnode->tn_dir.tn_dirhead, de, td_entries);
+       crit_exit();
+
+       TMPFS_NODE_LOCK(dnode);
+       TMPFS_ASSERT_ELOCKED(dnode);
+       dnode->tn_size += sizeof(struct tmpfs_dirent);
+       dnode->tn_status |= TMPFS_NODE_ACCESSED | TMPFS_NODE_CHANGED | \
+           TMPFS_NODE_MODIFIED;
+       TMPFS_NODE_UNLOCK(dnode);
+}
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * 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.
+ */
+void
+tmpfs_dir_detach(struct vnode *vp, struct tmpfs_dirent *de)
+{
+       struct tmpfs_node *dnode;
+
+       dnode = VP_TO_TMPFS_DIR(vp);
+
+
+       crit_enter();
+       if (dnode->tn_dir.tn_readdir_lastp == de) {
+               dnode->tn_dir.tn_readdir_lastn = 0;
+               dnode->tn_dir.tn_readdir_lastp = NULL;
+       }
+
+       TAILQ_REMOVE(&dnode->tn_dir.tn_dirhead, de, td_entries);
+       crit_exit();
+
+       TMPFS_NODE_LOCK(dnode);
+       TMPFS_ASSERT_ELOCKED(dnode);
+       dnode->tn_size -= sizeof(struct tmpfs_dirent);
+       dnode->tn_status |= TMPFS_NODE_ACCESSED | TMPFS_NODE_CHANGED | \
+           TMPFS_NODE_MODIFIED;
+       TMPFS_NODE_UNLOCK(dnode);
+}
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Looks for a directory entry in the directory represented by node.
+ * 'ncp' 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 tmpfs_node *f,
+    struct namecache *ncp)
+{
+       void *found;
+       struct tmpfs_dirent *de;
+       int len = ncp->nc_nlen;
+
+       TMPFS_VALIDATE_DIR(node);
+
+       found = 0;
+       TAILQ_FOREACH(de, &node->tn_dir.tn_dirhead, td_entries) {
+               if (f != NULL && de->td_node != f)
+                   continue;
+               if (len == de->td_namelen) {
+                       if (!memcmp(ncp->nc_name, de->td_name, len)) {
+                               found = node;
+                               break;
+                       }
+               }
+       }
+
+       TMPFS_NODE_LOCK(node);
+       node->tn_status |= TMPFS_NODE_ACCESSED;
+       TMPFS_NODE_UNLOCK(node);
+
+       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 dirsize;
+
+       TMPFS_VALIDATE_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';
+       dirsize = _DIRENT_DIRSIZ(&dent);
+
+       if (dirsize > uio->uio_resid)
+               error = -1;
+       else {
+               error = uiomove((caddr_t)&dent, dirsize, 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;
+       struct dirent dent;
+       int dirsize;
+
+       TMPFS_VALIDATE_DIR(node);
+       KKASSERT(uio->uio_offset == TMPFS_DIRCOOKIE_DOTDOT);
+
+       /*
+        * Return ENOENT if the current node is already removed.
+        */
+       if (node->tn_dir.tn_parent == NULL) {
+               return (ENOENT);
+       }
+
+       TMPFS_NODE_LOCK(node->tn_dir.tn_parent);
+       dent.d_ino = node->tn_dir.tn_parent->tn_id;
+       TMPFS_NODE_UNLOCK(node->tn_dir.tn_parent);
+
+       dent.d_type = DT_DIR;
+       dent.d_namlen = 2;
+       dent.d_name[0] = '.';
+       dent.d_name[1] = '.';
+       dent.d_name[2] = '\0';
+       dirsize = _DIRENT_DIRSIZ(&dent);
+
+       if (dirsize > uio->uio_resid)
+               error = -1;
+       else {
+               error = uiomove((caddr_t)&dent, dirsize, uio);
+               if (error == 0) {
+                       struct tmpfs_dirent *de;
+
+                       de = TAILQ_FIRST(&node->tn_dir.tn_dirhead);
+                       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;
+
+       if (cookie == node->tn_dir.tn_readdir_lastn &&
+           node->tn_dir.tn_readdir_lastp != NULL) {
+               return node->tn_dir.tn_readdir_lastp;
+       }
+
+       TAILQ_FOREACH(de, &node->tn_dir.tn_dirhead, 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 tmpfs_dirent *de;
+
+       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 {
+               struct dirent d;
+               int reclen;
+
+               /* Create a dirent structure representing the current
+                * tmpfs_node and fill it. */
+               d.d_ino = de->td_node->tn_id;
+               switch (de->td_node->tn_type) {
+               case VBLK:
+                       d.d_type = DT_BLK;
+                       break;
+
+               case VCHR:
+                       d.d_type = DT_CHR;
+                       break;
+
+               case VDIR:
+                       d.d_type = DT_DIR;
+                       break;
+
+               case VFIFO:
+                       d.d_type = DT_FIFO;
+                       break;
+
+               case VLNK:
+                       d.d_type = DT_LNK;
+                       break;
+
+               case VREG:
+                       d.d_type = DT_REG;
+                       break;
+
+               case VSOCK:
+                       d.d_type = DT_SOCK;
+                       break;
+
+               default:
+                       panic("tmpfs_dir_getdents: type %p %d",
+                           de->td_node, (int)de->td_node->tn_type);
+               }
+               d.d_namlen = de->td_namelen;
+               KKASSERT(de->td_namelen < sizeof(d.d_name));
+               bcopy(de->td_name, d.d_name, d.d_namlen);
+               d.d_name[d.d_namlen] = '\0';
+               reclen = _DIRENT_RECLEN(d.d_namlen);
+
+               /* Stop reading if the directory entry we are treating is
+                * bigger than the amount of data that can be returned. */
+               if (reclen > uio->uio_resid) {
+                       error = -1;
+                       break;
+               }
+
+               /* Copy the new dirent structure into the output buffer and
+                * advance pointers. */
+               error = uiomove((caddr_t)&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_dir.tn_readdir_lastn = 0;
+               node->tn_dir.tn_readdir_lastp = NULL;
+       } else {
+               node->tn_dir.tn_readdir_lastn = uio->uio_offset = tmpfs_dircookie(de);
+               node->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.
+ *
+ * pass trivial as 1 when buf content will be overwritten, otherwise set 0
+ * to be zero filled.
+ *
+ * Returns zero on success or an appropriate error code on failure.
+ */
+int
+tmpfs_reg_resize(struct vnode *vp, off_t newsize, int trivial)
+{
+       int error;
+       size_t newpages, oldpages;
+       struct tmpfs_mount *tmp;
+       struct tmpfs_node *node;
+       off_t oldsize;
+       int biosize = vp->v_mount->mnt_stat.f_iosize;
+
+#ifdef INVARIANTS
+       KKASSERT(vp->v_type == VREG);
+       KKASSERT(newsize >= 0);
+#endif
+
+       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_reg.tn_aobj_pages);
+       newpages = round_page(newsize) / PAGE_SIZE;
+
+       if (newpages > oldpages &&
+           newpages - oldpages > TMPFS_PAGES_AVAIL(tmp)) {
+               error = ENOSPC;
+               goto out;
+       }
+
+       TMPFS_LOCK(tmp);
+       tmp->tm_pages_used += (newpages - oldpages);
+       TMPFS_UNLOCK(tmp);
+
+       TMPFS_NODE_LOCK(node);
+       node->tn_reg.tn_aobj_pages = newpages;
+       node->tn_size = newsize;
+       TMPFS_NODE_UNLOCK(node);
+
+       if (newsize < oldsize)
+               error = nvtruncbuf(vp, newsize, biosize, -1);
+       else
+               error = nvextendbuf(vp, oldsize, newsize, biosize, biosize,
+                                   -1, -1, trivial);
+
+out:
+       return error;
+}
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * 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;
+       int fmode, mode;
+
+       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;
+
+       fmode = FFLAGS(node->tn_flags);
+       mode = 0;
+       if (((fmode & (FREAD | FWRITE)) == 0) || (fmode & O_CREAT))
+               return EINVAL;
+       if (fmode & (FWRITE | O_TRUNC)) {
+               if (vp->v_type == VDIR) {
+                       return EISDIR;
+               }
+               error = vn_writechk(vp, NULL);
+               if (error)
+                       return (error);
+
+               mode |= VWRITE;
+       }
+       if (fmode & FREAD)
+               mode |= VREAD;
+       if (mode) {
+               error = VOP_ACCESS(vp, mode, cred);
+               if (error)
+                       return (error);
+       }
+       /*
+        * Unprivileged processes are not permitted to unset system
+        * flags, or modify flags if any system flags are set.
+        */
+       TMPFS_NODE_LOCK(node);
+       if (!priv_check_cred(cred, PRIV_VFS_SYSFLAGS, 0)) {
+#if 0
+               if (node->tn_flags
+                 & (SF_NOUNLINK | SF_IMMUTABLE | SF_APPEND)) {
+                       error = securelevel_gt(cred, 0);
+                       if (error)
+                               return (error);
+               }
+               /* Snapshot flag cannot be set or cleared */
+               if (((flags & SF_SNAPSHOT) != 0 &&
+                 (node->tn_flags & SF_SNAPSHOT) == 0) ||
+                 ((flags & SF_SNAPSHOT) == 0 &&
+                 (node->tn_flags & SF_SNAPSHOT) != 0))
+                       return (EPERM);
+#endif
+               node->tn_flags = flags;
+       } else {
+               if (node->tn_flags
+                 & (SF_NOUNLINK | SF_IMMUTABLE | SF_APPEND) ||
+                 (flags & UF_SETTABLE) != flags)
+                       return (EPERM);
+               node->tn_flags &= SF_SETTABLE;
+               node->tn_flags |= (flags & UF_SETTABLE);
+       }
+       node->tn_status |= TMPFS_NODE_CHANGED;
+       TMPFS_NODE_UNLOCK(node);
+
+       KKASSERT(vn_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;
+       struct tmpfs_node *node;
+       int fmode, accmode;
+
+       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;
+
+       /* Immutable or append-only files cannot be modified, either. */
+       if (node->tn_flags & (IMMUTABLE | APPEND))
+               return EPERM;
+
+       fmode = FFLAGS(node->tn_flags);
+       accmode = 0;
+       if (((fmode & (FREAD | FWRITE)) == 0) || (fmode & O_CREAT))
+               return EINVAL;
+       if (fmode & (FWRITE | O_TRUNC)) {
+               if (vp->v_type == VDIR) {
+                       return EISDIR;
+               }
+               error = vn_writechk(vp, NULL);
+               if (error)
+                       return (error);
+
+               accmode |= VWRITE;
+       }
+       if (fmode & FREAD)
+               accmode |= VREAD;
+       if (accmode) {
+               error = VOP_ACCESS(vp, accmode, cred);
+               if (error)
+                       return (error);
+       }
+
+       /*
+        * Privileged processes may set the sticky bit on non-directories,
+        * as well as set the setgid bit on a file with a group that the
+        * process is not a member of.
+        */
+       if (vp->v_type != VDIR && (mode & S_ISTXT)) {
+               if (priv_check_cred(cred, PRIV_VFS_STICKYFILE, 0))
+                       return (EFTYPE);
+       }
+       if (!groupmember(node->tn_gid, cred) && (mode & S_ISGID)) {
+               error = priv_check_cred(cred, PRIV_VFS_SETGID, 0);
+               if (error)
+                       return (error);
+       }
+
+
+       TMPFS_NODE_LOCK(node);
+       node->tn_mode &= ~ALLPERMS;
+       node->tn_mode |= mode & ALLPERMS;
+
+       node->tn_status |= TMPFS_NODE_CHANGED;
+       TMPFS_NODE_UNLOCK(node);
+
+       KKASSERT(vn_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;
+       struct tmpfs_node *node;
+       uid_t ouid;
+       gid_t ogid;
+       int fmode, mode;
+
+       KKASSERT(vn_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;
+
+       fmode = FFLAGS(node->tn_flags);
+       mode = 0;
+       if (((fmode & (FREAD | FWRITE)) == 0) || (fmode & O_CREAT))
+               return EINVAL;
+       if (fmode & (FWRITE | O_TRUNC)) {
+               if (vp->v_type == VDIR) {
+                       return EISDIR;
+               }
+               error = vn_writechk(vp, NULL);
+               if (error)
+                       return (error);
+
+               mode |= VWRITE;
+       }
+       if (fmode & FREAD)
+               mode |= VREAD;
+       if (mode) {
+               error = VOP_ACCESS(vp, mode, cred);
+               if (error)
+                       return (error);
+       }
+
+       /*
+        * To change the owner of a file, or change the group of a file to a
+        * group of which we are not a member, the caller must have
+        * privilege.
+        */
+       if ((uid != node->tn_uid ||
+           (gid != node->tn_gid && !groupmember(gid, cred))) &&
+           (error = priv_check_cred(cred, PRIV_VFS_CHOWN, 0)))
+               return (error);
+
+       ogid = node->tn_gid;
+       ouid = node->tn_uid;
+
+       TMPFS_NODE_LOCK(node);
+       node->tn_uid = uid;
+       node->tn_gid = gid;
+
+       node->tn_status |= TMPFS_NODE_CHANGED;
+
+       if ((node->tn_mode & (S_ISUID | S_ISGID)) && (ouid != uid || ogid != gid)) {
+               if (priv_check_cred(cred, PRIV_VFS_RETAINSUGID, 0))
+                       node->tn_mode &= ~(S_ISUID | S_ISGID);
+       }
+       TMPFS_NODE_UNLOCK(node);
+
+       KKASSERT(vn_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;
+
+       KKASSERT(vn_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(vn_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, struct timespec *atime, struct timespec *mtime,
+       int vaflags, struct ucred *cred)
+{
+       int error;
+       struct tmpfs_node *node;
+       int fmode, mode;
+
+       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;
+
+       /* Immutable or append-only files cannot be modified, either. */
+       if (node->tn_flags & (IMMUTABLE | APPEND))
+               return EPERM;
+
+       /* Determine if the user have proper privilege to update time. */
+       fmode = FFLAGS(node->tn_flags);
+       mode = 0;
+       if (((fmode & (FREAD | FWRITE)) == 0) || (fmode & O_CREAT))
+               return EINVAL;
+       if (fmode & (FWRITE | O_TRUNC)) {
+               if (vp->v_type == VDIR) {
+                       return EISDIR;
+               }
+               error = vn_writechk(vp, NULL);
+               if (error)
+                       return (error);
+
+               mode |= VWRITE;
+       }
+       if (fmode & FREAD)
+               mode |= VREAD;
+
+       if (mode) {
+               if (vaflags & VA_UTIMES_NULL) {
+                       error = VOP_ACCESS(vp, mode, cred);
+                       if (error)
+                               error = VOP_ACCESS(vp, VWRITE, cred);
+               } else
+                       error = VOP_ACCESS(vp, mode, cred);
+               if (error)
+                       return (error);
+       }
+
+       TMPFS_NODE_LOCK(node);
+       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;
+
+       TMPFS_NODE_UNLOCK(node);
+
+       tmpfs_itimes(vp, atime, mtime);
+
+       KKASSERT(vn_islocked(vp));
+
+       return 0;
+}
+
+/* --------------------------------------------------------------------- */
+/* Sync timestamps */
+void
+tmpfs_itimes(struct vnode *vp, const struct timespec *acc,
+    const struct timespec *mod)
+{
+       struct tmpfs_node *node;
+       struct timespec now;
+
+       node = VP_TO_TMPFS_NODE(vp);
+
+       if ((node->tn_status & (TMPFS_NODE_ACCESSED | TMPFS_NODE_MODIFIED |
+           TMPFS_NODE_CHANGED)) == 0)
+               return;
+
+       vfs_timestamp(&now);
+
+       TMPFS_NODE_LOCK(node);
+       if (node->tn_status & TMPFS_NODE_ACCESSED) {
+               if (acc == NULL)
+                        acc = &now;
+               node->tn_atime = acc->tv_sec;
+               node->tn_atimensec = acc->tv_nsec;
+       }
+       if (node->tn_status & TMPFS_NODE_MODIFIED) {
+               if (mod == NULL)
+                       mod = &now;
+               node->tn_mtime = mod->tv_sec;
+               node->tn_mtimensec = mod->tv_nsec;
+       }
+       if (node->tn_status & TMPFS_NODE_CHANGED) {
+               node->tn_ctime = now.tv_sec;
+               node->tn_ctimensec = now.tv_nsec;
+       }
+       node->tn_status &=
+           ~(TMPFS_NODE_ACCESSED | TMPFS_NODE_MODIFIED | TMPFS_NODE_CHANGED);
+       TMPFS_NODE_UNLOCK(node);
+}
+
+/* --------------------------------------------------------------------- */
+
+void
+tmpfs_update(struct vnode *vp)
+{
+
+       tmpfs_itimes(vp, NULL, NULL);
+}
+
+/* --------------------------------------------------------------------- */
+
+int
+tmpfs_truncate(struct vnode *vp, off_t length)
+{
+       int error;
+       struct tmpfs_node *node;
+
+       node = VP_TO_TMPFS_NODE(vp);
+
+       if (length < 0) {
+               error = EINVAL;
+               goto out;
+       }
+
+       if (node->tn_size == length) {
+               error = 0;
+               goto out;
+       }
+
+       if (length > VFS_TO_TMPFS(vp->v_mount)->tm_maxfilesize)
+               return (EFBIG);
+
+
+       error = tmpfs_reg_resize(vp, length, 1);
+
+       if (error == 0) {
+               TMPFS_NODE_LOCK(node);
+               node->tn_status |= TMPFS_NODE_CHANGED | TMPFS_NODE_MODIFIED;
+               TMPFS_NODE_UNLOCK(node);
+       }
+
+out:
+       tmpfs_update(vp);
+
+       return error;
+}
+
+/* --------------------------------------------------------------------- */
+
+static ino_t
+tmpfs_fetch_ino(void)
+{
+       ino_t   ret;
+
+       spin_lock_wr(&ino_lock);
+       ret = t_ino++;
+       spin_unlock_wr(&ino_lock);
+
+       return ret;
+}
diff --git a/sys/vfs/tmpfs/tmpfs_vfsops.c b/sys/vfs/tmpfs/tmpfs_vfsops.c
new file mode 100644 (file)
index 0000000..3dde5a9
--- /dev/null
@@ -0,0 +1,461 @@
+/*     $NetBSD: tmpfs_vfsops.c,v 1.10 2005/12/11 12:24:29 christos Exp $       */
+
+/*-
+ * Copyright (c) 2005 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>
+#include <sys/conf.h>
+#include <sys/param.h>
+#include <sys/limits.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/kernel.h>
+#include <sys/stat.h>
+#include <sys/systm.h>
+#include <sys/sysctl.h>
+#include <sys/objcache.h>
+
+#include <vm/vm.h>
+#include <vm/vm_object.h>
+#include <vm/vm_param.h>
+
+#include <vfs/tmpfs/tmpfs.h>
+#include <vfs/tmpfs/tmpfs_vnops.h>
+
+/*
+ * Default permission for root node
+ */
+#define TMPFS_DEFAULT_ROOT_MODE        (S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH)
+
+MALLOC_DEFINE(M_TMPFSMNT, "tmpfs mount", "tmpfs mount structures");
+MALLOC_DEFINE(M_TMPFSNAME, "tmpfs name", "tmpfs file names");
+MALLOC_DEFINE(M_TMPFS_DIRENT, "tmpfs dirent", "tmpfs dirent structures");
+MALLOC_DEFINE(M_TMPFS_NODE, "tmpfs node", "tmpfs node structures");
+
+/* --------------------------------------------------------------------- */
+
+static int     tmpfs_mount(struct mount *, char *, caddr_t, struct ucred *);
+static int     tmpfs_unmount(struct mount *, int);
+static int     tmpfs_root(struct mount *, struct vnode **);
+static int     tmpfs_fhtovp(struct mount *, struct vnode *, struct fid *, struct vnode **);
+static int     tmpfs_statfs(struct mount *, struct statfs *, struct ucred *cred);
+
+/* --------------------------------------------------------------------- */
+
+#define SWI_MAXMIB     3
+static u_int
+get_swpgtotal(void)
+{
+       struct swdevt swinfo;
+       char *sname = "vm.swap_info";
+       int soid[SWI_MAXMIB], oid[2];
+       u_int unswdev, total, dmmax, nswapdev;
+       size_t mibi, len;
+
+       total = 0;
+
+       len = sizeof(dmmax);
+       if (kernel_sysctlbyname("vm.dmmax", &dmmax, &len,
+                               NULL, 0, NULL) != 0)
+               return total;
+
+       len = sizeof(nswapdev);
+       if (kernel_sysctlbyname("vm.nswapdev", &nswapdev, &len,
+                               NULL, 0, NULL) != 0)
+               return total;
+
+       mibi = (SWI_MAXMIB - 1) * sizeof(int);
+       oid[0] = 0;
+       oid[1] = 3;
+
+       if (kernel_sysctl(oid, 2,
+                       soid, &mibi, (void *)sname, strlen(sname),
+                       NULL) != 0)
+               return total;
+
+       mibi = (SWI_MAXMIB - 1);
+       for (unswdev = 0; unswdev < nswapdev; ++unswdev) {
+               soid[mibi] = unswdev;
+               len = sizeof(struct swdevt);
+               if (kernel_sysctl(soid, mibi + 1, &swinfo, &len, NULL, 0,
+                               NULL) != 0)
+                       return total;
+               if (len == sizeof(struct swdevt))
+                       total += (swinfo.sw_nblks - dmmax);
+       }
+
+       return total;
+}
+
+/* --------------------------------------------------------------------- */
+static int
+tmpfs_node_ctor(void *obj, void *privdata, int flags)
+{
+       struct tmpfs_node *node = (struct tmpfs_node *)obj;
+
+       node->tn_gen++;
+       node->tn_size = 0;
+       node->tn_status = 0;
+       node->tn_flags = 0;
+       node->tn_links = 0;
+       node->tn_vnode = NULL;
+       node->tn_vpstate = TMPFS_VNODE_WANT;
+
+       return (1);
+}
+
+static void
+tmpfs_node_dtor(void *obj, void *privdata)
+{
+       struct tmpfs_node *node = (struct tmpfs_node *)obj;
+       node->tn_type = VNON;
+       node->tn_vpstate = TMPFS_VNODE_DOOMED;
+}
+
+static void*
+tmpfs_node_init(void *args, int flags)
+{
+       struct tmpfs_node *node = (struct tmpfs_node *)objcache_malloc_alloc(args, flags);
+       node->tn_id = 0;
+
+       lockinit(&node->tn_interlock, "tmpfs node interlock", 0, LK_CANRECURSE);
+       node->tn_gen = karc4random();
+
+       return node;
+}
+
+static void
+tmpfs_node_fini(void *obj, void *args)
+{
+       struct tmpfs_node *node = (struct tmpfs_node *)obj;
+       lockuninit(&node->tn_interlock);
+       objcache_malloc_free(obj, args);
+}
+
+struct objcache_malloc_args tmpfs_dirent_pool_malloc_args =
+       { sizeof(struct tmpfs_dirent), M_TMPFS_DIRENT };
+struct objcache_malloc_args tmpfs_node_pool_malloc_args =
+       { sizeof(struct tmpfs_node), M_TMPFS_NODE };
+
+static int
+tmpfs_mount(struct mount *mp, char *path, caddr_t data, struct ucred *cred)
+{
+       struct tmpfs_mount *tmp;
+       struct tmpfs_node *root;
+       size_t pages, mem_size;
+       ino_t nodes;
+       int error;
+       /* Size counters. */
+       ino_t   nodes_max = 0;
+       size_t  size_max = 0;
+       size_t size;
+
+       /* Root node attributes. */
+       uid_t   root_uid = cred->cr_uid;
+       gid_t   root_gid = cred->cr_gid;
+       mode_t  root_mode = (VREAD | VWRITE);
+
+       if (mp->mnt_flag & MNT_UPDATE) {
+               /* XXX: There is no support yet to update file system
+                * settings.  Should be added. */
+
+               return EOPNOTSUPP;
+       }
+
+       kprintf("WARNING: TMPFS is considered to be a highly experimental "
+           "feature in DragonFly.\n");
+
+       /* Do not allow mounts if we do not have enough memory to preserve
+        * the minimum reserved pages. */
+       mem_size = vmstats.v_free_count + vmstats.v_inactive_count + get_swpgtotal();
+       mem_size -= mem_size > vmstats.v_wire_count ? vmstats.v_wire_count : mem_size;
+       if (mem_size < TMPFS_PAGES_RESERVED)
+               return ENOSPC;
+
+       /*
+        * If mount by non-root, then verify that user has necessary
+        * permissions on the device.
+        */
+       if (cred->cr_uid != 0) {
+               root_mode = VREAD;
+               if ((mp->mnt_flag & MNT_RDONLY) == 0)
+                       root_mode |= VWRITE;
+       }
+
+       /* 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 (size_max < PAGE_SIZE || size_max >= SIZE_MAX)
+               pages = SIZE_MAX;
+       else
+               pages = howmany(size_max, PAGE_SIZE);
+       KKASSERT(pages > 0);
+
+       if (nodes_max <= 3)
+               nodes = 3 + pages * PAGE_SIZE / 1024;
+       else
+               nodes = nodes_max;
+       KKASSERT(nodes >= 3);
+
+       /* Allocate the tmpfs mount structure and fill it. */
+       tmp = (struct tmpfs_mount *)kmalloc(sizeof(struct tmpfs_mount),
+           M_TMPFSMNT, M_WAITOK | M_ZERO);
+
+       lockinit(&(tmp->allnode_lock), "tmpfs allnode lock", 0, LK_CANRECURSE);
+       tmp->tm_nodes_max = nodes;
+       tmp->tm_nodes_inuse = 0;
+       tmp->tm_maxfilesize = (u_int64_t)(vmstats.v_page_count + get_swpgtotal()) * PAGE_SIZE;
+       LIST_INIT(&tmp->tm_nodes_used);
+
+       tmp->tm_pages_max = pages;
+       tmp->tm_pages_used = 0;
+       tmp->tm_dirent_pool =  objcache_create( "tmpfs dirent cache",
+           0, 0,
+           NULL, NULL, NULL,
+           objcache_malloc_alloc, objcache_malloc_free,
+           &tmpfs_dirent_pool_malloc_args);
+       tmp->tm_node_pool = objcache_create( "tmpfs node cache",
+           0, 0,
+           tmpfs_node_ctor, tmpfs_node_dtor, NULL,
+           tmpfs_node_init, tmpfs_node_fini,
+           &tmpfs_node_pool_malloc_args);
+
+       /* Allocate the root node. */
+       error = tmpfs_alloc_node(tmp, VDIR, root_uid,
+           root_gid, root_mode & ALLPERMS, NULL, NULL,
+           VNOVAL, VNOVAL, &root);
+
+       if (error != 0 || root == NULL) {
+           objcache_destroy(tmp->tm_node_pool);
+           objcache_destroy(tmp->tm_dirent_pool);
+           kfree(tmp, M_TMPFSMNT);
+           return error;
+       }
+       KASSERT(root->tn_id >= 0, ("tmpfs root with invalid ino: %d", (int)root->tn_id));
+       tmp->tm_root = root;
+
+       mp->mnt_flag |= MNT_LOCAL;
+       mp->mnt_kern_flag |= MNTK_RD_MPSAFE | MNTK_WR_MPSAFE | MNTK_GA_MPSAFE  |
+                            MNTK_IN_MPSAFE | MNTK_SG_MPSAFE;
+       mp->mnt_data = (qaddr_t)tmp;
+       vfs_getnewfsid(mp);
+
+
+       vfs_add_vnodeops(mp, &tmpfs_vnode_vops, &mp->mnt_vn_norm_ops);
+       vfs_add_vnodeops(mp, &tmpfs_fifo_vops, &mp->mnt_vn_fifo_ops);
+
+       copystr("tmpfs", mp->mnt_stat.f_mntfromname, MNAMELEN - 1, &size);
+       bzero(mp->mnt_stat.f_mntfromname +size, MNAMELEN - size);
+       bzero(mp->mnt_stat.f_mntonname, sizeof(mp->mnt_stat.f_mntonname));
+       copyinstr(path, mp->mnt_stat.f_mntonname,
+                 sizeof(mp->mnt_stat.f_mntonname) -1,
+                 &size);
+
+       tmpfs_statfs(mp, &mp->mnt_stat, cred);
+
+       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;
+       struct vnode *vp;
+
+       /* Handle forced unmounts. */
+       if (mntflags & MNT_FORCE)
+               flags |= FORCECLOSE;
+
+       /* Finalize all pending I/O. */
+       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_used);
+       while (node != NULL) {
+               struct tmpfs_node *next;
+
+               if (node->tn_type == VDIR) {
+                       struct tmpfs_dirent *de;
+
+                       de = TAILQ_FIRST(&node->tn_dir.tn_dirhead);
+                       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);
+               vp = node->tn_vnode;
+               if (vp != NULL) {
+                       tmpfs_free_vp(vp);
+                       vrecycle(vp);
+                        node->tn_vnode = NULL;
+               }
+               tmpfs_free_node(tmp, node);
+               node = next;
+       }
+
+       objcache_destroy(tmp->tm_dirent_pool);
+       objcache_destroy(tmp->tm_node_pool);
+
+       lockuninit(&tmp->allnode_lock);
+       KKASSERT(tmp->tm_pages_used == 0);
+       KKASSERT(tmp->tm_nodes_inuse == 0);
+
+       /* Throw away the tmpfs_mount structure. */
+       kfree(mp->mnt_data, M_TMPFSMNT);
+       mp->mnt_data = NULL;
+
+       mp->mnt_flag &= ~MNT_LOCAL;
+       return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int
+tmpfs_root(struct mount *mp, struct vnode **vpp)
+{
+       int error;
+       error = tmpfs_alloc_vp(mp, VFS_TO_TMPFS(mp)->tm_root, LK_EXCLUSIVE, vpp);
+       (*vpp)->v_flag |= VROOT;
+       (*vpp)->v_type = VDIR;
+
+       return error;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int
+tmpfs_fhtovp(struct mount *mp, struct vnode *rootvp, struct fid *fhp, struct vnode **vpp)
+{
+       boolean_t found;
+       struct tmpfs_fid *tfhp;
+       struct tmpfs_mount *tmp;
+       struct tmpfs_node *node;
+
+       tmp = VFS_TO_TMPFS(mp);
+
+       tfhp = (struct tmpfs_fid *)fhp;
+       if (tfhp->tf_len != sizeof(struct tmpfs_fid))
+               return EINVAL;
+
+       if (tfhp->tf_id >= tmp->tm_nodes_max)
+               return EINVAL;
+
+       found = FALSE;
+
+       TMPFS_LOCK(tmp);
+       LIST_FOREACH(node, &tmp->tm_nodes_used, tn_entries) {
+               if (node->tn_id == tfhp->tf_id &&
+                   node->tn_gen == tfhp->tf_gen) {
+                       found = TRUE;
+                       break;
+               }
+       }
+       TMPFS_UNLOCK(tmp);
+
+       if (found)
+               return (tmpfs_alloc_vp(mp, node, LK_EXCLUSIVE, vpp));
+
+       return (EINVAL);
+}
+
+/* --------------------------------------------------------------------- */
+
+/* ARGSUSED2 */
+static int
+tmpfs_statfs(struct mount *mp, struct statfs *sbp, struct ucred *cred)
+{
+       fsfilcnt_t freenodes;
+       struct tmpfs_mount *tmp;
+
+       tmp = VFS_TO_TMPFS(mp);
+
+       sbp->f_iosize = PAGE_SIZE;
+       sbp->f_bsize = PAGE_SIZE;
+
+       sbp->f_blocks = TMPFS_PAGES_MAX(tmp);
+       sbp->f_bavail = sbp->f_bfree = TMPFS_PAGES_AVAIL(tmp);
+
+       freenodes = MIN(tmp->tm_nodes_max - tmp->tm_nodes_inuse,
+           TMPFS_PAGES_AVAIL(tmp) * PAGE_SIZE / sizeof(struct tmpfs_node));
+
+       sbp->f_files = freenodes + tmp->tm_nodes_inuse;
+       sbp->f_ffree = freenodes;
+       /* sbp->f_owner = tmp->tn_uid; */
+
+       return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * tmpfs vfs operations.
+ */
+
+static struct vfsops tmpfs_vfsops = {
+       .vfs_mount =                    tmpfs_mount,
+       .vfs_unmount =                  tmpfs_unmount,
+       .vfs_root =                     tmpfs_root,
+       .vfs_statfs =                   tmpfs_statfs,
+       .vfs_fhtovp =                   tmpfs_fhtovp,
+       .vfs_sync =                     vfs_stdsync
+};
+
+VFS_SET(tmpfs_vfsops, tmpfs, 0);
diff --git a/sys/vfs/tmpfs/tmpfs_vnops.c b/sys/vfs/tmpfs/tmpfs_vnops.c
new file mode 100644 (file)
index 0000000..40754e4
--- /dev/null
@@ -0,0 +1,1564 @@
+/*     $NetBSD: tmpfs_vnops.c,v 1.39 2007/07/23 15:41:01 jmmv 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.
+ */
+
+/*
+ * tmpfs vnode interface.
+ */
+#include <sys/cdefs.h>
+
+#include <sys/kernel.h>
+#include <sys/kern_syscall.h>
+#include <sys/param.h>
+#include <sys/fcntl.h>
+#include <sys/lockf.h>
+#include <sys/priv.h>
+#include <sys/proc.h>
+#include <sys/resourcevar.h>
+#include <sys/sched.h>
+#include <sys/sfbuf.h>
+#include <sys/stat.h>
+#include <sys/systm.h>
+#include <sys/unistd.h>
+#include <sys/vfsops.h>
+#include <sys/vnode.h>
+
+#include <sys/mplock2.h>
+
+#include <vm/vm.h>
+#include <vm/vm_object.h>
+#include <vm/vm_page.h>
+#include <vm/vm_pager.h>
+
+#include <vfs/fifofs/fifo.h>
+#include <vfs/tmpfs/tmpfs_vnops.h>
+#include <vfs/tmpfs/tmpfs.h>
+
+MALLOC_DECLARE(M_TMPFS);
+
+/* --------------------------------------------------------------------- */
+
+static int
+tmpfs_nresolve(struct vop_nresolve_args *v)
+{
+       struct vnode *dvp = v->a_dvp;
+       struct vnode *vp = NULL;
+       struct namecache *ncp = v->a_nch->ncp;
+       struct ucred *cred = v->a_cred;
+
+       int error;
+       struct tmpfs_dirent *de;
+       struct tmpfs_node *dnode;
+
+       dnode = VP_TO_TMPFS_DIR(dvp);
+
+       if (!vn_islocked(dvp));
+               vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY);
+
+       /* Check accessibility of requested node as a first step. */
+       error = VOP_ACCESS(dvp, VEXEC, cred);
+       if (error != 0)
+               goto out;
+
+       if (dnode->tn_dir.tn_parent == NULL) {
+               error = ENOENT;
+               goto out;
+       }
+
+       de = tmpfs_dir_lookup(dnode, NULL, ncp);
+       if (de == NULL) {
+               /* The entry was not found in the directory.
+                * This is OK if we are creating or renaming an
+                * entry and are working on the last component of
+                * the path name. */
+               error = VOP_ACCESS(dvp, VWRITE, cred);
+               if (error != 0)
+                       goto out;
+               else {
+                       error = ENOENT;
+                       goto out;
+               }
+       } else {
+               struct tmpfs_node *tnode;
+
+               /* The entry was found, so get its associated
+                * tmpfs_node. */
+               tnode = de->td_node;
+
+               /* If we are not at the last path component and
+                * found a non-directory or non-link entry (which
+                * may itself be pointing to a directory), raise
+                * an error. */
+               if (tnode->tn_links > 1 &&
+                   tnode->tn_type != VDIR && tnode->tn_type != VLNK) {
+                       error = ENOTDIR;
+                       goto out;
+               }
+
+               error = VOP_ACCESS(dvp, VWRITE, cred);
+               if (error != 0)
+                       goto out;
+
+               /* Allocate a new vnode on the matching entry. */
+               error = tmpfs_alloc_vp(dvp->v_mount, tnode,
+                               LK_EXCLUSIVE | LK_RETRY, &vp);
+               if (error != 0)
+                       goto out;
+
+               if ((dnode->tn_mode & S_ISTXT) &&
+                   VOP_ACCESS(vp, VWRITE, cred)) {
+                       error = EPERM;
+                       vp = NULL;
+                       goto out;
+               }
+       }
+
+       KKASSERT(vp);
+
+out:
+       vn_unlock(dvp);
+       /* Store the result of this lookup in the cache.  Avoid this if the
+        * request was for creation, as it does not improve timings on
+        * emprical tests. */
+       if (vp) {
+               vn_unlock(vp);
+               cache_setvp(v->a_nch, vp);
+               vrele(vp);
+       }
+       if (error == ENOENT) {
+               cache_setvp(v->a_nch, NULL);
+       }
+       return error;
+}
+
+static int
+tmpfs_nlookupdotdot(struct vop_nlookupdotdot_args *v)
+{
+       struct vnode *dvp = v->a_dvp;
+       struct vnode **vpp = v->a_vpp;
+       struct tmpfs_node *dnode = VP_TO_TMPFS_NODE(dvp);
+       struct ucred *cred = v->a_cred;
+       int error;
+
+       *vpp = NULL;
+       /* Check accessibility of requested node as a first step. */
+       error = VOP_ACCESS(dvp, VEXEC, cred);
+       if (error != 0)
+               return error;
+
+       if (dnode->tn_dir.tn_parent != NULL) {
+               /* Allocate a new vnode on the matching entry. */
+               error = tmpfs_alloc_vp(dvp->v_mount, dnode->tn_dir.tn_parent,
+                   LK_EXCLUSIVE | LK_RETRY, vpp);
+
+               if (*vpp)
+                       vn_unlock(*vpp);
+       }
+
+       return (*vpp == NULL) ? ENOENT : 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int
+tmpfs_ncreate(struct vop_ncreate_args *v)
+{
+       struct vnode *dvp = v->a_dvp;
+       struct vnode **vpp = v->a_vpp;
+       struct namecache *ncp = v->a_nch->ncp;
+       struct vattr *vap = v->a_vap;
+       struct ucred *cred = v->a_cred;
+       int error;
+
+       KKASSERT(vap->va_type == VREG || vap->va_type == VSOCK);
+
+       vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY);
+       error = tmpfs_alloc_file(dvp, vpp, vap, ncp, cred, NULL);
+       if (error == 0) {
+               cache_setunresolved(v->a_nch);
+               cache_setvp(v->a_nch, *vpp);
+       }
+       vn_unlock(dvp);
+
+       return error;
+}
+/* --------------------------------------------------------------------- */
+
+static int
+tmpfs_nmknod(struct vop_nmknod_args *v)
+{
+       struct vnode *dvp = v->a_dvp;
+       struct vnode **vpp = v->a_vpp;
+       struct namecache *ncp = v->a_nch->ncp;
+       struct vattr *vap = v->a_vap;
+       struct ucred *cred = v->a_cred;
+       int error;
+
+       if (vap->va_type != VBLK && vap->va_type != VCHR &&
+           vap->va_type != VFIFO)
+               return EINVAL;
+
+       vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY);
+       error = tmpfs_alloc_file(dvp, vpp, vap, ncp, cred, NULL);
+       if (error == 0) {
+               cache_setunresolved(v->a_nch);
+               cache_setvp(v->a_nch, *vpp);
+       }
+       vn_unlock(dvp);
+
+       return error;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int
+tmpfs_open(struct vop_open_args *v)
+{
+       struct vnode *vp = v->a_vp;
+       int mode = v->a_mode;
+
+       int error;
+       struct tmpfs_node *node;
+
+       KKASSERT(vn_islocked(vp));
+
+       node = VP_TO_TMPFS_NODE(vp);
+
+       /* 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)
+               return (ENOENT);
+
+       /* If the file is marked append-only, deny write requests. */
+       if (node->tn_flags & APPEND && (mode & (FWRITE | O_APPEND)) == FWRITE)
+               error = EPERM;
+       else {
+               return (vop_stdopen(v));
+       }
+
+       KKASSERT(vn_islocked(vp));
+       return error;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int
+tmpfs_close(struct vop_close_args *v)
+{
+       struct vnode *vp = v->a_vp;
+       struct tmpfs_node *node;
+
+       node = VP_TO_TMPFS_NODE(vp);
+
+       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);
+       }
+
+       return vop_stdclose(v);
+}
+
+/* --------------------------------------------------------------------- */
+
+int
+tmpfs_access(struct vop_access_args *v)
+{
+       struct vnode *vp = v->a_vp;
+       int error;
+       struct tmpfs_node *node;
+
+       KKASSERT(vn_islocked(vp));
+
+       node = VP_TO_TMPFS_NODE(vp);
+
+       switch (vp->v_type) {
+       case VDIR:
+               /* FALLTHROUGH */
+       case VLNK:
+               /* FALLTHROUGH */
+       case VREG:
+               if (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 (VWRITE && node->tn_flags & IMMUTABLE) {
+               error = EPERM;
+               goto out;
+       }
+
+       error = vop_helper_access(v, node->tn_uid, node->tn_gid, node->tn_mode, 0);
+
+out:
+
+       return error;
+}
+
+/* --------------------------------------------------------------------- */
+
+int
+tmpfs_getattr(struct vop_getattr_args *v)
+{
+       struct vnode *vp = v->a_vp;
+       struct vattr *vap = v->a_vap;
+
+       struct tmpfs_node *node;
+       int needunlock = 0;
+
+       if(!vn_islocked(vp)) {
+               needunlock = 1;
+               vn_lock(vp, LK_SHARED | LK_RETRY);
+       }
+
+       node = VP_TO_TMPFS_NODE(vp);
+
+       tmpfs_update(vp);
+
+       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.tv_sec = node->tn_atime;
+       vap->va_atime.tv_nsec = node->tn_atimensec;
+       vap->va_mtime.tv_sec = node->tn_mtime;
+       vap->va_mtime.tv_nsec = node->tn_mtimensec;
+       vap->va_ctime.tv_sec = node->tn_ctime;
+       vap->va_ctime.tv_nsec = node->tn_ctimensec;
+       vap->va_gen = node->tn_gen;
+       vap->va_flags = node->tn_flags;
+       if (vp->v_type == VBLK || vp->v_type == VCHR)
+       {
+               vap->va_rmajor = umajor(node->tn_rdev);
+               vap->va_rminor = uminor(node->tn_rdev);
+       }
+       vap->va_bytes = round_page(node->tn_size);
+       vap->va_filerev = 0;
+
+       if (needunlock)
+               vn_unlock(vp);
+
+       return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+int
+tmpfs_setattr(struct vop_setattr_args *v)
+{
+       struct vnode *vp = v->a_vp;
+       struct vattr *vap = v->a_vap;
+       struct ucred *cred = v->a_cred;
+
+       int error = 0;
+       int needunlock = 0;
+
+       if(!vn_islocked(vp)) {
+               needunlock = 1;
+               vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
+       }
+
+       /* 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 ||
+           vap->va_gen != VNOVAL ||
+           vap->va_rmajor != 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 != (mode_t)VNOVAL))
+               error = tmpfs_chmod(vp, vap->va_mode, cred);
+
+       if (error == 0 && ((vap->va_atime.tv_sec != VNOVAL &&
+           vap->va_atime.tv_nsec != VNOVAL) ||
+           (vap->va_mtime.tv_sec != VNOVAL &&
+           vap->va_mtime.tv_nsec != VNOVAL) ))
+               error = tmpfs_chtimes(vp, &vap->va_atime, &vap->va_mtime,
+                       vap->va_vaflags, cred);
+
+       /* 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);
+
+       if (needunlock)
+               vn_unlock(vp);
+
+       return error;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int
+tmpfs_fsync(struct vop_fsync_args *v)
+{
+       struct vnode *vp = v->a_vp;
+
+       tmpfs_update(vp);
+
+
+       return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int
+tmpfs_read (struct vop_read_args *ap)
+{
+       struct buf *bp;
+       struct vnode *vp = ap->a_vp;
+       struct uio *uio = ap->a_uio;
+       struct tmpfs_node *node;
+       int error;
+       off_t offset;
+       off_t base_offset;
+       size_t len;
+       int got_mplock;
+
+       error = 0;
+       if (uio->uio_resid == 0) {
+               return error;
+       }
+
+       node = VP_TO_TMPFS_NODE(vp);
+
+       if (uio->uio_offset < 0)
+               return (EINVAL);
+       if (vp->v_type != VREG)
+               return (EINVAL);
+
+       vn_lock(vp, LK_SHARED | LK_RETRY);
+
+#ifdef SMP
+       if(curthread->td_mpcount)
+               got_mplock = -1;
+       else
+               got_mplock = 0;
+#else
+               got_mplock = -1;
+#endif
+
+       while (uio->uio_resid > 0 && uio->uio_offset < node->tn_size) {
+               /*
+                * Use buffer cache I/O (via tmpfs_strategy)
+                */
+               offset = (off_t)uio->uio_offset & BMASK;
+               base_offset = (off_t)uio->uio_offset - offset;
+               bp = getcacheblk(vp, base_offset);
+               if (bp == NULL)
+               {
+                       if (got_mplock == 0) {
+                               got_mplock = 1;
+                               get_mplock();
+                       }
+
+                       error = bread(vp, base_offset, BSIZE, &bp);
+                       if (error) {
+                               brelse(bp);
+                               kprintf("tmpfs_read bread error %d\n", error);
+                               break;
+                       }
+               }
+
+               if (got_mplock == 0) {
+                       got_mplock = 1;
+                       get_mplock();
+               }
+
+               /*
+                * Figure out how many bytes we can actually copy this loop.
+                */
+               len = BSIZE - offset;
+               if (len > uio->uio_resid)
+                       len = uio->uio_resid;
+               if (len > node->tn_size - uio->uio_offset)
+                       len = (size_t)(node->tn_size - uio->uio_offset);
+
+               error = uiomove((char *)bp->b_data + offset, len, uio);
+               bqrelse(bp);
+               if (error) {
+                       kprintf("tmpfs_read uiomove error %d\n", error);
+                       break;
+               }
+       }
+
+       if (got_mplock > 0)
+               rel_mplock();
+
+       TMPFS_NODE_LOCK(node);
+       node->tn_status |= TMPFS_NODE_ACCESSED;
+       TMPFS_NODE_UNLOCK(node);
+
+       vn_unlock(vp);
+
+       return(error);
+}
+
+static int
+tmpfs_write (struct vop_write_args *ap)
+{
+       struct buf *bp;
+       struct vnode *vp = ap->a_vp;
+       struct uio *uio = ap->a_uio;
+       struct thread *td = uio->uio_td;
+       struct tmpfs_node *node;
+       boolean_t extended;
+       off_t oldsize;
+       int error;
+       off_t offset;
+       off_t base_offset;
+       size_t len;
+       struct rlimit limit;
+       int got_mplock;
+       int trivial = 0;
+
+       error = 0;
+       if (uio->uio_resid == 0) {
+               return error;
+       }
+
+       node = VP_TO_TMPFS_NODE(vp);
+
+       if (vp->v_type != VREG)
+               return (EINVAL);
+
+       oldsize = node->tn_size;
+       if (ap->a_ioflag & IO_APPEND)
+               uio->uio_offset = node->tn_size;
+
+       /*
+        * Check for illegal write offsets.
+        */
+       if (uio->uio_offset + uio->uio_resid >
+         VFS_TO_TMPFS(vp->v_mount)->tm_maxfilesize)
+               return (EFBIG);
+
+       if (vp->v_type == VREG && td != NULL) {
+               error = kern_getrlimit(RLIMIT_FSIZE, &limit);
+               if (error != 0)
+                       return error;
+               if (uio->uio_offset + uio->uio_resid > limit.rlim_cur) {
+                       ksignal(td->td_proc, SIGXFSZ);
+                       return (EFBIG);
+               }
+       }
+
+
+       /*
+        * Extend the file's size if necessary
+        */
+       extended = (uio->uio_offset + uio->uio_resid) > node->tn_size;
+
+       vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
+#ifdef SMP
+       if(curthread->td_mpcount)
+               got_mplock = -1;
+       else {
+               got_mplock = 1;
+               get_mplock();
+       }
+#else
+               got_mplock = -1;
+#endif
+       crit_enter();
+       while (uio->uio_resid > 0) {
+               /*
+                * Use buffer cache I/O (via tmpfs_strategy)
+                */
+               offset = (off_t)uio->uio_offset & BMASK;
+               base_offset = (off_t)uio->uio_offset - offset;
+               len = BSIZE - offset;
+               if (len > uio->uio_resid)
+                       len = uio->uio_resid;
+
+               if ((uio->uio_offset + len) > node->tn_size) {
+                       trivial = uio->uio_offset <= node->tn_size;
+                       error = tmpfs_reg_resize(vp, uio->uio_offset + len,  trivial);
+                       if (error)
+                               break;
+               }
+
+               bp = getblk(vp, base_offset, BSIZE, GETBLK_BHEAVY, 0);
+               vfs_bio_clrbuf(bp);
+
+               error = uiomove((char *)bp->b_data + offset, len, uio);
+               if (error) {
+                       kprintf("tmpfs_write uiomove error %d\n", error);
+                       brelse(bp);
+                       break;
+               }
+
+               if (uio->uio_offset > node->tn_size)
+                       node->tn_size = uio->uio_offset;
+
+               /*
+                * The data has been loaded into the buffer, write it out. (via tmpfs_strategy)
+                *
+                * call bdwrite() because we don't care about storage io flag (ap->a_ioflag) for a swap I/O
+                * maybe bawrite() for IO_DIRECT, bwrite() for IO_SYNC
+                *
+                * XXX: need to implement tmpfs_bmap() for a dirty bit handling of bdwrite()
+                */
+               bdwrite(bp);
+               if (bp->b_error) {
+                       kprintf("tmpfs_write bwrite error %d\n", error);
+                       break;
+               }
+       }
+       crit_exit();
+
+       if (got_mplock > 0)
+               rel_mplock();
+
+       if (error) {
+               if (extended)
+                       (void)tmpfs_reg_resize(vp, oldsize, trivial);
+               return error;
+       }
+
+       TMPFS_NODE_LOCK(node);
+       node->tn_status |= TMPFS_NODE_ACCESSED | TMPFS_NODE_MODIFIED |
+           (extended? TMPFS_NODE_CHANGED : 0);
+
+       if (node->tn_mode & (S_ISUID | S_ISGID)) {
+               if (priv_check_cred(ap->a_cred, PRIV_VFS_RETAINSUGID, 0))
+                       node->tn_mode &= ~(S_ISUID | S_ISGID);
+       }
+       TMPFS_NODE_UNLOCK(node);
+
+       vn_unlock(vp);
+
+       return(error);
+}
+
+static int
+tmpfs_advlock (struct vop_advlock_args *ap)
+{
+       struct tmpfs_node *node;
+       struct vnode *vp = ap->a_vp;
+
+       node = VP_TO_TMPFS_NODE(vp);
+
+       return (lf_advlock(ap, &node->tn_advlock, node->tn_size));
+}
+
+
+static int
+tmpfs_strategy(struct vop_strategy_args *ap)
+{
+       struct bio *bio = ap->a_bio;
+       struct vnode *vp = ap->a_vp;
+       struct tmpfs_node *node;
+       vm_object_t uobj;
+
+       if (vp->v_type != VREG)
+               return EINVAL;
+
+       node = VP_TO_TMPFS_NODE(vp);
+
+       uobj = node->tn_reg.tn_aobj;
+       /*
+        * call swap_pager_strategy to store vm object into swap device
+        */
+       swap_pager_strategy(uobj, bio);
+
+       return 0;
+}
+
+static int
+tmpfs_bmap(struct vop_bmap_args *ap)
+{
+       if (ap->a_doffsetp != NULL)
+               *ap->a_doffsetp = ap->a_loffset;
+       if (ap->a_runp != NULL)
+               *ap->a_runp = 0;
+       if (ap->a_runb != NULL)
+               *ap->a_runb = 0;
+
+       return 0;
+}
+/* --------------------------------------------------------------------- */
+
+static int
+tmpfs_nremove(struct vop_nremove_args *v)
+{
+       struct vnode *dvp = v->a_dvp;
+       struct namecache *ncp = v->a_nch->ncp;
+       struct vnode *vp = ncp->nc_vp;
+       int error;
+       struct tmpfs_dirent *de;
+       struct tmpfs_mount *tmp;
+       struct tmpfs_node *dnode;
+       struct tmpfs_node *node;
+
+       vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY);
+       vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
+
+       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, node, ncp);
+       if (de == NULL) {
+               error = ENOENT;
+               goto out;
+       }
+
+       /* Files marked as immutable or append-only cannot be deleted. */
+       if ((node->tn_flags & (IMMUTABLE | APPEND | NOUNLINK)) ||
+           (dnode->tn_flags & 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);
+
+       if (node->tn_links > 0) {
+               TMPFS_NODE_LOCK(node);
+               node->tn_status |= TMPFS_NODE_ACCESSED | TMPFS_NODE_CHANGED | \
+                       TMPFS_NODE_MODIFIED;
+               TMPFS_NODE_UNLOCK(node);
+       }
+
+       cache_setunresolved(v->a_nch);
+       cache_setvp(v->a_nch, NULL);
+       cache_inval_vp(vp, CINV_DESTROY);
+       error = 0;
+
+
+out:
+       vn_unlock(vp);
+       vn_unlock(dvp);
+
+       return error;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int
+tmpfs_nlink(struct vop_nlink_args *v)
+{
+       struct vnode *dvp = v->a_dvp;
+       struct vnode *vp = v->a_vp;
+       struct namecache *ncp = v->a_nch->ncp;
+
+       int error;
+       struct tmpfs_dirent *de;
+       struct tmpfs_node *node;
+
+       vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY);
+       vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
+
+
+       KKASSERT(dvp != vp); /* XXX When can this be false? */
+
+       node = VP_TO_TMPFS_NODE(vp);
+
+       /* XXX: Why aren't the following two tests done by the caller? */
+
+       /* Hard links of directories are forbidden. */
+       if (vp->v_type == VDIR) {
+               error = EPERM;
+               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,
+           ncp->nc_name, 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. */
+
+       TMPFS_NODE_LOCK(node);
+       node->tn_status |= TMPFS_NODE_CHANGED;
+       TMPFS_NODE_UNLOCK(node);
+       tmpfs_update(vp);
+
+       cache_setunresolved(v->a_nch);
+       cache_setvp(v->a_nch, vp);
+       error = 0;
+
+out:
+       vn_unlock(vp);
+       vn_unlock(dvp);
+
+       return error;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int
+tmpfs_nrename(struct vop_nrename_args *v)
+{
+       struct vnode *fdvp = v->a_fdvp;
+       struct namecache *fncp = v->a_fnch->ncp;
+       struct vnode *fvp = fncp->nc_vp;
+       struct vnode *tdvp = v->a_tdvp;
+       struct namecache *tncp = v->a_tnch->ncp;
+       struct vnode *tvp = tncp->nc_vp;
+
+       char *newname;
+       int error;
+       struct tmpfs_dirent *de;
+       struct tmpfs_mount *tmp;
+       struct tmpfs_node *fdnode;
+       struct tmpfs_node *fnode;
+       struct tmpfs_node *tnode;
+       struct tmpfs_node *tdnode;
+
+       vn_lock(tdvp, LK_EXCLUSIVE | LK_RETRY);
+       if(tvp != NULL && tdvp != tvp)
+               vn_lock(tvp, LK_EXCLUSIVE | LK_RETRY);
+
+       tnode = (tvp == NULL) ? NULL : VP_TO_TMPFS_NODE(tvp);
+
+       /* Disallow cross-device renames.
+        * XXX Why isn't this done by the caller? */
+       if (fvp->v_mount != tdvp->v_mount ||
+           (tvp != NULL && fvp->v_mount != tvp->v_mount)) {
+               error = EXDEV;
+               goto out;
+       }
+
+       tmp = VFS_TO_TMPFS(tdvp->v_mount);
+       tdnode = VP_TO_TMPFS_DIR(tdvp);
+
+       /* If source and target are the same file, there is nothing to do. */
+       if (fvp == tvp) {
+               error = 0;
+               goto out;
+       }
+
+       /* If we need to move the directory between entries, lock the
+        * source so that we can safely operate on it. */
+       if (tdvp != fdvp) {
+               error = vn_lock(fdvp, LK_EXCLUSIVE | LK_RETRY);
+               if (error != 0)
+                       goto out;
+       }
+       fdnode = VP_TO_TMPFS_DIR(fdvp);
+       fnode = VP_TO_TMPFS_NODE(fvp);
+       de = tmpfs_dir_lookup(fdnode, fnode, fncp);
+
+       /* Avoid manipulating '.' and '..' entries. */
+       if (de == NULL) {
+               error = ENOENT;
+               goto out_locked;
+       }
+       KKASSERT(de->td_node == fnode);
+
+       /* If re-naming a directory to another preexisting directory
+        * ensure that the target directory is empty so that its
+        * removal causes no side effects.
+        * Kern_rename gurantees the destination to be a directory
+        * if the source is one. */
+       if (tvp != NULL) {
+               KKASSERT(tnode != NULL);
+
+               if ((tnode->tn_flags & (NOUNLINK | IMMUTABLE | APPEND)) ||
+                   (tdnode->tn_flags & (APPEND | IMMUTABLE))) {
+                       error = EPERM;
+                       goto out_locked;
+               }
+
+               if (fnode->tn_type == VDIR && tnode->tn_type == VDIR) {
+                       if (tnode->tn_size > 0) {
+                               error = ENOTEMPTY;
+                               goto out_locked;
+                       }
+               } else if (fnode->tn_type == VDIR && tnode->tn_type != VDIR) {
+                       error = ENOTDIR;
+                       goto out_locked;
+               } else if (fnode->tn_type != VDIR && tnode->tn_type == VDIR) {
+                       error = EISDIR;
+                       goto out_locked;
+               } else {
+                       KKASSERT(fnode->tn_type != VDIR &&
+                               tnode->tn_type != VDIR);
+               }
+       }
+
+       if ((fnode->tn_flags & (NOUNLINK | IMMUTABLE | APPEND))
+           || (fdnode->tn_flags & (APPEND | IMMUTABLE))) {
+               error = EPERM;
+               goto out_locked;
+       }
+
+       /* Ensure that we have enough memory to hold the new name, if it
+        * has to be changed. */
+       if (fncp->nc_nlen != tncp->nc_nlen ||
+           bcmp(fncp->nc_name, tncp->nc_name, fncp->nc_nlen) != 0) {
+               newname = kmalloc(tncp->nc_nlen, M_TMPFSNAME, M_WAITOK);
+       } 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 (de->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;
+                       /* TMPFS_LOCK garanties that no nodes are freed while
+                        * traversing the list. Nodes can only be marked as
+                        * removed: tn_parent == NULL. */
+                       TMPFS_LOCK(tmp);
+                       TMPFS_NODE_LOCK(n);
+                       while (n != n->tn_dir.tn_parent) {
+                               struct tmpfs_node *parent;
+
+                               if (n == fnode) {
+                                       TMPFS_NODE_UNLOCK(n);
+                                       TMPFS_UNLOCK(tmp);
+                                       error = EINVAL;
+                                       if (newname != NULL)
+                                                   kfree(newname, M_TMPFSNAME);
+                                       goto out_locked;
+                               }
+                               parent = n->tn_dir.tn_parent;
+                               if (parent == NULL) {
+                                       n = NULL;
+                                       break;
+                               }
+                               TMPFS_NODE_LOCK(parent);
+                               if (parent->tn_dir.tn_parent == NULL) {
+                                       TMPFS_NODE_UNLOCK(parent);
+                                       n = NULL;
+                                       break;
+                               }
+                               n = parent;
+                       }
+                       TMPFS_NODE_UNLOCK(n);
+                       TMPFS_UNLOCK(tmp);
+                       if (n == NULL) {
+                               error = EINVAL;
+                               if (newname != NULL)
+                                           kfree(newname, M_TMPFSNAME);
+                               goto out_locked;
+                       }
+
+                       /* Adjust the parent pointer. */
+                       TMPFS_VALIDATE_DIR(fnode);
+                       TMPFS_NODE_LOCK(de->td_node);
+                       de->td_node->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. */
+                       TMPFS_NODE_LOCK(tdnode);
+                       TMPFS_ASSERT_LOCKED(tdnode);
+                       TMPFS_NODE_LOCK(fdnode);
+                       TMPFS_ASSERT_LOCKED(fdnode);
+
+                       tdnode->tn_links++;
+                       fdnode->tn_links--;
+
+                       TMPFS_NODE_UNLOCK(fdnode);
+                       TMPFS_NODE_UNLOCK(tdnode);
+                       TMPFS_NODE_UNLOCK(de->td_node);
+               }
+
+               /* Do the move: just remove the entry from the source directory
+                * and insert it into the target one. */
+               tmpfs_dir_detach(fdvp, de);
+               tmpfs_dir_attach(tdvp, de);
+       }
+
+       /* If the name has changed, we need to make it effective by changing
+        * it in the directory entry. */
+       if (newname != NULL) {
+
+               kfree(de->td_name, M_TMPFSNAME);
+               de->td_namelen = (uint16_t)tncp->nc_nlen;
+               bcopy(tncp->nc_name, newname, tncp->nc_nlen);
+               newname[tncp->nc_nlen] = '\0';
+               de->td_name = newname;
+
+               TMPFS_NODE_LOCK(tdnode);
+               TMPFS_NODE_LOCK(fdnode);
+
+               fnode->tn_status |= TMPFS_NODE_CHANGED;
+               tdnode->tn_status |= TMPFS_NODE_MODIFIED;
+
+               TMPFS_NODE_UNLOCK(fdnode);
+               TMPFS_NODE_UNLOCK(tdnode);
+       }
+
+       /* If we are overwriting an entry, we have to remove the old one
+        * from the target directory. */
+       if (tvp != NULL) {
+               /* Remove the old entry from the target directory. */
+               de = tmpfs_dir_lookup(tdnode, tnode, tncp);
+               tmpfs_dir_detach(tdvp, 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(VFS_TO_TMPFS(tvp->v_mount), de, TRUE);
+
+               cache_inval_vp(tvp, CINV_DESTROY);
+       }
+
+       cache_rename(v->a_fnch, v->a_tnch);
+       error = 0;
+
+out_locked:
+       if (fdnode != tdnode)
+               vn_unlock(fdvp);
+
+out:
+       /* Release target nodes. */
+       /* XXX: I don't understand when tdvp can be the same as tvp, but
+        * other code takes care of this... */
+       if (tdvp == tvp)
+               vrele(tdvp);
+       else {
+               if (tvp != NULL)
+                       vn_unlock(tvp);
+               vn_unlock(tdvp);
+       }
+
+       return error;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int
+tmpfs_nmkdir(struct vop_nmkdir_args *v)
+{
+       struct vnode *dvp = v->a_dvp;
+       struct vnode **vpp = v->a_vpp;
+       struct namecache *ncp = v->a_nch->ncp;
+       struct vattr *vap = v->a_vap;
+       struct ucred *cred = v->a_cred;
+       int error;
+
+       KKASSERT(vap->va_type == VDIR);
+
+       vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY);
+       error = tmpfs_alloc_file(dvp, vpp, vap, ncp, cred, NULL);
+       if (error == 0) {
+               cache_setunresolved(v->a_nch);
+               cache_setvp(v->a_nch, *vpp);
+       }
+       vn_unlock(dvp);
+
+       return error;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int
+tmpfs_nrmdir(struct vop_nrmdir_args *v)
+{
+       struct vnode *dvp = v->a_dvp;
+       struct namecache *ncp = v->a_nch->ncp;
+       struct vnode *vp = ncp->nc_vp;
+
+       int error;
+       struct tmpfs_dirent *de;
+       struct tmpfs_mount *tmp;
+       struct tmpfs_node *dnode;
+       struct tmpfs_node *node;
+
+       vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY);
+       vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
+
+       tmp = VFS_TO_TMPFS(dvp->v_mount);
+       dnode = VP_TO_TMPFS_DIR(dvp);
+       node = VP_TO_TMPFS_DIR(vp);
+
+       /* Directories with more than two entries ('.' and '..') cannot be
+        * removed. */
+        if (node->tn_size > 0) {
+                error = ENOTEMPTY;
+                goto out;
+        }
+
+       if ((dnode->tn_flags & APPEND)
+           || (node->tn_flags & (NOUNLINK | IMMUTABLE | APPEND))) {
+               error = EPERM;
+               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_dir.tn_parent == dnode);
+
+       /* Get the directory entry associated with node (vp).  This was
+        * filled by tmpfs_lookup while looking up the entry. */
+       de = tmpfs_dir_lookup(dnode, node, ncp);
+       KKASSERT(TMPFS_DIRENT_MATCHES(de,
+           ncp->nc_name,
+           ncp->nc_nlen));
+
+       /* Check flags to see if we are allowed to remove the directory. */
+       if (dnode->tn_flags & APPEND
+               || node->tn_flags & (NOUNLINK | IMMUTABLE | APPEND)) {
+               error = EPERM;
+               goto out;
+       }
+
+
+       /* Detach the directory entry from the directory (dnode). */
+       tmpfs_dir_detach(dvp, de);
+
+       /* No vnode should be allocated for this entry from this point */
+       TMPFS_NODE_LOCK(node);
+       TMPFS_ASSERT_ELOCKED(node);
+       TMPFS_NODE_LOCK(dnode);
+       TMPFS_ASSERT_ELOCKED(dnode);
+
+       node->tn_links--;
+       node->tn_dir.tn_parent = NULL;
+       node->tn_status |= TMPFS_NODE_ACCESSED | TMPFS_NODE_CHANGED | \
+           TMPFS_NODE_MODIFIED;
+
+       dnode->tn_links--;
+       dnode->tn_status |= TMPFS_NODE_ACCESSED | \
+           TMPFS_NODE_CHANGED | TMPFS_NODE_MODIFIED;
+
+       TMPFS_NODE_UNLOCK(dnode);
+       TMPFS_NODE_UNLOCK(node);
+
+       /* 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);
+
+       /* Release the deleted vnode (will destroy the node, notify
+        * interested parties and clean it from the cache). */
+
+       TMPFS_NODE_LOCK(dnode);
+       dnode->tn_status |= TMPFS_NODE_CHANGED;
+       TMPFS_NODE_UNLOCK(dnode);
+       tmpfs_update(dvp);
+
+       cache_setunresolved(v->a_nch);
+       cache_setvp(v->a_nch, NULL);
+       cache_inval_vp(vp, CINV_DESTROY);
+       error = 0;
+
+out:
+       vn_unlock(vp);
+       vn_unlock(dvp);
+
+       return error;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int
+tmpfs_nsymlink(struct vop_nsymlink_args *v)
+{
+       struct vnode *dvp = v->a_dvp;
+       struct vnode **vpp = v->a_vpp;
+       struct namecache *ncp = v->a_nch->ncp;
+       struct vattr *vap = v->a_vap;
+       struct ucred *cred = v->a_cred;
+       char *target = v->a_target;
+       int error;
+
+       vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY);
+       vap->va_type = VLNK;
+       error = tmpfs_alloc_file(dvp, vpp, vap, ncp, cred, target);
+       if (error == 0) {
+               cache_setunresolved(v->a_nch);
+               cache_setvp(v->a_nch, *vpp);
+       }
+       vn_unlock(dvp);
+
+       return error;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int
+tmpfs_readdir(struct vop_readdir_args *v)
+{
+       struct vnode *vp = v->a_vp;
+       struct uio *uio = v->a_uio;
+       int *eofflag = v->a_eofflag;
+       off_t **cookies = v->a_cookies;
+       int *ncookies = v->a_ncookies;
+
+       int error;
+       off_t startoff;
+       off_t cnt = 0;
+       struct tmpfs_node *node;
+
+       /* This operation only makes sense on directory nodes. */
+       if (vp->v_type != VDIR)
+               return ENOTDIR;
+
+       vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
+
+       node = VP_TO_TMPFS_DIR(vp);
+       startoff = uio->uio_offset;
+
+       if (uio->uio_offset == TMPFS_DIRCOOKIE_DOT) {
+               error = tmpfs_dir_getdotdent(node, uio);
+               if (error != 0)
+                       goto outok;
+               cnt++;
+       }
+
+       if (uio->uio_offset == TMPFS_DIRCOOKIE_DOTDOT) {
+               error = tmpfs_dir_getdotdotdent(node, uio);
+               if (error != 0)
+                       goto outok;
+               cnt++;
+       }
+
+       error = tmpfs_dir_getdents(node, uio, &cnt);
+
+outok:
+       KKASSERT(error >= -1);
+
+       if (error == -1)
+               error = 0;
+
+       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_dir.tn_dirhead);
+                               } 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);
+       }
+       vn_unlock(vp);
+
+       return error;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int
+tmpfs_readlink(struct vop_readlink_args *v)
+{
+       struct vnode *vp = v->a_vp;
+       struct uio *uio = v->a_uio;
+
+       int error;
+       struct tmpfs_node *node;
+
+       KKASSERT(uio->uio_offset == 0);
+       KKASSERT(vp->v_type == VLNK);
+
+       node = VP_TO_TMPFS_NODE(vp);
+
+       error = uiomove(node->tn_link, MIN(node->tn_size, uio->uio_resid),
+           uio);
+       TMPFS_NODE_LOCK(node);
+       node->tn_status |= TMPFS_NODE_ACCESSED;
+       TMPFS_NODE_UNLOCK(node);
+
+       return error;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int
+tmpfs_inactive(struct vop_inactive_args *v)
+{
+       struct vnode *vp = v->a_vp;
+
+       struct tmpfs_node *node;
+
+       KKASSERT(vn_islocked(vp));
+
+       node = VP_TO_TMPFS_NODE(vp);
+
+       TMPFS_NODE_LOCK(node);
+       if (node->tn_links == 0 &&
+           (node->tn_vpstate & TMPFS_VNODE_DOOMED)) {
+               TMPFS_NODE_UNLOCK(node);
+               vrecycle(vp);
+       }
+       else
+               TMPFS_NODE_UNLOCK(node);
+
+       return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+int
+tmpfs_reclaim(struct vop_reclaim_args *v)
+{
+       struct vnode *vp = v->a_vp;
+
+       struct tmpfs_mount *tmp;
+       struct tmpfs_node *node;
+
+       node = VP_TO_TMPFS_NODE(vp);
+       tmp = VFS_TO_TMPFS(vp->v_mount);
+
+       vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
+       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). */
+       TMPFS_NODE_LOCK(node);
+       if (node->tn_links == 0 &&
+           (node->tn_vpstate & TMPFS_VNODE_ALLOCATING) == 0) {
+               node->tn_vpstate = TMPFS_VNODE_DOOMED;
+               TMPFS_NODE_UNLOCK(node);
+               tmpfs_free_node(tmp, node);
+       }
+       else
+               TMPFS_NODE_UNLOCK(node);
+
+       vn_unlock(vp);
+
+       KKASSERT(vp->v_data == NULL);
+       return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int
+tmpfs_print(struct vop_print_args *v)
+{
+       struct vnode *vp = v->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 %ju, status 0x%x\n",
+           node->tn_mode, node->tn_uid, node->tn_gid,
+           (uintmax_t)node->tn_size, node->tn_status);
+
+       if (vp->v_type == VFIFO)
+               fifo_printinfo(vp);
+
+       kprintf("\n");
+
+       return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int
+tmpfs_pathconf(struct vop_pathconf_args *v)
+{
+       int name = v->a_name;
+       register_t *retval = v->a_retval;
+
+       int error;
+
+       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;
+
+       case _PC_SYNC_IO:
+               *retval = 1;
+               break;
+
+       case _PC_FILESIZEBITS:
+               *retval = 0; /* XXX Don't know which value should I return. */
+               break;
+
+       default:
+               error = EINVAL;
+       }
+
+       return error;
+}
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * vnode operations vector used for files stored in a tmpfs file system.
+ */
+struct vop_ops tmpfs_vnode_vops = {
+       .vop_default =                  vop_defaultop,
+       .vop_getpages =                 vop_stdgetpages,
+       .vop_putpages =                 vop_stdputpages,
+       .vop_ncreate =                  tmpfs_ncreate,
+       .vop_nresolve =                 tmpfs_nresolve,
+       .vop_nlookupdotdot =            tmpfs_nlookupdotdot,
+       .vop_nmknod =                   tmpfs_nmknod,
+       .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_fsync =                    tmpfs_fsync,
+       .vop_nremove =                  tmpfs_nremove,
+       .vop_nlink =                    tmpfs_nlink,
+       .vop_nrename =                  tmpfs_nrename,
+       .vop_nmkdir =                   tmpfs_nmkdir,
+       .vop_nrmdir =                   tmpfs_nrmdir,
+       .vop_nsymlink =                 tmpfs_nsymlink,
+       .vop_readdir =                  tmpfs_readdir,
+       .vop_readlink =                 tmpfs_readlink,
+       .vop_inactive =                 tmpfs_inactive,
+       .vop_reclaim =                  tmpfs_reclaim,
+       .vop_print =                    tmpfs_print,
+       .vop_pathconf =                 tmpfs_pathconf,
+//     .vop_bmap =                     tmpfs_bmap,
+       .vop_bmap =                     (void *)vop_eopnotsupp,
+       .vop_strategy =                 tmpfs_strategy,
+       .vop_advlock =                  tmpfs_advlock,
+};
diff --git a/sys/vfs/tmpfs/tmpfs_vnops.h b/sys/vfs/tmpfs/tmpfs_vnops.h
new file mode 100644 (file)
index 0000000..1215bbc
--- /dev/null
@@ -0,0 +1,58 @@
+/*     $NetBSD: tmpfs_vnops.h,v 1.7 2005/12/03 17:34:44 christos Exp $ */
+
+/*-
+ * Copyright (c) 2005 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.
+ *
+ * $FreeBSD: src/sys/fs/tmpfs/tmpfs_vnops.h,v 1.3 2008/09/03 18:53:48 delphij Exp $
+ */
+
+#ifndef _VFS_TMPFS_TMPFS_VNOPS_H_
+#define _VFS_TMPFS_TMPFS_VNOPS_H_
+
+#if !defined(_KERNEL)
+#error not supposed to be exposed to userland.
+#endif
+
+/* --------------------------------------------------------------------- */
+
+extern struct vop_ops tmpfs_vnode_vops;
+extern struct vop_ops tmpfs_fifo_vops;
+
+/*
+ * Declarations for tmpfs_vnops.c.
+ */
+
+int tmpfs_access(struct vop_access_args *);
+int tmpfs_getattr(struct vop_getattr_args *);
+int tmpfs_setattr(struct vop_setattr_args *);
+int tmpfs_reclaim(struct vop_reclaim_args *);
+
+/* --------------------------------------------------------------------- */
+
+#endif /* _VFS_TMPFS_TMPFS_VNOPS_H_ */