xref: /freebsd/sys/fs/tmpfs/tmpfs_vfsops.c (revision 0359a12eadcd66a9298e283fc5f3c90a9393322a)
1d1fa59e9SXin LI /*	$NetBSD: tmpfs_vfsops.c,v 1.10 2005/12/11 12:24:29 christos Exp $	*/
2d1fa59e9SXin LI 
3d1fa59e9SXin LI /*
4d1fa59e9SXin LI  * Copyright (c) 2005 The NetBSD Foundation, Inc.
5d1fa59e9SXin LI  * All rights reserved.
6d1fa59e9SXin LI  *
7d1fa59e9SXin LI  * This code is derived from software contributed to The NetBSD Foundation
8d1fa59e9SXin LI  * by Julio M. Merino Vidal, developed as part of Google's Summer of Code
9d1fa59e9SXin LI  * 2005 program.
10d1fa59e9SXin LI  *
11d1fa59e9SXin LI  * Redistribution and use in source and binary forms, with or without
12d1fa59e9SXin LI  * modification, are permitted provided that the following conditions
13d1fa59e9SXin LI  * are met:
14d1fa59e9SXin LI  * 1. Redistributions of source code must retain the above copyright
15d1fa59e9SXin LI  *    notice, this list of conditions and the following disclaimer.
16d1fa59e9SXin LI  * 2. Redistributions in binary form must reproduce the above copyright
17d1fa59e9SXin LI  *    notice, this list of conditions and the following disclaimer in the
18d1fa59e9SXin LI  *    documentation and/or other materials provided with the distribution.
19d1fa59e9SXin LI  * 3. All advertising materials mentioning features or use of this software
20d1fa59e9SXin LI  *    must display the following acknowledgement:
21d1fa59e9SXin LI  *        This product includes software developed by the NetBSD
22d1fa59e9SXin LI  *        Foundation, Inc. and its contributors.
23d1fa59e9SXin LI  * 4. Neither the name of The NetBSD Foundation nor the names of its
24d1fa59e9SXin LI  *    contributors may be used to endorse or promote products derived
25d1fa59e9SXin LI  *    from this software without specific prior written permission.
26d1fa59e9SXin LI  *
27d1fa59e9SXin LI  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
28d1fa59e9SXin LI  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
29d1fa59e9SXin LI  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
30d1fa59e9SXin LI  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
31d1fa59e9SXin LI  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
32d1fa59e9SXin LI  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
33d1fa59e9SXin LI  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
34d1fa59e9SXin LI  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
35d1fa59e9SXin LI  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
36d1fa59e9SXin LI  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
37d1fa59e9SXin LI  * POSSIBILITY OF SUCH DAMAGE.
38d1fa59e9SXin LI  */
39d1fa59e9SXin LI 
40d1fa59e9SXin LI /*
41d1fa59e9SXin LI  * Efficient memory file system.
42d1fa59e9SXin LI  *
43d1fa59e9SXin LI  * tmpfs is a file system that uses NetBSD's virtual memory sub-system
44d1fa59e9SXin LI  * (the well-known UVM) to store file data and metadata in an efficient
45d1fa59e9SXin LI  * way.  This means that it does not follow the structure of an on-disk
46d1fa59e9SXin LI  * file system because it simply does not need to.  Instead, it uses
47d1fa59e9SXin LI  * memory-specific data structures and algorithms to automatically
48d1fa59e9SXin LI  * allocate and release resources.
49d1fa59e9SXin LI  */
50d1fa59e9SXin LI #include <sys/cdefs.h>
51d1fa59e9SXin LI __FBSDID("$FreeBSD$");
52d1fa59e9SXin LI 
53d1fa59e9SXin LI #include <sys/param.h>
541df86a32SXin LI #include <sys/limits.h>
55d1fa59e9SXin LI #include <sys/lock.h>
56d1fa59e9SXin LI #include <sys/mutex.h>
57d1fa59e9SXin LI #include <sys/kernel.h>
58d1fa59e9SXin LI #include <sys/stat.h>
59d1fa59e9SXin LI #include <sys/systm.h>
60d1fa59e9SXin LI #include <sys/sysctl.h>
61d1fa59e9SXin LI 
62d1fa59e9SXin LI #include <vm/vm.h>
63d1fa59e9SXin LI #include <vm/vm_object.h>
64d1fa59e9SXin LI #include <vm/vm_param.h>
65d1fa59e9SXin LI 
66d1fa59e9SXin LI #include <fs/tmpfs/tmpfs.h>
67d1fa59e9SXin LI 
68d1fa59e9SXin LI /*
69d1fa59e9SXin LI  * Default permission for root node
70d1fa59e9SXin LI  */
71d1fa59e9SXin LI #define TMPFS_DEFAULT_ROOT_MODE	(S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH)
72d1fa59e9SXin LI 
73d1fa59e9SXin LI MALLOC_DEFINE(M_TMPFSMNT, "tmpfs mount", "tmpfs mount structures");
749b258fcaSXin LI MALLOC_DEFINE(M_TMPFSNAME, "tmpfs name", "tmpfs file names");
75d1fa59e9SXin LI 
76d1fa59e9SXin LI /* --------------------------------------------------------------------- */
77d1fa59e9SXin LI 
78d1fa59e9SXin LI static int	tmpfs_mount(struct mount *, struct thread *);
79d1fa59e9SXin LI static int	tmpfs_unmount(struct mount *, int, struct thread *);
80d1fa59e9SXin LI static int	tmpfs_root(struct mount *, int flags, struct vnode **,
81d1fa59e9SXin LI 		    struct thread *);
82d1fa59e9SXin LI static int	tmpfs_fhtovp(struct mount *, struct fid *, struct vnode **);
83d1fa59e9SXin LI static int	tmpfs_statfs(struct mount *, struct statfs *, struct thread *);
84d1fa59e9SXin LI 
85d1fa59e9SXin LI /* --------------------------------------------------------------------- */
86d1fa59e9SXin LI 
87d1fa59e9SXin LI static const char *tmpfs_opts[] = {
883543c1b4SXin LI 	"from", "size", "inodes", "uid", "gid", "mode", "export",
89d1fa59e9SXin LI 	NULL
90d1fa59e9SXin LI };
91d1fa59e9SXin LI 
92d1fa59e9SXin LI /* --------------------------------------------------------------------- */
93d1fa59e9SXin LI 
94d1fa59e9SXin LI #define SWI_MAXMIB	3
95d1fa59e9SXin LI 
967adb1776SXin LI static u_int
97d1fa59e9SXin LI get_swpgtotal(void)
98d1fa59e9SXin LI {
99d1fa59e9SXin LI 	struct xswdev xsd;
100d1fa59e9SXin LI 	char *sname = "vm.swap_info";
101d1fa59e9SXin LI 	int soid[SWI_MAXMIB], oid[2];
1027adb1776SXin LI 	u_int unswdev, total, dmmax, nswapdev;
103d1fa59e9SXin LI 	size_t mibi, len;
104d1fa59e9SXin LI 
105d1fa59e9SXin LI 	total = 0;
106d1fa59e9SXin LI 
107d1fa59e9SXin LI 	len = sizeof(dmmax);
108d1fa59e9SXin LI 	if (kernel_sysctlbyname(curthread, "vm.dmmax", &dmmax, &len,
109d1fa59e9SXin LI 				NULL, 0, NULL, 0) != 0)
110d1fa59e9SXin LI 		return total;
111d1fa59e9SXin LI 
112d1fa59e9SXin LI 	len = sizeof(nswapdev);
113d1fa59e9SXin LI 	if (kernel_sysctlbyname(curthread, "vm.nswapdev",
114d1fa59e9SXin LI 				&nswapdev, &len,
115d1fa59e9SXin LI 				NULL, 0, NULL, 0) != 0)
116d1fa59e9SXin LI 		return total;
117d1fa59e9SXin LI 
118d1fa59e9SXin LI 	mibi = (SWI_MAXMIB - 1) * sizeof(int);
119d1fa59e9SXin LI 	oid[0] = 0;
120d1fa59e9SXin LI 	oid[1] = 3;
121d1fa59e9SXin LI 
122d1fa59e9SXin LI 	if (kernel_sysctl(curthread, oid, 2,
123d1fa59e9SXin LI 			soid, &mibi, (void *)sname, strlen(sname),
124d1fa59e9SXin LI 			NULL, 0) != 0)
125d1fa59e9SXin LI 		return total;
126d1fa59e9SXin LI 
127d1fa59e9SXin LI 	mibi = (SWI_MAXMIB - 1);
128d1fa59e9SXin LI 	for (unswdev = 0; unswdev < nswapdev; ++unswdev) {
129d1fa59e9SXin LI 		soid[mibi] = unswdev;
130d1fa59e9SXin LI 		len = sizeof(struct xswdev);
131d1fa59e9SXin LI 		if (kernel_sysctl(curthread,
132d1fa59e9SXin LI 				soid, mibi + 1, &xsd, &len, NULL, 0,
133d1fa59e9SXin LI 				NULL, 0) != 0)
134d1fa59e9SXin LI 			return total;
135d1fa59e9SXin LI 		if (len == sizeof(struct xswdev))
136d1fa59e9SXin LI 			total += (xsd.xsw_nblks - dmmax);
137d1fa59e9SXin LI 	}
138d1fa59e9SXin LI 
139d1fa59e9SXin LI 	/* Not Reached */
140d1fa59e9SXin LI 	return total;
141d1fa59e9SXin LI }
142d1fa59e9SXin LI 
143d1fa59e9SXin LI /* --------------------------------------------------------------------- */
1447adb1776SXin LI static int
1457adb1776SXin LI tmpfs_node_ctor(void *mem, int size, void *arg, int flags)
1467adb1776SXin LI {
1477adb1776SXin LI 	struct tmpfs_node *node = (struct tmpfs_node *)mem;
1487adb1776SXin LI 
1497adb1776SXin LI 	node->tn_gen++;
1507adb1776SXin LI 	node->tn_size = 0;
1517adb1776SXin LI 	node->tn_status = 0;
1527adb1776SXin LI 	node->tn_flags = 0;
1537adb1776SXin LI 	node->tn_links = 0;
1547adb1776SXin LI 	node->tn_vnode = NULL;
1557adb1776SXin LI 	node->tn_vpstate = 0;
1567adb1776SXin LI 
1577adb1776SXin LI 	return (0);
1587adb1776SXin LI }
1597adb1776SXin LI 
1607adb1776SXin LI static void
1617adb1776SXin LI tmpfs_node_dtor(void *mem, int size, void *arg)
1627adb1776SXin LI {
1637adb1776SXin LI 	struct tmpfs_node *node = (struct tmpfs_node *)mem;
1647adb1776SXin LI 	node->tn_type = VNON;
1657adb1776SXin LI }
1667adb1776SXin LI 
1677adb1776SXin LI static int
1687adb1776SXin LI tmpfs_node_init(void *mem, int size, int flags)
1697adb1776SXin LI {
1707adb1776SXin LI 	struct tmpfs_node *node = (struct tmpfs_node *)mem;
1717adb1776SXin LI 	node->tn_id = 0;
1727adb1776SXin LI 
1737adb1776SXin LI 	mtx_init(&node->tn_interlock, "tmpfs node interlock", NULL, MTX_DEF);
1748d9a89a3SXin LI 	node->tn_gen = arc4random();
1757adb1776SXin LI 
1767adb1776SXin LI 	return (0);
1777adb1776SXin LI }
1787adb1776SXin LI 
1797adb1776SXin LI static void
1807adb1776SXin LI tmpfs_node_fini(void *mem, int size)
1817adb1776SXin LI {
1827adb1776SXin LI 	struct tmpfs_node *node = (struct tmpfs_node *)mem;
1837adb1776SXin LI 
1847adb1776SXin LI 	mtx_destroy(&node->tn_interlock);
1857adb1776SXin LI }
186d1fa59e9SXin LI 
187d1fa59e9SXin LI static int
1881df86a32SXin LI tmpfs_mount(struct mount *mp, struct thread *td)
189d1fa59e9SXin LI {
190d1fa59e9SXin LI 	struct tmpfs_mount *tmp;
191d1fa59e9SXin LI 	struct tmpfs_node *root;
192d1fa59e9SXin LI 	size_t pages, mem_size;
193d1fa59e9SXin LI 	ino_t nodes;
194d1fa59e9SXin LI 	int error;
1951df86a32SXin LI 	/* Size counters. */
1961df86a32SXin LI 	ino_t	nodes_max;
197745973bdSXin LI 	size_t	size_max;
1981df86a32SXin LI 
1991df86a32SXin LI 	/* Root node attributes. */
2001df86a32SXin LI 	uid_t	root_uid;
2011df86a32SXin LI 	gid_t	root_gid;
2021df86a32SXin LI 	mode_t	root_mode;
2031df86a32SXin LI 
2041df86a32SXin LI 	struct vattr	va;
205d1fa59e9SXin LI 
206d1fa59e9SXin LI 	if (vfs_filteropt(mp->mnt_optnew, tmpfs_opts))
207d1fa59e9SXin LI 		return (EINVAL);
208d1fa59e9SXin LI 
209d1fa59e9SXin LI 	if (mp->mnt_flag & MNT_UPDATE) {
210d1fa59e9SXin LI 		/* XXX: There is no support yet to update file system
211d1fa59e9SXin LI 		 * settings.  Should be added. */
212d1fa59e9SXin LI 
213d1fa59e9SXin LI 		return EOPNOTSUPP;
214d1fa59e9SXin LI 	}
215d1fa59e9SXin LI 
216386c9692SXin LI 	printf("WARNING: TMPFS is considered to be a highly experimental "
217386c9692SXin LI 	    "feature in FreeBSD.\n");
218386c9692SXin LI 
219cb05b60aSAttilio Rao 	vn_lock(mp->mnt_vnodecovered, LK_SHARED | LK_RETRY);
2200359a12eSAttilio Rao 	error = VOP_GETATTR(mp->mnt_vnodecovered, &va, mp->mnt_cred);
22122db15c0SAttilio Rao 	VOP_UNLOCK(mp->mnt_vnodecovered, 0);
2221df86a32SXin LI 	if (error)
2231df86a32SXin LI 		return (error);
2241df86a32SXin LI 
2251df86a32SXin LI 	if (mp->mnt_cred->cr_ruid != 0 ||
2261df86a32SXin LI 	    vfs_scanopt(mp->mnt_optnew, "gid", "%d", &root_gid) != 1)
2271df86a32SXin LI 		root_gid = va.va_gid;
2281df86a32SXin LI 	if (mp->mnt_cred->cr_ruid != 0 ||
2291df86a32SXin LI 	    vfs_scanopt(mp->mnt_optnew, "uid", "%d", &root_uid) != 1)
2301df86a32SXin LI 		root_uid = va.va_uid;
2311df86a32SXin LI 	if (mp->mnt_cred->cr_ruid != 0 ||
232eed4ee29SXin LI 	    vfs_scanopt(mp->mnt_optnew, "mode", "%ho", &root_mode) != 1)
2331df86a32SXin LI 		root_mode = va.va_mode;
2341df86a32SXin LI 	if (vfs_scanopt(mp->mnt_optnew, "inodes", "%d", &nodes_max) != 1)
2351df86a32SXin LI 		nodes_max = 0;
236e0f51ae7SXin LI 	if (vfs_scanopt(mp->mnt_optnew, "size", "%qu", &size_max) != 1)
2371df86a32SXin LI 		size_max = 0;
238d1fa59e9SXin LI 
239d1fa59e9SXin LI 	/* Do not allow mounts if we do not have enough memory to preserve
240d1fa59e9SXin LI 	 * the minimum reserved pages. */
241d1fa59e9SXin LI 	mem_size = cnt.v_free_count + cnt.v_inactive_count + get_swpgtotal();
242d1fa59e9SXin LI 	mem_size -= mem_size > cnt.v_wire_count ? cnt.v_wire_count : mem_size;
243d1fa59e9SXin LI 	if (mem_size < TMPFS_PAGES_RESERVED)
244d1fa59e9SXin LI 		return ENOSPC;
245d1fa59e9SXin LI 
246d1fa59e9SXin LI 	/* Get the maximum number of memory pages this file system is
247d1fa59e9SXin LI 	 * allowed to use, based on the maximum size the user passed in
248d1fa59e9SXin LI 	 * the mount structure.  A value of zero is treated as if the
249d1fa59e9SXin LI 	 * maximum available space was requested. */
2501df86a32SXin LI 	if (size_max < PAGE_SIZE || size_max >= SIZE_MAX)
251d1fa59e9SXin LI 		pages = SIZE_MAX;
252d1fa59e9SXin LI 	else
2531df86a32SXin LI 		pages = howmany(size_max, PAGE_SIZE);
254d1fa59e9SXin LI 	MPASS(pages > 0);
255d1fa59e9SXin LI 
2561df86a32SXin LI 	if (nodes_max <= 3)
257d1fa59e9SXin LI 		nodes = 3 + pages * PAGE_SIZE / 1024;
258d1fa59e9SXin LI 	else
2591df86a32SXin LI 		nodes = nodes_max;
260d1fa59e9SXin LI 	MPASS(nodes >= 3);
261d1fa59e9SXin LI 
262d1fa59e9SXin LI 	/* Allocate the tmpfs mount structure and fill it. */
263d1fa59e9SXin LI 	tmp = (struct tmpfs_mount *)malloc(sizeof(struct tmpfs_mount),
264d1fa59e9SXin LI 	    M_TMPFSMNT, M_WAITOK | M_ZERO);
265d1fa59e9SXin LI 
266d1fa59e9SXin LI 	mtx_init(&tmp->allnode_lock, "tmpfs allnode lock", NULL, MTX_DEF);
267d1fa59e9SXin LI 	tmp->tm_nodes_max = nodes;
268d1fa59e9SXin LI 	tmp->tm_nodes_inuse = 0;
269f62e5595SXin LI 	tmp->tm_maxfilesize = (u_int64_t)(cnt.v_page_count + get_swpgtotal()) * PAGE_SIZE;
270d1fa59e9SXin LI 	LIST_INIT(&tmp->tm_nodes_used);
271d1fa59e9SXin LI 
272d1fa59e9SXin LI 	tmp->tm_pages_max = pages;
273d1fa59e9SXin LI 	tmp->tm_pages_used = 0;
2748d9a89a3SXin LI 	tmp->tm_ino_unr = new_unrhdr(2, INT_MAX, &tmp->allnode_lock);
275e0f51ae7SXin LI 	tmp->tm_dirent_pool = uma_zcreate("TMPFS dirent",
276d1fa59e9SXin LI 	    sizeof(struct tmpfs_dirent),
2777adb1776SXin LI 	    NULL, NULL, NULL, NULL,
278e0f51ae7SXin LI 	    UMA_ALIGN_PTR, 0);
279e0f51ae7SXin LI 	tmp->tm_node_pool = uma_zcreate("TMPFS node",
280d1fa59e9SXin LI 	    sizeof(struct tmpfs_node),
2817adb1776SXin LI 	    tmpfs_node_ctor, tmpfs_node_dtor,
2827adb1776SXin LI 	    tmpfs_node_init, tmpfs_node_fini,
283e0f51ae7SXin LI 	    UMA_ALIGN_PTR, 0);
284d1fa59e9SXin LI 
285d1fa59e9SXin LI 	/* Allocate the root node. */
2861df86a32SXin LI 	error = tmpfs_alloc_node(tmp, VDIR, root_uid,
2871df86a32SXin LI 	    root_gid, root_mode & ALLPERMS, NULL, NULL,
2881df86a32SXin LI 	    VNOVAL, td, &root);
289d1fa59e9SXin LI 
290d1fa59e9SXin LI 	if (error != 0 || root == NULL) {
2917adb1776SXin LI 	    uma_zdestroy(tmp->tm_node_pool);
2927adb1776SXin LI 	    uma_zdestroy(tmp->tm_dirent_pool);
2938d9a89a3SXin LI 	    delete_unrhdr(tmp->tm_ino_unr);
294d1fa59e9SXin LI 	    free(tmp, M_TMPFSMNT);
295d1fa59e9SXin LI 	    return error;
296d1fa59e9SXin LI 	}
2978d9a89a3SXin LI 	KASSERT(root->tn_id == 2, ("tmpfs root with invalid ino: %d", root->tn_id));
298d1fa59e9SXin LI 	tmp->tm_root = root;
299d1fa59e9SXin LI 
300d1fa59e9SXin LI 	MNT_ILOCK(mp);
301d1fa59e9SXin LI 	mp->mnt_flag |= MNT_LOCAL;
302d1fa59e9SXin LI 	mp->mnt_kern_flag |= MNTK_MPSAFE;
303d1fa59e9SXin LI 	MNT_IUNLOCK(mp);
304d1fa59e9SXin LI 
305d1fa59e9SXin LI 	mp->mnt_data = tmp;
306d1fa59e9SXin LI 	mp->mnt_stat.f_namemax = MAXNAMLEN;
307d1fa59e9SXin LI 	vfs_getnewfsid(mp);
308d1fa59e9SXin LI 	vfs_mountedfrom(mp, "tmpfs");
309d1fa59e9SXin LI 
310d1fa59e9SXin LI 	return 0;
311d1fa59e9SXin LI }
312d1fa59e9SXin LI 
313d1fa59e9SXin LI /* --------------------------------------------------------------------- */
314d1fa59e9SXin LI 
315d1fa59e9SXin LI /* ARGSUSED2 */
316d1fa59e9SXin LI static int
317d1fa59e9SXin LI tmpfs_unmount(struct mount *mp, int mntflags, struct thread *l)
318d1fa59e9SXin LI {
319d1fa59e9SXin LI 	int error;
320d1fa59e9SXin LI 	int flags = 0;
321d1fa59e9SXin LI 	struct tmpfs_mount *tmp;
322d1fa59e9SXin LI 	struct tmpfs_node *node;
323d1fa59e9SXin LI 
324d1fa59e9SXin LI 	/* Handle forced unmounts. */
325d1fa59e9SXin LI 	if (mntflags & MNT_FORCE)
326d1fa59e9SXin LI 		flags |= FORCECLOSE;
327d1fa59e9SXin LI 
328d1fa59e9SXin LI 	/* Finalize all pending I/O. */
329d1fa59e9SXin LI 	error = vflush(mp, 0, flags, l);
330d1fa59e9SXin LI 	if (error != 0)
331d1fa59e9SXin LI 		return error;
332d1fa59e9SXin LI 
333d1fa59e9SXin LI 	tmp = VFS_TO_TMPFS(mp);
334d1fa59e9SXin LI 
335d1fa59e9SXin LI 	/* Free all associated data.  The loop iterates over the linked list
336d1fa59e9SXin LI 	 * we have containing all used nodes.  For each of them that is
337d1fa59e9SXin LI 	 * a directory, we free all its directory entries.  Note that after
338d1fa59e9SXin LI 	 * freeing a node, it will automatically go to the available list,
339d1fa59e9SXin LI 	 * so we will later have to iterate over it to release its items. */
340d1fa59e9SXin LI 	node = LIST_FIRST(&tmp->tm_nodes_used);
341d1fa59e9SXin LI 	while (node != NULL) {
342d1fa59e9SXin LI 		struct tmpfs_node *next;
343d1fa59e9SXin LI 
344d1fa59e9SXin LI 		if (node->tn_type == VDIR) {
345d1fa59e9SXin LI 			struct tmpfs_dirent *de;
346d1fa59e9SXin LI 
347d1fa59e9SXin LI 			de = TAILQ_FIRST(&node->tn_dir.tn_dirhead);
348d1fa59e9SXin LI 			while (de != NULL) {
349d1fa59e9SXin LI 				struct tmpfs_dirent *nde;
350d1fa59e9SXin LI 
351d1fa59e9SXin LI 				nde = TAILQ_NEXT(de, td_entries);
352d1fa59e9SXin LI 				tmpfs_free_dirent(tmp, de, FALSE);
353d1fa59e9SXin LI 				de = nde;
354d1fa59e9SXin LI 				node->tn_size -= sizeof(struct tmpfs_dirent);
355d1fa59e9SXin LI 			}
356d1fa59e9SXin LI 		}
357d1fa59e9SXin LI 
358d1fa59e9SXin LI 		next = LIST_NEXT(node, tn_entries);
359d1fa59e9SXin LI 		tmpfs_free_node(tmp, node);
360d1fa59e9SXin LI 		node = next;
361d1fa59e9SXin LI 	}
362d1fa59e9SXin LI 
3637adb1776SXin LI 	uma_zdestroy(tmp->tm_dirent_pool);
3647adb1776SXin LI 	uma_zdestroy(tmp->tm_node_pool);
3658d9a89a3SXin LI 	delete_unrhdr(tmp->tm_ino_unr);
366d1fa59e9SXin LI 
367d1fa59e9SXin LI 	mtx_destroy(&tmp->allnode_lock);
368d1fa59e9SXin LI 	MPASS(tmp->tm_pages_used == 0);
3691df86a32SXin LI 	MPASS(tmp->tm_nodes_inuse == 0);
370d1fa59e9SXin LI 
371d1fa59e9SXin LI 	/* Throw away the tmpfs_mount structure. */
372d1fa59e9SXin LI 	free(mp->mnt_data, M_TMPFSMNT);
373d1fa59e9SXin LI 	mp->mnt_data = NULL;
374d1fa59e9SXin LI 
375d1fa59e9SXin LI 	MNT_ILOCK(mp);
376d1fa59e9SXin LI 	mp->mnt_flag &= ~MNT_LOCAL;
377d1fa59e9SXin LI 	MNT_IUNLOCK(mp);
378d1fa59e9SXin LI 	return 0;
379d1fa59e9SXin LI }
380d1fa59e9SXin LI 
381d1fa59e9SXin LI /* --------------------------------------------------------------------- */
382d1fa59e9SXin LI 
383d1fa59e9SXin LI static int
384d1fa59e9SXin LI tmpfs_root(struct mount *mp, int flags, struct vnode **vpp, struct thread *td)
385d1fa59e9SXin LI {
386d1fa59e9SXin LI 	int error;
3870ae6383dSXin LI 	error = tmpfs_alloc_vp(mp, VFS_TO_TMPFS(mp)->tm_root, flags, vpp, td);
388d1fa59e9SXin LI 
389d1fa59e9SXin LI 	if (!error)
3907adb1776SXin LI 		(*vpp)->v_vflag |= VV_ROOT;
391d1fa59e9SXin LI 
392d1fa59e9SXin LI 	return error;
393d1fa59e9SXin LI }
394d1fa59e9SXin LI 
395d1fa59e9SXin LI /* --------------------------------------------------------------------- */
396d1fa59e9SXin LI 
397d1fa59e9SXin LI static int
398d1fa59e9SXin LI tmpfs_fhtovp(struct mount *mp, struct fid *fhp, struct vnode **vpp)
399d1fa59e9SXin LI {
400d1fa59e9SXin LI 	boolean_t found;
401d1fa59e9SXin LI 	struct tmpfs_fid *tfhp;
402d1fa59e9SXin LI 	struct tmpfs_mount *tmp;
403d1fa59e9SXin LI 	struct tmpfs_node *node;
404d1fa59e9SXin LI 
405d1fa59e9SXin LI 	tmp = VFS_TO_TMPFS(mp);
406d1fa59e9SXin LI 
407d1fa59e9SXin LI 	tfhp = (struct tmpfs_fid *)fhp;
408d1fa59e9SXin LI 	if (tfhp->tf_len != sizeof(struct tmpfs_fid))
409d1fa59e9SXin LI 		return EINVAL;
410d1fa59e9SXin LI 
411d1fa59e9SXin LI 	if (tfhp->tf_id >= tmp->tm_nodes_max)
412d1fa59e9SXin LI 		return EINVAL;
413d1fa59e9SXin LI 
414d1fa59e9SXin LI 	found = FALSE;
415d1fa59e9SXin LI 
416d1fa59e9SXin LI 	TMPFS_LOCK(tmp);
417d1fa59e9SXin LI 	LIST_FOREACH(node, &tmp->tm_nodes_used, tn_entries) {
418d1fa59e9SXin LI 		if (node->tn_id == tfhp->tf_id &&
419d1fa59e9SXin LI 		    node->tn_gen == tfhp->tf_gen) {
420d1fa59e9SXin LI 			found = TRUE;
421d1fa59e9SXin LI 			break;
422d1fa59e9SXin LI 		}
423d1fa59e9SXin LI 	}
424d1fa59e9SXin LI 	TMPFS_UNLOCK(tmp);
425d1fa59e9SXin LI 
4260ae6383dSXin LI 	if (found)
4270ae6383dSXin LI 		return (tmpfs_alloc_vp(mp, node, LK_EXCLUSIVE, vpp, curthread));
4280ae6383dSXin LI 
4290ae6383dSXin LI 	return (EINVAL);
430d1fa59e9SXin LI }
431d1fa59e9SXin LI 
432d1fa59e9SXin LI /* --------------------------------------------------------------------- */
433d1fa59e9SXin LI 
434d1fa59e9SXin LI /* ARGSUSED2 */
435d1fa59e9SXin LI static int
436d1fa59e9SXin LI tmpfs_statfs(struct mount *mp, struct statfs *sbp, struct thread *l)
437d1fa59e9SXin LI {
438d1fa59e9SXin LI 	fsfilcnt_t freenodes;
439d1fa59e9SXin LI 	struct tmpfs_mount *tmp;
440d1fa59e9SXin LI 
441d1fa59e9SXin LI 	tmp = VFS_TO_TMPFS(mp);
442d1fa59e9SXin LI 
443d1fa59e9SXin LI 	sbp->f_iosize = PAGE_SIZE;
444d1fa59e9SXin LI 	sbp->f_bsize = PAGE_SIZE;
445d1fa59e9SXin LI 
446d1fa59e9SXin LI 	sbp->f_blocks = TMPFS_PAGES_MAX(tmp);
447d1fa59e9SXin LI 	sbp->f_bavail = sbp->f_bfree = TMPFS_PAGES_AVAIL(tmp);
448d1fa59e9SXin LI 
449d1fa59e9SXin LI 	freenodes = MIN(tmp->tm_nodes_max - tmp->tm_nodes_inuse,
450d1fa59e9SXin LI 	    TMPFS_PAGES_AVAIL(tmp) * PAGE_SIZE / sizeof(struct tmpfs_node));
451d1fa59e9SXin LI 
452d1fa59e9SXin LI 	sbp->f_files = freenodes + tmp->tm_nodes_inuse;
453d1fa59e9SXin LI 	sbp->f_ffree = freenodes;
454d1fa59e9SXin LI 	/* sbp->f_owner = tmp->tn_uid; */
455d1fa59e9SXin LI 
456d1fa59e9SXin LI 	return 0;
457d1fa59e9SXin LI }
458d1fa59e9SXin LI 
459d1fa59e9SXin LI /* --------------------------------------------------------------------- */
460d1fa59e9SXin LI 
461d1fa59e9SXin LI /*
462d1fa59e9SXin LI  * tmpfs vfs operations.
463d1fa59e9SXin LI  */
464d1fa59e9SXin LI 
465d1fa59e9SXin LI struct vfsops tmpfs_vfsops = {
466d1fa59e9SXin LI 	.vfs_mount =			tmpfs_mount,
467d1fa59e9SXin LI 	.vfs_unmount =			tmpfs_unmount,
468d1fa59e9SXin LI 	.vfs_root =			tmpfs_root,
469d1fa59e9SXin LI 	.vfs_statfs =			tmpfs_statfs,
470d1fa59e9SXin LI 	.vfs_fhtovp =			tmpfs_fhtovp,
471d1fa59e9SXin LI };
472d1fa59e9SXin LI VFS_SET(tmpfs_vfsops, tmpfs, 0);
473