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