mount_null \
mount_portal \
mount_std \
+ mount_tmpfs \
mount_udf \
mount_union \
mountd \
.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 ,
--- /dev/null
+#
+# $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>
--- /dev/null
+.\" $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.
--- /dev/null
+/* $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
--- /dev/null
+/* $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_ */
shells.5 \
stab.5 \
sysctl.conf.5 \
+ tmpfs.5 \
utf2.5 \
utf8.5 \
utmp.5 \
--- /dev/null
+.\"
+.\" 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.
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
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
NFS
NWFS
USERFS
+TMPFS
HAMMER
# If you are following the conditions in the copyright,
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
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
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
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
${SYS}/emulation/posix4/*.[ch] \
${SYS}/vfs/mfs/*.[ch] \
${SYS}/vfs/ufs/*.[ch] \
+ ${SYS}/vfs/tmpfs/*.[ch] \
${SYS}/vm/*.[ch] \
${SYS}/sys/*.[ch]
${SYS}/vfs/specfs \
${SYS}/vfs/union \
${SYS}/vfs/ufs \
- ${SYS}/vfs/mfs
+ ${SYS}/vfs/mfs \
+ ${SYS}/vfs/tmpfs
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
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
#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 */
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
};
/*
SUBDIR=fifofs msdosfs portal gnu nfs procfs \
hpfs ntfs smbfs isofs fdesc mfs nwfs udf \
- nullfs hammer
+ nullfs hammer tmpfs
.include <bsd.subdir.mk>
--- /dev/null
+
+KMOD= tmpfs
+SRCS= tmpfs_vnops.c tmpfs_subr.c \
+ tmpfs_fifoops.c tmpfs_vfsops.c
+NOMAN=
+
+.include <bsd.kmod.mk>
--- /dev/null
+/* $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_ */
--- /dev/null
+/* $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_ */
--- /dev/null
+/* $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,
+};
--- /dev/null
+/* $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_ */
--- /dev/null
+/* $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;
+}
--- /dev/null
+/* $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);
--- /dev/null
+/* $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,
+};
--- /dev/null
+/* $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_ */