jcs's openbsd hax
openbsd
at jcs 383 lines 10 kB view raw
1/* $OpenBSD: tmpfs_vfsops.c,v 1.21 2025/11/21 09:49:33 mvs Exp $ */ 2/* $NetBSD: tmpfs_vfsops.c,v 1.52 2011/09/27 01:10:43 christos Exp $ */ 3 4/* 5 * Copyright (c) 2005, 2006, 2007 The NetBSD Foundation, Inc. 6 * All rights reserved. 7 * 8 * This code is derived from software contributed to The NetBSD Foundation 9 * by Julio M. Merino Vidal, developed as part of Google's Summer of Code 10 * 2005 program. 11 * 12 * Redistribution and use in source and binary forms, with or without 13 * modification, are permitted provided that the following conditions 14 * are met: 15 * 1. Redistributions of source code must retain the above copyright 16 * notice, this list of conditions and the following disclaimer. 17 * 2. Redistributions in binary form must reproduce the above copyright 18 * notice, this list of conditions and the following disclaimer in the 19 * documentation and/or other materials provided with the distribution. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 23 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 24 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 25 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 29 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 * POSSIBILITY OF SUCH DAMAGE. 32 */ 33 34/* 35 * Efficient memory file system. 36 * 37 * tmpfs is a file system that uses NetBSD's virtual memory sub-system 38 * (the well-known UVM) to store file data and metadata in an efficient 39 * way. This means that it does not follow the structure of an on-disk 40 * file system because it simply does not need to. Instead, it uses 41 * memory-specific data structures and algorithms to automatically 42 * allocate and release resources. 43 */ 44 45#include <sys/param.h> 46#include <sys/mount.h> 47#include <sys/stat.h> 48#include <sys/systm.h> 49#include <sys/vnode.h> 50#include <sys/malloc.h> 51 52#include <tmpfs/tmpfs.h> 53 54/* MODULE(MODULE_CLASS_VFS, tmpfs, NULL); */ 55 56extern uint64_t tmpfs_bytes_limit; 57extern uint64_t tmpfs_bytes_used; 58 59struct pool tmpfs_dirent_pool; 60struct pool tmpfs_node_pool; 61 62int tmpfs_mount(struct mount *, const char *, void *, struct nameidata *, 63 struct proc *); 64int tmpfs_start(struct mount *, int, struct proc *); 65int tmpfs_unmount(struct mount *, int, struct proc *); 66int tmpfs_root(struct mount *, struct vnode **); 67int tmpfs_vget(struct mount *, ino_t, struct vnode **); 68int tmpfs_fhtovp(struct mount *, struct fid *, struct vnode **); 69int tmpfs_vptofh(struct vnode *, struct fid *); 70int tmpfs_statfs(struct mount *, struct statfs *, struct proc *); 71int tmpfs_sync(struct mount *, int, int, struct ucred *, struct proc *); 72int tmpfs_init(struct vfsconf *); 73int tmpfs_mount_update(struct mount *); 74 75int 76tmpfs_init(struct vfsconf *vfsp) 77{ 78 tmpfs_bytes_limit = ((uint64_t)(uvmexp.npages / 2) << PAGE_SHIFT); 79 80 pool_init(&tmpfs_dirent_pool, sizeof(tmpfs_dirent_t), 0, IPL_NONE, 81 PR_WAITOK, "tmpfs_dirent", NULL); 82 pool_init(&tmpfs_node_pool, sizeof(tmpfs_node_t), 0, IPL_NONE, 83 PR_WAITOK, "tmpfs_node", NULL); 84 85 return 0; 86} 87 88int 89tmpfs_mount_update(struct mount *mp) 90{ 91 tmpfs_mount_t *tmp; 92 struct vnode *rootvp; 93 int error; 94 95 if ((mp->mnt_flag & MNT_RDONLY) == 0) 96 return EOPNOTSUPP; 97 98 /* ro->rw transition: nothing to do? */ 99 if (mp->mnt_flag & MNT_WANTRDWR) 100 return 0; 101 102 tmp = mp->mnt_data; 103 rootvp = tmp->tm_root->tn_vnode; 104 105 /* Lock root to prevent lookups. */ 106 error = vn_lock(rootvp, LK_EXCLUSIVE | LK_RETRY); 107 if (error) 108 return error; 109 110 /* Lock mount point to prevent nodes from being added/removed. */ 111 rw_enter_write(&tmp->tm_lock); 112 113 /* Flush files opened for writing; skip rootvp. */ 114 error = vflush(mp, rootvp, WRITECLOSE); 115 116 rw_exit_write(&tmp->tm_lock); 117 VOP_UNLOCK(rootvp); 118 119 return error; 120} 121 122int 123tmpfs_mount(struct mount *mp, const char *path, void *data, 124 struct nameidata *ndp, struct proc *p) 125{ 126 struct tmpfs_args *args = data; 127 tmpfs_mount_t *tmp; 128 tmpfs_node_t *root; 129 uint64_t memlimit = 0; 130 uint64_t nodes; 131 int error; 132 133 if (mp->mnt_flag & MNT_UPDATE) 134 return (tmpfs_mount_update(mp)); 135 136 if (args->ta_root_uid == VNOVAL || args->ta_root_gid == VNOVAL || 137 args->ta_root_mode == VNOVAL) 138 return EINVAL; 139 140 /* Get the memory usage limit for this file-system. */ 141 if (args->ta_size_max) { 142 memlimit = roundup(args->ta_size_max, PAGE_SIZE); 143 144 if ((tmpfs_bytes_limit - tmpfs_bytes_used) < memlimit) 145 return EINVAL; /* historic error */ 146 tmpfs_bytes_used += memlimit; 147 } 148 149 if (args->ta_nodes_max <= 3) { 150 nodes = 3 + ((memlimit ? memlimit : UINT64_MAX) / 1024); 151 } else { 152 nodes = args->ta_nodes_max; 153 } 154 nodes = MIN(nodes, INT_MAX); 155 KASSERT(nodes >= 3); 156 157 /* Allocate the tmpfs mount structure and fill it. */ 158 tmp = malloc(sizeof(tmpfs_mount_t), M_MISCFSMNT, M_WAITOK); 159 160 tmp->tm_nodes_max = (ino_t)nodes; 161 tmp->tm_nodes_cnt = 0; 162 tmp->tm_highest_inode = 1; 163 LIST_INIT(&tmp->tm_nodes); 164 165 rw_init(&tmp->tm_lock, "tmplk"); 166 tmpfs_mntmem_init(tmp, memlimit); 167 168 /* Allocate the root node. */ 169 error = tmpfs_alloc_node(tmp, VDIR, args->ta_root_uid, 170 args->ta_root_gid, args->ta_root_mode & ALLPERMS, NULL, 171 VNOVAL, &root); 172 KASSERT(error == 0 && root != NULL); 173 174 /* 175 * Parent of the root inode is itself. Also, root inode has no 176 * directory entry (i.e. is never attached), thus hold an extra 177 * reference (link) for it. 178 */ 179 root->tn_links++; 180 root->tn_spec.tn_dir.tn_parent = root; 181 tmp->tm_root = root; 182 183 mp->mnt_data = tmp; 184 mp->mnt_flag |= MNT_LOCAL; 185 mp->mnt_stat.f_namemax = TMPFS_MAXNAMLEN; 186 vfs_getnewfsid(mp); 187 188 mp->mnt_stat.mount_info.tmpfs_args = *args; 189 190 bzero(&mp->mnt_stat.f_mntonname, sizeof(mp->mnt_stat.f_mntonname)); 191 bzero(&mp->mnt_stat.f_mntfromname, sizeof(mp->mnt_stat.f_mntfromname)); 192 bzero(&mp->mnt_stat.f_mntfromspec, sizeof(mp->mnt_stat.f_mntfromspec)); 193 194 strlcpy(mp->mnt_stat.f_mntonname, path, 195 sizeof(mp->mnt_stat.f_mntonname) - 1); 196 strlcpy(mp->mnt_stat.f_mntfromname, "tmpfs", 197 sizeof(mp->mnt_stat.f_mntfromname) - 1); 198 strlcpy(mp->mnt_stat.f_mntfromspec, "tmpfs", 199 sizeof(mp->mnt_stat.f_mntfromspec) - 1); 200 201 return error; 202} 203 204int 205tmpfs_start(struct mount *mp, int flags, struct proc *p) 206{ 207 return 0; 208} 209 210int 211tmpfs_unmount(struct mount *mp, int mntflags, struct proc *p) 212{ 213 tmpfs_mount_t *tmp = VFS_TO_TMPFS(mp); 214 tmpfs_node_t *node, *cnode; 215 int error, flags = 0; 216 217 /* Handle forced unmounts. */ 218 if (mntflags & MNT_FORCE) 219 flags |= FORCECLOSE; 220 221 /* Finalize all pending I/O. */ 222 error = vflush(mp, NULL, flags); 223 if (error != 0) 224 return error; 225 226 /* 227 * First round, detach and destroy all directory entries. 228 * Also, clear the pointers to the vnodes - they are gone. 229 */ 230 LIST_FOREACH(node, &tmp->tm_nodes, tn_entries) { 231 tmpfs_dirent_t *de; 232 233 node->tn_vnode = NULL; 234 if (node->tn_type != VDIR) { 235 continue; 236 } 237 while ((de = TAILQ_FIRST(&node->tn_spec.tn_dir.tn_dir)) != NULL) { 238 cnode = de->td_node; 239 if (cnode) 240 cnode->tn_vnode = NULL; 241 tmpfs_dir_detach(node, de); 242 tmpfs_free_dirent(tmp, de); 243 } 244 } 245 246 /* Second round, destroy all inodes. */ 247 while ((node = LIST_FIRST(&tmp->tm_nodes)) != NULL) { 248 tmpfs_free_node(tmp, node); 249 } 250 251 if (tmp->tm_mem_limit) 252 tmpfs_bytes_used -= tmp->tm_mem_limit; 253 254 /* Throw away the tmpfs_mount structure. */ 255 tmpfs_mntmem_destroy(tmp); 256 /* mutex_destroy(&tmp->tm_lock); */ 257 /* kmem_free(tmp, sizeof(*tmp)); */ 258 free(tmp, M_MISCFSMNT, sizeof(tmpfs_mount_t)); 259 mp->mnt_data = NULL; 260 261 return 0; 262} 263 264int 265tmpfs_root(struct mount *mp, struct vnode **vpp) 266{ 267 tmpfs_node_t *node = VFS_TO_TMPFS(mp)->tm_root; 268 269 rw_enter_write(&node->tn_nlock); 270 return tmpfs_vnode_get(mp, node, vpp); 271} 272 273int 274tmpfs_vget(struct mount *mp, ino_t ino, struct vnode **vpp) 275{ 276 277 printf("tmpfs_vget called; need for it unknown yet\n"); 278 return EOPNOTSUPP; 279} 280 281int 282tmpfs_fhtovp(struct mount *mp, struct fid *fhp, struct vnode **vpp) 283{ 284 tmpfs_mount_t *tmp = VFS_TO_TMPFS(mp); 285 tmpfs_node_t *node; 286 tmpfs_fid_t tfh; 287 288 if (fhp->fid_len != sizeof(tmpfs_fid_t)) { 289 return EINVAL; 290 } 291 memcpy(&tfh, fhp, sizeof(tmpfs_fid_t)); 292 293 rw_enter_write(&tmp->tm_lock); 294 LIST_FOREACH(node, &tmp->tm_nodes, tn_entries) { 295 if (node->tn_id != tfh.tf_id) { 296 continue; 297 } 298 if (TMPFS_NODE_GEN(node) != tfh.tf_gen) { 299 continue; 300 } 301 rw_enter_write(&node->tn_nlock); 302 break; 303 } 304 rw_exit_write(&tmp->tm_lock); 305 306 /* Will release the tn_nlock. */ 307 return node ? tmpfs_vnode_get(mp, node, vpp) : ESTALE; 308} 309 310int 311tmpfs_vptofh(struct vnode *vp, struct fid *fhp) 312{ 313 tmpfs_fid_t tfh; 314 tmpfs_node_t *node; 315 316 node = VP_TO_TMPFS_NODE(vp); 317 318 memset(&tfh, 0, sizeof(tfh)); 319 tfh.tf_len = sizeof(tmpfs_fid_t); 320 tfh.tf_gen = TMPFS_NODE_GEN(node); 321 tfh.tf_id = node->tn_id; 322 memcpy(fhp, &tfh, sizeof(tfh)); 323 324 return 0; 325} 326 327int 328tmpfs_statfs(struct mount *mp, struct statfs *sbp, struct proc *p) 329{ 330 tmpfs_mount_t *tmp; 331 fsfilcnt_t freenodes; 332 uint64_t avail; 333 334 tmp = VFS_TO_TMPFS(mp); 335 336 sbp->f_iosize = sbp->f_bsize = PAGE_SIZE; 337 338 rw_enter_read(&tmp->tm_acc_lock); 339 avail = tmpfs_pages_avail(tmp); 340 sbp->f_blocks = tmpfs_pages_total(tmp); 341 sbp->f_bfree = avail; 342 sbp->f_bavail = avail & INT64_MAX; /* f_bavail is int64_t */ 343 344 freenodes = MIN(tmp->tm_nodes_max - tmp->tm_nodes_cnt, 345 avail * PAGE_SIZE / sizeof(tmpfs_node_t)); 346 347 sbp->f_files = tmp->tm_nodes_cnt + freenodes; 348 sbp->f_ffree = freenodes; 349 sbp->f_favail = freenodes & INT64_MAX; /* f_favail is int64_t */ 350 rw_exit_read(&tmp->tm_acc_lock); 351 352 copy_statfs_info(sbp, mp); 353 354 return 0; 355} 356 357int 358tmpfs_sync(struct mount *mp, int waitfor, int stall, struct ucred *cred, 359 struct proc *p) 360{ 361 362 return 0; 363} 364 365/* 366 * tmpfs vfs operations. 367 */ 368 369const struct vfsops tmpfs_vfsops = { 370 .vfs_mount = tmpfs_mount, 371 .vfs_start = tmpfs_start, 372 .vfs_unmount = tmpfs_unmount, 373 .vfs_root = tmpfs_root, 374 .vfs_quotactl = (void *)eopnotsupp, 375 .vfs_statfs = tmpfs_statfs, 376 .vfs_sync = tmpfs_sync, 377 .vfs_vget = tmpfs_vget, 378 .vfs_fhtovp = tmpfs_fhtovp, 379 .vfs_vptofh = tmpfs_vptofh, 380 .vfs_init = tmpfs_init, 381 .vfs_sysctl = (void *)eopnotsupp, 382 .vfs_checkexp = (void *)eopnotsupp, 383};