1 /* $NetBSD: tmpfs_vfsops.c,v 1.10 2005/12/11 12:24:29 christos Exp $ */ 2 3 /*- 4 * Copyright (c) 2005 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Julio M. Merino Vidal, developed as part of Google's Summer of Code 9 * 2005 program. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 24 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30 * POSSIBILITY OF SUCH DAMAGE. 31 */ 32 33 /* 34 * Efficient memory file system. 35 * 36 * tmpfs is a file system that uses NetBSD's virtual memory sub-system 37 * (the well-known UVM) to store file data and metadata in an efficient 38 * way. This means that it does not follow the structure of an on-disk 39 * file system because it simply does not need to. Instead, it uses 40 * memory-specific data structures and algorithms to automatically 41 * allocate and release resources. 42 */ 43 #include <sys/cdefs.h> 44 #include <sys/conf.h> 45 #include <sys/param.h> 46 #include <sys/limits.h> 47 #include <sys/lock.h> 48 #include <sys/mutex.h> 49 #include <sys/kernel.h> 50 #include <sys/stat.h> 51 #include <sys/systm.h> 52 #include <sys/sysctl.h> 53 #include <sys/objcache.h> 54 55 #include <vm/vm.h> 56 #include <vm/vm_object.h> 57 #include <vm/vm_param.h> 58 59 #include <vfs/tmpfs/tmpfs.h> 60 #include <vfs/tmpfs/tmpfs_vnops.h> 61 #include <vfs/tmpfs/tmpfs_args.h> 62 63 /* 64 * Default permission for root node 65 */ 66 #define TMPFS_DEFAULT_ROOT_MODE (S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH) 67 68 MALLOC_DEFINE(M_TMPFSMNT, "tmpfs mount", "tmpfs mount structures"); 69 MALLOC_DEFINE(M_TMPFSNAME, "tmpfs name", "tmpfs file names"); 70 MALLOC_DEFINE(M_TMPFS_DIRENT, "tmpfs dirent", "tmpfs dirent structures"); 71 MALLOC_DEFINE(M_TMPFS_NODE, "tmpfs node", "tmpfs node structures"); 72 73 /* --------------------------------------------------------------------- */ 74 75 static int tmpfs_mount(struct mount *, char *, caddr_t, struct ucred *); 76 static int tmpfs_unmount(struct mount *, int); 77 static int tmpfs_root(struct mount *, struct vnode **); 78 static int tmpfs_fhtovp(struct mount *, struct vnode *, struct fid *, struct vnode **); 79 static int tmpfs_statfs(struct mount *, struct statfs *, struct ucred *cred); 80 81 /* --------------------------------------------------------------------- */ 82 int 83 tmpfs_node_ctor(void *obj, void *privdata, int flags) 84 { 85 struct tmpfs_node *node = (struct tmpfs_node *)obj; 86 87 node->tn_gen++; 88 node->tn_size = 0; 89 node->tn_status = 0; 90 node->tn_flags = 0; 91 node->tn_links = 0; 92 node->tn_vnode = NULL; 93 node->tn_vpstate = TMPFS_VNODE_WANT; 94 bzero(&node->tn_spec, sizeof(node->tn_spec)); 95 96 return (1); 97 } 98 99 static void 100 tmpfs_node_dtor(void *obj, void *privdata) 101 { 102 struct tmpfs_node *node = (struct tmpfs_node *)obj; 103 node->tn_type = VNON; 104 node->tn_vpstate = TMPFS_VNODE_DOOMED; 105 } 106 107 static void* 108 tmpfs_node_init(void *args, int flags) 109 { 110 struct tmpfs_node *node = (struct tmpfs_node *)objcache_malloc_alloc(args, flags); 111 node->tn_id = 0; 112 113 lockinit(&node->tn_interlock, "tmpfs node interlock", 0, LK_CANRECURSE); 114 node->tn_gen = karc4random(); 115 116 return node; 117 } 118 119 static void 120 tmpfs_node_fini(void *obj, void *args) 121 { 122 struct tmpfs_node *node = (struct tmpfs_node *)obj; 123 lockuninit(&node->tn_interlock); 124 objcache_malloc_free(obj, args); 125 } 126 127 struct objcache_malloc_args tmpfs_dirent_pool_malloc_args = 128 { sizeof(struct tmpfs_dirent), M_TMPFS_DIRENT }; 129 struct objcache_malloc_args tmpfs_node_pool_malloc_args = 130 { sizeof(struct tmpfs_node), M_TMPFS_NODE }; 131 132 static int 133 tmpfs_mount(struct mount *mp, char *path, caddr_t data, struct ucred *cred) 134 { 135 struct tmpfs_mount *tmp; 136 struct tmpfs_node *root; 137 struct tmpfs_args args; 138 vm_pindex_t pages; 139 vm_pindex_t pages_limit; 140 ino_t nodes; 141 u_int64_t maxfsize; 142 int error; 143 /* Size counters. */ 144 ino_t nodes_max; 145 off_t size_max; 146 size_t maxfsize_max; 147 size_t size; 148 149 /* Root node attributes. */ 150 uid_t root_uid = cred->cr_uid; 151 gid_t root_gid = cred->cr_gid; 152 mode_t root_mode = (VREAD | VWRITE); 153 154 if (mp->mnt_flag & MNT_UPDATE) { 155 /* XXX: There is no support yet to update file system 156 * settings. Should be added. */ 157 158 return EOPNOTSUPP; 159 } 160 161 /* 162 * mount info 163 */ 164 bzero(&args, sizeof(args)); 165 size_max = 0; 166 nodes_max = 0; 167 maxfsize_max = 0; 168 169 if (path) { 170 if (data) { 171 error = copyin(data, &args, sizeof(args)); 172 if (error) 173 return (error); 174 } 175 size_max = args.ta_size_max; 176 nodes_max = args.ta_nodes_max; 177 maxfsize_max = args.ta_maxfsize_max; 178 root_uid = args.ta_root_uid; 179 root_gid = args.ta_root_gid; 180 root_mode = args.ta_root_mode; 181 } 182 183 /* 184 * If mount by non-root, then verify that user has necessary 185 * permissions on the device. 186 */ 187 if (cred->cr_uid != 0) { 188 root_mode = VREAD; 189 if ((mp->mnt_flag & MNT_RDONLY) == 0) 190 root_mode |= VWRITE; 191 } 192 193 pages_limit = vm_swap_max + vmstats.v_page_count / 2; 194 195 if (size_max == 0) 196 pages = pages_limit / 2; 197 else if (size_max < PAGE_SIZE) 198 pages = 1; 199 else if (OFF_TO_IDX(size_max) > pages_limit) 200 pages = pages_limit; 201 else 202 pages = OFF_TO_IDX(size_max); 203 204 if (nodes_max == 0) 205 nodes = 3 + pages * PAGE_SIZE / 1024; 206 else if (nodes_max < 3) 207 nodes = 3; 208 else if (nodes_max > pages) 209 nodes = pages; 210 else 211 nodes = nodes_max; 212 213 maxfsize = IDX_TO_OFF(pages_limit); 214 if (maxfsize_max != 0 && maxfsize > maxfsize_max) 215 maxfsize = maxfsize_max; 216 217 /* Allocate the tmpfs mount structure and fill it. */ 218 tmp = kmalloc(sizeof(*tmp), M_TMPFSMNT, M_WAITOK | M_ZERO); 219 220 lockinit(&(tmp->allnode_lock), "tmpfs allnode lock", 0, LK_CANRECURSE); 221 tmp->tm_nodes_max = nodes; 222 tmp->tm_nodes_inuse = 0; 223 tmp->tm_maxfilesize = maxfsize; 224 LIST_INIT(&tmp->tm_nodes_used); 225 226 tmp->tm_pages_max = pages; 227 tmp->tm_pages_used = 0; 228 229 tmp->tm_dirent_pool = objcache_create( "tmpfs dirent cache", 230 0, 0, 231 NULL, NULL, NULL, 232 objcache_malloc_alloc, objcache_malloc_free, 233 &tmpfs_dirent_pool_malloc_args); 234 tmp->tm_node_pool = objcache_create( "tmpfs node cache", 235 0, 0, 236 tmpfs_node_ctor, tmpfs_node_dtor, NULL, 237 tmpfs_node_init, tmpfs_node_fini, 238 &tmpfs_node_pool_malloc_args); 239 240 /* Allocate the root node. */ 241 error = tmpfs_alloc_node(tmp, VDIR, root_uid, root_gid, 242 root_mode & ALLPERMS, NULL, NULL, 243 VNOVAL, VNOVAL, &root); 244 245 /* 246 * We are backed by swap, set snocache chflags flag so we 247 * don't trip over swapcache. 248 */ 249 root->tn_flags = SF_NOCACHE; 250 251 if (error != 0 || root == NULL) { 252 objcache_destroy(tmp->tm_node_pool); 253 objcache_destroy(tmp->tm_dirent_pool); 254 kfree(tmp, M_TMPFSMNT); 255 return error; 256 } 257 KASSERT(root->tn_id >= 0, ("tmpfs root with invalid ino: %d", (int)root->tn_id)); 258 tmp->tm_root = root; 259 260 mp->mnt_flag |= MNT_LOCAL; 261 #if 0 262 mp->mnt_kern_flag |= MNTK_RD_MPSAFE | MNTK_WR_MPSAFE | MNTK_GA_MPSAFE | 263 MNTK_IN_MPSAFE | MNTK_SG_MPSAFE; 264 #endif 265 mp->mnt_kern_flag |= MNTK_NOMSYNC; 266 mp->mnt_data = (qaddr_t)tmp; 267 vfs_getnewfsid(mp); 268 269 270 vfs_add_vnodeops(mp, &tmpfs_vnode_vops, &mp->mnt_vn_norm_ops); 271 vfs_add_vnodeops(mp, &tmpfs_fifo_vops, &mp->mnt_vn_fifo_ops); 272 273 copystr("tmpfs", mp->mnt_stat.f_mntfromname, MNAMELEN - 1, &size); 274 bzero(mp->mnt_stat.f_mntfromname +size, MNAMELEN - size); 275 bzero(mp->mnt_stat.f_mntonname, sizeof(mp->mnt_stat.f_mntonname)); 276 copyinstr(path, mp->mnt_stat.f_mntonname, 277 sizeof(mp->mnt_stat.f_mntonname) -1, 278 &size); 279 280 tmpfs_statfs(mp, &mp->mnt_stat, cred); 281 282 return 0; 283 } 284 285 /* --------------------------------------------------------------------- */ 286 287 /* ARGSUSED2 */ 288 static int 289 tmpfs_unmount(struct mount *mp, int mntflags) 290 { 291 int error; 292 int flags = 0; 293 int found; 294 struct tmpfs_mount *tmp; 295 struct tmpfs_node *node; 296 297 /* Handle forced unmounts. */ 298 if (mntflags & MNT_FORCE) 299 flags |= FORCECLOSE; 300 301 tmp = VFS_TO_TMPFS(mp); 302 303 /* 304 * Finalize all pending I/O. In the case of tmpfs we want 305 * to throw all the data away so clean out the buffer cache 306 * and vm objects before calling vflush(). 307 */ 308 LIST_FOREACH(node, &tmp->tm_nodes_used, tn_entries) { 309 if (node->tn_type == VREG && node->tn_vnode) { 310 ++node->tn_links; 311 TMPFS_NODE_LOCK(node); 312 vx_get(node->tn_vnode); 313 tmpfs_truncate(node->tn_vnode, 0); 314 vx_put(node->tn_vnode); 315 TMPFS_NODE_UNLOCK(node); 316 --node->tn_links; 317 } 318 } 319 error = vflush(mp, 0, flags); 320 if (error != 0) 321 return error; 322 323 /* 324 * First pass get rid of all the directory entries and 325 * vnode associations. The directory structure will 326 * remain via the extra link count representing tn_dir.tn_parent. 327 * 328 * No vnodes should remain after the vflush above. 329 */ 330 LIST_FOREACH(node, &tmp->tm_nodes_used, tn_entries) { 331 ++node->tn_links; 332 TMPFS_NODE_LOCK(node); 333 if (node->tn_type == VDIR) { 334 struct tmpfs_dirent *de; 335 336 while (!TAILQ_EMPTY(&node->tn_dir.tn_dirhead)) { 337 de = TAILQ_FIRST(&node->tn_dir.tn_dirhead); 338 tmpfs_dir_detach(node, de); 339 tmpfs_free_dirent(tmp, de); 340 node->tn_size -= sizeof(struct tmpfs_dirent); 341 } 342 } 343 KKASSERT(node->tn_vnode == NULL); 344 #if 0 345 vp = node->tn_vnode; 346 if (vp != NULL) { 347 tmpfs_free_vp(vp); 348 vrecycle(vp); 349 node->tn_vnode = NULL; 350 } 351 #endif 352 TMPFS_NODE_UNLOCK(node); 353 --node->tn_links; 354 } 355 356 /* 357 * Now get rid of all nodes. We can remove any node with a 358 * link count of 0 or any directory node with a link count of 359 * 1. The parents will not be destroyed until all their children 360 * have been destroyed. 361 * 362 * Recursion in tmpfs_free_node() can further modify the list so 363 * we cannot use a next pointer here. 364 * 365 * The root node will be destroyed by this loop (it will be last). 366 */ 367 while (!LIST_EMPTY(&tmp->tm_nodes_used)) { 368 found = 0; 369 LIST_FOREACH(node, &tmp->tm_nodes_used, tn_entries) { 370 if (node->tn_links == 0 || 371 (node->tn_links == 1 && node->tn_type == VDIR)) { 372 TMPFS_NODE_LOCK(node); 373 tmpfs_free_node(tmp, node); 374 /* eats lock */ 375 found = 1; 376 break; 377 } 378 } 379 if (found == 0) { 380 kprintf("tmpfs: Cannot free entire node tree!"); 381 break; 382 } 383 } 384 385 KKASSERT(tmp->tm_root == NULL); 386 387 objcache_destroy(tmp->tm_dirent_pool); 388 objcache_destroy(tmp->tm_node_pool); 389 390 lockuninit(&tmp->allnode_lock); 391 KKASSERT(tmp->tm_pages_used == 0); 392 KKASSERT(tmp->tm_nodes_inuse == 0); 393 394 /* Throw away the tmpfs_mount structure. */ 395 kfree(tmp, M_TMPFSMNT); 396 mp->mnt_data = NULL; 397 398 mp->mnt_flag &= ~MNT_LOCAL; 399 return 0; 400 } 401 402 /* --------------------------------------------------------------------- */ 403 404 static int 405 tmpfs_root(struct mount *mp, struct vnode **vpp) 406 { 407 struct tmpfs_mount *tmp; 408 int error; 409 410 tmp = VFS_TO_TMPFS(mp); 411 if (tmp->tm_root == NULL) { 412 kprintf("tmpfs_root: called without root node %p\n", mp); 413 print_backtrace(-1); 414 *vpp = NULL; 415 error = EINVAL; 416 } else { 417 error = tmpfs_alloc_vp(mp, tmp->tm_root, LK_EXCLUSIVE, vpp); 418 (*vpp)->v_flag |= VROOT; 419 (*vpp)->v_type = VDIR; 420 } 421 return error; 422 } 423 424 /* --------------------------------------------------------------------- */ 425 426 static int 427 tmpfs_fhtovp(struct mount *mp, struct vnode *rootvp, struct fid *fhp, struct vnode **vpp) 428 { 429 boolean_t found; 430 struct tmpfs_fid *tfhp; 431 struct tmpfs_mount *tmp; 432 struct tmpfs_node *node; 433 434 tmp = VFS_TO_TMPFS(mp); 435 436 tfhp = (struct tmpfs_fid *)fhp; 437 if (tfhp->tf_len != sizeof(struct tmpfs_fid)) 438 return EINVAL; 439 440 if (tfhp->tf_id >= tmp->tm_nodes_max) 441 return EINVAL; 442 443 found = FALSE; 444 445 TMPFS_LOCK(tmp); 446 LIST_FOREACH(node, &tmp->tm_nodes_used, tn_entries) { 447 if (node->tn_id == tfhp->tf_id && 448 node->tn_gen == tfhp->tf_gen) { 449 found = TRUE; 450 break; 451 } 452 } 453 TMPFS_UNLOCK(tmp); 454 455 if (found) 456 return (tmpfs_alloc_vp(mp, node, LK_EXCLUSIVE, vpp)); 457 458 return (EINVAL); 459 } 460 461 /* --------------------------------------------------------------------- */ 462 463 /* ARGSUSED2 */ 464 static int 465 tmpfs_statfs(struct mount *mp, struct statfs *sbp, struct ucred *cred) 466 { 467 fsfilcnt_t freenodes; 468 struct tmpfs_mount *tmp; 469 470 tmp = VFS_TO_TMPFS(mp); 471 472 sbp->f_iosize = PAGE_SIZE; 473 sbp->f_bsize = PAGE_SIZE; 474 475 sbp->f_blocks = tmp->tm_pages_max; 476 sbp->f_bavail = tmp->tm_pages_max - tmp->tm_pages_used; 477 sbp->f_bfree = sbp->f_bavail; 478 479 freenodes = tmp->tm_nodes_max - tmp->tm_nodes_inuse; 480 481 sbp->f_files = freenodes + tmp->tm_nodes_inuse; 482 sbp->f_ffree = freenodes; 483 sbp->f_owner = tmp->tm_root->tn_uid; 484 485 return 0; 486 } 487 488 /* --------------------------------------------------------------------- */ 489 490 /* 491 * tmpfs vfs operations. 492 */ 493 494 static struct vfsops tmpfs_vfsops = { 495 .vfs_mount = tmpfs_mount, 496 .vfs_unmount = tmpfs_unmount, 497 .vfs_root = tmpfs_root, 498 .vfs_statfs = tmpfs_statfs, 499 .vfs_fhtovp = tmpfs_fhtovp, 500 .vfs_sync = vfs_stdsync 501 }; 502 503 VFS_SET(tmpfs_vfsops, tmpfs, 0); 504