1 /* $NetBSD: tmpfs_vfsops.c,v 1.27 2007/08/03 13:00:19 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.27 2007/08/03 13:00:19 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 67 /* --------------------------------------------------------------------- */ 68 69 static int tmpfs_mount(struct mount *, const char *, void *, size_t *, 70 struct lwp *); 71 static int tmpfs_start(struct mount *, int, struct lwp *); 72 static int tmpfs_unmount(struct mount *, int, struct lwp *); 73 static int tmpfs_root(struct mount *, struct vnode **); 74 static int tmpfs_quotactl(struct mount *, int, uid_t, void *, 75 struct lwp *); 76 static int tmpfs_vget(struct mount *, ino_t, struct vnode **); 77 static int tmpfs_fhtovp(struct mount *, struct fid *, struct vnode **); 78 static int tmpfs_vptofh(struct vnode *, struct fid *, size_t *); 79 static int tmpfs_statvfs(struct mount *, struct statvfs *, struct lwp *); 80 static int tmpfs_sync(struct mount *, int, kauth_cred_t, struct lwp *); 81 static void tmpfs_init(void); 82 static void tmpfs_done(void); 83 static int tmpfs_snapshot(struct mount *, struct vnode *, 84 struct timespec *); 85 86 /* --------------------------------------------------------------------- */ 87 88 static int 89 tmpfs_mount(struct mount *mp, const char *path, void *data, size_t *data_len, 90 struct lwp *l) 91 { 92 int error; 93 ino_t nodes; 94 size_t pages; 95 struct tmpfs_mount *tmp; 96 struct tmpfs_node *root; 97 struct tmpfs_args *args = data; 98 99 if (*data_len < sizeof *args) 100 return EINVAL; 101 102 /* Handle retrieval of mount point arguments. */ 103 if (mp->mnt_flag & MNT_GETARGS) { 104 if (mp->mnt_data == NULL) 105 return EIO; 106 tmp = VFS_TO_TMPFS(mp); 107 108 args->ta_version = TMPFS_ARGS_VERSION; 109 args->ta_nodes_max = tmp->tm_nodes_max; 110 args->ta_size_max = tmp->tm_pages_max * PAGE_SIZE; 111 112 root = tmp->tm_root; 113 args->ta_root_uid = root->tn_uid; 114 args->ta_root_gid = root->tn_gid; 115 args->ta_root_mode = root->tn_mode; 116 117 *data_len = sizeof *args; 118 return 0; 119 } 120 121 if (mp->mnt_flag & MNT_UPDATE) { 122 /* XXX: There is no support yet to update file system 123 * settings. Should be added. */ 124 125 return EOPNOTSUPP; 126 } 127 128 if (args->ta_version != TMPFS_ARGS_VERSION) 129 return EINVAL; 130 131 /* Do not allow mounts if we do not have enough memory to preserve 132 * the minimum reserved pages. */ 133 if (tmpfs_mem_info(true) < TMPFS_PAGES_RESERVED) 134 return EINVAL; 135 136 /* Get the maximum number of memory pages this file system is 137 * allowed to use, based on the maximum size the user passed in 138 * the mount structure. A value of zero is treated as if the 139 * maximum available space was requested. */ 140 if (args->ta_size_max < PAGE_SIZE || args->ta_size_max >= SIZE_MAX) 141 pages = SIZE_MAX; 142 else 143 pages = args->ta_size_max / PAGE_SIZE + 144 (args->ta_size_max % PAGE_SIZE == 0 ? 0 : 1); 145 KASSERT(pages > 0); 146 147 if (args->ta_nodes_max <= 3) 148 nodes = 3 + pages * PAGE_SIZE / 1024; 149 else 150 nodes = args->ta_nodes_max; 151 KASSERT(nodes >= 3); 152 153 /* Allocate the tmpfs mount structure and fill it. */ 154 tmp = (struct tmpfs_mount *)malloc(sizeof(struct tmpfs_mount), 155 M_TMPFSMNT, M_WAITOK); 156 KASSERT(tmp != NULL); 157 158 tmp->tm_nodes_max = nodes; 159 tmp->tm_nodes_last = 2; 160 LIST_INIT(&tmp->tm_nodes_used); 161 LIST_INIT(&tmp->tm_nodes_avail); 162 163 tmp->tm_pages_max = pages; 164 tmp->tm_pages_used = 0; 165 tmpfs_pool_init(&tmp->tm_dirent_pool, sizeof(struct tmpfs_dirent), 166 "dirent", tmp); 167 tmpfs_pool_init(&tmp->tm_node_pool, sizeof(struct tmpfs_node), 168 "node", tmp); 169 tmpfs_str_pool_init(&tmp->tm_str_pool, tmp); 170 171 /* Allocate the root node. */ 172 error = tmpfs_alloc_node(tmp, VDIR, args->ta_root_uid, 173 args->ta_root_gid, args->ta_root_mode & ALLPERMS, NULL, NULL, 174 VNOVAL, l->l_proc, &root); 175 KASSERT(error == 0 && root != NULL); 176 tmp->tm_root = root; 177 178 mp->mnt_data = tmp; 179 mp->mnt_flag |= MNT_LOCAL; 180 mp->mnt_stat.f_namemax = MAXNAMLEN; 181 mp->mnt_fs_bshift = PAGE_SHIFT; 182 mp->mnt_dev_bshift = DEV_BSHIFT; 183 vfs_getnewfsid(mp); 184 185 return set_statvfs_info(path, UIO_USERSPACE, "tmpfs", UIO_SYSSPACE, 186 mp->mnt_op->vfs_name, mp, l); 187 } 188 189 /* --------------------------------------------------------------------- */ 190 191 static int 192 tmpfs_start(struct mount *mp, int flags, 193 struct lwp *l) 194 { 195 196 return 0; 197 } 198 199 /* --------------------------------------------------------------------- */ 200 201 /* ARGSUSED2 */ 202 static int 203 tmpfs_unmount(struct mount *mp, int mntflags, struct lwp *l) 204 { 205 int error; 206 int flags = 0; 207 struct tmpfs_mount *tmp; 208 struct tmpfs_node *node; 209 210 /* Handle forced unmounts. */ 211 if (mntflags & MNT_FORCE) 212 flags |= FORCECLOSE; 213 214 /* Finalize all pending I/O. */ 215 error = vflush(mp, NULL, flags); 216 if (error != 0) 217 return error; 218 219 tmp = VFS_TO_TMPFS(mp); 220 221 /* Free all associated data. The loop iterates over the linked list 222 * we have containing all used nodes. For each of them that is 223 * a directory, we free all its directory entries. Note that after 224 * freeing a node, it will automatically go to the available list, 225 * so we will later have to iterate over it to release its items. */ 226 node = LIST_FIRST(&tmp->tm_nodes_used); 227 while (node != NULL) { 228 struct tmpfs_node *next; 229 230 if (node->tn_type == VDIR) { 231 struct tmpfs_dirent *de; 232 233 de = TAILQ_FIRST(&node->tn_spec.tn_dir.tn_dir); 234 while (de != NULL) { 235 struct tmpfs_dirent *nde; 236 237 nde = TAILQ_NEXT(de, td_entries); 238 KASSERT(de->td_node->tn_vnode == NULL); 239 tmpfs_free_dirent(tmp, de, false); 240 de = nde; 241 node->tn_size -= sizeof(struct tmpfs_dirent); 242 } 243 } 244 245 next = LIST_NEXT(node, tn_entries); 246 tmpfs_free_node(tmp, node); 247 node = next; 248 } 249 node = LIST_FIRST(&tmp->tm_nodes_avail); 250 while (node != NULL) { 251 struct tmpfs_node *next; 252 253 next = LIST_NEXT(node, tn_entries); 254 LIST_REMOVE(node, tn_entries); 255 TMPFS_POOL_PUT(&tmp->tm_node_pool, node); 256 node = next; 257 } 258 259 tmpfs_pool_destroy(&tmp->tm_dirent_pool); 260 tmpfs_pool_destroy(&tmp->tm_node_pool); 261 tmpfs_str_pool_destroy(&tmp->tm_str_pool); 262 263 KASSERT(tmp->tm_pages_used == 0); 264 265 /* Throw away the tmpfs_mount structure. */ 266 free(mp->mnt_data, M_TMPFSMNT); 267 mp->mnt_data = NULL; 268 269 return 0; 270 } 271 272 /* --------------------------------------------------------------------- */ 273 274 static int 275 tmpfs_root(struct mount *mp, struct vnode **vpp) 276 { 277 278 return tmpfs_alloc_vp(mp, VFS_TO_TMPFS(mp)->tm_root, vpp); 279 } 280 281 /* --------------------------------------------------------------------- */ 282 283 static int 284 tmpfs_quotactl(struct mount *mp, int cmd, uid_t uid, 285 void *arg, struct lwp *l) 286 { 287 288 printf("tmpfs_quotactl called; need for it unknown yet\n"); 289 return EOPNOTSUPP; 290 } 291 292 /* --------------------------------------------------------------------- */ 293 294 static int 295 tmpfs_vget(struct mount *mp, ino_t ino, 296 struct vnode **vpp) 297 { 298 299 printf("tmpfs_vget called; need for it unknown yet\n"); 300 return EOPNOTSUPP; 301 } 302 303 /* --------------------------------------------------------------------- */ 304 305 static int 306 tmpfs_fhtovp(struct mount *mp, struct fid *fhp, struct vnode **vpp) 307 { 308 bool found; 309 struct tmpfs_fid tfh; 310 struct tmpfs_mount *tmp; 311 struct tmpfs_node *node; 312 313 tmp = VFS_TO_TMPFS(mp); 314 315 if (fhp->fid_len != sizeof(struct tmpfs_fid)) 316 return EINVAL; 317 318 memcpy(&tfh, fhp, sizeof(struct tmpfs_fid)); 319 320 if (tfh.tf_id >= tmp->tm_nodes_max) 321 return EINVAL; 322 323 found = false; 324 LIST_FOREACH(node, &tmp->tm_nodes_used, tn_entries) { 325 if (node->tn_id == tfh.tf_id && 326 node->tn_gen == tfh.tf_gen) { 327 found = true; 328 break; 329 } 330 } 331 332 return found ? tmpfs_alloc_vp(mp, node, vpp) : EINVAL; 333 } 334 335 /* --------------------------------------------------------------------- */ 336 337 static int 338 tmpfs_vptofh(struct vnode *vp, struct fid *fhp, size_t *fh_size) 339 { 340 struct tmpfs_fid tfh; 341 struct tmpfs_node *node; 342 343 if (*fh_size < sizeof(struct tmpfs_fid)) { 344 *fh_size = sizeof(struct tmpfs_fid); 345 return E2BIG; 346 } 347 *fh_size = sizeof(struct tmpfs_fid); 348 node = VP_TO_TMPFS_NODE(vp); 349 350 memset(&tfh, 0, sizeof(tfh)); 351 tfh.tf_len = sizeof(struct tmpfs_fid); 352 tfh.tf_gen = node->tn_gen; 353 tfh.tf_id = node->tn_id; 354 memcpy(fhp, &tfh, sizeof(tfh)); 355 356 return 0; 357 } 358 359 /* --------------------------------------------------------------------- */ 360 361 /* ARGSUSED2 */ 362 static int 363 tmpfs_statvfs(struct mount *mp, struct statvfs *sbp, struct lwp *l) 364 { 365 fsfilcnt_t freenodes, usednodes; 366 struct tmpfs_mount *tmp; 367 struct tmpfs_node *dummy; 368 369 tmp = VFS_TO_TMPFS(mp); 370 371 sbp->f_iosize = sbp->f_frsize = sbp->f_bsize = PAGE_SIZE; 372 373 sbp->f_blocks = TMPFS_PAGES_MAX(tmp); 374 sbp->f_bavail = sbp->f_bfree = TMPFS_PAGES_AVAIL(tmp); 375 sbp->f_bresvd = 0; 376 377 freenodes = MIN(tmp->tm_nodes_max - tmp->tm_nodes_last, 378 TMPFS_PAGES_AVAIL(tmp) * PAGE_SIZE / sizeof(struct tmpfs_node)); 379 LIST_FOREACH(dummy, &tmp->tm_nodes_avail, tn_entries) 380 freenodes++; 381 382 usednodes = 0; 383 LIST_FOREACH(dummy, &tmp->tm_nodes_used, tn_entries) 384 usednodes++; 385 386 sbp->f_files = freenodes + usednodes; 387 sbp->f_favail = sbp->f_ffree = freenodes; 388 sbp->f_fresvd = 0; 389 390 copy_statvfs_info(sbp, mp); 391 392 return 0; 393 } 394 395 /* --------------------------------------------------------------------- */ 396 397 /* ARGSUSED0 */ 398 static int 399 tmpfs_sync(struct mount *mp, int waitfor, 400 kauth_cred_t uc, struct lwp *l) 401 { 402 403 return 0; 404 } 405 406 /* --------------------------------------------------------------------- */ 407 408 static void 409 tmpfs_init(void) 410 { 411 412 malloc_type_attach(M_TMPFSMNT); 413 } 414 415 /* --------------------------------------------------------------------- */ 416 417 static void 418 tmpfs_done(void) 419 { 420 421 malloc_type_detach(M_TMPFSMNT); 422 } 423 424 /* --------------------------------------------------------------------- */ 425 426 static int 427 tmpfs_snapshot(struct mount *mp, struct vnode *vp, 428 struct timespec *ctime) 429 { 430 431 return EOPNOTSUPP; 432 } 433 434 /* --------------------------------------------------------------------- */ 435 436 /* 437 * tmpfs vfs operations. 438 */ 439 440 extern const struct vnodeopv_desc tmpfs_fifoop_opv_desc; 441 extern const struct vnodeopv_desc tmpfs_specop_opv_desc; 442 extern const struct vnodeopv_desc tmpfs_vnodeop_opv_desc; 443 444 const struct vnodeopv_desc * const tmpfs_vnodeopv_descs[] = { 445 &tmpfs_fifoop_opv_desc, 446 &tmpfs_specop_opv_desc, 447 &tmpfs_vnodeop_opv_desc, 448 NULL, 449 }; 450 451 struct vfsops tmpfs_vfsops = { 452 MOUNT_TMPFS, /* vfs_name */ 453 sizeof (struct tmpfs_args), 454 tmpfs_mount, /* vfs_mount */ 455 tmpfs_start, /* vfs_start */ 456 tmpfs_unmount, /* vfs_unmount */ 457 tmpfs_root, /* vfs_root */ 458 tmpfs_quotactl, /* vfs_quotactl */ 459 tmpfs_statvfs, /* vfs_statvfs */ 460 tmpfs_sync, /* vfs_sync */ 461 tmpfs_vget, /* vfs_vget */ 462 tmpfs_fhtovp, /* vfs_fhtovp */ 463 tmpfs_vptofh, /* vfs_vptofh */ 464 tmpfs_init, /* vfs_init */ 465 NULL, /* vfs_reinit */ 466 tmpfs_done, /* vfs_done */ 467 NULL, /* vfs_mountroot */ 468 tmpfs_snapshot, /* vfs_snapshot */ 469 vfs_stdextattrctl, /* vfs_extattrctl */ 470 (void *)eopnotsupp, /* vfs_suspendctl */ 471 tmpfs_vnodeopv_descs, 472 0, /* vfs_refcount */ 473 { NULL, NULL }, 474 }; 475 VFS_ATTACH(tmpfs_vfsops); 476