1 /* $NetBSD: tmpfs_vfsops.c,v 1.33 2007/12/08 19:29:45 pooka Exp $ */ 2 3 /* 4 * Copyright (c) 2005, 2006 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 * 3. All advertising materials mentioning features or use of this software 20 * must display the following acknowledgement: 21 * This product includes software developed by the NetBSD 22 * Foundation, Inc. and its contributors. 23 * 4. Neither the name of The NetBSD Foundation nor the names of its 24 * contributors may be used to endorse or promote products derived 25 * from this software without specific prior written permission. 26 * 27 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 28 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 29 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 30 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 31 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 32 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 33 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 34 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 35 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 36 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 37 * POSSIBILITY OF SUCH DAMAGE. 38 */ 39 40 /* 41 * Efficient memory file system. 42 * 43 * tmpfs is a file system that uses NetBSD's virtual memory sub-system 44 * (the well-known UVM) to store file data and metadata in an efficient 45 * way. This means that it does not follow the structure of an on-disk 46 * file system because it simply does not need to. Instead, it uses 47 * memory-specific data structures and algorithms to automatically 48 * allocate and release resources. 49 */ 50 51 #include <sys/cdefs.h> 52 __KERNEL_RCSID(0, "$NetBSD: tmpfs_vfsops.c,v 1.33 2007/12/08 19:29:45 pooka Exp $"); 53 54 #include <sys/param.h> 55 #include <sys/types.h> 56 #include <sys/malloc.h> 57 #include <sys/mount.h> 58 #include <sys/stat.h> 59 #include <sys/systm.h> 60 #include <sys/vnode.h> 61 #include <sys/proc.h> 62 63 #include <fs/tmpfs/tmpfs.h> 64 65 MALLOC_JUSTDEFINE(M_TMPFSMNT, "tmpfs mount", "tmpfs mount structures"); 66 MALLOC_JUSTDEFINE(M_TMPFSTMP, "tmpfs temp", "tmpfs temporary structures"); 67 68 /* --------------------------------------------------------------------- */ 69 70 static int tmpfs_mount(struct mount *, const char *, void *, size_t *); 71 static int tmpfs_start(struct mount *, int); 72 static int tmpfs_unmount(struct mount *, int); 73 static int tmpfs_root(struct mount *, struct vnode **); 74 static int tmpfs_vget(struct mount *, ino_t, struct vnode **); 75 static int tmpfs_fhtovp(struct mount *, struct fid *, struct vnode **); 76 static int tmpfs_vptofh(struct vnode *, struct fid *, size_t *); 77 static int tmpfs_statvfs(struct mount *, struct statvfs *); 78 static int tmpfs_sync(struct mount *, int, kauth_cred_t); 79 static void tmpfs_init(void); 80 static void tmpfs_done(void); 81 static int tmpfs_snapshot(struct mount *, struct vnode *, 82 struct timespec *); 83 84 /* --------------------------------------------------------------------- */ 85 86 static int 87 tmpfs_mount(struct mount *mp, const char *path, void *data, size_t *data_len) 88 { 89 struct lwp *l = curlwp; 90 int error; 91 ino_t nodes; 92 size_t pages; 93 struct tmpfs_mount *tmp; 94 struct tmpfs_node *root; 95 struct tmpfs_args *args = data; 96 97 if (*data_len < sizeof *args) 98 return EINVAL; 99 100 /* Handle retrieval of mount point arguments. */ 101 if (mp->mnt_flag & MNT_GETARGS) { 102 if (mp->mnt_data == NULL) 103 return EIO; 104 tmp = VFS_TO_TMPFS(mp); 105 106 args->ta_version = TMPFS_ARGS_VERSION; 107 args->ta_nodes_max = tmp->tm_nodes_max; 108 args->ta_size_max = tmp->tm_pages_max * PAGE_SIZE; 109 110 root = tmp->tm_root; 111 args->ta_root_uid = root->tn_uid; 112 args->ta_root_gid = root->tn_gid; 113 args->ta_root_mode = root->tn_mode; 114 115 *data_len = sizeof *args; 116 return 0; 117 } 118 119 if (mp->mnt_flag & MNT_UPDATE) { 120 /* XXX: There is no support yet to update file system 121 * settings. Should be added. */ 122 123 return EOPNOTSUPP; 124 } 125 126 if (args->ta_version != TMPFS_ARGS_VERSION) 127 return EINVAL; 128 129 /* Do not allow mounts if we do not have enough memory to preserve 130 * the minimum reserved pages. */ 131 if (tmpfs_mem_info(true) < TMPFS_PAGES_RESERVED) 132 return EINVAL; 133 134 /* Get the maximum number of memory pages this file system is 135 * allowed to use, based on the maximum size the user passed in 136 * the mount structure. A value of zero is treated as if the 137 * maximum available space was requested. */ 138 if (args->ta_size_max < PAGE_SIZE || args->ta_size_max >= SIZE_MAX) 139 pages = SIZE_MAX; 140 else 141 pages = args->ta_size_max / PAGE_SIZE + 142 (args->ta_size_max % PAGE_SIZE == 0 ? 0 : 1); 143 KASSERT(pages > 0); 144 145 if (args->ta_nodes_max <= 3) 146 nodes = 3 + pages * PAGE_SIZE / 1024; 147 else 148 nodes = args->ta_nodes_max; 149 KASSERT(nodes >= 3); 150 151 /* Allocate the tmpfs mount structure and fill it. */ 152 tmp = (struct tmpfs_mount *)malloc(sizeof(struct tmpfs_mount), 153 M_TMPFSMNT, M_WAITOK); 154 KASSERT(tmp != NULL); 155 156 tmp->tm_nodes_max = nodes; 157 tmp->tm_nodes_last = 2; 158 LIST_INIT(&tmp->tm_nodes_used); 159 LIST_INIT(&tmp->tm_nodes_avail); 160 161 tmp->tm_pages_max = pages; 162 tmp->tm_pages_used = 0; 163 tmpfs_pool_init(&tmp->tm_dirent_pool, sizeof(struct tmpfs_dirent), 164 "dirent", tmp); 165 tmpfs_pool_init(&tmp->tm_node_pool, sizeof(struct tmpfs_node), 166 "node", tmp); 167 tmpfs_str_pool_init(&tmp->tm_str_pool, tmp); 168 169 /* Allocate the root node. */ 170 error = tmpfs_alloc_node(tmp, VDIR, args->ta_root_uid, 171 args->ta_root_gid, args->ta_root_mode & ALLPERMS, NULL, NULL, 172 VNOVAL, &root); 173 KASSERT(error == 0 && root != NULL); 174 tmp->tm_root = root; 175 176 mp->mnt_data = tmp; 177 mp->mnt_flag |= MNT_LOCAL; 178 mp->mnt_stat.f_namemax = MAXNAMLEN; 179 mp->mnt_fs_bshift = PAGE_SHIFT; 180 mp->mnt_dev_bshift = DEV_BSHIFT; 181 vfs_getnewfsid(mp); 182 183 return set_statvfs_info(path, UIO_USERSPACE, "tmpfs", UIO_SYSSPACE, 184 mp->mnt_op->vfs_name, mp, l); 185 } 186 187 /* --------------------------------------------------------------------- */ 188 189 static int 190 tmpfs_start(struct mount *mp, int flags) 191 { 192 193 return 0; 194 } 195 196 /* --------------------------------------------------------------------- */ 197 198 /* ARGSUSED2 */ 199 static int 200 tmpfs_unmount(struct mount *mp, int mntflags) 201 { 202 int error; 203 int flags = 0; 204 struct tmpfs_mount *tmp; 205 struct tmpfs_node *node; 206 207 /* Handle forced unmounts. */ 208 if (mntflags & MNT_FORCE) 209 flags |= FORCECLOSE; 210 211 /* Finalize all pending I/O. */ 212 error = vflush(mp, NULL, flags); 213 if (error != 0) 214 return error; 215 216 tmp = VFS_TO_TMPFS(mp); 217 218 /* Free all associated data. The loop iterates over the linked list 219 * we have containing all used nodes. For each of them that is 220 * a directory, we free all its directory entries. Note that after 221 * freeing a node, it will automatically go to the available list, 222 * so we will later have to iterate over it to release its items. */ 223 node = LIST_FIRST(&tmp->tm_nodes_used); 224 while (node != NULL) { 225 struct tmpfs_node *next; 226 227 if (node->tn_type == VDIR) { 228 struct tmpfs_dirent *de; 229 230 de = TAILQ_FIRST(&node->tn_spec.tn_dir.tn_dir); 231 while (de != NULL) { 232 struct tmpfs_dirent *nde; 233 234 nde = TAILQ_NEXT(de, td_entries); 235 KASSERT(de->td_node->tn_vnode == NULL); 236 tmpfs_free_dirent(tmp, de, false); 237 de = nde; 238 node->tn_size -= sizeof(struct tmpfs_dirent); 239 } 240 } 241 242 next = LIST_NEXT(node, tn_entries); 243 tmpfs_free_node(tmp, node); 244 node = next; 245 } 246 node = LIST_FIRST(&tmp->tm_nodes_avail); 247 while (node != NULL) { 248 struct tmpfs_node *next; 249 250 next = LIST_NEXT(node, tn_entries); 251 LIST_REMOVE(node, tn_entries); 252 TMPFS_POOL_PUT(&tmp->tm_node_pool, node); 253 node = next; 254 } 255 256 tmpfs_pool_destroy(&tmp->tm_dirent_pool); 257 tmpfs_pool_destroy(&tmp->tm_node_pool); 258 tmpfs_str_pool_destroy(&tmp->tm_str_pool); 259 260 KASSERT(tmp->tm_pages_used == 0); 261 262 /* Throw away the tmpfs_mount structure. */ 263 free(mp->mnt_data, M_TMPFSMNT); 264 mp->mnt_data = NULL; 265 266 return 0; 267 } 268 269 /* --------------------------------------------------------------------- */ 270 271 static int 272 tmpfs_root(struct mount *mp, struct vnode **vpp) 273 { 274 275 return tmpfs_alloc_vp(mp, VFS_TO_TMPFS(mp)->tm_root, vpp); 276 } 277 278 /* --------------------------------------------------------------------- */ 279 280 static int 281 tmpfs_vget(struct mount *mp, ino_t ino, 282 struct vnode **vpp) 283 { 284 285 printf("tmpfs_vget called; need for it unknown yet\n"); 286 return EOPNOTSUPP; 287 } 288 289 /* --------------------------------------------------------------------- */ 290 291 static int 292 tmpfs_fhtovp(struct mount *mp, struct fid *fhp, struct vnode **vpp) 293 { 294 bool found; 295 struct tmpfs_fid tfh; 296 struct tmpfs_mount *tmp; 297 struct tmpfs_node *node; 298 299 tmp = VFS_TO_TMPFS(mp); 300 301 if (fhp->fid_len != sizeof(struct tmpfs_fid)) 302 return EINVAL; 303 304 memcpy(&tfh, fhp, sizeof(struct tmpfs_fid)); 305 306 if (tfh.tf_id >= tmp->tm_nodes_max) 307 return EINVAL; 308 309 found = false; 310 LIST_FOREACH(node, &tmp->tm_nodes_used, tn_entries) { 311 if (node->tn_id == tfh.tf_id && 312 node->tn_gen == tfh.tf_gen) { 313 found = true; 314 break; 315 } 316 } 317 318 return found ? tmpfs_alloc_vp(mp, node, vpp) : EINVAL; 319 } 320 321 /* --------------------------------------------------------------------- */ 322 323 static int 324 tmpfs_vptofh(struct vnode *vp, struct fid *fhp, size_t *fh_size) 325 { 326 struct tmpfs_fid tfh; 327 struct tmpfs_node *node; 328 329 if (*fh_size < sizeof(struct tmpfs_fid)) { 330 *fh_size = sizeof(struct tmpfs_fid); 331 return E2BIG; 332 } 333 *fh_size = sizeof(struct tmpfs_fid); 334 node = VP_TO_TMPFS_NODE(vp); 335 336 memset(&tfh, 0, sizeof(tfh)); 337 tfh.tf_len = sizeof(struct tmpfs_fid); 338 tfh.tf_gen = node->tn_gen; 339 tfh.tf_id = node->tn_id; 340 memcpy(fhp, &tfh, sizeof(tfh)); 341 342 return 0; 343 } 344 345 /* --------------------------------------------------------------------- */ 346 347 /* ARGSUSED2 */ 348 static int 349 tmpfs_statvfs(struct mount *mp, struct statvfs *sbp) 350 { 351 fsfilcnt_t freenodes, usednodes; 352 struct tmpfs_mount *tmp; 353 struct tmpfs_node *dummy; 354 355 tmp = VFS_TO_TMPFS(mp); 356 357 sbp->f_iosize = sbp->f_frsize = sbp->f_bsize = PAGE_SIZE; 358 359 sbp->f_blocks = TMPFS_PAGES_MAX(tmp); 360 sbp->f_bavail = sbp->f_bfree = TMPFS_PAGES_AVAIL(tmp); 361 sbp->f_bresvd = 0; 362 363 freenodes = MIN(tmp->tm_nodes_max - tmp->tm_nodes_last, 364 TMPFS_PAGES_AVAIL(tmp) * PAGE_SIZE / sizeof(struct tmpfs_node)); 365 LIST_FOREACH(dummy, &tmp->tm_nodes_avail, tn_entries) 366 freenodes++; 367 368 usednodes = 0; 369 LIST_FOREACH(dummy, &tmp->tm_nodes_used, tn_entries) 370 usednodes++; 371 372 sbp->f_files = freenodes + usednodes; 373 sbp->f_favail = sbp->f_ffree = freenodes; 374 sbp->f_fresvd = 0; 375 376 copy_statvfs_info(sbp, mp); 377 378 return 0; 379 } 380 381 /* --------------------------------------------------------------------- */ 382 383 /* ARGSUSED0 */ 384 static int 385 tmpfs_sync(struct mount *mp, int waitfor, 386 kauth_cred_t uc) 387 { 388 389 return 0; 390 } 391 392 /* --------------------------------------------------------------------- */ 393 394 static void 395 tmpfs_init(void) 396 { 397 398 malloc_type_attach(M_TMPFSMNT); 399 malloc_type_attach(M_TMPFSTMP); 400 } 401 402 /* --------------------------------------------------------------------- */ 403 404 static void 405 tmpfs_done(void) 406 { 407 408 malloc_type_detach(M_TMPFSTMP); 409 malloc_type_detach(M_TMPFSMNT); 410 } 411 412 /* --------------------------------------------------------------------- */ 413 414 static int 415 tmpfs_snapshot(struct mount *mp, struct vnode *vp, 416 struct timespec *ctime) 417 { 418 419 return EOPNOTSUPP; 420 } 421 422 /* --------------------------------------------------------------------- */ 423 424 /* 425 * tmpfs vfs operations. 426 */ 427 428 extern const struct vnodeopv_desc tmpfs_fifoop_opv_desc; 429 extern const struct vnodeopv_desc tmpfs_specop_opv_desc; 430 extern const struct vnodeopv_desc tmpfs_vnodeop_opv_desc; 431 432 const struct vnodeopv_desc * const tmpfs_vnodeopv_descs[] = { 433 &tmpfs_fifoop_opv_desc, 434 &tmpfs_specop_opv_desc, 435 &tmpfs_vnodeop_opv_desc, 436 NULL, 437 }; 438 439 struct vfsops tmpfs_vfsops = { 440 MOUNT_TMPFS, /* vfs_name */ 441 sizeof (struct tmpfs_args), 442 tmpfs_mount, /* vfs_mount */ 443 tmpfs_start, /* vfs_start */ 444 tmpfs_unmount, /* vfs_unmount */ 445 tmpfs_root, /* vfs_root */ 446 (void *)eopnotsupp, /* vfs_quotactl */ 447 tmpfs_statvfs, /* vfs_statvfs */ 448 tmpfs_sync, /* vfs_sync */ 449 tmpfs_vget, /* vfs_vget */ 450 tmpfs_fhtovp, /* vfs_fhtovp */ 451 tmpfs_vptofh, /* vfs_vptofh */ 452 tmpfs_init, /* vfs_init */ 453 NULL, /* vfs_reinit */ 454 tmpfs_done, /* vfs_done */ 455 NULL, /* vfs_mountroot */ 456 tmpfs_snapshot, /* vfs_snapshot */ 457 vfs_stdextattrctl, /* vfs_extattrctl */ 458 (void *)eopnotsupp, /* vfs_suspendctl */ 459 tmpfs_vnodeopv_descs, 460 0, /* vfs_refcount */ 461 { NULL, NULL }, 462 }; 463 VFS_ATTACH(tmpfs_vfsops); 464