xref: /openbsd-src/sys/tmpfs/tmpfs_vfsops.c (revision 6434e45f8504f47346c03a833ed70d3e01a9a8e5)
1*6434e45fSpatrick /*	$OpenBSD: tmpfs_vfsops.c,v 1.19 2021/10/24 15:41:47 patrick Exp $	*/
27013b092Sespie /*	$NetBSD: tmpfs_vfsops.c,v 1.52 2011/09/27 01:10:43 christos Exp $	*/
37013b092Sespie 
47013b092Sespie /*
57013b092Sespie  * Copyright (c) 2005, 2006, 2007 The NetBSD Foundation, Inc.
67013b092Sespie  * All rights reserved.
77013b092Sespie  *
87013b092Sespie  * This code is derived from software contributed to The NetBSD Foundation
97013b092Sespie  * by Julio M. Merino Vidal, developed as part of Google's Summer of Code
107013b092Sespie  * 2005 program.
117013b092Sespie  *
127013b092Sespie  * Redistribution and use in source and binary forms, with or without
137013b092Sespie  * modification, are permitted provided that the following conditions
147013b092Sespie  * are met:
157013b092Sespie  * 1. Redistributions of source code must retain the above copyright
167013b092Sespie  *    notice, this list of conditions and the following disclaimer.
177013b092Sespie  * 2. Redistributions in binary form must reproduce the above copyright
187013b092Sespie  *    notice, this list of conditions and the following disclaimer in the
197013b092Sespie  *    documentation and/or other materials provided with the distribution.
207013b092Sespie  *
217013b092Sespie  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
227013b092Sespie  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
237013b092Sespie  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
247013b092Sespie  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
257013b092Sespie  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
267013b092Sespie  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
277013b092Sespie  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
287013b092Sespie  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
297013b092Sespie  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
307013b092Sespie  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
317013b092Sespie  * POSSIBILITY OF SUCH DAMAGE.
327013b092Sespie  */
337013b092Sespie 
347013b092Sespie /*
357013b092Sespie  * Efficient memory file system.
367013b092Sespie  *
377013b092Sespie  * tmpfs is a file system that uses NetBSD's virtual memory sub-system
387013b092Sespie  * (the well-known UVM) to store file data and metadata in an efficient
397013b092Sespie  * way.  This means that it does not follow the structure of an on-disk
407013b092Sespie  * file system because it simply does not need to.  Instead, it uses
417013b092Sespie  * memory-specific data structures and algorithms to automatically
427013b092Sespie  * allocate and release resources.
437013b092Sespie  */
447013b092Sespie 
457013b092Sespie #include <sys/param.h>
467013b092Sespie #include <sys/mount.h>
477013b092Sespie #include <sys/stat.h>
487013b092Sespie #include <sys/systm.h>
497013b092Sespie #include <sys/vnode.h>
507013b092Sespie #include <sys/malloc.h>
517013b092Sespie 
527013b092Sespie #include <tmpfs/tmpfs.h>
537013b092Sespie 
547013b092Sespie /* MODULE(MODULE_CLASS_VFS, tmpfs, NULL); */
557013b092Sespie 
567013b092Sespie struct pool	tmpfs_dirent_pool;
577013b092Sespie struct pool	tmpfs_node_pool;
587013b092Sespie 
597013b092Sespie int	tmpfs_mount(struct mount *, const char *, void *, struct nameidata *,
607013b092Sespie 	    struct proc *);
617013b092Sespie int	tmpfs_start(struct mount *, int, struct proc *);
627013b092Sespie int	tmpfs_unmount(struct mount *, int, struct proc *);
637013b092Sespie int	tmpfs_root(struct mount *, struct vnode **);
647013b092Sespie int	tmpfs_vget(struct mount *, ino_t, struct vnode **);
657013b092Sespie int	tmpfs_fhtovp(struct mount *, struct fid *, struct vnode **);
667013b092Sespie int	tmpfs_vptofh(struct vnode *, struct fid *);
677013b092Sespie int	tmpfs_statfs(struct mount *, struct statfs *, struct proc *);
6896e02447Sderaadt int	tmpfs_sync(struct mount *, int, int, struct ucred *, struct proc *);
697013b092Sespie int	tmpfs_init(struct vfsconf *);
70170ee3fbSpatrick int	tmpfs_mount_update(struct mount *);
717013b092Sespie 
727013b092Sespie int
tmpfs_init(struct vfsconf * vfsp)737013b092Sespie tmpfs_init(struct vfsconf *vfsp)
747013b092Sespie {
757013b092Sespie 
761378bae2Sdlg 	pool_init(&tmpfs_dirent_pool, sizeof(tmpfs_dirent_t), 0, IPL_NONE,
771378bae2Sdlg 	    PR_WAITOK, "tmpfs_dirent", NULL);
781378bae2Sdlg 	pool_init(&tmpfs_node_pool, sizeof(tmpfs_node_t), 0, IPL_NONE,
791378bae2Sdlg 	    PR_WAITOK, "tmpfs_node", NULL);
807013b092Sespie 
817013b092Sespie 	return 0;
827013b092Sespie }
837013b092Sespie 
847013b092Sespie int
tmpfs_mount_update(struct mount * mp)85170ee3fbSpatrick tmpfs_mount_update(struct mount *mp)
86170ee3fbSpatrick {
87170ee3fbSpatrick 	tmpfs_mount_t *tmp;
88170ee3fbSpatrick 	struct vnode *rootvp;
89170ee3fbSpatrick 	int error;
90170ee3fbSpatrick 
91170ee3fbSpatrick 	if ((mp->mnt_flag & MNT_RDONLY) == 0)
92170ee3fbSpatrick 		return EOPNOTSUPP;
93170ee3fbSpatrick 
94170ee3fbSpatrick 	/* ro->rw transition: nothing to do? */
95170ee3fbSpatrick 	if (mp->mnt_flag & MNT_WANTRDWR)
96170ee3fbSpatrick 		return 0;
97170ee3fbSpatrick 
98170ee3fbSpatrick 	tmp = mp->mnt_data;
99170ee3fbSpatrick 	rootvp = tmp->tm_root->tn_vnode;
100170ee3fbSpatrick 
101170ee3fbSpatrick 	/* Lock root to prevent lookups. */
102170ee3fbSpatrick 	error = vn_lock(rootvp, LK_EXCLUSIVE | LK_RETRY);
103170ee3fbSpatrick 	if (error)
104170ee3fbSpatrick 		return error;
105170ee3fbSpatrick 
106170ee3fbSpatrick 	/* Lock mount point to prevent nodes from being added/removed. */
107170ee3fbSpatrick 	rw_enter_write(&tmp->tm_lock);
108170ee3fbSpatrick 
109170ee3fbSpatrick 	/* Flush files opened for writing; skip rootvp. */
110170ee3fbSpatrick 	error = vflush(mp, rootvp, WRITECLOSE);
111170ee3fbSpatrick 
112170ee3fbSpatrick 	rw_exit_write(&tmp->tm_lock);
113170ee3fbSpatrick 	VOP_UNLOCK(rootvp);
114170ee3fbSpatrick 
115170ee3fbSpatrick 	return error;
116170ee3fbSpatrick }
117170ee3fbSpatrick 
118170ee3fbSpatrick int
tmpfs_mount(struct mount * mp,const char * path,void * data,struct nameidata * ndp,struct proc * p)1197013b092Sespie tmpfs_mount(struct mount *mp, const char *path, void *data,
1207013b092Sespie     struct nameidata *ndp, struct proc *p)
1217013b092Sespie {
1227efda1a1Sderaadt 	struct tmpfs_args *args = data;
1237013b092Sespie 	tmpfs_mount_t *tmp;
1247013b092Sespie 	tmpfs_node_t *root;
1257013b092Sespie 	uint64_t memlimit;
1267013b092Sespie 	uint64_t nodes;
1277013b092Sespie 	int error;
1287013b092Sespie 
129170ee3fbSpatrick 	if (mp->mnt_flag & MNT_UPDATE)
130170ee3fbSpatrick 		return (tmpfs_mount_update(mp));
1317013b092Sespie 
1327013b092Sespie 	/* Prohibit mounts if there is not enough memory. */
1337013b092Sespie 	if (tmpfs_mem_info(1) < TMPFS_PAGES_RESERVED)
1347013b092Sespie 		return EINVAL;
1357013b092Sespie 
1367efda1a1Sderaadt 	if (args->ta_root_uid == VNOVAL || args->ta_root_gid == VNOVAL ||
1377efda1a1Sderaadt 	    args->ta_root_mode == VNOVAL)
13877ad037fStedu 		return EINVAL;
1397013b092Sespie 
1407013b092Sespie 	/* Get the memory usage limit for this file-system. */
1417efda1a1Sderaadt 	if (args->ta_size_max < PAGE_SIZE) {
1427013b092Sespie 		memlimit = UINT64_MAX;
1437013b092Sespie 	} else {
1447efda1a1Sderaadt 		memlimit = args->ta_size_max;
1457013b092Sespie 	}
1467013b092Sespie 	KASSERT(memlimit > 0);
1477013b092Sespie 
1487efda1a1Sderaadt 	if (args->ta_nodes_max <= 3) {
1497013b092Sespie 		nodes = 3 + (memlimit / 1024);
1507013b092Sespie 	} else {
1517efda1a1Sderaadt 		nodes = args->ta_nodes_max;
1527013b092Sespie 	}
1537013b092Sespie 	nodes = MIN(nodes, INT_MAX);
1547013b092Sespie 	KASSERT(nodes >= 3);
1557013b092Sespie 
1567013b092Sespie 	/* Allocate the tmpfs mount structure and fill it. */
1577013b092Sespie 	tmp = malloc(sizeof(tmpfs_mount_t), M_MISCFSMNT, M_WAITOK);
1587013b092Sespie 
1597013b092Sespie 	tmp->tm_nodes_max = (ino_t)nodes;
1607013b092Sespie 	tmp->tm_nodes_cnt = 0;
16157331246Sespie 	tmp->tm_highest_inode = 1;
1627013b092Sespie 	LIST_INIT(&tmp->tm_nodes);
1637013b092Sespie 
1647013b092Sespie 	rw_init(&tmp->tm_lock, "tmplk");
1657013b092Sespie 	tmpfs_mntmem_init(tmp, memlimit);
1667013b092Sespie 
1677013b092Sespie 	/* Allocate the root node. */
1687efda1a1Sderaadt 	error = tmpfs_alloc_node(tmp, VDIR, args->ta_root_uid,
1697efda1a1Sderaadt 	    args->ta_root_gid, args->ta_root_mode & ALLPERMS, NULL,
1707013b092Sespie 	    VNOVAL, &root);
1717013b092Sespie 	KASSERT(error == 0 && root != NULL);
1727013b092Sespie 
1737013b092Sespie 	/*
1747013b092Sespie 	 * Parent of the root inode is itself.  Also, root inode has no
1757013b092Sespie 	 * directory entry (i.e. is never attached), thus hold an extra
1767013b092Sespie 	 * reference (link) for it.
1777013b092Sespie 	 */
1787013b092Sespie 	root->tn_links++;
1797013b092Sespie 	root->tn_spec.tn_dir.tn_parent = root;
1807013b092Sespie 	tmp->tm_root = root;
1817013b092Sespie 
1827013b092Sespie 	mp->mnt_data = tmp;
1837013b092Sespie 	mp->mnt_flag |= MNT_LOCAL;
1847013b092Sespie 	mp->mnt_stat.f_namemax = TMPFS_MAXNAMLEN;
1857013b092Sespie 	vfs_getnewfsid(mp);
1867013b092Sespie 
1877efda1a1Sderaadt 	mp->mnt_stat.mount_info.tmpfs_args = *args;
18857331246Sespie 
18957331246Sespie 	bzero(&mp->mnt_stat.f_mntonname, sizeof(mp->mnt_stat.f_mntonname));
19057331246Sespie 	bzero(&mp->mnt_stat.f_mntfromname, sizeof(mp->mnt_stat.f_mntfromname));
19157331246Sespie 	bzero(&mp->mnt_stat.f_mntfromspec, sizeof(mp->mnt_stat.f_mntfromspec));
19257331246Sespie 
19357331246Sespie 	strlcpy(mp->mnt_stat.f_mntonname, path,
19457331246Sespie 	    sizeof(mp->mnt_stat.f_mntonname) - 1);
19557331246Sespie 	strlcpy(mp->mnt_stat.f_mntfromname, "tmpfs",
19657331246Sespie 	    sizeof(mp->mnt_stat.f_mntfromname) - 1);
19757331246Sespie 	strlcpy(mp->mnt_stat.f_mntfromspec, "tmpfs",
19857331246Sespie 	    sizeof(mp->mnt_stat.f_mntfromspec) - 1);
1997013b092Sespie 
2007013b092Sespie 	return error;
2017013b092Sespie }
2027013b092Sespie 
2037013b092Sespie int
tmpfs_start(struct mount * mp,int flags,struct proc * p)2047013b092Sespie tmpfs_start(struct mount *mp, int flags, struct proc *p)
2057013b092Sespie {
2067013b092Sespie 	return 0;
2077013b092Sespie }
2087013b092Sespie 
2097013b092Sespie int
tmpfs_unmount(struct mount * mp,int mntflags,struct proc * p)2107013b092Sespie tmpfs_unmount(struct mount *mp, int mntflags, struct proc *p)
2117013b092Sespie {
21257331246Sespie 	tmpfs_mount_t *tmp = VFS_TO_TMPFS(mp);
21357331246Sespie 	tmpfs_node_t *node, *cnode;
2147013b092Sespie 	int error, flags = 0;
2157013b092Sespie 
2167013b092Sespie 	/* Handle forced unmounts. */
2177013b092Sespie 	if (mntflags & MNT_FORCE)
2187013b092Sespie 		flags |= FORCECLOSE;
2197013b092Sespie 
2207013b092Sespie 	/* Finalize all pending I/O. */
2217013b092Sespie 	error = vflush(mp, NULL, flags);
2227013b092Sespie 	if (error != 0)
2237013b092Sespie 		return error;
2247013b092Sespie 
22557331246Sespie 	/*
22657331246Sespie 	 * First round, detach and destroy all directory entries.
22757331246Sespie 	 * Also, clear the pointers to the vnodes - they are gone.
22857331246Sespie 	 */
22957331246Sespie 	LIST_FOREACH(node, &tmp->tm_nodes, tn_entries) {
2307013b092Sespie 		tmpfs_dirent_t *de;
2317013b092Sespie 
23257331246Sespie 		node->tn_vnode = NULL;
23357331246Sespie 		if (node->tn_type != VDIR) {
23457331246Sespie 			continue;
23557331246Sespie 		}
23657331246Sespie 		while ((de = TAILQ_FIRST(&node->tn_spec.tn_dir.tn_dir)) != NULL) {
23757331246Sespie 			cnode = de->td_node;
238*6434e45fSpatrick 			if (cnode)
23957331246Sespie 				cnode->tn_vnode = NULL;
24057331246Sespie 			tmpfs_dir_detach(node, de);
2417013b092Sespie 			tmpfs_free_dirent(tmp, de);
2427013b092Sespie 		}
2437013b092Sespie 	}
24457331246Sespie 
24557331246Sespie 	/* Second round, destroy all inodes. */
24657331246Sespie 	while ((node = LIST_FIRST(&tmp->tm_nodes)) != NULL) {
2477013b092Sespie 		tmpfs_free_node(tmp, node);
2487013b092Sespie 	}
2497013b092Sespie 
2507013b092Sespie 	/* Throw away the tmpfs_mount structure. */
2517013b092Sespie 	tmpfs_mntmem_destroy(tmp);
2527013b092Sespie 	/* mutex_destroy(&tmp->tm_lock); */
25357331246Sespie 	/* kmem_free(tmp, sizeof(*tmp)); */
25437742db1Stedu 	free(tmp, M_MISCFSMNT, sizeof(tmpfs_mount_t));
2557013b092Sespie 	mp->mnt_data = NULL;
2567013b092Sespie 
2577013b092Sespie 	return 0;
2587013b092Sespie }
2597013b092Sespie 
2607013b092Sespie int
tmpfs_root(struct mount * mp,struct vnode ** vpp)2617013b092Sespie tmpfs_root(struct mount *mp, struct vnode **vpp)
2627013b092Sespie {
2637013b092Sespie 	tmpfs_node_t *node = VFS_TO_TMPFS(mp)->tm_root;
2647013b092Sespie 
2657013b092Sespie 	rw_enter_write(&node->tn_nlock);
2667013b092Sespie 	return tmpfs_vnode_get(mp, node, vpp);
2677013b092Sespie }
2687013b092Sespie 
2697013b092Sespie int
tmpfs_vget(struct mount * mp,ino_t ino,struct vnode ** vpp)2707013b092Sespie tmpfs_vget(struct mount *mp, ino_t ino, struct vnode **vpp)
2717013b092Sespie {
2727013b092Sespie 
2737013b092Sespie 	printf("tmpfs_vget called; need for it unknown yet\n");
2747013b092Sespie 	return EOPNOTSUPP;
2757013b092Sespie }
2767013b092Sespie 
2777013b092Sespie int
tmpfs_fhtovp(struct mount * mp,struct fid * fhp,struct vnode ** vpp)2787013b092Sespie tmpfs_fhtovp(struct mount *mp, struct fid *fhp, struct vnode **vpp)
2797013b092Sespie {
2807013b092Sespie 	tmpfs_mount_t *tmp = VFS_TO_TMPFS(mp);
2817013b092Sespie 	tmpfs_node_t *node;
2827013b092Sespie 	tmpfs_fid_t tfh;
2837013b092Sespie 
2847013b092Sespie 	if (fhp->fid_len != sizeof(tmpfs_fid_t)) {
2857013b092Sespie 		return EINVAL;
2867013b092Sespie 	}
2877013b092Sespie 	memcpy(&tfh, fhp, sizeof(tmpfs_fid_t));
2887013b092Sespie 
2897013b092Sespie 	rw_enter_write(&tmp->tm_lock);
2907013b092Sespie 	LIST_FOREACH(node, &tmp->tm_nodes, tn_entries) {
2917013b092Sespie 		if (node->tn_id != tfh.tf_id) {
2927013b092Sespie 			continue;
2937013b092Sespie 		}
2947013b092Sespie 		if (TMPFS_NODE_GEN(node) != tfh.tf_gen) {
2957013b092Sespie 			continue;
2967013b092Sespie 		}
2977013b092Sespie 		rw_enter_write(&node->tn_nlock);
2987013b092Sespie 		break;
2997013b092Sespie 	}
3007013b092Sespie 	rw_exit_write(&tmp->tm_lock);
3017013b092Sespie 
3027013b092Sespie 	/* Will release the tn_nlock. */
3037013b092Sespie 	return node ? tmpfs_vnode_get(mp, node, vpp) : ESTALE;
3047013b092Sespie }
3057013b092Sespie 
3067013b092Sespie int
tmpfs_vptofh(struct vnode * vp,struct fid * fhp)3077013b092Sespie tmpfs_vptofh(struct vnode *vp, struct fid *fhp)
3087013b092Sespie {
3097013b092Sespie 	tmpfs_fid_t tfh;
3107013b092Sespie 	tmpfs_node_t *node;
3117013b092Sespie 
3127013b092Sespie 	node = VP_TO_TMPFS_NODE(vp);
3137013b092Sespie 
3147013b092Sespie 	memset(&tfh, 0, sizeof(tfh));
3157013b092Sespie 	tfh.tf_len = sizeof(tmpfs_fid_t);
3167013b092Sespie 	tfh.tf_gen = TMPFS_NODE_GEN(node);
3177013b092Sespie 	tfh.tf_id = node->tn_id;
3187013b092Sespie 	memcpy(fhp, &tfh, sizeof(tfh));
3197013b092Sespie 
3207013b092Sespie 	return 0;
3217013b092Sespie }
3227013b092Sespie 
3237013b092Sespie int
tmpfs_statfs(struct mount * mp,struct statfs * sbp,struct proc * p)3247013b092Sespie tmpfs_statfs(struct mount *mp, struct statfs *sbp, struct proc *p)
3257013b092Sespie {
3267013b092Sespie 	tmpfs_mount_t *tmp;
3277013b092Sespie 	fsfilcnt_t freenodes;
3287013b092Sespie 	uint64_t avail;
3297013b092Sespie 
3307013b092Sespie 	tmp = VFS_TO_TMPFS(mp);
3317013b092Sespie 
3327013b092Sespie 	sbp->f_iosize = sbp->f_bsize = PAGE_SIZE;
3337013b092Sespie 
3347013b092Sespie 	rw_enter_write(&tmp->tm_acc_lock);
3357013b092Sespie 	avail =  tmpfs_pages_avail(tmp);
3367013b092Sespie 	sbp->f_blocks = (tmpfs_bytes_max(tmp) >> PAGE_SHIFT);
3377013b092Sespie 	sbp->f_bfree = avail;
3387013b092Sespie 	sbp->f_bavail = avail & INT64_MAX; /* f_bavail is int64_t */
3397013b092Sespie 
3407013b092Sespie 	freenodes = MIN(tmp->tm_nodes_max - tmp->tm_nodes_cnt,
3417013b092Sespie 	    avail * PAGE_SIZE / sizeof(tmpfs_node_t));
3427013b092Sespie 
3437013b092Sespie 	sbp->f_files = tmp->tm_nodes_cnt + freenodes;
3447013b092Sespie 	sbp->f_ffree = freenodes;
3457013b092Sespie 	sbp->f_favail = freenodes & INT64_MAX; /* f_favail is int64_t */
3467013b092Sespie 	rw_exit_write(&tmp->tm_acc_lock);
3477013b092Sespie 
3487013b092Sespie 	copy_statfs_info(sbp, mp);
3497013b092Sespie 
3507013b092Sespie 	return 0;
3517013b092Sespie }
3527013b092Sespie 
3537013b092Sespie int
tmpfs_sync(struct mount * mp,int waitfor,int stall,struct ucred * cred,struct proc * p)35496e02447Sderaadt tmpfs_sync(struct mount *mp, int waitfor, int stall, struct ucred *cred,
35596e02447Sderaadt     struct proc *p)
3567013b092Sespie {
3577013b092Sespie 
3587013b092Sespie 	return 0;
3597013b092Sespie }
3607013b092Sespie 
3617013b092Sespie /*
3627013b092Sespie  * tmpfs vfs operations.
3637013b092Sespie  */
3647013b092Sespie 
36541f642fcSbluhm const struct vfsops tmpfs_vfsops = {
36641f642fcSbluhm 	.vfs_mount	= tmpfs_mount,
36741f642fcSbluhm 	.vfs_start	= tmpfs_start,
36841f642fcSbluhm 	.vfs_unmount	= tmpfs_unmount,
36941f642fcSbluhm 	.vfs_root	= tmpfs_root,
37041f642fcSbluhm 	.vfs_quotactl	= (void *)eopnotsupp,
37141f642fcSbluhm 	.vfs_statfs	= tmpfs_statfs,
37241f642fcSbluhm 	.vfs_sync	= tmpfs_sync,
37341f642fcSbluhm 	.vfs_vget	= tmpfs_vget,
37441f642fcSbluhm 	.vfs_fhtovp	= tmpfs_fhtovp,
37541f642fcSbluhm 	.vfs_vptofh	= tmpfs_vptofh,
37641f642fcSbluhm 	.vfs_init	= tmpfs_init,
37741f642fcSbluhm 	.vfs_sysctl	= (void *)eopnotsupp,
37841f642fcSbluhm 	.vfs_checkexp	= (void *)eopnotsupp,
3797013b092Sespie };
380