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