xref: /dflybsd-src/sys/vfs/tmpfs/tmpfs_vfsops.c (revision e9dbfea17a45e13134f19ed8fa22fbe8d11ac99c)
17a2de9a4SMatthew Dillon /*	$NetBSD: tmpfs_vfsops.c,v 1.10 2005/12/11 12:24:29 christos Exp $	*/
27a2de9a4SMatthew Dillon 
37a2de9a4SMatthew Dillon /*-
47a2de9a4SMatthew Dillon  * Copyright (c) 2005 The NetBSD Foundation, Inc.
57a2de9a4SMatthew Dillon  * All rights reserved.
67a2de9a4SMatthew Dillon  *
77a2de9a4SMatthew Dillon  * This code is derived from software contributed to The NetBSD Foundation
87a2de9a4SMatthew Dillon  * by Julio M. Merino Vidal, developed as part of Google's Summer of Code
97a2de9a4SMatthew Dillon  * 2005 program.
107a2de9a4SMatthew Dillon  *
117a2de9a4SMatthew Dillon  * Redistribution and use in source and binary forms, with or without
127a2de9a4SMatthew Dillon  * modification, are permitted provided that the following conditions
137a2de9a4SMatthew Dillon  * are met:
147a2de9a4SMatthew Dillon  * 1. Redistributions of source code must retain the above copyright
157a2de9a4SMatthew Dillon  *    notice, this list of conditions and the following disclaimer.
167a2de9a4SMatthew Dillon  * 2. Redistributions in binary form must reproduce the above copyright
177a2de9a4SMatthew Dillon  *    notice, this list of conditions and the following disclaimer in the
187a2de9a4SMatthew Dillon  *    documentation and/or other materials provided with the distribution.
197a2de9a4SMatthew Dillon  *
207a2de9a4SMatthew Dillon  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
217a2de9a4SMatthew Dillon  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
227a2de9a4SMatthew Dillon  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
237a2de9a4SMatthew Dillon  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
247a2de9a4SMatthew Dillon  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
257a2de9a4SMatthew Dillon  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
267a2de9a4SMatthew Dillon  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
277a2de9a4SMatthew Dillon  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
287a2de9a4SMatthew Dillon  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
297a2de9a4SMatthew Dillon  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
307a2de9a4SMatthew Dillon  * POSSIBILITY OF SUCH DAMAGE.
317a2de9a4SMatthew Dillon  */
327a2de9a4SMatthew Dillon 
337a2de9a4SMatthew Dillon /*
347a2de9a4SMatthew Dillon  * Efficient memory file system.
357a2de9a4SMatthew Dillon  *
36aa1adbf0SMatthew Dillon  * tmpfs is a file system that uses virtual memory to store file data and
37aa1adbf0SMatthew Dillon  * metadata efficiently. It does not follow the structure of an on-disk
387a2de9a4SMatthew Dillon  * file system because it simply does not need to. Instead, it uses
397a2de9a4SMatthew Dillon  * memory-specific data structures and algorithms to automatically
407a2de9a4SMatthew Dillon  * allocate and release resources.
417a2de9a4SMatthew Dillon  */
42651eeb07SSascha Wildner 
437a2de9a4SMatthew Dillon #include <sys/conf.h>
447a2de9a4SMatthew Dillon #include <sys/param.h>
457a2de9a4SMatthew Dillon #include <sys/limits.h>
467a2de9a4SMatthew Dillon #include <sys/lock.h>
477a2de9a4SMatthew Dillon #include <sys/kernel.h>
487a2de9a4SMatthew Dillon #include <sys/stat.h>
497a2de9a4SMatthew Dillon #include <sys/systm.h>
507a2de9a4SMatthew Dillon #include <sys/sysctl.h>
517a2de9a4SMatthew Dillon 
527a2de9a4SMatthew Dillon #include <vm/vm.h>
537a2de9a4SMatthew Dillon #include <vm/vm_object.h>
547a2de9a4SMatthew Dillon #include <vm/vm_param.h>
557a2de9a4SMatthew Dillon 
56aa1adbf0SMatthew Dillon #if 0
577a2de9a4SMatthew Dillon #include <vfs/tmpfs/tmpfs.h>
58aa1adbf0SMatthew Dillon #endif
59aa1adbf0SMatthew Dillon #include "tmpfs.h"
607a2de9a4SMatthew Dillon #include <vfs/tmpfs/tmpfs_vnops.h>
61f3c171e4SMarkus Pfeiffer #include <vfs/tmpfs/tmpfs_mount.h>
627a2de9a4SMatthew Dillon 
637a2de9a4SMatthew Dillon /*
647a2de9a4SMatthew Dillon  * Default permission for root node
657a2de9a4SMatthew Dillon  */
667a2de9a4SMatthew Dillon #define TMPFS_DEFAULT_ROOT_MODE	(S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH)
677a2de9a4SMatthew Dillon 
687a2de9a4SMatthew Dillon MALLOC_DEFINE(M_TMPFSMNT, "tmpfs mount", "tmpfs mount structures");
697a2de9a4SMatthew Dillon 
707a2de9a4SMatthew Dillon /* --------------------------------------------------------------------- */
717a2de9a4SMatthew Dillon 
727a2de9a4SMatthew Dillon static int	tmpfs_mount(struct mount *, char *, caddr_t, struct ucred *);
737a2de9a4SMatthew Dillon static int	tmpfs_unmount(struct mount *, int);
747a2de9a4SMatthew Dillon static int	tmpfs_root(struct mount *, struct vnode **);
757a2de9a4SMatthew Dillon static int	tmpfs_fhtovp(struct mount *, struct vnode *, struct fid *, struct vnode **);
767a2de9a4SMatthew Dillon static int	tmpfs_statfs(struct mount *, struct statfs *, struct ucred *cred);
777a2de9a4SMatthew Dillon 
787a2de9a4SMatthew Dillon /* --------------------------------------------------------------------- */
79*e9dbfea1SMatthew Dillon 
80*e9dbfea1SMatthew Dillon void
tmpfs_node_init(struct tmpfs_node * node)81*e9dbfea1SMatthew Dillon tmpfs_node_init(struct tmpfs_node *node)
827a2de9a4SMatthew Dillon {
839cd86db5SMatthew Dillon 	node->tn_blksize = PAGE_SIZE;	/* start small */
847a2de9a4SMatthew Dillon 	lockinit(&node->tn_interlock, "tmpfs node interlock", 0, LK_CANRECURSE);
857a2de9a4SMatthew Dillon 	node->tn_gen = karc4random();
867a2de9a4SMatthew Dillon }
877a2de9a4SMatthew Dillon 
88*e9dbfea1SMatthew Dillon void
tmpfs_node_uninit(struct tmpfs_node * node)89*e9dbfea1SMatthew Dillon tmpfs_node_uninit(struct tmpfs_node *node)
907a2de9a4SMatthew Dillon {
91*e9dbfea1SMatthew Dillon 	node->tn_type = VNON;
92*e9dbfea1SMatthew Dillon 	node->tn_vpstate = TMPFS_VNODE_DOOMED;
937a2de9a4SMatthew Dillon 	lockuninit(&node->tn_interlock);
947a2de9a4SMatthew Dillon }
957a2de9a4SMatthew Dillon 
967a2de9a4SMatthew Dillon static int
tmpfs_mount(struct mount * mp,char * path,caddr_t data,struct ucred * cred)977a2de9a4SMatthew Dillon tmpfs_mount(struct mount *mp, char *path, caddr_t data, struct ucred *cred)
987a2de9a4SMatthew Dillon {
997a2de9a4SMatthew Dillon 	struct tmpfs_mount *tmp;
1007a2de9a4SMatthew Dillon 	struct tmpfs_node *root;
10125a86e44SMarkus Pfeiffer 	struct tmpfs_mount_info args;
10229ffeb28SMatthew Dillon 	vm_pindex_t pages;
10329ffeb28SMatthew Dillon 	vm_pindex_t pages_limit;
1047a2de9a4SMatthew Dillon 	ino_t nodes;
105817a2fd9SMatthew Dillon 	u_int64_t	maxfsize;
1067a2de9a4SMatthew Dillon 	int error;
1077a2de9a4SMatthew Dillon 	/* Size counters. */
10829ffeb28SMatthew Dillon 	ino_t	nodes_max;
10929ffeb28SMatthew Dillon 	off_t	size_max;
110817a2fd9SMatthew Dillon 	size_t	maxfsize_max;
1117a2de9a4SMatthew Dillon 	size_t	size;
1127a2de9a4SMatthew Dillon 
1137a2de9a4SMatthew Dillon 	/* Root node attributes. */
1147a2de9a4SMatthew Dillon 	uid_t	root_uid = cred->cr_uid;
1157a2de9a4SMatthew Dillon 	gid_t	root_gid = cred->cr_gid;
1167a2de9a4SMatthew Dillon 	mode_t	root_mode = (VREAD | VWRITE);
1177a2de9a4SMatthew Dillon 
1187a2de9a4SMatthew Dillon 	if (mp->mnt_flag & MNT_UPDATE) {
1197a2de9a4SMatthew Dillon 		/* XXX: There is no support yet to update file system
1207a2de9a4SMatthew Dillon 		 * settings.  Should be added. */
1217a2de9a4SMatthew Dillon 
1227a2de9a4SMatthew Dillon 		return EOPNOTSUPP;
1237a2de9a4SMatthew Dillon 	}
1247a2de9a4SMatthew Dillon 
12529ffeb28SMatthew Dillon 	/*
12629ffeb28SMatthew Dillon 	 * mount info
12729ffeb28SMatthew Dillon 	 */
12829ffeb28SMatthew Dillon 	bzero(&args, sizeof(args));
12929ffeb28SMatthew Dillon 	size_max  = 0;
13029ffeb28SMatthew Dillon 	nodes_max = 0;
131817a2fd9SMatthew Dillon 	maxfsize_max = 0;
13229ffeb28SMatthew Dillon 
13329ffeb28SMatthew Dillon 	if (path) {
13429ffeb28SMatthew Dillon 		if (data) {
13529ffeb28SMatthew Dillon 			error = copyin(data, &args, sizeof(args));
13629ffeb28SMatthew Dillon 			if (error)
13729ffeb28SMatthew Dillon 				return (error);
13829ffeb28SMatthew Dillon 		}
13929ffeb28SMatthew Dillon 		size_max = args.ta_size_max;
14029ffeb28SMatthew Dillon 		nodes_max = args.ta_nodes_max;
141817a2fd9SMatthew Dillon 		maxfsize_max = args.ta_maxfsize_max;
1421b5c5debSMatthew Dillon 		root_uid = args.ta_root_uid;
1431b5c5debSMatthew Dillon 		root_gid = args.ta_root_gid;
1441b5c5debSMatthew Dillon 		root_mode = args.ta_root_mode;
14529ffeb28SMatthew Dillon 	}
1467a2de9a4SMatthew Dillon 
1477a2de9a4SMatthew Dillon 	/*
1487a2de9a4SMatthew Dillon 	 * If mount by non-root, then verify that user has necessary
1497a2de9a4SMatthew Dillon 	 * permissions on the device.
1507a2de9a4SMatthew Dillon 	 */
1517a2de9a4SMatthew Dillon 	if (cred->cr_uid != 0) {
1527a2de9a4SMatthew Dillon 		root_mode = VREAD;
1537a2de9a4SMatthew Dillon 		if ((mp->mnt_flag & MNT_RDONLY) == 0)
1547a2de9a4SMatthew Dillon 			root_mode |= VWRITE;
1557a2de9a4SMatthew Dillon 	}
1567a2de9a4SMatthew Dillon 
15729ffeb28SMatthew Dillon 	pages_limit = vm_swap_max + vmstats.v_page_count / 2;
1587a2de9a4SMatthew Dillon 
1592a3a6ffdSMatthew Dillon 	if (size_max == 0) {
16029ffeb28SMatthew Dillon 		pages = pages_limit / 2;
1612a3a6ffdSMatthew Dillon 	} else if (size_max < PAGE_SIZE) {
16229ffeb28SMatthew Dillon 		pages = 1;
1632a3a6ffdSMatthew Dillon 	} else if (OFF_TO_IDX(size_max) > pages_limit) {
1642a3a6ffdSMatthew Dillon 		/*
1652a3a6ffdSMatthew Dillon 		 * do not force pages = pages_limit for this case, otherwise
1662a3a6ffdSMatthew Dillon 		 * we might not honor tmpfs size requests from /etc/fstab
1672a3a6ffdSMatthew Dillon 		 * during boot because they are mounted prior to swap being
1682a3a6ffdSMatthew Dillon 		 * turned on.
1692a3a6ffdSMatthew Dillon 		 */
17029ffeb28SMatthew Dillon 		pages = OFF_TO_IDX(size_max);
1712a3a6ffdSMatthew Dillon 	} else {
1722a3a6ffdSMatthew Dillon 		pages = OFF_TO_IDX(size_max);
1732a3a6ffdSMatthew Dillon 	}
17429ffeb28SMatthew Dillon 
17529ffeb28SMatthew Dillon 	if (nodes_max == 0)
1767a2de9a4SMatthew Dillon 		nodes = 3 + pages * PAGE_SIZE / 1024;
17729ffeb28SMatthew Dillon 	else if (nodes_max < 3)
17829ffeb28SMatthew Dillon 		nodes = 3;
17929ffeb28SMatthew Dillon 	else if (nodes_max > pages)
18029ffeb28SMatthew Dillon 		nodes = pages;
1817a2de9a4SMatthew Dillon 	else
1827a2de9a4SMatthew Dillon 		nodes = nodes_max;
1837a2de9a4SMatthew Dillon 
184b2394163SMatthew Dillon 	maxfsize = 0x7FFFFFFFFFFFFFFFLLU - TMPFS_BLKSIZE;
185817a2fd9SMatthew Dillon 	if (maxfsize_max != 0 && maxfsize > maxfsize_max)
186817a2fd9SMatthew Dillon 		maxfsize = maxfsize_max;
187817a2fd9SMatthew Dillon 
1887a2de9a4SMatthew Dillon 	/* Allocate the tmpfs mount structure and fill it. */
18929ffeb28SMatthew Dillon 	tmp = kmalloc(sizeof(*tmp), M_TMPFSMNT, M_WAITOK | M_ZERO);
1907a2de9a4SMatthew Dillon 
191aa1adbf0SMatthew Dillon 	tmp->tm_mount = mp;
1927a2de9a4SMatthew Dillon 	tmp->tm_nodes_max = nodes;
1937a2de9a4SMatthew Dillon 	tmp->tm_nodes_inuse = 0;
194817a2fd9SMatthew Dillon 	tmp->tm_maxfilesize = maxfsize;
1957a2de9a4SMatthew Dillon 	LIST_INIT(&tmp->tm_nodes_used);
1967a2de9a4SMatthew Dillon 
1977a2de9a4SMatthew Dillon 	tmp->tm_pages_max = pages;
1987a2de9a4SMatthew Dillon 	tmp->tm_pages_used = 0;
19929ffeb28SMatthew Dillon 
200*e9dbfea1SMatthew Dillon 	kmalloc_create_obj(&tmp->tm_node_zone, "tmpfs node",
201*e9dbfea1SMatthew Dillon 			sizeof(struct tmpfs_node));
202*e9dbfea1SMatthew Dillon 	kmalloc_create_obj(&tmp->tm_dirent_zone, "tmpfs dirent",
203*e9dbfea1SMatthew Dillon 			sizeof(struct tmpfs_dirent));
204d00cd01cSVenkatesh Srinivas 	kmalloc_create(&tmp->tm_name_zone, "tmpfs name zone");
205dcaa8a41SVenkatesh Srinivas 
206*e9dbfea1SMatthew Dillon 	kmalloc_obj_raise_limit(tmp->tm_node_zone,
207*e9dbfea1SMatthew Dillon 				sizeof(struct tmpfs_node) * tmp->tm_nodes_max);
2087a2de9a4SMatthew Dillon 
2095b09d16cSTomohiro Kusumi 	tmp->tm_ino = TMPFS_ROOTINO;
210f7db522fSVenkatesh Srinivas 
2117a2de9a4SMatthew Dillon 	/* Allocate the root node. */
212d4623db3SMatthew Dillon 	error = tmpfs_alloc_node(tmp, VDIR, root_uid, root_gid,
2136e0c5aabSMatthew Dillon 				 root_mode & ALLPERMS, NULL,
2147a2de9a4SMatthew Dillon 				 VNOVAL, VNOVAL, &root);
2157a2de9a4SMatthew Dillon 
216d4623db3SMatthew Dillon 	/*
217d4623db3SMatthew Dillon 	 * We are backed by swap, set snocache chflags flag so we
218d4623db3SMatthew Dillon 	 * don't trip over swapcache.
219d4623db3SMatthew Dillon 	 */
220d4623db3SMatthew Dillon 	root->tn_flags = SF_NOCACHE;
221d4623db3SMatthew Dillon 
2227a2de9a4SMatthew Dillon 	if (error != 0 || root == NULL) {
223*e9dbfea1SMatthew Dillon 	    kmalloc_destroy(&tmp->tm_name_zone);
224*e9dbfea1SMatthew Dillon 	    kmalloc_destroy(&tmp->tm_dirent_zone_obj);
225*e9dbfea1SMatthew Dillon 	    kmalloc_destroy(&tmp->tm_node_zone_obj);
2267a2de9a4SMatthew Dillon 	    kfree(tmp, M_TMPFSMNT);
2277a2de9a4SMatthew Dillon 	    return error;
2287a2de9a4SMatthew Dillon 	}
2295b09d16cSTomohiro Kusumi 	KASSERT(root->tn_id == TMPFS_ROOTINO,
230d9d4a3f4STomohiro Kusumi 		("tmpfs root with invalid ino: %ju", (uintmax_t)root->tn_id));
231aa1adbf0SMatthew Dillon 
2324d22d8eeSMatthew Dillon 	atomic_add_int(&root->tn_links, 1);	/* keep around */
2337a2de9a4SMatthew Dillon 	tmp->tm_root = root;
2347a2de9a4SMatthew Dillon 
2357a2de9a4SMatthew Dillon 	mp->mnt_flag |= MNT_LOCAL;
236aa1adbf0SMatthew Dillon 	mp->mnt_kern_flag |= MNTK_ALL_MPSAFE;
237f96f2f39SMatthew Dillon 	mp->mnt_kern_flag |= MNTK_NOMSYNC;
238cf6a53caSMatthew Dillon 	mp->mnt_kern_flag |= MNTK_THR_SYNC;	/* new vsyncscan semantics */
23951a529dbSMatthew Dillon 	mp->mnt_kern_flag |= MNTK_QUICKHALT;	/* no teardown needed on halt */
2407a2de9a4SMatthew Dillon 	mp->mnt_data = (qaddr_t)tmp;
2414eb0bb82SMatthew Dillon 	mp->mnt_iosize_max = MAXBSIZE;
2427a2de9a4SMatthew Dillon 	vfs_getnewfsid(mp);
2437a2de9a4SMatthew Dillon 
2447a2de9a4SMatthew Dillon 	vfs_add_vnodeops(mp, &tmpfs_vnode_vops, &mp->mnt_vn_norm_ops);
2457a2de9a4SMatthew Dillon 	vfs_add_vnodeops(mp, &tmpfs_fifo_vops, &mp->mnt_vn_fifo_ops);
2467a2de9a4SMatthew Dillon 
2477a2de9a4SMatthew Dillon 	copystr("tmpfs", mp->mnt_stat.f_mntfromname, MNAMELEN - 1, &size);
2487a2de9a4SMatthew Dillon 	bzero(mp->mnt_stat.f_mntfromname +size, MNAMELEN - size);
2497a2de9a4SMatthew Dillon 	bzero(mp->mnt_stat.f_mntonname, sizeof(mp->mnt_stat.f_mntonname));
2507a2de9a4SMatthew Dillon 	copyinstr(path, mp->mnt_stat.f_mntonname,
2517a2de9a4SMatthew Dillon 		  sizeof(mp->mnt_stat.f_mntonname) -1,
2527a2de9a4SMatthew Dillon 		  &size);
2537a2de9a4SMatthew Dillon 
2547a2de9a4SMatthew Dillon 	tmpfs_statfs(mp, &mp->mnt_stat, cred);
2557a2de9a4SMatthew Dillon 
2567a2de9a4SMatthew Dillon 	return 0;
2577a2de9a4SMatthew Dillon }
2587a2de9a4SMatthew Dillon 
2597a2de9a4SMatthew Dillon /* --------------------------------------------------------------------- */
2607a2de9a4SMatthew Dillon 
2617a2de9a4SMatthew Dillon /* ARGSUSED2 */
2627a2de9a4SMatthew Dillon static int
tmpfs_unmount(struct mount * mp,int mntflags)2637a2de9a4SMatthew Dillon tmpfs_unmount(struct mount *mp, int mntflags)
2647a2de9a4SMatthew Dillon {
2657a2de9a4SMatthew Dillon 	int error;
2667a2de9a4SMatthew Dillon 	int flags = 0;
2677a2de9a4SMatthew Dillon 	struct tmpfs_mount *tmp;
2687a2de9a4SMatthew Dillon 	struct tmpfs_node *node;
269aa21b4baSMatthew Dillon 	struct vnode *vp;
270aa21b4baSMatthew Dillon 	int isok;
2717a2de9a4SMatthew Dillon 
272aa1adbf0SMatthew Dillon 	tmp = VFS_TO_TMPFS(mp);
273aa1adbf0SMatthew Dillon 	TMPFS_LOCK(tmp);
274aa1adbf0SMatthew Dillon 
2757a2de9a4SMatthew Dillon 	/* Handle forced unmounts. */
2767a2de9a4SMatthew Dillon 	if (mntflags & MNT_FORCE)
2777a2de9a4SMatthew Dillon 		flags |= FORCECLOSE;
2787a2de9a4SMatthew Dillon 
279d4623db3SMatthew Dillon 	/*
280d4623db3SMatthew Dillon 	 * Finalize all pending I/O.  In the case of tmpfs we want
281d4623db3SMatthew Dillon 	 * to throw all the data away so clean out the buffer cache
282d4623db3SMatthew Dillon 	 * and vm objects before calling vflush().
283d4623db3SMatthew Dillon 	 */
284d4623db3SMatthew Dillon 	LIST_FOREACH(node, &tmp->tm_nodes_used, tn_entries) {
285aa21b4baSMatthew Dillon 		/*
286aa21b4baSMatthew Dillon 		 * tn_links is mnt_token protected
287aa21b4baSMatthew Dillon 		 */
2884d22d8eeSMatthew Dillon 		atomic_add_int(&node->tn_links, 1);
289ff837cd5SMatthew Dillon 		TMPFS_NODE_LOCK(node);
290ff837cd5SMatthew Dillon 
291aa21b4baSMatthew Dillon 		while (node->tn_type == VREG && node->tn_vnode) {
292aa21b4baSMatthew Dillon 			vp = node->tn_vnode;
293ee173d09SSascha Wildner 			vhold(vp);
29489984f3dSMatthew Dillon 			TMPFS_NODE_UNLOCK(node);
295ace53c28SMatthew Dillon 			lwkt_yield();
296aa1adbf0SMatthew Dillon 
297aa1adbf0SMatthew Dillon 			/*
298aa1adbf0SMatthew Dillon 			 * vx_get/vx_put and tmpfs_truncate may block,
299aa1adbf0SMatthew Dillon 			 * releasing the tmpfs mountpoint token.
300aa21b4baSMatthew Dillon 			 *
301aa21b4baSMatthew Dillon 			 * Make sure the lock order is correct.
302aa1adbf0SMatthew Dillon 			 */
303aa21b4baSMatthew Dillon 			vx_get(vp);		/* held vnode */
304aa21b4baSMatthew Dillon 			TMPFS_NODE_LOCK(node);
305aa21b4baSMatthew Dillon 			if (node->tn_vnode == vp) {
306aa21b4baSMatthew Dillon 				tmpfs_truncate(vp, 0);
307aa21b4baSMatthew Dillon 				isok = 1;
308aa21b4baSMatthew Dillon 			} else {
309aa21b4baSMatthew Dillon 				isok = 0;
310aa21b4baSMatthew Dillon 			}
311d4623db3SMatthew Dillon 			TMPFS_NODE_UNLOCK(node);
312aa21b4baSMatthew Dillon 			vx_put(vp);
313aa21b4baSMatthew Dillon 			vdrop(vp);
31489984f3dSMatthew Dillon 			TMPFS_NODE_LOCK(node);
315aa21b4baSMatthew Dillon 			if (isok)
316aa21b4baSMatthew Dillon 				break;
317aa21b4baSMatthew Dillon 			/* retry */
318d4623db3SMatthew Dillon 		}
319ff837cd5SMatthew Dillon 
320ff837cd5SMatthew Dillon 		TMPFS_NODE_UNLOCK(node);
3214d22d8eeSMatthew Dillon 		atomic_add_int(&node->tn_links, -1);
322d4623db3SMatthew Dillon 	}
323aa21b4baSMatthew Dillon 
324aa1adbf0SMatthew Dillon 	/*
3254b3494c0STomohiro Kusumi 	 * Flush all vnodes on the unmount.
326aa1adbf0SMatthew Dillon 	 *
327aa1adbf0SMatthew Dillon 	 * If we fail to flush, we cannot unmount, but all the nodes have
328aa1adbf0SMatthew Dillon 	 * already been truncated. Erroring out is the best we can do.
329aa1adbf0SMatthew Dillon 	 */
3307a2de9a4SMatthew Dillon 	error = vflush(mp, 0, flags);
331aa1adbf0SMatthew Dillon 	if (error != 0) {
332aa1adbf0SMatthew Dillon 		TMPFS_UNLOCK(tmp);
333aa1adbf0SMatthew Dillon 		return (error);
334aa1adbf0SMatthew Dillon 	}
3357a2de9a4SMatthew Dillon 
3360786baf1SMatthew Dillon 	/*
3370786baf1SMatthew Dillon 	 * First pass get rid of all the directory entries and
3386e0c5aabSMatthew Dillon 	 * vnode associations.  This will also destroy the
3396e0c5aabSMatthew Dillon 	 * directory topology and should drop all link counts
3406e0c5aabSMatthew Dillon 	 * to 0 except for the root.
3410786baf1SMatthew Dillon 	 *
3420786baf1SMatthew Dillon 	 * No vnodes should remain after the vflush above.
3430786baf1SMatthew Dillon 	 */
3440786baf1SMatthew Dillon 	LIST_FOREACH(node, &tmp->tm_nodes_used, tn_entries) {
345ace53c28SMatthew Dillon 		lwkt_yield();
346aa1adbf0SMatthew Dillon 
3474d22d8eeSMatthew Dillon 		atomic_add_int(&node->tn_links, 1);
348ff837cd5SMatthew Dillon 		TMPFS_NODE_LOCK(node);
3497a2de9a4SMatthew Dillon 		if (node->tn_type == VDIR) {
3507a2de9a4SMatthew Dillon 			struct tmpfs_dirent *de;
3517a2de9a4SMatthew Dillon 
3526e0c5aabSMatthew Dillon 			while ((de = RB_ROOT(&node->tn_dir.tn_dirtree)) != NULL)			{
353307bf766SMatthew Dillon 				tmpfs_dir_detach_locked(node, de);
3540786baf1SMatthew Dillon 				tmpfs_free_dirent(tmp, de);
3557a2de9a4SMatthew Dillon 			}
3567a2de9a4SMatthew Dillon 		}
3570786baf1SMatthew Dillon 		KKASSERT(node->tn_vnode == NULL);
358aa1adbf0SMatthew Dillon 
3590786baf1SMatthew Dillon 		TMPFS_NODE_UNLOCK(node);
3604d22d8eeSMatthew Dillon 		atomic_add_int(&node->tn_links, -1);
3617a2de9a4SMatthew Dillon 	}
3627a2de9a4SMatthew Dillon 
3630786baf1SMatthew Dillon 	/*
3646e0c5aabSMatthew Dillon 	 * Allow the root node to be destroyed by dropping the link count
3656e0c5aabSMatthew Dillon 	 * we bumped in the mount code.
3660786baf1SMatthew Dillon 	 */
3676e0c5aabSMatthew Dillon 	KKASSERT(tmp->tm_root);
368ff837cd5SMatthew Dillon 	TMPFS_NODE_LOCK(tmp->tm_root);
3694d22d8eeSMatthew Dillon 	atomic_add_int(&tmp->tm_root->tn_links, -1);
370ff837cd5SMatthew Dillon 	TMPFS_NODE_UNLOCK(tmp->tm_root);
3716e0c5aabSMatthew Dillon 
3726e0c5aabSMatthew Dillon 	/*
3736e0c5aabSMatthew Dillon 	 * At this point all nodes, including the root node, should have a
3746e0c5aabSMatthew Dillon 	 * link count of 0.  The root is not necessarily going to be last.
3756e0c5aabSMatthew Dillon 	 */
3766e0c5aabSMatthew Dillon 	while ((node = LIST_FIRST(&tmp->tm_nodes_used)) != NULL) {
377*e9dbfea1SMatthew Dillon 		if (node->tn_links) {
378aa1adbf0SMatthew Dillon 			panic("tmpfs: Dangling nodes during umount (%p)!\n",
379aa1adbf0SMatthew Dillon 			      node);
380*e9dbfea1SMatthew Dillon 		}
381aa1adbf0SMatthew Dillon 
3820786baf1SMatthew Dillon 		TMPFS_NODE_LOCK(node);
3830786baf1SMatthew Dillon 		tmpfs_free_node(tmp, node);
3840786baf1SMatthew Dillon 		/* eats lock */
385ace53c28SMatthew Dillon 		lwkt_yield();
3860786baf1SMatthew Dillon 	}
3870786baf1SMatthew Dillon 	KKASSERT(tmp->tm_root == NULL);
3880786baf1SMatthew Dillon 
389d00cd01cSVenkatesh Srinivas 	kmalloc_destroy(&tmp->tm_name_zone);
390*e9dbfea1SMatthew Dillon 	kmalloc_destroy(&tmp->tm_dirent_zone_obj);
391*e9dbfea1SMatthew Dillon 	kmalloc_destroy(&tmp->tm_node_zone_obj);
392dcaa8a41SVenkatesh Srinivas 
393*e9dbfea1SMatthew Dillon 	tmp->tm_node_zone_obj = NULL;
394*e9dbfea1SMatthew Dillon 	tmp->tm_dirent_zone_obj = NULL;
3958e771504SVenkatesh Srinivas 
3967a2de9a4SMatthew Dillon 	KKASSERT(tmp->tm_pages_used == 0);
3977a2de9a4SMatthew Dillon 	KKASSERT(tmp->tm_nodes_inuse == 0);
3987a2de9a4SMatthew Dillon 
399aa1adbf0SMatthew Dillon 	TMPFS_UNLOCK(tmp);
400aa1adbf0SMatthew Dillon 
4017a2de9a4SMatthew Dillon 	/* Throw away the tmpfs_mount structure. */
4020786baf1SMatthew Dillon 	kfree(tmp, M_TMPFSMNT);
4037a2de9a4SMatthew Dillon 	mp->mnt_data = NULL;
4047a2de9a4SMatthew Dillon 
4057a2de9a4SMatthew Dillon 	mp->mnt_flag &= ~MNT_LOCAL;
4067a2de9a4SMatthew Dillon 	return 0;
4077a2de9a4SMatthew Dillon }
4087a2de9a4SMatthew Dillon 
4097a2de9a4SMatthew Dillon /* --------------------------------------------------------------------- */
4107a2de9a4SMatthew Dillon 
4117a2de9a4SMatthew Dillon static int
tmpfs_root(struct mount * mp,struct vnode ** vpp)4127a2de9a4SMatthew Dillon tmpfs_root(struct mount *mp, struct vnode **vpp)
4137a2de9a4SMatthew Dillon {
4140786baf1SMatthew Dillon 	struct tmpfs_mount *tmp;
4157a2de9a4SMatthew Dillon 	int error;
4160786baf1SMatthew Dillon 
4170786baf1SMatthew Dillon 	tmp = VFS_TO_TMPFS(mp);
4180786baf1SMatthew Dillon 	if (tmp->tm_root == NULL) {
4190786baf1SMatthew Dillon 		kprintf("tmpfs_root: called without root node %p\n", mp);
4207ce2998eSAlex Hornung 		print_backtrace(-1);
4210786baf1SMatthew Dillon 		*vpp = NULL;
4220786baf1SMatthew Dillon 		error = EINVAL;
4230786baf1SMatthew Dillon 	} else {
424d89a0e31SMatthew Dillon 		error = tmpfs_alloc_vp(mp, NULL, tmp->tm_root,
425d89a0e31SMatthew Dillon 				       LK_EXCLUSIVE, vpp);
4267a2de9a4SMatthew Dillon 		(*vpp)->v_flag |= VROOT;
4277a2de9a4SMatthew Dillon 		(*vpp)->v_type = VDIR;
4280786baf1SMatthew Dillon 	}
4297a2de9a4SMatthew Dillon 	return error;
4307a2de9a4SMatthew Dillon }
4317a2de9a4SMatthew Dillon 
4327a2de9a4SMatthew Dillon /* --------------------------------------------------------------------- */
4337a2de9a4SMatthew Dillon 
4347a2de9a4SMatthew Dillon static int
tmpfs_fhtovp(struct mount * mp,struct vnode * rootvp,struct fid * fhp,struct vnode ** vpp)435aa1adbf0SMatthew Dillon tmpfs_fhtovp(struct mount *mp, struct vnode *rootvp, struct fid *fhp,
436aa1adbf0SMatthew Dillon 	     struct vnode **vpp)
4377a2de9a4SMatthew Dillon {
4387a2de9a4SMatthew Dillon 	boolean_t found;
4397a2de9a4SMatthew Dillon 	struct tmpfs_fid *tfhp;
4407a2de9a4SMatthew Dillon 	struct tmpfs_mount *tmp;
4417a2de9a4SMatthew Dillon 	struct tmpfs_node *node;
442aa1adbf0SMatthew Dillon 	int rc;
4437a2de9a4SMatthew Dillon 
4447a2de9a4SMatthew Dillon 	tmp = VFS_TO_TMPFS(mp);
4457a2de9a4SMatthew Dillon 
4467a2de9a4SMatthew Dillon 	tfhp = (struct tmpfs_fid *) fhp;
4477a2de9a4SMatthew Dillon 	if (tfhp->tf_len != sizeof(struct tmpfs_fid))
4487a2de9a4SMatthew Dillon 		return EINVAL;
4497a2de9a4SMatthew Dillon 
450aa1adbf0SMatthew Dillon 	rc = EINVAL;
4517a2de9a4SMatthew Dillon 	found = FALSE;
4527a2de9a4SMatthew Dillon 
4537a2de9a4SMatthew Dillon 	TMPFS_LOCK(tmp);
4547a2de9a4SMatthew Dillon 	LIST_FOREACH(node, &tmp->tm_nodes_used, tn_entries) {
4557a2de9a4SMatthew Dillon 		if (node->tn_id == tfhp->tf_id &&
4567a2de9a4SMatthew Dillon 		    node->tn_gen == tfhp->tf_gen) {
4577a2de9a4SMatthew Dillon 			found = TRUE;
4587a2de9a4SMatthew Dillon 			break;
4597a2de9a4SMatthew Dillon 		}
4607a2de9a4SMatthew Dillon 	}
4617a2de9a4SMatthew Dillon 
4627a2de9a4SMatthew Dillon 	if (found)
463d89a0e31SMatthew Dillon 		rc = tmpfs_alloc_vp(mp, NULL, node, LK_EXCLUSIVE, vpp);
4647a2de9a4SMatthew Dillon 
465aa1adbf0SMatthew Dillon 	TMPFS_UNLOCK(tmp);
466aa1adbf0SMatthew Dillon 
467aa1adbf0SMatthew Dillon 	return (rc);
4687a2de9a4SMatthew Dillon }
4697a2de9a4SMatthew Dillon 
4707a2de9a4SMatthew Dillon /* --------------------------------------------------------------------- */
4717a2de9a4SMatthew Dillon 
4727a2de9a4SMatthew Dillon /* ARGSUSED2 */
4737a2de9a4SMatthew Dillon static int
tmpfs_statfs(struct mount * mp,struct statfs * sbp,struct ucred * cred)4747a2de9a4SMatthew Dillon tmpfs_statfs(struct mount *mp, struct statfs *sbp, struct ucred *cred)
4757a2de9a4SMatthew Dillon {
4767a2de9a4SMatthew Dillon 	fsfilcnt_t freenodes;
4777a2de9a4SMatthew Dillon 	struct tmpfs_mount *tmp;
4787a2de9a4SMatthew Dillon 
4797a2de9a4SMatthew Dillon 	tmp = VFS_TO_TMPFS(mp);
4807a2de9a4SMatthew Dillon 
4814d22d8eeSMatthew Dillon 	/* TMPFS_LOCK(tmp); not really needed */
4824d22d8eeSMatthew Dillon 
4837a2de9a4SMatthew Dillon 	sbp->f_iosize = PAGE_SIZE;
4847a2de9a4SMatthew Dillon 	sbp->f_bsize = PAGE_SIZE;
4857a2de9a4SMatthew Dillon 
48629ffeb28SMatthew Dillon 	sbp->f_blocks = tmp->tm_pages_max;
48729ffeb28SMatthew Dillon 	sbp->f_bavail = tmp->tm_pages_max - tmp->tm_pages_used;
48829ffeb28SMatthew Dillon 	sbp->f_bfree = sbp->f_bavail;
4897a2de9a4SMatthew Dillon 
49029ffeb28SMatthew Dillon 	freenodes = tmp->tm_nodes_max - tmp->tm_nodes_inuse;
4917a2de9a4SMatthew Dillon 
4927a2de9a4SMatthew Dillon 	sbp->f_files = freenodes + tmp->tm_nodes_inuse;
4937a2de9a4SMatthew Dillon 	sbp->f_ffree = freenodes;
494817a2fd9SMatthew Dillon 	sbp->f_owner = tmp->tm_root->tn_uid;
4957a2de9a4SMatthew Dillon 
4964d22d8eeSMatthew Dillon 	/* TMPFS_UNLOCK(tmp); */
497aa1adbf0SMatthew Dillon 
4987a2de9a4SMatthew Dillon 	return 0;
4997a2de9a4SMatthew Dillon }
5007a2de9a4SMatthew Dillon 
5017a2de9a4SMatthew Dillon /* --------------------------------------------------------------------- */
5027a2de9a4SMatthew Dillon 
503190c11ccSSepherosa Ziehau static int
tmpfs_vptofh(struct vnode * vp,struct fid * fhp)504190c11ccSSepherosa Ziehau tmpfs_vptofh(struct vnode *vp, struct fid *fhp)
505190c11ccSSepherosa Ziehau {
506190c11ccSSepherosa Ziehau 	struct tmpfs_node *node;
507190c11ccSSepherosa Ziehau 	struct tmpfs_fid tfh;
508190c11ccSSepherosa Ziehau 	node = VP_TO_TMPFS_NODE(vp);
509190c11ccSSepherosa Ziehau 	memset(&tfh, 0, sizeof(tfh));
510190c11ccSSepherosa Ziehau 	tfh.tf_len = sizeof(struct tmpfs_fid);
511190c11ccSSepherosa Ziehau 	tfh.tf_gen = node->tn_gen;
512190c11ccSSepherosa Ziehau 	tfh.tf_id = node->tn_id;
513190c11ccSSepherosa Ziehau 	memcpy(fhp, &tfh, sizeof(tfh));
514190c11ccSSepherosa Ziehau 	return (0);
515190c11ccSSepherosa Ziehau }
516190c11ccSSepherosa Ziehau 
517190c11ccSSepherosa Ziehau /* --------------------------------------------------------------------- */
518190c11ccSSepherosa Ziehau 
51966fa44e7SVenkatesh Srinivas static int
tmpfs_checkexp(struct mount * mp,struct sockaddr * nam,int * exflagsp,struct ucred ** credanonp)52066fa44e7SVenkatesh Srinivas tmpfs_checkexp(struct mount *mp, struct sockaddr *nam, int *exflagsp,
52166fa44e7SVenkatesh Srinivas 	       struct ucred **credanonp)
52266fa44e7SVenkatesh Srinivas {
52366fa44e7SVenkatesh Srinivas 	struct tmpfs_mount *tmp;
52466fa44e7SVenkatesh Srinivas 	struct netcred *nc;
52566fa44e7SVenkatesh Srinivas 
52666fa44e7SVenkatesh Srinivas 	tmp = (struct tmpfs_mount *) mp->mnt_data;
52766fa44e7SVenkatesh Srinivas 	nc = vfs_export_lookup(mp, &tmp->tm_export, nam);
52866fa44e7SVenkatesh Srinivas 	if (nc == NULL)
52966fa44e7SVenkatesh Srinivas 		return (EACCES);
53066fa44e7SVenkatesh Srinivas 
53166fa44e7SVenkatesh Srinivas 	*exflagsp = nc->netc_exflags;
53266fa44e7SVenkatesh Srinivas 	*credanonp = &nc->netc_anon;
53366fa44e7SVenkatesh Srinivas 
53466fa44e7SVenkatesh Srinivas 	return (0);
53566fa44e7SVenkatesh Srinivas }
53666fa44e7SVenkatesh Srinivas 
53766fa44e7SVenkatesh Srinivas /* --------------------------------------------------------------------- */
53866fa44e7SVenkatesh Srinivas 
5397a2de9a4SMatthew Dillon /*
5407a2de9a4SMatthew Dillon  * tmpfs vfs operations.
5417a2de9a4SMatthew Dillon  */
5427a2de9a4SMatthew Dillon 
5437a2de9a4SMatthew Dillon static struct vfsops tmpfs_vfsops = {
54400369c4aSMatthew Dillon 	.vfs_flags =			0,
5457a2de9a4SMatthew Dillon 	.vfs_mount =			tmpfs_mount,
5467a2de9a4SMatthew Dillon 	.vfs_unmount =			tmpfs_unmount,
5477a2de9a4SMatthew Dillon 	.vfs_root =			tmpfs_root,
5487a2de9a4SMatthew Dillon 	.vfs_statfs =			tmpfs_statfs,
5497a2de9a4SMatthew Dillon 	.vfs_fhtovp =			tmpfs_fhtovp,
550190c11ccSSepherosa Ziehau 	.vfs_vptofh =			tmpfs_vptofh,
55166fa44e7SVenkatesh Srinivas 	.vfs_checkexp =			tmpfs_checkexp,
5527a2de9a4SMatthew Dillon };
5537a2de9a4SMatthew Dillon 
55487f62b1cSMatthew Dillon VFS_SET(tmpfs_vfsops, tmpfs, VFCF_MPSAFE);
555e5e63c20SSascha Wildner MODULE_VERSION(tmpfs, 1);
556