tmpfs_vfsops.c revision 173570
1170808Sdelphij/*	$NetBSD: tmpfs_vfsops.c,v 1.10 2005/12/11 12:24:29 christos Exp $	*/
2170808Sdelphij
3170808Sdelphij/*
4170808Sdelphij * Copyright (c) 2005 The NetBSD Foundation, Inc.
5170808Sdelphij * All rights reserved.
6170808Sdelphij *
7170808Sdelphij * This code is derived from software contributed to The NetBSD Foundation
8170808Sdelphij * by Julio M. Merino Vidal, developed as part of Google's Summer of Code
9170808Sdelphij * 2005 program.
10170808Sdelphij *
11170808Sdelphij * Redistribution and use in source and binary forms, with or without
12170808Sdelphij * modification, are permitted provided that the following conditions
13170808Sdelphij * are met:
14170808Sdelphij * 1. Redistributions of source code must retain the above copyright
15170808Sdelphij *    notice, this list of conditions and the following disclaimer.
16170808Sdelphij * 2. Redistributions in binary form must reproduce the above copyright
17170808Sdelphij *    notice, this list of conditions and the following disclaimer in the
18170808Sdelphij *    documentation and/or other materials provided with the distribution.
19170808Sdelphij * 3. All advertising materials mentioning features or use of this software
20170808Sdelphij *    must display the following acknowledgement:
21170808Sdelphij *        This product includes software developed by the NetBSD
22170808Sdelphij *        Foundation, Inc. and its contributors.
23170808Sdelphij * 4. Neither the name of The NetBSD Foundation nor the names of its
24170808Sdelphij *    contributors may be used to endorse or promote products derived
25170808Sdelphij *    from this software without specific prior written permission.
26170808Sdelphij *
27170808Sdelphij * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
28170808Sdelphij * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
29170808Sdelphij * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
30170808Sdelphij * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
31170808Sdelphij * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
32170808Sdelphij * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
33170808Sdelphij * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
34170808Sdelphij * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
35170808Sdelphij * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
36170808Sdelphij * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
37170808Sdelphij * POSSIBILITY OF SUCH DAMAGE.
38170808Sdelphij */
39170808Sdelphij
40170808Sdelphij/*
41170808Sdelphij * Efficient memory file system.
42170808Sdelphij *
43170808Sdelphij * tmpfs is a file system that uses NetBSD's virtual memory sub-system
44170808Sdelphij * (the well-known UVM) to store file data and metadata in an efficient
45170808Sdelphij * way.  This means that it does not follow the structure of an on-disk
46170808Sdelphij * file system because it simply does not need to.  Instead, it uses
47170808Sdelphij * memory-specific data structures and algorithms to automatically
48170808Sdelphij * allocate and release resources.
49170808Sdelphij */
50170808Sdelphij#include <sys/cdefs.h>
51170808Sdelphij__FBSDID("$FreeBSD: head/sys/fs/tmpfs/tmpfs_vfsops.c 173570 2007-11-12 18:57:33Z delphij $");
52170808Sdelphij
53170808Sdelphij#include <sys/param.h>
54171308Sdelphij#include <sys/limits.h>
55170808Sdelphij#include <sys/lock.h>
56170808Sdelphij#include <sys/mutex.h>
57170808Sdelphij#include <sys/kernel.h>
58170808Sdelphij#include <sys/stat.h>
59170808Sdelphij#include <sys/systm.h>
60170808Sdelphij#include <sys/sysctl.h>
61170808Sdelphij
62170808Sdelphij#include <vm/vm.h>
63170808Sdelphij#include <vm/vm_object.h>
64170808Sdelphij#include <vm/vm_param.h>
65170808Sdelphij
66170808Sdelphij#include <fs/tmpfs/tmpfs.h>
67170808Sdelphij
68170808Sdelphij/*
69171070Sdelphij * Default permission for root node
70170808Sdelphij */
71170808Sdelphij#define TMPFS_DEFAULT_ROOT_MODE	(S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH)
72170808Sdelphij
73170808SdelphijMALLOC_DEFINE(M_TMPFSMNT, "tmpfs mount", "tmpfs mount structures");
74171087SdelphijMALLOC_DEFINE(M_TMPFSNAME, "tmpfs name", "tmpfs file names");
75170808Sdelphij
76170808Sdelphij/* --------------------------------------------------------------------- */
77170808Sdelphij
78170808Sdelphijstatic int	tmpfs_mount(struct mount *, struct thread *);
79170808Sdelphijstatic int	tmpfs_unmount(struct mount *, int, struct thread *);
80170808Sdelphijstatic int	tmpfs_root(struct mount *, int flags, struct vnode **,
81170808Sdelphij		    struct thread *);
82170808Sdelphijstatic int	tmpfs_fhtovp(struct mount *, struct fid *, struct vnode **);
83170808Sdelphijstatic int	tmpfs_statfs(struct mount *, struct statfs *, struct thread *);
84170808Sdelphij
85170808Sdelphij/* --------------------------------------------------------------------- */
86170808Sdelphij
87170808Sdelphijstatic const char *tmpfs_opts[] = {
88172442Sdelphij	"from", "size", "inodes", "uid", "gid", "mode", "export",
89170808Sdelphij	NULL
90170808Sdelphij};
91170808Sdelphij
92170808Sdelphij/* --------------------------------------------------------------------- */
93170808Sdelphij
94170808Sdelphij#define SWI_MAXMIB	3
95170808Sdelphij
96171029Sdelphijstatic u_int
97170808Sdelphijget_swpgtotal(void)
98170808Sdelphij{
99170808Sdelphij	struct xswdev xsd;
100170808Sdelphij	char *sname = "vm.swap_info";
101170808Sdelphij	int soid[SWI_MAXMIB], oid[2];
102171029Sdelphij	u_int unswdev, total, dmmax, nswapdev;
103170808Sdelphij	size_t mibi, len;
104170808Sdelphij
105170808Sdelphij	total = 0;
106170808Sdelphij
107170808Sdelphij	len = sizeof(dmmax);
108170808Sdelphij	if (kernel_sysctlbyname(curthread, "vm.dmmax", &dmmax, &len,
109170808Sdelphij				NULL, 0, NULL, 0) != 0)
110170808Sdelphij		return total;
111170808Sdelphij
112170808Sdelphij	len = sizeof(nswapdev);
113171070Sdelphij	if (kernel_sysctlbyname(curthread, "vm.nswapdev",
114170808Sdelphij				&nswapdev, &len,
115170808Sdelphij				NULL, 0, NULL, 0) != 0)
116170808Sdelphij		return total;
117170808Sdelphij
118170808Sdelphij	mibi = (SWI_MAXMIB - 1) * sizeof(int);
119170808Sdelphij	oid[0] = 0;
120170808Sdelphij	oid[1] = 3;
121170808Sdelphij
122171070Sdelphij	if (kernel_sysctl(curthread, oid, 2,
123170808Sdelphij			soid, &mibi, (void *)sname, strlen(sname),
124170808Sdelphij			NULL, 0) != 0)
125170808Sdelphij		return total;
126170808Sdelphij
127170808Sdelphij	mibi = (SWI_MAXMIB - 1);
128170808Sdelphij	for (unswdev = 0; unswdev < nswapdev; ++unswdev) {
129170808Sdelphij		soid[mibi] = unswdev;
130170808Sdelphij		len = sizeof(struct xswdev);
131171070Sdelphij		if (kernel_sysctl(curthread,
132171070Sdelphij				soid, mibi + 1, &xsd, &len, NULL, 0,
133170808Sdelphij				NULL, 0) != 0)
134170808Sdelphij			return total;
135170808Sdelphij		if (len == sizeof(struct xswdev))
136170808Sdelphij			total += (xsd.xsw_nblks - dmmax);
137170808Sdelphij	}
138170808Sdelphij
139170808Sdelphij	/* Not Reached */
140170808Sdelphij	return total;
141170808Sdelphij}
142170808Sdelphij
143170808Sdelphij/* --------------------------------------------------------------------- */
144171029Sdelphijstatic int
145171029Sdelphijtmpfs_node_ctor(void *mem, int size, void *arg, int flags)
146171029Sdelphij{
147171029Sdelphij	struct tmpfs_node *node = (struct tmpfs_node *)mem;
148171070Sdelphij
149171362Sdelphij	node->tn_gen++;
150171029Sdelphij	node->tn_size = 0;
151171029Sdelphij	node->tn_status = 0;
152171029Sdelphij	node->tn_flags = 0;
153171029Sdelphij	node->tn_links = 0;
154171029Sdelphij	node->tn_lockf = NULL;
155171029Sdelphij	node->tn_vnode = NULL;
156171029Sdelphij	node->tn_vpstate = 0;
157171029Sdelphij	node->tn_lookup_dirent = NULL;
158170808Sdelphij
159171029Sdelphij	return (0);
160171029Sdelphij}
161171029Sdelphij
162171029Sdelphijstatic void
163171029Sdelphijtmpfs_node_dtor(void *mem, int size, void *arg)
164171029Sdelphij{
165171029Sdelphij	struct tmpfs_node *node = (struct tmpfs_node *)mem;
166171029Sdelphij	node->tn_type = VNON;
167171029Sdelphij}
168171029Sdelphij
169171070Sdelphijstatic int
170171029Sdelphijtmpfs_node_init(void *mem, int size, int flags)
171171029Sdelphij{
172171029Sdelphij	struct tmpfs_node *node = (struct tmpfs_node *)mem;
173171029Sdelphij	node->tn_id = 0;
174171029Sdelphij
175171029Sdelphij	mtx_init(&node->tn_interlock, "tmpfs node interlock", NULL, MTX_DEF);
176171362Sdelphij	node->tn_gen = arc4random();
177171070Sdelphij
178171029Sdelphij	return (0);
179171029Sdelphij}
180171029Sdelphij
181171029Sdelphijstatic void
182171029Sdelphijtmpfs_node_fini(void *mem, int size)
183171029Sdelphij{
184171029Sdelphij	struct tmpfs_node *node = (struct tmpfs_node *)mem;
185171070Sdelphij
186171029Sdelphij	mtx_destroy(&node->tn_interlock);
187171029Sdelphij}
188171029Sdelphij
189170808Sdelphijstatic int
190171308Sdelphijtmpfs_mount(struct mount *mp, struct thread *td)
191170808Sdelphij{
192170808Sdelphij	struct tmpfs_mount *tmp;
193170808Sdelphij	struct tmpfs_node *root;
194170808Sdelphij	size_t pages, mem_size;
195170808Sdelphij	ino_t nodes;
196170808Sdelphij	int error;
197171308Sdelphij	/* Size counters. */
198171308Sdelphij	ino_t	nodes_max;
199171308Sdelphij	off_t	size_max;
200170808Sdelphij
201171308Sdelphij	/* Root node attributes. */
202171308Sdelphij	uid_t	root_uid;
203171308Sdelphij	gid_t	root_gid;
204171308Sdelphij	mode_t	root_mode;
205171308Sdelphij
206171308Sdelphij	struct vattr	va;
207171308Sdelphij
208170808Sdelphij	if (vfs_filteropt(mp->mnt_optnew, tmpfs_opts))
209170808Sdelphij		return (EINVAL);
210170808Sdelphij
211170808Sdelphij	if (mp->mnt_flag & MNT_UPDATE) {
212170808Sdelphij		/* XXX: There is no support yet to update file system
213170808Sdelphij		 * settings.  Should be added. */
214170808Sdelphij
215170808Sdelphij		return EOPNOTSUPP;
216170808Sdelphij	}
217170808Sdelphij
218172441Sdelphij	printf("WARNING: TMPFS is considered to be a highly experimental "
219172441Sdelphij		"feature in FreeBSD.\n");
220172441Sdelphij
221171308Sdelphij	vn_lock(mp->mnt_vnodecovered, LK_SHARED | LK_RETRY, td);
222171308Sdelphij	error = VOP_GETATTR(mp->mnt_vnodecovered, &va, mp->mnt_cred, td);
223171308Sdelphij	VOP_UNLOCK(mp->mnt_vnodecovered, 0, td);
224171308Sdelphij	if (error)
225171308Sdelphij		return (error);
226170808Sdelphij
227171308Sdelphij	if (mp->mnt_cred->cr_ruid != 0 ||
228171308Sdelphij	    vfs_scanopt(mp->mnt_optnew, "gid", "%d", &root_gid) != 1)
229171308Sdelphij		root_gid = va.va_gid;
230171308Sdelphij	if (mp->mnt_cred->cr_ruid != 0 ||
231171308Sdelphij	    vfs_scanopt(mp->mnt_optnew, "uid", "%d", &root_uid) != 1)
232171308Sdelphij		root_uid = va.va_uid;
233171308Sdelphij	if (mp->mnt_cred->cr_ruid != 0 ||
234173570Sdelphij	    vfs_scanopt(mp->mnt_optnew, "mode", "%ho", &root_mode) != 1)
235171308Sdelphij		root_mode = va.va_mode;
236171308Sdelphij	if(vfs_scanopt(mp->mnt_optnew, "inodes", "%d", &nodes_max) != 1)
237171308Sdelphij		nodes_max = 0;
238171308Sdelphij
239171070Sdelphij	if(vfs_scanopt(mp->mnt_optnew,
240171070Sdelphij			"size",
241171308Sdelphij			"%qu", &size_max) != 1)
242171308Sdelphij		size_max = 0;
243170808Sdelphij
244170808Sdelphij	/* Do not allow mounts if we do not have enough memory to preserve
245170808Sdelphij	 * the minimum reserved pages. */
246170808Sdelphij	mem_size = cnt.v_free_count + cnt.v_inactive_count + get_swpgtotal();
247170808Sdelphij	mem_size -= mem_size > cnt.v_wire_count ? cnt.v_wire_count : mem_size;
248170808Sdelphij	if (mem_size < TMPFS_PAGES_RESERVED)
249170808Sdelphij		return ENOSPC;
250170808Sdelphij
251170808Sdelphij	/* Get the maximum number of memory pages this file system is
252170808Sdelphij	 * allowed to use, based on the maximum size the user passed in
253170808Sdelphij	 * the mount structure.  A value of zero is treated as if the
254170808Sdelphij	 * maximum available space was requested. */
255171308Sdelphij	if (size_max < PAGE_SIZE || size_max >= SIZE_MAX)
256170808Sdelphij		pages = SIZE_MAX;
257170808Sdelphij	else
258171308Sdelphij		pages = howmany(size_max, PAGE_SIZE);
259170808Sdelphij	MPASS(pages > 0);
260170808Sdelphij
261171308Sdelphij	if (nodes_max <= 3)
262170808Sdelphij		nodes = 3 + pages * PAGE_SIZE / 1024;
263170808Sdelphij	else
264171308Sdelphij		nodes = nodes_max;
265170808Sdelphij	MPASS(nodes >= 3);
266170808Sdelphij
267170808Sdelphij	/* Allocate the tmpfs mount structure and fill it. */
268170808Sdelphij	tmp = (struct tmpfs_mount *)malloc(sizeof(struct tmpfs_mount),
269170808Sdelphij	    M_TMPFSMNT, M_WAITOK | M_ZERO);
270171070Sdelphij
271170808Sdelphij	mtx_init(&tmp->allnode_lock, "tmpfs allnode lock", NULL, MTX_DEF);
272170808Sdelphij	tmp->tm_nodes_max = nodes;
273170808Sdelphij	tmp->tm_nodes_inuse = 0;
274171570Sdelphij	tmp->tm_maxfilesize = (u_int64_t)(cnt.v_page_count + get_swpgtotal()) * PAGE_SIZE;
275170808Sdelphij	LIST_INIT(&tmp->tm_nodes_used);
276171070Sdelphij
277170808Sdelphij	tmp->tm_pages_max = pages;
278170808Sdelphij	tmp->tm_pages_used = 0;
279171362Sdelphij	tmp->tm_ino_unr = new_unrhdr(2, INT_MAX, &tmp->allnode_lock);
280171029Sdelphij	tmp->tm_dirent_pool = uma_zcreate(
281170808Sdelphij					"TMPFS dirent",
282170808Sdelphij					sizeof(struct tmpfs_dirent),
283171029Sdelphij					NULL, NULL, NULL, NULL,
284170808Sdelphij					UMA_ALIGN_PTR,
285171070Sdelphij					0);
286171029Sdelphij	tmp->tm_node_pool = uma_zcreate(
287170808Sdelphij					"TMPFS node",
288170808Sdelphij					sizeof(struct tmpfs_node),
289171029Sdelphij					tmpfs_node_ctor, tmpfs_node_dtor,
290171029Sdelphij					tmpfs_node_init, tmpfs_node_fini,
291170808Sdelphij					UMA_ALIGN_PTR,
292171308Sdelphij					0);
293170808Sdelphij
294170808Sdelphij	/* Allocate the root node. */
295171308Sdelphij	error = tmpfs_alloc_node(tmp, VDIR, root_uid,
296171308Sdelphij	    root_gid, root_mode & ALLPERMS, NULL, NULL,
297171308Sdelphij	    VNOVAL, td, &root);
298170808Sdelphij
299170808Sdelphij	if (error != 0 || root == NULL) {
300171029Sdelphij	    uma_zdestroy(tmp->tm_node_pool);
301171029Sdelphij	    uma_zdestroy(tmp->tm_dirent_pool);
302171362Sdelphij	    delete_unrhdr(tmp->tm_ino_unr);
303170808Sdelphij	    free(tmp, M_TMPFSMNT);
304170808Sdelphij	    return error;
305170808Sdelphij	}
306171362Sdelphij	KASSERT(root->tn_id == 2, ("tmpfs root with invalid ino: %d", root->tn_id));
307170808Sdelphij	tmp->tm_root = root;
308170808Sdelphij
309170808Sdelphij	MNT_ILOCK(mp);
310170808Sdelphij	mp->mnt_flag |= MNT_LOCAL;
311170808Sdelphij	mp->mnt_kern_flag |= MNTK_MPSAFE;
312170808Sdelphij	MNT_IUNLOCK(mp);
313171070Sdelphij
314170808Sdelphij	mp->mnt_data = tmp;
315170808Sdelphij	mp->mnt_stat.f_namemax = MAXNAMLEN;
316170808Sdelphij	vfs_getnewfsid(mp);
317170808Sdelphij	vfs_mountedfrom(mp, "tmpfs");
318170808Sdelphij
319170808Sdelphij	return 0;
320170808Sdelphij}
321170808Sdelphij
322170808Sdelphij/* --------------------------------------------------------------------- */
323170808Sdelphij
324170808Sdelphij/* ARGSUSED2 */
325170808Sdelphijstatic int
326170808Sdelphijtmpfs_unmount(struct mount *mp, int mntflags, struct thread *l)
327170808Sdelphij{
328170808Sdelphij	int error;
329170808Sdelphij	int flags = 0;
330170808Sdelphij	struct tmpfs_mount *tmp;
331170808Sdelphij	struct tmpfs_node *node;
332170808Sdelphij
333170808Sdelphij	/* Handle forced unmounts. */
334170808Sdelphij	if (mntflags & MNT_FORCE)
335170808Sdelphij		flags |= FORCECLOSE;
336170808Sdelphij
337170808Sdelphij	/* Finalize all pending I/O. */
338170808Sdelphij	error = vflush(mp, 0, flags, l);
339170808Sdelphij	if (error != 0)
340170808Sdelphij		return error;
341170808Sdelphij
342170808Sdelphij	tmp = VFS_TO_TMPFS(mp);
343171070Sdelphij
344170808Sdelphij	/* Free all associated data.  The loop iterates over the linked list
345170808Sdelphij	 * we have containing all used nodes.  For each of them that is
346170808Sdelphij	 * a directory, we free all its directory entries.  Note that after
347170808Sdelphij	 * freeing a node, it will automatically go to the available list,
348170808Sdelphij	 * so we will later have to iterate over it to release its items. */
349170808Sdelphij	node = LIST_FIRST(&tmp->tm_nodes_used);
350170808Sdelphij	while (node != NULL) {
351170808Sdelphij		struct tmpfs_node *next;
352170808Sdelphij
353170808Sdelphij		if (node->tn_type == VDIR) {
354170808Sdelphij			struct tmpfs_dirent *de;
355170808Sdelphij
356170808Sdelphij			de = TAILQ_FIRST(&node->tn_dir.tn_dirhead);
357170808Sdelphij			while (de != NULL) {
358170808Sdelphij				struct tmpfs_dirent *nde;
359170808Sdelphij
360170808Sdelphij				nde = TAILQ_NEXT(de, td_entries);
361170808Sdelphij				tmpfs_free_dirent(tmp, de, FALSE);
362170808Sdelphij				de = nde;
363170808Sdelphij				node->tn_size -= sizeof(struct tmpfs_dirent);
364170808Sdelphij			}
365170808Sdelphij		}
366170808Sdelphij
367170808Sdelphij		next = LIST_NEXT(node, tn_entries);
368170808Sdelphij		tmpfs_free_node(tmp, node);
369170808Sdelphij		node = next;
370170808Sdelphij	}
371170808Sdelphij
372171029Sdelphij	uma_zdestroy(tmp->tm_dirent_pool);
373171029Sdelphij	uma_zdestroy(tmp->tm_node_pool);
374171362Sdelphij	delete_unrhdr(tmp->tm_ino_unr);
375170808Sdelphij
376170808Sdelphij	mtx_destroy(&tmp->allnode_lock);
377170808Sdelphij	MPASS(tmp->tm_pages_used == 0);
378171308Sdelphij	MPASS(tmp->tm_nodes_inuse == 0);
379170808Sdelphij
380170808Sdelphij	/* Throw away the tmpfs_mount structure. */
381170808Sdelphij	free(mp->mnt_data, M_TMPFSMNT);
382170808Sdelphij	mp->mnt_data = NULL;
383171070Sdelphij
384170808Sdelphij	MNT_ILOCK(mp);
385170808Sdelphij	mp->mnt_flag &= ~MNT_LOCAL;
386170808Sdelphij	MNT_IUNLOCK(mp);
387170808Sdelphij	return 0;
388170808Sdelphij}
389170808Sdelphij
390170808Sdelphij/* --------------------------------------------------------------------- */
391170808Sdelphij
392170808Sdelphijstatic int
393170808Sdelphijtmpfs_root(struct mount *mp, int flags, struct vnode **vpp, struct thread *td)
394170808Sdelphij{
395170808Sdelphij	int error;
396171799Sdelphij	error = tmpfs_alloc_vp(mp, VFS_TO_TMPFS(mp)->tm_root, flags, vpp, td);
397170808Sdelphij
398170808Sdelphij	if (!error)
399171029Sdelphij		(*vpp)->v_vflag |= VV_ROOT;
400170808Sdelphij
401170808Sdelphij	return error;
402170808Sdelphij}
403170808Sdelphij
404170808Sdelphij/* --------------------------------------------------------------------- */
405170808Sdelphij
406170808Sdelphijstatic int
407170808Sdelphijtmpfs_fhtovp(struct mount *mp, struct fid *fhp, struct vnode **vpp)
408170808Sdelphij{
409170808Sdelphij	boolean_t found;
410170808Sdelphij	struct tmpfs_fid *tfhp;
411170808Sdelphij	struct tmpfs_mount *tmp;
412170808Sdelphij	struct tmpfs_node *node;
413170808Sdelphij
414170808Sdelphij	tmp = VFS_TO_TMPFS(mp);
415170808Sdelphij
416170808Sdelphij	tfhp = (struct tmpfs_fid *)fhp;
417170808Sdelphij	if (tfhp->tf_len != sizeof(struct tmpfs_fid))
418170808Sdelphij		return EINVAL;
419170808Sdelphij
420170808Sdelphij	if (tfhp->tf_id >= tmp->tm_nodes_max)
421170808Sdelphij		return EINVAL;
422170808Sdelphij
423170808Sdelphij	found = FALSE;
424170808Sdelphij
425170808Sdelphij	TMPFS_LOCK(tmp);
426170808Sdelphij	LIST_FOREACH(node, &tmp->tm_nodes_used, tn_entries) {
427170808Sdelphij		if (node->tn_id == tfhp->tf_id &&
428170808Sdelphij		    node->tn_gen == tfhp->tf_gen) {
429170808Sdelphij			found = TRUE;
430170808Sdelphij			break;
431170808Sdelphij		}
432170808Sdelphij	}
433170808Sdelphij	TMPFS_UNLOCK(tmp);
434170808Sdelphij
435171799Sdelphij	if (found)
436171799Sdelphij		return (tmpfs_alloc_vp(mp, node, LK_EXCLUSIVE, vpp, curthread));
437171799Sdelphij
438171799Sdelphij	return (EINVAL);
439170808Sdelphij}
440170808Sdelphij
441170808Sdelphij/* --------------------------------------------------------------------- */
442170808Sdelphij
443170808Sdelphij/* ARGSUSED2 */
444170808Sdelphijstatic int
445170808Sdelphijtmpfs_statfs(struct mount *mp, struct statfs *sbp, struct thread *l)
446170808Sdelphij{
447170808Sdelphij	fsfilcnt_t freenodes;
448170808Sdelphij	struct tmpfs_mount *tmp;
449170808Sdelphij
450170808Sdelphij	tmp = VFS_TO_TMPFS(mp);
451170808Sdelphij
452171070Sdelphij	sbp->f_iosize = PAGE_SIZE;
453170808Sdelphij	sbp->f_bsize = PAGE_SIZE;
454170808Sdelphij
455170808Sdelphij	sbp->f_blocks = TMPFS_PAGES_MAX(tmp);
456170808Sdelphij	sbp->f_bavail = sbp->f_bfree = TMPFS_PAGES_AVAIL(tmp);
457170808Sdelphij
458170808Sdelphij	freenodes = MIN(tmp->tm_nodes_max - tmp->tm_nodes_inuse,
459170808Sdelphij	    TMPFS_PAGES_AVAIL(tmp) * PAGE_SIZE / sizeof(struct tmpfs_node));
460170808Sdelphij
461170808Sdelphij	sbp->f_files = freenodes + tmp->tm_nodes_inuse;
462170808Sdelphij	sbp->f_ffree = freenodes;
463170808Sdelphij	/* sbp->f_owner = tmp->tn_uid; */
464170808Sdelphij
465170808Sdelphij	return 0;
466170808Sdelphij}
467170808Sdelphij
468170808Sdelphij/* --------------------------------------------------------------------- */
469170808Sdelphij
470170808Sdelphij/*
471170808Sdelphij * tmpfs vfs operations.
472170808Sdelphij */
473170808Sdelphij
474170808Sdelphijstruct vfsops tmpfs_vfsops = {
475170808Sdelphij	.vfs_mount =			tmpfs_mount,
476170808Sdelphij	.vfs_unmount =			tmpfs_unmount,
477170808Sdelphij	.vfs_root =			tmpfs_root,
478170808Sdelphij	.vfs_statfs =			tmpfs_statfs,
479170808Sdelphij	.vfs_fhtovp =			tmpfs_fhtovp,
480170808Sdelphij};
481170808SdelphijVFS_SET(tmpfs_vfsops, tmpfs, 0);
482