tmpfs_vfsops.c revision 1.30
1258945Sroberto/* $NetBSD: tmpfs_vfsops.c,v 1.30 2007/11/06 19:50:55 ad Exp $ */ 2258945Sroberto 3258945Sroberto/* 4258945Sroberto * Copyright (c) 2005, 2006, 2007 The NetBSD Foundation, Inc. 5258945Sroberto * All rights reserved. 6258945Sroberto * 7258945Sroberto * This code is derived from software contributed to The NetBSD Foundation 8258945Sroberto * by Julio M. Merino Vidal, developed as part of Google's Summer of Code 9258945Sroberto * 2005 program. 10258945Sroberto * 11258945Sroberto * Redistribution and use in source and binary forms, with or without 12258945Sroberto * modification, are permitted provided that the following conditions 13258945Sroberto * are met: 14258945Sroberto * 1. Redistributions of source code must retain the above copyright 15258945Sroberto * notice, this list of conditions and the following disclaimer. 16258945Sroberto * 2. Redistributions in binary form must reproduce the above copyright 17258945Sroberto * notice, this list of conditions and the following disclaimer in the 18258945Sroberto * documentation and/or other materials provided with the distribution. 19258945Sroberto * 3. All advertising materials mentioning features or use of this software 20258945Sroberto * must display the following acknowledgement: 21258945Sroberto * This product includes software developed by the NetBSD 22258945Sroberto * Foundation, Inc. and its contributors. 23258945Sroberto * 4. Neither the name of The NetBSD Foundation nor the names of its 24258945Sroberto * contributors may be used to endorse or promote products derived 25258945Sroberto * from this software without specific prior written permission. 26258945Sroberto * 27258945Sroberto * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 28258945Sroberto * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 29258945Sroberto * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 30258945Sroberto * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 31258945Sroberto * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 32258945Sroberto * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 33258945Sroberto * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 34258945Sroberto * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 35258945Sroberto * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 36258945Sroberto * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 37258945Sroberto * POSSIBILITY OF SUCH DAMAGE. 38258945Sroberto */ 39258945Sroberto 40258945Sroberto/* 41258945Sroberto * Efficient memory file system. 42258945Sroberto * 43258945Sroberto * tmpfs is a file system that uses NetBSD's virtual memory sub-system 44258945Sroberto * (the well-known UVM) to store file data and metadata in an efficient 45258945Sroberto * way. This means that it does not follow the structure of an on-disk 46258945Sroberto * file system because it simply does not need to. Instead, it uses 47258945Sroberto * memory-specific data structures and algorithms to automatically 48258945Sroberto * allocate and release resources. 49258945Sroberto */ 50258945Sroberto 51258945Sroberto#include <sys/cdefs.h> 52258945Sroberto__KERNEL_RCSID(0, "$NetBSD: tmpfs_vfsops.c,v 1.30 2007/11/06 19:50:55 ad Exp $"); 53258945Sroberto 54258945Sroberto#include <sys/param.h> 55258945Sroberto#include <sys/types.h> 56258945Sroberto#include <sys/kmem.h> 57258945Sroberto#include <sys/mount.h> 58258945Sroberto#include <sys/stat.h> 59258945Sroberto#include <sys/systm.h> 60258945Sroberto#include <sys/vnode.h> 61258945Sroberto#include <sys/proc.h> 62258945Sroberto 63258945Sroberto#include <fs/tmpfs/tmpfs.h> 64258945Sroberto 65258945Sroberto/* --------------------------------------------------------------------- */ 66258945Sroberto 67258945Srobertostatic int tmpfs_mount(struct mount *, const char *, void *, size_t *, 68258945Sroberto struct lwp *); 69258945Srobertostatic int tmpfs_start(struct mount *, int, struct lwp *); 70258945Srobertostatic int tmpfs_unmount(struct mount *, int, struct lwp *); 71258945Srobertostatic int tmpfs_root(struct mount *, struct vnode **); 72258945Srobertostatic int tmpfs_quotactl(struct mount *, int, uid_t, void *, 73258945Sroberto struct lwp *); 74258945Srobertostatic int tmpfs_vget(struct mount *, ino_t, struct vnode **); 75258945Srobertostatic int tmpfs_fhtovp(struct mount *, struct fid *, struct vnode **); 76258945Srobertostatic int tmpfs_vptofh(struct vnode *, struct fid *, size_t *); 77258945Srobertostatic int tmpfs_statvfs(struct mount *, struct statvfs *, struct lwp *); 78258945Srobertostatic int tmpfs_sync(struct mount *, int, kauth_cred_t, struct lwp *); 79258945Srobertostatic void tmpfs_init(void); 80258945Srobertostatic void tmpfs_done(void); 81258945Srobertostatic int tmpfs_snapshot(struct mount *, struct vnode *, 82258945Sroberto struct timespec *); 83258945Sroberto 84258945Sroberto/* --------------------------------------------------------------------- */ 85258945Sroberto 86258945Srobertostatic int 87258945Srobertotmpfs_mount(struct mount *mp, const char *path, void *data, size_t *data_len, 88258945Sroberto struct lwp *l) 89258945Sroberto{ 90258945Sroberto int error; 91258945Sroberto ino_t nodes; 92258945Sroberto size_t pages; 93258945Sroberto struct tmpfs_mount *tmp; 94258945Sroberto struct tmpfs_node *root; 95258945Sroberto struct tmpfs_args *args = data; 96258945Sroberto 97258945Sroberto if (*data_len < sizeof *args) 98258945Sroberto return EINVAL; 99258945Sroberto 100258945Sroberto /* Handle retrieval of mount point arguments. */ 101258945Sroberto if (mp->mnt_flag & MNT_GETARGS) { 102258945Sroberto if (mp->mnt_data == NULL) 103258945Sroberto return EIO; 104258945Sroberto tmp = VFS_TO_TMPFS(mp); 105258945Sroberto 106258945Sroberto args->ta_version = TMPFS_ARGS_VERSION; 107258945Sroberto mutex_enter(&tmp->tm_lock); 108258945Sroberto args->ta_nodes_max = tmp->tm_nodes_max; 109258945Sroberto args->ta_size_max = tmp->tm_pages_max * PAGE_SIZE; 110258945Sroberto root = tmp->tm_root; 111258945Sroberto mutex_exit(&tmp->tm_lock); 112258945Sroberto 113258945Sroberto args->ta_root_uid = root->tn_uid; 114258945Sroberto args->ta_root_gid = root->tn_gid; 115258945Sroberto args->ta_root_mode = root->tn_mode; 116258945Sroberto 117258945Sroberto *data_len = sizeof *args; 118258945Sroberto return 0; 119258945Sroberto } 120258945Sroberto 121258945Sroberto if (mp->mnt_flag & MNT_UPDATE) { 122258945Sroberto /* XXX: There is no support yet to update file system 123258945Sroberto * settings. Should be added. */ 124258945Sroberto 125258945Sroberto return EOPNOTSUPP; 126258945Sroberto } 127258945Sroberto 128258945Sroberto if (args->ta_version != TMPFS_ARGS_VERSION) 129258945Sroberto return EINVAL; 130258945Sroberto 131258945Sroberto /* Do not allow mounts if we do not have enough memory to preserve 132258945Sroberto * the minimum reserved pages. */ 133258945Sroberto if (tmpfs_mem_info(true) < TMPFS_PAGES_RESERVED) 134258945Sroberto return EINVAL; 135258945Sroberto 136258945Sroberto /* Get the maximum number of memory pages this file system is 137258945Sroberto * allowed to use, based on the maximum size the user passed in 138258945Sroberto * the mount structure. A value of zero is treated as if the 139258945Sroberto * maximum available space was requested. */ 140258945Sroberto if (args->ta_size_max < PAGE_SIZE || args->ta_size_max >= SIZE_MAX) 141258945Sroberto pages = SIZE_MAX; 142258945Sroberto else 143258945Sroberto pages = args->ta_size_max / PAGE_SIZE + 144258945Sroberto (args->ta_size_max % PAGE_SIZE == 0 ? 0 : 1); 145258945Sroberto KASSERT(pages > 0); 146258945Sroberto 147258945Sroberto if (args->ta_nodes_max <= 3) 148258945Sroberto nodes = 3 + pages * PAGE_SIZE / 1024; 149258945Sroberto else 150258945Sroberto nodes = args->ta_nodes_max; 151258945Sroberto KASSERT(nodes >= 3); 152258945Sroberto 153258945Sroberto /* Allocate the tmpfs mount structure and fill it. */ 154258945Sroberto tmp = kmem_alloc(sizeof(struct tmpfs_mount), KM_SLEEP); 155258945Sroberto if (tmp == NULL) 156258945Sroberto return ENOMEM; 157258945Sroberto 158258945Sroberto tmp->tm_nodes_max = nodes; 159258945Sroberto tmp->tm_nodes_cnt = 0; 160258945Sroberto LIST_INIT(&tmp->tm_nodes); 161258945Sroberto 162258945Sroberto mutex_init(&tmp->tm_lock, MUTEX_DEFAULT, IPL_NONE); 163258945Sroberto 164258945Sroberto tmp->tm_pages_max = pages; 165258945Sroberto tmp->tm_pages_used = 0; 166258945Sroberto tmpfs_pool_init(&tmp->tm_dirent_pool, sizeof(struct tmpfs_dirent), 167258945Sroberto "dirent", tmp); 168258945Sroberto tmpfs_pool_init(&tmp->tm_node_pool, sizeof(struct tmpfs_node), 169258945Sroberto "node", tmp); 170258945Sroberto tmpfs_str_pool_init(&tmp->tm_str_pool, tmp); 171258945Sroberto 172258945Sroberto /* Allocate the root node. */ 173258945Sroberto error = tmpfs_alloc_node(tmp, VDIR, args->ta_root_uid, 174258945Sroberto args->ta_root_gid, args->ta_root_mode & ALLPERMS, NULL, NULL, 175258945Sroberto VNOVAL, l->l_proc, &root); 176258945Sroberto KASSERT(error == 0 && root != NULL); 177258945Sroberto tmp->tm_root = root; 178258945Sroberto 179258945Sroberto mp->mnt_data = tmp; 180258945Sroberto mp->mnt_flag |= MNT_LOCAL; 181258945Sroberto mp->mnt_stat.f_namemax = MAXNAMLEN; 182258945Sroberto mp->mnt_fs_bshift = PAGE_SHIFT; 183258945Sroberto mp->mnt_dev_bshift = DEV_BSHIFT; 184258945Sroberto mp->mnt_iflag |= IMNT_MPSAFE; 185258945Sroberto vfs_getnewfsid(mp); 186258945Sroberto 187258945Sroberto return set_statvfs_info(path, UIO_USERSPACE, "tmpfs", UIO_SYSSPACE, 188258945Sroberto mp->mnt_op->vfs_name, mp, l); 189258945Sroberto} 190258945Sroberto 191258945Sroberto/* --------------------------------------------------------------------- */ 192258945Sroberto 193258945Srobertostatic int 194258945Srobertotmpfs_start(struct mount *mp, int flags, 195258945Sroberto struct lwp *l) 196258945Sroberto{ 197258945Sroberto 198258945Sroberto return 0; 199258945Sroberto} 200258945Sroberto 201258945Sroberto/* --------------------------------------------------------------------- */ 202258945Sroberto 203258945Sroberto/* ARGSUSED2 */ 204258945Srobertostatic int 205258945Srobertotmpfs_unmount(struct mount *mp, int mntflags, struct lwp *l) 206258945Sroberto{ 207258945Sroberto int error; 208258945Sroberto int flags = 0; 209258945Sroberto struct tmpfs_mount *tmp; 210258945Sroberto struct tmpfs_node *node; 211258945Sroberto 212258945Sroberto /* Handle forced unmounts. */ 213258945Sroberto if (mntflags & MNT_FORCE) 214258945Sroberto flags |= FORCECLOSE; 215258945Sroberto 216258945Sroberto /* Finalize all pending I/O. */ 217258945Sroberto error = vflush(mp, NULL, flags); 218258945Sroberto if (error != 0) 219258945Sroberto return error; 220258945Sroberto 221258945Sroberto tmp = VFS_TO_TMPFS(mp); 222258945Sroberto 223258945Sroberto /* Free all associated data. The loop iterates over the linked list 224258945Sroberto * we have containing all used nodes. For each of them that is 225258945Sroberto * a directory, we free all its directory entries. Note that after 226258945Sroberto * freeing a node, it will automatically go to the available list, 227258945Sroberto * so we will later have to iterate over it to release its items. */ 228258945Sroberto while ((node = LIST_FIRST(&tmp->tm_nodes)) != NULL) { 229258945Sroberto if (node->tn_type == VDIR) { 230258945Sroberto struct tmpfs_dirent *de; 231258945Sroberto 232258945Sroberto de = TAILQ_FIRST(&node->tn_spec.tn_dir.tn_dir); 233258945Sroberto while (de != NULL) { 234258945Sroberto struct tmpfs_dirent *nde; 235258945Sroberto 236258945Sroberto nde = TAILQ_NEXT(de, td_entries); 237258945Sroberto KASSERT(de->td_node->tn_vnode == NULL); 238258945Sroberto tmpfs_free_dirent(tmp, de, false); 239258945Sroberto de = nde; 240258945Sroberto node->tn_size -= sizeof(struct tmpfs_dirent); 241258945Sroberto } 242258945Sroberto } 243258945Sroberto 244258945Sroberto tmpfs_free_node(tmp, node); 245258945Sroberto } 246258945Sroberto 247258945Sroberto tmpfs_pool_destroy(&tmp->tm_dirent_pool); 248258945Sroberto tmpfs_pool_destroy(&tmp->tm_node_pool); 249258945Sroberto tmpfs_str_pool_destroy(&tmp->tm_str_pool); 250258945Sroberto 251258945Sroberto KASSERT(tmp->tm_pages_used == 0); 252258945Sroberto 253258945Sroberto /* Throw away the tmpfs_mount structure. */ 254258945Sroberto mutex_destroy(&tmp->tm_lock); 255258945Sroberto kmem_free(tmp, sizeof(*tmp)); 256258945Sroberto mp->mnt_data = NULL; 257258945Sroberto 258258945Sroberto return 0; 259258945Sroberto} 260258945Sroberto 261258945Sroberto/* --------------------------------------------------------------------- */ 262258945Sroberto 263258945Srobertostatic int 264258945Srobertotmpfs_root(struct mount *mp, struct vnode **vpp) 265258945Sroberto{ 266258945Sroberto 267258945Sroberto return tmpfs_alloc_vp(mp, VFS_TO_TMPFS(mp)->tm_root, vpp); 268258945Sroberto} 269258945Sroberto 270258945Sroberto/* --------------------------------------------------------------------- */ 271258945Sroberto 272258945Srobertostatic int 273258945Srobertotmpfs_quotactl(struct mount *mp, int cmd, uid_t uid, 274258945Sroberto void *arg, struct lwp *l) 275258945Sroberto{ 276258945Sroberto 277258945Sroberto printf("tmpfs_quotactl called; need for it unknown yet\n"); 278258945Sroberto return EOPNOTSUPP; 279258945Sroberto} 280258945Sroberto 281258945Sroberto/* --------------------------------------------------------------------- */ 282258945Sroberto 283258945Srobertostatic int 284258945Srobertotmpfs_vget(struct mount *mp, ino_t ino, 285258945Sroberto struct vnode **vpp) 286258945Sroberto{ 287258945Sroberto 288258945Sroberto printf("tmpfs_vget called; need for it unknown yet\n"); 289258945Sroberto return EOPNOTSUPP; 290258945Sroberto} 291258945Sroberto 292258945Sroberto/* --------------------------------------------------------------------- */ 293258945Sroberto 294258945Srobertostatic int 295258945Srobertotmpfs_fhtovp(struct mount *mp, struct fid *fhp, struct vnode **vpp) 296258945Sroberto{ 297258945Sroberto bool found; 298258945Sroberto struct tmpfs_fid tfh; 299258945Sroberto struct tmpfs_mount *tmp; 300258945Sroberto struct tmpfs_node *node; 301258945Sroberto 302258945Sroberto tmp = VFS_TO_TMPFS(mp); 303258945Sroberto 304258945Sroberto if (fhp->fid_len != sizeof(struct tmpfs_fid)) 305258945Sroberto return EINVAL; 306258945Sroberto 307258945Sroberto memcpy(&tfh, fhp, sizeof(struct tmpfs_fid)); 308258945Sroberto 309258945Sroberto mutex_enter(&tmp->tm_lock); 310258945Sroberto if (tfh.tf_id >= tmp->tm_nodes_max) { 311258945Sroberto mutex_exit(&tmp->tm_lock); 312258945Sroberto return EINVAL; 313258945Sroberto } 314258945Sroberto 315258945Sroberto found = false; 316258945Sroberto LIST_FOREACH(node, &tmp->tm_nodes, tn_entries) { 317258945Sroberto if (node->tn_id == tfh.tf_id && 318258945Sroberto node->tn_gen == tfh.tf_gen) { 319258945Sroberto found = true; 320258945Sroberto break; 321258945Sroberto } 322258945Sroberto } 323258945Sroberto mutex_exit(&tmp->tm_lock); 324258945Sroberto 325258945Sroberto /* XXXAD nothing to prevent 'node' from being removed. */ 326258945Sroberto return found ? tmpfs_alloc_vp(mp, node, vpp) : EINVAL; 327258945Sroberto} 328258945Sroberto 329258945Sroberto/* --------------------------------------------------------------------- */ 330258945Sroberto 331258945Srobertostatic int 332258945Srobertotmpfs_vptofh(struct vnode *vp, struct fid *fhp, size_t *fh_size) 333258945Sroberto{ 334258945Sroberto struct tmpfs_fid tfh; 335258945Sroberto struct tmpfs_node *node; 336258945Sroberto 337258945Sroberto if (*fh_size < sizeof(struct tmpfs_fid)) { 338258945Sroberto *fh_size = sizeof(struct tmpfs_fid); 339258945Sroberto return E2BIG; 340258945Sroberto } 341258945Sroberto *fh_size = sizeof(struct tmpfs_fid); 342258945Sroberto node = VP_TO_TMPFS_NODE(vp); 343258945Sroberto 344258945Sroberto memset(&tfh, 0, sizeof(tfh)); 345258945Sroberto tfh.tf_len = sizeof(struct tmpfs_fid); 346258945Sroberto tfh.tf_gen = node->tn_gen; 347258945Sroberto tfh.tf_id = node->tn_id; 348258945Sroberto memcpy(fhp, &tfh, sizeof(tfh)); 349258945Sroberto 350258945Sroberto return 0; 351258945Sroberto} 352258945Sroberto 353258945Sroberto/* --------------------------------------------------------------------- */ 354258945Sroberto 355258945Sroberto/* ARGSUSED2 */ 356258945Srobertostatic int 357258945Srobertotmpfs_statvfs(struct mount *mp, struct statvfs *sbp, struct lwp *l) 358258945Sroberto{ 359258945Sroberto fsfilcnt_t freenodes; 360258945Sroberto struct tmpfs_mount *tmp; 361258945Sroberto 362258945Sroberto tmp = VFS_TO_TMPFS(mp); 363258945Sroberto 364258945Sroberto mutex_enter(&tmp->tm_lock); 365258945Sroberto 366258945Sroberto sbp->f_iosize = sbp->f_frsize = sbp->f_bsize = PAGE_SIZE; 367258945Sroberto 368258945Sroberto sbp->f_blocks = TMPFS_PAGES_MAX(tmp); 369258945Sroberto sbp->f_bavail = sbp->f_bfree = TMPFS_PAGES_AVAIL(tmp); 370258945Sroberto sbp->f_bresvd = 0; 371258945Sroberto 372258945Sroberto freenodes = MIN(tmp->tm_nodes_max - tmp->tm_nodes_cnt, 373258945Sroberto TMPFS_PAGES_AVAIL(tmp) * PAGE_SIZE / sizeof(struct tmpfs_node)); 374258945Sroberto 375258945Sroberto sbp->f_files = tmp->tm_nodes_cnt + freenodes; 376258945Sroberto sbp->f_favail = sbp->f_ffree = freenodes; 377258945Sroberto sbp->f_fresvd = 0; 378258945Sroberto 379258945Sroberto mutex_exit(&tmp->tm_lock); 380258945Sroberto 381258945Sroberto copy_statvfs_info(sbp, mp); 382258945Sroberto 383258945Sroberto return 0; 384258945Sroberto} 385258945Sroberto 386258945Sroberto/* --------------------------------------------------------------------- */ 387258945Sroberto 388258945Sroberto/* ARGSUSED0 */ 389258945Srobertostatic int 390258945Srobertotmpfs_sync(struct mount *mp, int waitfor, 391258945Sroberto kauth_cred_t uc, struct lwp *l) 392258945Sroberto{ 393258945Sroberto 394258945Sroberto return 0; 395258945Sroberto} 396258945Sroberto 397258945Sroberto/* --------------------------------------------------------------------- */ 398258945Sroberto 399258945Srobertostatic void 400258945Srobertotmpfs_init(void) 401258945Sroberto{ 402258945Sroberto 403258945Sroberto} 404258945Sroberto 405258945Sroberto/* --------------------------------------------------------------------- */ 406258945Sroberto 407258945Srobertostatic void 408258945Srobertotmpfs_done(void) 409258945Sroberto{ 410258945Sroberto 411258945Sroberto} 412258945Sroberto 413258945Sroberto/* --------------------------------------------------------------------- */ 414258945Sroberto 415258945Srobertostatic int 416258945Srobertotmpfs_snapshot(struct mount *mp, struct vnode *vp, 417258945Sroberto struct timespec *ctime) 418258945Sroberto{ 419258945Sroberto 420258945Sroberto return EOPNOTSUPP; 421258945Sroberto} 422258945Sroberto 423258945Sroberto/* --------------------------------------------------------------------- */ 424258945Sroberto 425258945Sroberto/* 426258945Sroberto * tmpfs vfs operations. 427258945Sroberto */ 428258945Sroberto 429258945Srobertoextern const struct vnodeopv_desc tmpfs_fifoop_opv_desc; 430258945Srobertoextern const struct vnodeopv_desc tmpfs_specop_opv_desc; 431258945Srobertoextern const struct vnodeopv_desc tmpfs_vnodeop_opv_desc; 432258945Sroberto 433258945Srobertoconst struct vnodeopv_desc * const tmpfs_vnodeopv_descs[] = { 434258945Sroberto &tmpfs_fifoop_opv_desc, 435258945Sroberto &tmpfs_specop_opv_desc, 436258945Sroberto &tmpfs_vnodeop_opv_desc, 437258945Sroberto NULL, 438258945Sroberto}; 439258945Sroberto 440258945Srobertostruct vfsops tmpfs_vfsops = { 441258945Sroberto MOUNT_TMPFS, /* vfs_name */ 442258945Sroberto sizeof (struct tmpfs_args), 443258945Sroberto tmpfs_mount, /* vfs_mount */ 444258945Sroberto tmpfs_start, /* vfs_start */ 445258945Sroberto tmpfs_unmount, /* vfs_unmount */ 446258945Sroberto tmpfs_root, /* vfs_root */ 447258945Sroberto tmpfs_quotactl, /* vfs_quotactl */ 448258945Sroberto tmpfs_statvfs, /* vfs_statvfs */ 449258945Sroberto tmpfs_sync, /* vfs_sync */ 450258945Sroberto tmpfs_vget, /* vfs_vget */ 451258945Sroberto tmpfs_fhtovp, /* vfs_fhtovp */ 452258945Sroberto tmpfs_vptofh, /* vfs_vptofh */ 453258945Sroberto tmpfs_init, /* vfs_init */ 454258945Sroberto NULL, /* vfs_reinit */ 455258945Sroberto tmpfs_done, /* vfs_done */ 456258945Sroberto NULL, /* vfs_mountroot */ 457258945Sroberto tmpfs_snapshot, /* vfs_snapshot */ 458258945Sroberto vfs_stdextattrctl, /* vfs_extattrctl */ 459258945Sroberto (void *)eopnotsupp, /* vfs_suspendctl */ 460258945Sroberto tmpfs_vnodeopv_descs, 461258945Sroberto 0, /* vfs_refcount */ 462258945Sroberto { NULL, NULL }, 463258945Sroberto}; 464258945SrobertoVFS_ATTACH(tmpfs_vfsops); 465258945Sroberto