tmpfs_vfsops.c revision 1.30
1258945Sroberto/*	$NetBSD: tmpfs_vfsops.c,v 1.30 2007/11/06 19:50:55 ad Exp $	*/
2258945Sroberto
3258945Sroberto/*
4258945Sroberto * Copyright (c) 2005, 2006, 2007 The NetBSD Foundation, Inc.
5258945Sroberto * All rights reserved.
6258945Sroberto *
7258945Sroberto * This code is derived from software contributed to The NetBSD Foundation
8258945Sroberto * by Julio M. Merino Vidal, developed as part of Google's Summer of Code
9258945Sroberto * 2005 program.
10258945Sroberto *
11258945Sroberto * Redistribution and use in source and binary forms, with or without
12258945Sroberto * modification, are permitted provided that the following conditions
13258945Sroberto * are met:
14258945Sroberto * 1. Redistributions of source code must retain the above copyright
15258945Sroberto *    notice, this list of conditions and the following disclaimer.
16258945Sroberto * 2. Redistributions in binary form must reproduce the above copyright
17258945Sroberto *    notice, this list of conditions and the following disclaimer in the
18258945Sroberto *    documentation and/or other materials provided with the distribution.
19258945Sroberto * 3. All advertising materials mentioning features or use of this software
20258945Sroberto *    must display the following acknowledgement:
21258945Sroberto *        This product includes software developed by the NetBSD
22258945Sroberto *        Foundation, Inc. and its contributors.
23258945Sroberto * 4. Neither the name of The NetBSD Foundation nor the names of its
24258945Sroberto *    contributors may be used to endorse or promote products derived
25258945Sroberto *    from this software without specific prior written permission.
26258945Sroberto *
27258945Sroberto * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
28258945Sroberto * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
29258945Sroberto * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
30258945Sroberto * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
31258945Sroberto * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
32258945Sroberto * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
33258945Sroberto * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
34258945Sroberto * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
35258945Sroberto * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
36258945Sroberto * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
37258945Sroberto * POSSIBILITY OF SUCH DAMAGE.
38258945Sroberto */
39258945Sroberto
40258945Sroberto/*
41258945Sroberto * Efficient memory file system.
42258945Sroberto *
43258945Sroberto * tmpfs is a file system that uses NetBSD's virtual memory sub-system
44258945Sroberto * (the well-known UVM) to store file data and metadata in an efficient
45258945Sroberto * way.  This means that it does not follow the structure of an on-disk
46258945Sroberto * file system because it simply does not need to.  Instead, it uses
47258945Sroberto * memory-specific data structures and algorithms to automatically
48258945Sroberto * allocate and release resources.
49258945Sroberto */
50258945Sroberto
51258945Sroberto#include <sys/cdefs.h>
52258945Sroberto__KERNEL_RCSID(0, "$NetBSD: tmpfs_vfsops.c,v 1.30 2007/11/06 19:50:55 ad Exp $");
53258945Sroberto
54258945Sroberto#include <sys/param.h>
55258945Sroberto#include <sys/types.h>
56258945Sroberto#include <sys/kmem.h>
57258945Sroberto#include <sys/mount.h>
58258945Sroberto#include <sys/stat.h>
59258945Sroberto#include <sys/systm.h>
60258945Sroberto#include <sys/vnode.h>
61258945Sroberto#include <sys/proc.h>
62258945Sroberto
63258945Sroberto#include <fs/tmpfs/tmpfs.h>
64258945Sroberto
65258945Sroberto/* --------------------------------------------------------------------- */
66258945Sroberto
67258945Srobertostatic int	tmpfs_mount(struct mount *, const char *, void *, size_t *,
68258945Sroberto		    struct lwp *);
69258945Srobertostatic int	tmpfs_start(struct mount *, int, struct lwp *);
70258945Srobertostatic int	tmpfs_unmount(struct mount *, int, struct lwp *);
71258945Srobertostatic int	tmpfs_root(struct mount *, struct vnode **);
72258945Srobertostatic int	tmpfs_quotactl(struct mount *, int, uid_t, void *,
73258945Sroberto		    struct lwp *);
74258945Srobertostatic int	tmpfs_vget(struct mount *, ino_t, struct vnode **);
75258945Srobertostatic int	tmpfs_fhtovp(struct mount *, struct fid *, struct vnode **);
76258945Srobertostatic int	tmpfs_vptofh(struct vnode *, struct fid *, size_t *);
77258945Srobertostatic int	tmpfs_statvfs(struct mount *, struct statvfs *, struct lwp *);
78258945Srobertostatic int	tmpfs_sync(struct mount *, int, kauth_cred_t, struct lwp *);
79258945Srobertostatic void	tmpfs_init(void);
80258945Srobertostatic void	tmpfs_done(void);
81258945Srobertostatic int	tmpfs_snapshot(struct mount *, struct vnode *,
82258945Sroberto		    struct timespec *);
83258945Sroberto
84258945Sroberto/* --------------------------------------------------------------------- */
85258945Sroberto
86258945Srobertostatic int
87258945Srobertotmpfs_mount(struct mount *mp, const char *path, void *data, size_t *data_len,
88258945Sroberto    struct lwp *l)
89258945Sroberto{
90258945Sroberto	int error;
91258945Sroberto	ino_t nodes;
92258945Sroberto	size_t pages;
93258945Sroberto	struct tmpfs_mount *tmp;
94258945Sroberto	struct tmpfs_node *root;
95258945Sroberto	struct tmpfs_args *args = data;
96258945Sroberto
97258945Sroberto	if (*data_len < sizeof *args)
98258945Sroberto		return EINVAL;
99258945Sroberto
100258945Sroberto	/* Handle retrieval of mount point arguments. */
101258945Sroberto	if (mp->mnt_flag & MNT_GETARGS) {
102258945Sroberto		if (mp->mnt_data == NULL)
103258945Sroberto			return EIO;
104258945Sroberto		tmp = VFS_TO_TMPFS(mp);
105258945Sroberto
106258945Sroberto		args->ta_version = TMPFS_ARGS_VERSION;
107258945Sroberto		mutex_enter(&tmp->tm_lock);
108258945Sroberto		args->ta_nodes_max = tmp->tm_nodes_max;
109258945Sroberto		args->ta_size_max = tmp->tm_pages_max * PAGE_SIZE;
110258945Sroberto		root = tmp->tm_root;
111258945Sroberto		mutex_exit(&tmp->tm_lock);
112258945Sroberto
113258945Sroberto		args->ta_root_uid = root->tn_uid;
114258945Sroberto		args->ta_root_gid = root->tn_gid;
115258945Sroberto		args->ta_root_mode = root->tn_mode;
116258945Sroberto
117258945Sroberto		*data_len = sizeof *args;
118258945Sroberto		return 0;
119258945Sroberto	}
120258945Sroberto
121258945Sroberto	if (mp->mnt_flag & MNT_UPDATE) {
122258945Sroberto		/* XXX: There is no support yet to update file system
123258945Sroberto		 * settings.  Should be added. */
124258945Sroberto
125258945Sroberto		return EOPNOTSUPP;
126258945Sroberto	}
127258945Sroberto
128258945Sroberto	if (args->ta_version != TMPFS_ARGS_VERSION)
129258945Sroberto		return EINVAL;
130258945Sroberto
131258945Sroberto	/* Do not allow mounts if we do not have enough memory to preserve
132258945Sroberto	 * the minimum reserved pages. */
133258945Sroberto	if (tmpfs_mem_info(true) < TMPFS_PAGES_RESERVED)
134258945Sroberto		return EINVAL;
135258945Sroberto
136258945Sroberto	/* Get the maximum number of memory pages this file system is
137258945Sroberto	 * allowed to use, based on the maximum size the user passed in
138258945Sroberto	 * the mount structure.  A value of zero is treated as if the
139258945Sroberto	 * maximum available space was requested. */
140258945Sroberto	if (args->ta_size_max < PAGE_SIZE || args->ta_size_max >= SIZE_MAX)
141258945Sroberto		pages = SIZE_MAX;
142258945Sroberto	else
143258945Sroberto		pages = args->ta_size_max / PAGE_SIZE +
144258945Sroberto		    (args->ta_size_max % PAGE_SIZE == 0 ? 0 : 1);
145258945Sroberto	KASSERT(pages > 0);
146258945Sroberto
147258945Sroberto	if (args->ta_nodes_max <= 3)
148258945Sroberto		nodes = 3 + pages * PAGE_SIZE / 1024;
149258945Sroberto	else
150258945Sroberto		nodes = args->ta_nodes_max;
151258945Sroberto	KASSERT(nodes >= 3);
152258945Sroberto
153258945Sroberto	/* Allocate the tmpfs mount structure and fill it. */
154258945Sroberto	tmp = kmem_alloc(sizeof(struct tmpfs_mount), KM_SLEEP);
155258945Sroberto	if (tmp == NULL)
156258945Sroberto		return ENOMEM;
157258945Sroberto
158258945Sroberto	tmp->tm_nodes_max = nodes;
159258945Sroberto	tmp->tm_nodes_cnt = 0;
160258945Sroberto	LIST_INIT(&tmp->tm_nodes);
161258945Sroberto
162258945Sroberto	mutex_init(&tmp->tm_lock, MUTEX_DEFAULT, IPL_NONE);
163258945Sroberto
164258945Sroberto	tmp->tm_pages_max = pages;
165258945Sroberto	tmp->tm_pages_used = 0;
166258945Sroberto	tmpfs_pool_init(&tmp->tm_dirent_pool, sizeof(struct tmpfs_dirent),
167258945Sroberto	    "dirent", tmp);
168258945Sroberto	tmpfs_pool_init(&tmp->tm_node_pool, sizeof(struct tmpfs_node),
169258945Sroberto	    "node", tmp);
170258945Sroberto	tmpfs_str_pool_init(&tmp->tm_str_pool, tmp);
171258945Sroberto
172258945Sroberto	/* Allocate the root node. */
173258945Sroberto	error = tmpfs_alloc_node(tmp, VDIR, args->ta_root_uid,
174258945Sroberto	    args->ta_root_gid, args->ta_root_mode & ALLPERMS, NULL, NULL,
175258945Sroberto	    VNOVAL, l->l_proc, &root);
176258945Sroberto	KASSERT(error == 0 && root != NULL);
177258945Sroberto	tmp->tm_root = root;
178258945Sroberto
179258945Sroberto	mp->mnt_data = tmp;
180258945Sroberto	mp->mnt_flag |= MNT_LOCAL;
181258945Sroberto	mp->mnt_stat.f_namemax = MAXNAMLEN;
182258945Sroberto	mp->mnt_fs_bshift = PAGE_SHIFT;
183258945Sroberto	mp->mnt_dev_bshift = DEV_BSHIFT;
184258945Sroberto	mp->mnt_iflag |= IMNT_MPSAFE;
185258945Sroberto	vfs_getnewfsid(mp);
186258945Sroberto
187258945Sroberto	return set_statvfs_info(path, UIO_USERSPACE, "tmpfs", UIO_SYSSPACE,
188258945Sroberto	    mp->mnt_op->vfs_name, mp, l);
189258945Sroberto}
190258945Sroberto
191258945Sroberto/* --------------------------------------------------------------------- */
192258945Sroberto
193258945Srobertostatic int
194258945Srobertotmpfs_start(struct mount *mp, int flags,
195258945Sroberto    struct lwp *l)
196258945Sroberto{
197258945Sroberto
198258945Sroberto	return 0;
199258945Sroberto}
200258945Sroberto
201258945Sroberto/* --------------------------------------------------------------------- */
202258945Sroberto
203258945Sroberto/* ARGSUSED2 */
204258945Srobertostatic int
205258945Srobertotmpfs_unmount(struct mount *mp, int mntflags, struct lwp *l)
206258945Sroberto{
207258945Sroberto	int error;
208258945Sroberto	int flags = 0;
209258945Sroberto	struct tmpfs_mount *tmp;
210258945Sroberto	struct tmpfs_node *node;
211258945Sroberto
212258945Sroberto	/* Handle forced unmounts. */
213258945Sroberto	if (mntflags & MNT_FORCE)
214258945Sroberto		flags |= FORCECLOSE;
215258945Sroberto
216258945Sroberto	/* Finalize all pending I/O. */
217258945Sroberto	error = vflush(mp, NULL, flags);
218258945Sroberto	if (error != 0)
219258945Sroberto		return error;
220258945Sroberto
221258945Sroberto	tmp = VFS_TO_TMPFS(mp);
222258945Sroberto
223258945Sroberto	/* Free all associated data.  The loop iterates over the linked list
224258945Sroberto	 * we have containing all used nodes.  For each of them that is
225258945Sroberto	 * a directory, we free all its directory entries.  Note that after
226258945Sroberto	 * freeing a node, it will automatically go to the available list,
227258945Sroberto	 * so we will later have to iterate over it to release its items. */
228258945Sroberto	while ((node = LIST_FIRST(&tmp->tm_nodes)) != NULL) {
229258945Sroberto		if (node->tn_type == VDIR) {
230258945Sroberto			struct tmpfs_dirent *de;
231258945Sroberto
232258945Sroberto			de = TAILQ_FIRST(&node->tn_spec.tn_dir.tn_dir);
233258945Sroberto			while (de != NULL) {
234258945Sroberto				struct tmpfs_dirent *nde;
235258945Sroberto
236258945Sroberto				nde = TAILQ_NEXT(de, td_entries);
237258945Sroberto				KASSERT(de->td_node->tn_vnode == NULL);
238258945Sroberto				tmpfs_free_dirent(tmp, de, false);
239258945Sroberto				de = nde;
240258945Sroberto				node->tn_size -= sizeof(struct tmpfs_dirent);
241258945Sroberto			}
242258945Sroberto		}
243258945Sroberto
244258945Sroberto		tmpfs_free_node(tmp, node);
245258945Sroberto	}
246258945Sroberto
247258945Sroberto	tmpfs_pool_destroy(&tmp->tm_dirent_pool);
248258945Sroberto	tmpfs_pool_destroy(&tmp->tm_node_pool);
249258945Sroberto	tmpfs_str_pool_destroy(&tmp->tm_str_pool);
250258945Sroberto
251258945Sroberto	KASSERT(tmp->tm_pages_used == 0);
252258945Sroberto
253258945Sroberto	/* Throw away the tmpfs_mount structure. */
254258945Sroberto	mutex_destroy(&tmp->tm_lock);
255258945Sroberto	kmem_free(tmp, sizeof(*tmp));
256258945Sroberto	mp->mnt_data = NULL;
257258945Sroberto
258258945Sroberto	return 0;
259258945Sroberto}
260258945Sroberto
261258945Sroberto/* --------------------------------------------------------------------- */
262258945Sroberto
263258945Srobertostatic int
264258945Srobertotmpfs_root(struct mount *mp, struct vnode **vpp)
265258945Sroberto{
266258945Sroberto
267258945Sroberto	return tmpfs_alloc_vp(mp, VFS_TO_TMPFS(mp)->tm_root, vpp);
268258945Sroberto}
269258945Sroberto
270258945Sroberto/* --------------------------------------------------------------------- */
271258945Sroberto
272258945Srobertostatic int
273258945Srobertotmpfs_quotactl(struct mount *mp, int cmd, uid_t uid,
274258945Sroberto    void *arg, struct lwp *l)
275258945Sroberto{
276258945Sroberto
277258945Sroberto	printf("tmpfs_quotactl called; need for it unknown yet\n");
278258945Sroberto	return EOPNOTSUPP;
279258945Sroberto}
280258945Sroberto
281258945Sroberto/* --------------------------------------------------------------------- */
282258945Sroberto
283258945Srobertostatic int
284258945Srobertotmpfs_vget(struct mount *mp, ino_t ino,
285258945Sroberto    struct vnode **vpp)
286258945Sroberto{
287258945Sroberto
288258945Sroberto	printf("tmpfs_vget called; need for it unknown yet\n");
289258945Sroberto	return EOPNOTSUPP;
290258945Sroberto}
291258945Sroberto
292258945Sroberto/* --------------------------------------------------------------------- */
293258945Sroberto
294258945Srobertostatic int
295258945Srobertotmpfs_fhtovp(struct mount *mp, struct fid *fhp, struct vnode **vpp)
296258945Sroberto{
297258945Sroberto	bool found;
298258945Sroberto	struct tmpfs_fid tfh;
299258945Sroberto	struct tmpfs_mount *tmp;
300258945Sroberto	struct tmpfs_node *node;
301258945Sroberto
302258945Sroberto	tmp = VFS_TO_TMPFS(mp);
303258945Sroberto
304258945Sroberto	if (fhp->fid_len != sizeof(struct tmpfs_fid))
305258945Sroberto		return EINVAL;
306258945Sroberto
307258945Sroberto	memcpy(&tfh, fhp, sizeof(struct tmpfs_fid));
308258945Sroberto
309258945Sroberto	mutex_enter(&tmp->tm_lock);
310258945Sroberto	if (tfh.tf_id >= tmp->tm_nodes_max) {
311258945Sroberto		mutex_exit(&tmp->tm_lock);
312258945Sroberto		return EINVAL;
313258945Sroberto	}
314258945Sroberto
315258945Sroberto	found = false;
316258945Sroberto	LIST_FOREACH(node, &tmp->tm_nodes, tn_entries) {
317258945Sroberto		if (node->tn_id == tfh.tf_id &&
318258945Sroberto		    node->tn_gen == tfh.tf_gen) {
319258945Sroberto			found = true;
320258945Sroberto			break;
321258945Sroberto		}
322258945Sroberto	}
323258945Sroberto	mutex_exit(&tmp->tm_lock);
324258945Sroberto
325258945Sroberto	/* XXXAD nothing to prevent 'node' from being removed. */
326258945Sroberto	return found ? tmpfs_alloc_vp(mp, node, vpp) : EINVAL;
327258945Sroberto}
328258945Sroberto
329258945Sroberto/* --------------------------------------------------------------------- */
330258945Sroberto
331258945Srobertostatic int
332258945Srobertotmpfs_vptofh(struct vnode *vp, struct fid *fhp, size_t *fh_size)
333258945Sroberto{
334258945Sroberto	struct tmpfs_fid tfh;
335258945Sroberto	struct tmpfs_node *node;
336258945Sroberto
337258945Sroberto	if (*fh_size < sizeof(struct tmpfs_fid)) {
338258945Sroberto		*fh_size = sizeof(struct tmpfs_fid);
339258945Sroberto		return E2BIG;
340258945Sroberto	}
341258945Sroberto	*fh_size = sizeof(struct tmpfs_fid);
342258945Sroberto	node = VP_TO_TMPFS_NODE(vp);
343258945Sroberto
344258945Sroberto	memset(&tfh, 0, sizeof(tfh));
345258945Sroberto	tfh.tf_len = sizeof(struct tmpfs_fid);
346258945Sroberto	tfh.tf_gen = node->tn_gen;
347258945Sroberto	tfh.tf_id = node->tn_id;
348258945Sroberto	memcpy(fhp, &tfh, sizeof(tfh));
349258945Sroberto
350258945Sroberto	return 0;
351258945Sroberto}
352258945Sroberto
353258945Sroberto/* --------------------------------------------------------------------- */
354258945Sroberto
355258945Sroberto/* ARGSUSED2 */
356258945Srobertostatic int
357258945Srobertotmpfs_statvfs(struct mount *mp, struct statvfs *sbp, struct lwp *l)
358258945Sroberto{
359258945Sroberto	fsfilcnt_t freenodes;
360258945Sroberto	struct tmpfs_mount *tmp;
361258945Sroberto
362258945Sroberto	tmp = VFS_TO_TMPFS(mp);
363258945Sroberto
364258945Sroberto	mutex_enter(&tmp->tm_lock);
365258945Sroberto
366258945Sroberto	sbp->f_iosize = sbp->f_frsize = sbp->f_bsize = PAGE_SIZE;
367258945Sroberto
368258945Sroberto	sbp->f_blocks = TMPFS_PAGES_MAX(tmp);
369258945Sroberto	sbp->f_bavail = sbp->f_bfree = TMPFS_PAGES_AVAIL(tmp);
370258945Sroberto	sbp->f_bresvd = 0;
371258945Sroberto
372258945Sroberto	freenodes = MIN(tmp->tm_nodes_max - tmp->tm_nodes_cnt,
373258945Sroberto	    TMPFS_PAGES_AVAIL(tmp) * PAGE_SIZE / sizeof(struct tmpfs_node));
374258945Sroberto
375258945Sroberto	sbp->f_files = tmp->tm_nodes_cnt + freenodes;
376258945Sroberto	sbp->f_favail = sbp->f_ffree = freenodes;
377258945Sroberto	sbp->f_fresvd = 0;
378258945Sroberto
379258945Sroberto	mutex_exit(&tmp->tm_lock);
380258945Sroberto
381258945Sroberto	copy_statvfs_info(sbp, mp);
382258945Sroberto
383258945Sroberto	return 0;
384258945Sroberto}
385258945Sroberto
386258945Sroberto/* --------------------------------------------------------------------- */
387258945Sroberto
388258945Sroberto/* ARGSUSED0 */
389258945Srobertostatic int
390258945Srobertotmpfs_sync(struct mount *mp, int waitfor,
391258945Sroberto    kauth_cred_t uc, struct lwp *l)
392258945Sroberto{
393258945Sroberto
394258945Sroberto	return 0;
395258945Sroberto}
396258945Sroberto
397258945Sroberto/* --------------------------------------------------------------------- */
398258945Sroberto
399258945Srobertostatic void
400258945Srobertotmpfs_init(void)
401258945Sroberto{
402258945Sroberto
403258945Sroberto}
404258945Sroberto
405258945Sroberto/* --------------------------------------------------------------------- */
406258945Sroberto
407258945Srobertostatic void
408258945Srobertotmpfs_done(void)
409258945Sroberto{
410258945Sroberto
411258945Sroberto}
412258945Sroberto
413258945Sroberto/* --------------------------------------------------------------------- */
414258945Sroberto
415258945Srobertostatic int
416258945Srobertotmpfs_snapshot(struct mount *mp, struct vnode *vp,
417258945Sroberto    struct timespec *ctime)
418258945Sroberto{
419258945Sroberto
420258945Sroberto	return EOPNOTSUPP;
421258945Sroberto}
422258945Sroberto
423258945Sroberto/* --------------------------------------------------------------------- */
424258945Sroberto
425258945Sroberto/*
426258945Sroberto * tmpfs vfs operations.
427258945Sroberto */
428258945Sroberto
429258945Srobertoextern const struct vnodeopv_desc tmpfs_fifoop_opv_desc;
430258945Srobertoextern const struct vnodeopv_desc tmpfs_specop_opv_desc;
431258945Srobertoextern const struct vnodeopv_desc tmpfs_vnodeop_opv_desc;
432258945Sroberto
433258945Srobertoconst struct vnodeopv_desc * const tmpfs_vnodeopv_descs[] = {
434258945Sroberto	&tmpfs_fifoop_opv_desc,
435258945Sroberto	&tmpfs_specop_opv_desc,
436258945Sroberto	&tmpfs_vnodeop_opv_desc,
437258945Sroberto	NULL,
438258945Sroberto};
439258945Sroberto
440258945Srobertostruct vfsops tmpfs_vfsops = {
441258945Sroberto	MOUNT_TMPFS,			/* vfs_name */
442258945Sroberto	sizeof (struct tmpfs_args),
443258945Sroberto	tmpfs_mount,			/* vfs_mount */
444258945Sroberto	tmpfs_start,			/* vfs_start */
445258945Sroberto	tmpfs_unmount,			/* vfs_unmount */
446258945Sroberto	tmpfs_root,			/* vfs_root */
447258945Sroberto	tmpfs_quotactl,			/* vfs_quotactl */
448258945Sroberto	tmpfs_statvfs,			/* vfs_statvfs */
449258945Sroberto	tmpfs_sync,			/* vfs_sync */
450258945Sroberto	tmpfs_vget,			/* vfs_vget */
451258945Sroberto	tmpfs_fhtovp,			/* vfs_fhtovp */
452258945Sroberto	tmpfs_vptofh,			/* vfs_vptofh */
453258945Sroberto	tmpfs_init,			/* vfs_init */
454258945Sroberto	NULL,				/* vfs_reinit */
455258945Sroberto	tmpfs_done,			/* vfs_done */
456258945Sroberto	NULL,				/* vfs_mountroot */
457258945Sroberto	tmpfs_snapshot,			/* vfs_snapshot */
458258945Sroberto	vfs_stdextattrctl,		/* vfs_extattrctl */
459258945Sroberto	(void *)eopnotsupp,		/* vfs_suspendctl */
460258945Sroberto	tmpfs_vnodeopv_descs,
461258945Sroberto	0,				/* vfs_refcount */
462258945Sroberto	{ NULL, NULL },
463258945Sroberto};
464258945SrobertoVFS_ATTACH(tmpfs_vfsops);
465258945Sroberto