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