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