Home | History | Annotate | Line # | Download | only in tmpfs
tmpfs_subr.c revision 1.18.4.2
      1 /*	$NetBSD: tmpfs_subr.c,v 1.18.4.2 2006/03/10 14:23:39 elad 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 supporting functions.
     42  */
     43 
     44 #include <sys/cdefs.h>
     45 __KERNEL_RCSID(0, "$NetBSD: tmpfs_subr.c,v 1.18.4.2 2006/03/10 14:23:39 elad Exp $");
     46 
     47 #include <sys/param.h>
     48 #include <sys/dirent.h>
     49 #include <sys/event.h>
     50 #include <sys/malloc.h>
     51 #include <sys/mount.h>
     52 #include <sys/namei.h>
     53 #include <sys/time.h>
     54 #include <sys/stat.h>
     55 #include <sys/systm.h>
     56 #include <sys/swap.h>
     57 #include <sys/vnode.h>
     58 
     59 #include <uvm/uvm.h>
     60 
     61 #include <miscfs/specfs/specdev.h>
     62 #include <fs/tmpfs/tmpfs.h>
     63 #include <fs/tmpfs/tmpfs_fifoops.h>
     64 #include <fs/tmpfs/tmpfs_specops.h>
     65 #include <fs/tmpfs/tmpfs_vnops.h>
     66 
     67 /* --------------------------------------------------------------------- */
     68 
     69 /*
     70  * Allocates a new node of type 'type' inside the 'tmp' mount point, with
     71  * its owner set to 'uid', its group to 'gid' and its mode set to 'mode',
     72  * using the credentials of the process 'p'.
     73  *
     74  * If the node type is set to 'VDIR', then the parent parameter must point
     75  * to the parent directory of the node being created.  It may only be NULL
     76  * while allocating the root node.
     77  *
     78  * If the node type is set to 'VBLK' or 'VCHR', then the rdev parameter
     79  * specifies the device the node represents.
     80  *
     81  * If the node type is set to 'VLNK', then the parameter target specifies
     82  * the file name of the target file for the symbolic link that is being
     83  * created.
     84  *
     85  * Note that new nodes are retrieved from the available list if it has
     86  * items or, if it is empty, from the node pool as long as there is enough
     87  * space to create them.
     88  *
     89  * Returns zero on success or an appropriate error code on failure.
     90  */
     91 int
     92 tmpfs_alloc_node(struct tmpfs_mount *tmp, enum vtype type,
     93     uid_t uid, gid_t gid, mode_t mode, struct tmpfs_node *parent,
     94     char *target, dev_t rdev, struct proc *p, struct tmpfs_node **node)
     95 {
     96 	struct tmpfs_node *nnode;
     97 
     98 	/* If the root directory of the 'tmp' file system is not yet
     99 	 * allocated, this must be the request to do it. */
    100 	KASSERT(IMPLIES(tmp->tm_root == NULL, parent == NULL && type == VDIR));
    101 
    102 	KASSERT(IFF(type == VLNK, target != NULL));
    103 	KASSERT(IFF(type == VBLK || type == VCHR, rdev != VNOVAL));
    104 
    105 	KASSERT(uid != VNOVAL && gid != VNOVAL && mode != VNOVAL);
    106 
    107 	nnode = NULL;
    108 	if (LIST_EMPTY(&tmp->tm_nodes_avail)) {
    109 		KASSERT(tmp->tm_nodes_last <= tmp->tm_nodes_max);
    110 		if (tmp->tm_nodes_last == tmp->tm_nodes_max)
    111 			return ENOSPC;
    112 
    113 		nnode =
    114 		    (struct tmpfs_node *)TMPFS_POOL_GET(&tmp->tm_node_pool, 0);
    115 		if (nnode == NULL)
    116 			return ENOSPC;
    117 		nnode->tn_id = tmp->tm_nodes_last++;
    118 		nnode->tn_gen = 0;
    119 	} else {
    120 		nnode = LIST_FIRST(&tmp->tm_nodes_avail);
    121 		LIST_REMOVE(nnode, tn_entries);
    122 		nnode->tn_gen++;
    123 	}
    124 	KASSERT(nnode != NULL);
    125 	LIST_INSERT_HEAD(&tmp->tm_nodes_used, nnode, tn_entries);
    126 
    127 	/* Generic initialization. */
    128 	nnode->tn_type = type;
    129 	nnode->tn_size = 0;
    130 	nnode->tn_status = 0;
    131 	nnode->tn_flags = 0;
    132 	nnode->tn_links = 0;
    133 	(void)nanotime(&nnode->tn_atime);
    134 	nnode->tn_birthtime = nnode->tn_ctime = nnode->tn_mtime =
    135 	    nnode->tn_atime;
    136 	nnode->tn_uid = uid;
    137 	nnode->tn_gid = gid;
    138 	nnode->tn_mode = mode;
    139 	nnode->tn_lockf = NULL;
    140 	nnode->tn_vnode = NULL;
    141 
    142 	/* Type-specific initialization. */
    143 	switch (nnode->tn_type) {
    144 	case VBLK:
    145 	case VCHR:
    146 		nnode->tn_spec.tn_dev.tn_rdev = rdev;
    147 		break;
    148 
    149 	case VDIR:
    150 		TAILQ_INIT(&nnode->tn_spec.tn_dir.tn_dir);
    151 		nnode->tn_spec.tn_dir.tn_parent =
    152 		    (parent == NULL) ? nnode : parent;
    153 		nnode->tn_spec.tn_dir.tn_readdir_lastn = 0;
    154 		nnode->tn_spec.tn_dir.tn_readdir_lastp = NULL;
    155 		nnode->tn_links++;
    156 		nnode->tn_spec.tn_dir.tn_parent->tn_links++;
    157 		break;
    158 
    159 	case VFIFO:
    160 		/* FALLTHROUGH */
    161 	case VSOCK:
    162 		break;
    163 
    164 	case VLNK:
    165 		KASSERT(strlen(target) < MAXPATHLEN);
    166 		nnode->tn_size = strlen(target);
    167 		nnode->tn_spec.tn_lnk.tn_link =
    168 		    tmpfs_str_pool_get(&tmp->tm_str_pool, nnode->tn_size, 0);
    169 		if (nnode->tn_spec.tn_lnk.tn_link == NULL) {
    170 			nnode->tn_type = VNON;
    171 			tmpfs_free_node(tmp, nnode);
    172 			return ENOSPC;
    173 		}
    174 		memcpy(nnode->tn_spec.tn_lnk.tn_link, target, nnode->tn_size);
    175 		break;
    176 
    177 	case VREG:
    178 		nnode->tn_spec.tn_reg.tn_aobj =
    179 		    uao_create(INT32_MAX - PAGE_SIZE, 0);
    180 		nnode->tn_spec.tn_reg.tn_aobj_pages = 0;
    181 		break;
    182 
    183 	default:
    184 		KASSERT(0);
    185 	}
    186 
    187 	*node = nnode;
    188 	return 0;
    189 }
    190 
    191 /* --------------------------------------------------------------------- */
    192 
    193 /*
    194  * Destroys the node pointed to by node from the file system 'tmp'.
    195  * If the node does not belong to the given mount point, the results are
    196  * unpredicted.
    197  *
    198  * If the node references a directory; no entries are allowed because
    199  * their removal could need a recursive algorithm, something forbidden in
    200  * kernel space.  Furthermore, there is not need to provide such
    201  * functionality (recursive removal) because the only primitives offered
    202  * to the user are the removal of empty directories and the deletion of
    203  * individual files.
    204  *
    205  * Note that nodes are not really deleted; in fact, when a node has been
    206  * allocated, it cannot be deleted during the whole life of the file
    207  * system.  Instead, they are moved to the available list and remain there
    208  * until reused.
    209  */
    210 void
    211 tmpfs_free_node(struct tmpfs_mount *tmp, struct tmpfs_node *node)
    212 {
    213 	ino_t id;
    214 	unsigned long gen;
    215 	size_t pages;
    216 
    217 	switch (node->tn_type) {
    218 	case VNON:
    219 		/* Do not do anything.  VNON is provided to let the
    220 		 * allocation routine clean itself easily by avoiding
    221 		 * duplicating code in it. */
    222 		/* FALLTHROUGH */
    223 	case VBLK:
    224 		/* FALLTHROUGH */
    225 	case VCHR:
    226 		/* FALLTHROUGH */
    227 	case VDIR:
    228 		/* FALLTHROUGH */
    229 	case VFIFO:
    230 		/* FALLTHROUGH */
    231 	case VSOCK:
    232 		pages = 0;
    233 		break;
    234 
    235 	case VLNK:
    236 		tmpfs_str_pool_put(&tmp->tm_str_pool,
    237 		    node->tn_spec.tn_lnk.tn_link, node->tn_size);
    238 		pages = 0;
    239 		break;
    240 
    241 	case VREG:
    242 		if (node->tn_spec.tn_reg.tn_aobj != NULL)
    243 			uao_detach(node->tn_spec.tn_reg.tn_aobj);
    244 		pages = node->tn_spec.tn_reg.tn_aobj_pages;
    245 		break;
    246 
    247 	default:
    248 		KASSERT(0);
    249 		pages = 0; /* Shut up gcc when !DIAGNOSTIC. */
    250 		break;
    251 	}
    252 
    253 	tmp->tm_pages_used -= pages;
    254 
    255 	LIST_REMOVE(node, tn_entries);
    256 	id = node->tn_id;
    257 	gen = node->tn_gen;
    258 	memset(node, 0, sizeof(struct tmpfs_node));
    259 	node->tn_id = id;
    260 	node->tn_type = VNON;
    261 	node->tn_gen = gen;
    262 	LIST_INSERT_HEAD(&tmp->tm_nodes_avail, node, tn_entries);
    263 }
    264 
    265 /* --------------------------------------------------------------------- */
    266 
    267 /*
    268  * Allocates a new directory entry for the node node with a name of name.
    269  * The new directory entry is returned in *de.
    270  *
    271  * The link count of node is increased by one to reflect the new object
    272  * referencing it.
    273  *
    274  * Returns zero on success or an appropriate error code on failure.
    275  */
    276 int
    277 tmpfs_alloc_dirent(struct tmpfs_mount *tmp, struct tmpfs_node *node,
    278     const char *name, uint16_t len, struct tmpfs_dirent **de)
    279 {
    280 	struct tmpfs_dirent *nde;
    281 
    282 	nde = (struct tmpfs_dirent *)TMPFS_POOL_GET(&tmp->tm_dirent_pool, 0);
    283 	if (nde == NULL)
    284 		return ENOSPC;
    285 
    286 	nde->td_name = tmpfs_str_pool_get(&tmp->tm_str_pool, len, 0);
    287 	if (nde->td_name == NULL) {
    288 		TMPFS_POOL_PUT(&tmp->tm_dirent_pool, nde);
    289 		return ENOSPC;
    290 	}
    291 	nde->td_namelen = len;
    292 	memcpy(nde->td_name, name, len);
    293 	nde->td_node = node;
    294 
    295 	node->tn_links++;
    296 	*de = nde;
    297 
    298 	return 0;
    299 }
    300 
    301 /* --------------------------------------------------------------------- */
    302 
    303 /*
    304  * Frees a directory entry.  It is the caller's responsibility to destroy
    305  * the node referenced by it if needed.
    306  *
    307  * The link count of node is decreased by one to reflect the removal of an
    308  * object that referenced it.  This only happens if 'node_exists' is true;
    309  * otherwise the function will not access the node referred to by the
    310  * directory entry, as it may already have been released from the outside.
    311  */
    312 void
    313 tmpfs_free_dirent(struct tmpfs_mount *tmp, struct tmpfs_dirent *de,
    314     boolean_t node_exists)
    315 {
    316 	if (node_exists) {
    317 		struct tmpfs_node *node;
    318 
    319 		node = de->td_node;
    320 
    321 		KASSERT(node->tn_links > 0);
    322 		node->tn_links--;
    323 	}
    324 
    325 	tmpfs_str_pool_put(&tmp->tm_str_pool, de->td_name, de->td_namelen);
    326 	TMPFS_POOL_PUT(&tmp->tm_dirent_pool, de);
    327 }
    328 
    329 /* --------------------------------------------------------------------- */
    330 
    331 /*
    332  * Allocates a new vnode for the node node or returns a new reference to
    333  * an existing one if the node had already a vnode referencing it.  The
    334  * resulting locked vnode is returned in *vpp.
    335  *
    336  * Returns zero on success or an appropriate error code on failure.
    337  */
    338 int
    339 tmpfs_alloc_vp(struct mount *mp, struct tmpfs_node *node, struct vnode **vpp)
    340 {
    341 	int error;
    342 	struct vnode *nvp;
    343 	struct vnode *vp;
    344 
    345 	vp = NULL;
    346 
    347 	if (node->tn_vnode != NULL) {
    348 		vp = node->tn_vnode;
    349 		vget(vp, LK_EXCLUSIVE | LK_RETRY);
    350 		error = 0;
    351 		goto out;
    352 	}
    353 
    354 	/* Get a new vnode and associate it with our node. */
    355 	error = getnewvnode(VT_TMPFS, mp, tmpfs_vnodeop_p, &vp);
    356 	if (error != 0)
    357 		goto out;
    358 	KASSERT(vp != NULL);
    359 
    360 	error = vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
    361 	if (error != 0) {
    362 		vp->v_data = NULL;
    363 		ungetnewvnode(vp);
    364 		vp = NULL;
    365 		goto out;
    366 	}
    367 
    368 	vp->v_data = node;
    369 	vp->v_type = node->tn_type;
    370 
    371 	/* Type-specific initialization. */
    372 	switch (node->tn_type) {
    373 	case VBLK:
    374 		/* FALLTHROUGH */
    375 	case VCHR:
    376 		vp->v_op = tmpfs_specop_p;
    377 		nvp = checkalias(vp, node->tn_spec.tn_dev.tn_rdev, mp);
    378 		if (nvp != NULL) {
    379 			/* Discard unneeded vnode, but save its inode. */
    380 			nvp->v_data = vp->v_data;
    381 			vp->v_data = NULL;
    382 
    383 			/* XXX spec_vnodeops has no locking, so we have to
    384 			 * do it explicitly. */
    385 			VOP_UNLOCK(vp, 0);
    386 			vp->v_op = spec_vnodeop_p;
    387 			vp->v_flag &= ~VLOCKSWORK;
    388 			vrele(vp);
    389 			vgone(vp);
    390 
    391 			/* Reinitialize aliased node. */
    392 			vp = nvp;
    393 			error = vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
    394 			if (error != 0) {
    395 				vp->v_data = NULL;
    396 				vp = NULL;
    397 				goto out;
    398 			}
    399 		}
    400 		break;
    401 
    402 	case VDIR:
    403 		vp->v_flag = node->tn_spec.tn_dir.tn_parent == node ? VROOT : 0;
    404 		break;
    405 
    406 	case VFIFO:
    407 		vp->v_op = tmpfs_fifoop_p;
    408 		break;
    409 
    410 	case VLNK:
    411 		/* FALLTHROUGH */
    412 	case VREG:
    413 		/* FALLTHROUGH */
    414 	case VSOCK:
    415 		break;
    416 
    417 	default:
    418 		KASSERT(0);
    419 	}
    420 
    421 	uvm_vnp_setsize(vp, node->tn_size);
    422 
    423 	error = 0;
    424 
    425 out:
    426 	*vpp = node->tn_vnode = vp;
    427 
    428 	KASSERT(IFF(error == 0, *vpp != NULL && VOP_ISLOCKED(*vpp)));
    429 	KASSERT(*vpp == node->tn_vnode);
    430 
    431 	return error;
    432 }
    433 
    434 /* --------------------------------------------------------------------- */
    435 
    436 /*
    437  * Destroys the association between the vnode vp and the node it
    438  * references.
    439  */
    440 void
    441 tmpfs_free_vp(struct vnode *vp)
    442 {
    443 	struct tmpfs_node *node;
    444 
    445 	node = VP_TO_TMPFS_NODE(vp);
    446 
    447 	node->tn_vnode = NULL;
    448 	vp->v_data = NULL;
    449 }
    450 
    451 /* --------------------------------------------------------------------- */
    452 
    453 /*
    454  * Allocates a new file of type 'type' and adds it to the parent directory
    455  * 'dvp'; this addition is done using the component name given in 'cnp'.
    456  * The ownership of the new file is automatically assigned based on the
    457  * credentials of the caller (through 'cnp'), the group is set based on
    458  * the parent directory and the mode is determined from the 'vap' argument.
    459  * If successful, *vpp holds a vnode to the newly created file and zero
    460  * is returned.  Otherwise *vpp is NULL and the function returns an
    461  * appropriate error code.
    462  */
    463 int
    464 tmpfs_alloc_file(struct vnode *dvp, struct vnode **vpp, struct vattr *vap,
    465     struct componentname *cnp, char *target)
    466 {
    467 	int error;
    468 	struct tmpfs_dirent *de;
    469 	struct tmpfs_mount *tmp;
    470 	struct tmpfs_node *dnode;
    471 	struct tmpfs_node *node;
    472 	struct tmpfs_node *parent;
    473 
    474 	KASSERT(VOP_ISLOCKED(dvp));
    475 	KASSERT(cnp->cn_flags & HASBUF);
    476 
    477 	tmp = VFS_TO_TMPFS(dvp->v_mount);
    478 	dnode = VP_TO_TMPFS_DIR(dvp);
    479 	*vpp = NULL;
    480 
    481 	/* If the entry we are creating is a directory, we cannot overflow
    482 	 * the number of links of its parent, because it will get a new
    483 	 * link. */
    484 	if (vap->va_type == VDIR) {
    485 		/* Ensure that we do not overflow the maximum number of links
    486 		 * imposed by the system. */
    487 		KASSERT(dnode->tn_links <= LINK_MAX);
    488 		if (dnode->tn_links == LINK_MAX) {
    489 			error = EMLINK;
    490 			goto out;
    491 		}
    492 
    493 		parent = dnode;
    494 	} else
    495 		parent = NULL;
    496 
    497 	/* Allocate a node that represents the new file. */
    498 	error = tmpfs_alloc_node(tmp, vap->va_type, cnp->cn_cred->cr_uid,
    499 	    dnode->tn_gid, vap->va_mode, parent, target, vap->va_rdev,
    500 	    cnp->cn_lwp->l_proc, &node);
    501 	if (error != 0)
    502 		goto out;
    503 
    504 	/* Allocate a directory entry that points to the new file. */
    505 	error = tmpfs_alloc_dirent(tmp, node, cnp->cn_nameptr, cnp->cn_namelen,
    506 	    &de);
    507 	if (error != 0) {
    508 		tmpfs_free_node(tmp, node);
    509 		goto out;
    510 	}
    511 
    512 	/* Allocate a vnode for the new file. */
    513 	error = tmpfs_alloc_vp(dvp->v_mount, node, vpp);
    514 	if (error != 0) {
    515 		tmpfs_free_dirent(tmp, de, TRUE);
    516 		tmpfs_free_node(tmp, node);
    517 		goto out;
    518 	}
    519 
    520 	/* Now that all required items are allocated, we can proceed to
    521 	 * insert the new node into the directory, an operation that
    522 	 * cannot fail. */
    523 	tmpfs_dir_attach(dvp, de);
    524 	VN_KNOTE(dvp, NOTE_WRITE);
    525 
    526 out:
    527 	if (error != 0 || !(cnp->cn_flags & SAVESTART))
    528 		PNBUF_PUT(cnp->cn_pnbuf);
    529 	vput(dvp);
    530 
    531 	KASSERT(!VOP_ISLOCKED(dvp));
    532 	KASSERT(IFF(error == 0, *vpp != NULL));
    533 
    534 	return error;
    535 }
    536 
    537 /* --------------------------------------------------------------------- */
    538 
    539 /*
    540  * Attaches the directory entry de to the directory represented by vp.
    541  * Note that this does not change the link count of the node pointed by
    542  * the directory entry, as this is done by tmpfs_alloc_dirent.
    543  */
    544 void
    545 tmpfs_dir_attach(struct vnode *vp, struct tmpfs_dirent *de)
    546 {
    547 	struct tmpfs_node *dnode;
    548 
    549 	dnode = VP_TO_TMPFS_DIR(vp);
    550 
    551 	TAILQ_INSERT_TAIL(&dnode->tn_spec.tn_dir.tn_dir, de, td_entries);
    552 	dnode->tn_size += sizeof(struct tmpfs_dirent);
    553 	dnode->tn_status |= TMPFS_NODE_ACCESSED | TMPFS_NODE_CHANGED | \
    554 	    TMPFS_NODE_MODIFIED;
    555 	uvm_vnp_setsize(vp, dnode->tn_size);
    556 }
    557 
    558 /* --------------------------------------------------------------------- */
    559 
    560 /*
    561  * Detaches the directory entry de from the directory represented by vp.
    562  * Note that this does not change the link count of the node pointed by
    563  * the directory entry, as this is done by tmpfs_free_dirent.
    564  */
    565 void
    566 tmpfs_dir_detach(struct vnode *vp, struct tmpfs_dirent *de)
    567 {
    568 	struct tmpfs_node *dnode;
    569 
    570 	KASSERT(VOP_ISLOCKED(vp));
    571 
    572 	dnode = VP_TO_TMPFS_DIR(vp);
    573 
    574 	if (dnode->tn_spec.tn_dir.tn_readdir_lastp == de) {
    575 		dnode->tn_spec.tn_dir.tn_readdir_lastn = 0;
    576 		dnode->tn_spec.tn_dir.tn_readdir_lastp = NULL;
    577 	}
    578 
    579 	TAILQ_REMOVE(&dnode->tn_spec.tn_dir.tn_dir, de, td_entries);
    580 	dnode->tn_size -= sizeof(struct tmpfs_dirent);
    581 	dnode->tn_status |= TMPFS_NODE_ACCESSED | TMPFS_NODE_CHANGED | \
    582 	    TMPFS_NODE_MODIFIED;
    583 	uvm_vnp_setsize(vp, dnode->tn_size);
    584 }
    585 
    586 /* --------------------------------------------------------------------- */
    587 
    588 /*
    589  * Looks for a directory entry in the directory represented by node.
    590  * 'cnp' describes the name of the entry to look for.  Note that the .
    591  * and .. components are not allowed as they do not physically exist
    592  * within directories.
    593  *
    594  * Returns a pointer to the entry when found, otherwise NULL.
    595  */
    596 struct tmpfs_dirent *
    597 tmpfs_dir_lookup(struct tmpfs_node *node, struct componentname *cnp)
    598 {
    599 	boolean_t found;
    600 	struct tmpfs_dirent *de;
    601 
    602 	KASSERT(IMPLIES(cnp->cn_namelen == 1, cnp->cn_nameptr[0] != '.'));
    603 	KASSERT(IMPLIES(cnp->cn_namelen == 2, !(cnp->cn_nameptr[0] == '.' &&
    604 	    cnp->cn_nameptr[1] == '.')));
    605 	TMPFS_VALIDATE_DIR(node);
    606 
    607 	node->tn_status |= TMPFS_NODE_ACCESSED;
    608 
    609 	found = 0;
    610 	TAILQ_FOREACH(de, &node->tn_spec.tn_dir.tn_dir, td_entries) {
    611 		KASSERT(cnp->cn_namelen < 0xffff);
    612 		if (de->td_namelen == (uint16_t)cnp->cn_namelen &&
    613 		    memcmp(de->td_name, cnp->cn_nameptr, de->td_namelen) == 0) {
    614 			found = 1;
    615 			break;
    616 		}
    617 	}
    618 
    619 	return found ? de : NULL;
    620 }
    621 
    622 /* --------------------------------------------------------------------- */
    623 
    624 /*
    625  * Helper function for tmpfs_readdir.  Creates a '.' entry for the given
    626  * directory and returns it in the uio space.  The function returns 0
    627  * on success, -1 if there was not enough space in the uio structure to
    628  * hold the directory entry or an appropriate error code if another
    629  * error happens.
    630  */
    631 int
    632 tmpfs_dir_getdotdent(struct tmpfs_node *node, struct uio *uio)
    633 {
    634 	int error;
    635 	struct dirent dent;
    636 
    637 	TMPFS_VALIDATE_DIR(node);
    638 	KASSERT(uio->uio_offset == TMPFS_DIRCOOKIE_DOT);
    639 
    640 	dent.d_fileno = node->tn_id;
    641 	dent.d_type = DT_DIR;
    642 	dent.d_namlen = 1;
    643 	dent.d_name[0] = '.';
    644 	dent.d_name[1] = '\0';
    645 	dent.d_reclen = _DIRENT_SIZE(&dent);
    646 
    647 	if (dent.d_reclen > uio->uio_resid)
    648 		error = -1;
    649 	else {
    650 		error = uiomove(&dent, dent.d_reclen, uio);
    651 		if (error == 0)
    652 			uio->uio_offset = TMPFS_DIRCOOKIE_DOTDOT;
    653 	}
    654 
    655 	node->tn_status |= TMPFS_NODE_ACCESSED;
    656 
    657 	return error;
    658 }
    659 
    660 /* --------------------------------------------------------------------- */
    661 
    662 /*
    663  * Helper function for tmpfs_readdir.  Creates a '..' entry for the given
    664  * directory and returns it in the uio space.  The function returns 0
    665  * on success, -1 if there was not enough space in the uio structure to
    666  * hold the directory entry or an appropriate error code if another
    667  * error happens.
    668  */
    669 int
    670 tmpfs_dir_getdotdotdent(struct tmpfs_node *node, struct uio *uio)
    671 {
    672 	int error;
    673 	struct dirent dent;
    674 
    675 	TMPFS_VALIDATE_DIR(node);
    676 	KASSERT(uio->uio_offset == TMPFS_DIRCOOKIE_DOTDOT);
    677 
    678 	dent.d_fileno = node->tn_spec.tn_dir.tn_parent->tn_id;
    679 	dent.d_type = DT_DIR;
    680 	dent.d_namlen = 2;
    681 	dent.d_name[0] = '.';
    682 	dent.d_name[1] = '.';
    683 	dent.d_name[2] = '\0';
    684 	dent.d_reclen = _DIRENT_SIZE(&dent);
    685 
    686 	if (dent.d_reclen > uio->uio_resid)
    687 		error = -1;
    688 	else {
    689 		error = uiomove(&dent, dent.d_reclen, uio);
    690 		if (error == 0) {
    691 			struct tmpfs_dirent *de;
    692 
    693 			de = TAILQ_FIRST(&node->tn_spec.tn_dir.tn_dir);
    694 			if (de == NULL)
    695 				uio->uio_offset = TMPFS_DIRCOOKIE_EOF;
    696 			else
    697 				uio->uio_offset = TMPFS_DIRCOOKIE(de);
    698 		}
    699 	}
    700 
    701 	node->tn_status |= TMPFS_NODE_ACCESSED;
    702 
    703 	return error;
    704 }
    705 
    706 /* --------------------------------------------------------------------- */
    707 
    708 /*
    709  * Lookup a directory entry by its associated cookie.
    710  */
    711 struct tmpfs_dirent *
    712 tmpfs_dir_lookupbycookie(struct tmpfs_node *node, off_t cookie)
    713 {
    714 	struct tmpfs_dirent *de;
    715 
    716 	if (cookie == node->tn_spec.tn_dir.tn_readdir_lastn &&
    717 	    node->tn_spec.tn_dir.tn_readdir_lastp != NULL) {
    718 		return node->tn_spec.tn_dir.tn_readdir_lastp;
    719 	}
    720 
    721 	TAILQ_FOREACH(de, &node->tn_spec.tn_dir.tn_dir, td_entries) {
    722 		if (TMPFS_DIRCOOKIE(de) == cookie) {
    723 			break;
    724 		}
    725 	}
    726 
    727 	return de;
    728 }
    729 
    730 /* --------------------------------------------------------------------- */
    731 
    732 /*
    733  * Helper function for tmpfs_readdir.  Returns as much directory entries
    734  * as can fit in the uio space.  The read starts at uio->uio_offset.
    735  * The function returns 0 on success, -1 if there was not enough space
    736  * in the uio structure to hold the directory entry or an appropriate
    737  * error code if another error happens.
    738  */
    739 int
    740 tmpfs_dir_getdents(struct tmpfs_node *node, struct uio *uio, off_t *cntp)
    741 {
    742 	int error;
    743 	off_t startcookie;
    744 	struct tmpfs_dirent *de;
    745 
    746 	TMPFS_VALIDATE_DIR(node);
    747 
    748 	/* Locate the first directory entry we have to return.  We have cached
    749 	 * the last readdir in the node, so use those values if appropriate.
    750 	 * Otherwise do a linear scan to find the requested entry. */
    751 	startcookie = uio->uio_offset;
    752 	KASSERT(startcookie != TMPFS_DIRCOOKIE_DOT);
    753 	KASSERT(startcookie != TMPFS_DIRCOOKIE_DOTDOT);
    754 	if (startcookie == TMPFS_DIRCOOKIE_EOF) {
    755 		return 0;
    756 	} else {
    757 		de = tmpfs_dir_lookupbycookie(node, startcookie);
    758 	}
    759 	if (de == NULL) {
    760 		return EINVAL;
    761 	}
    762 
    763 	/* Read as much entries as possible; i.e., until we reach the end of
    764 	 * the directory or we exhaust uio space. */
    765 	do {
    766 		struct dirent d;
    767 
    768 		/* Create a dirent structure representing the current
    769 		 * tmpfs_node and fill it. */
    770 		d.d_fileno = de->td_node->tn_id;
    771 		switch (de->td_node->tn_type) {
    772 		case VBLK:
    773 			d.d_type = DT_BLK;
    774 			break;
    775 
    776 		case VCHR:
    777 			d.d_type = DT_CHR;
    778 			break;
    779 
    780 		case VDIR:
    781 			d.d_type = DT_DIR;
    782 			break;
    783 
    784 		case VFIFO:
    785 			d.d_type = DT_FIFO;
    786 			break;
    787 
    788 		case VLNK:
    789 			d.d_type = DT_LNK;
    790 			break;
    791 
    792 		case VREG:
    793 			d.d_type = DT_REG;
    794 			break;
    795 
    796 		case VSOCK:
    797 			d.d_type = DT_SOCK;
    798 			break;
    799 
    800 		default:
    801 			KASSERT(0);
    802 		}
    803 		d.d_namlen = de->td_namelen;
    804 		KASSERT(de->td_namelen < sizeof(d.d_name));
    805 		(void)memcpy(d.d_name, de->td_name, de->td_namelen);
    806 		d.d_name[de->td_namelen] = '\0';
    807 		d.d_reclen = _DIRENT_SIZE(&d);
    808 
    809 		/* Stop reading if the directory entry we are treating is
    810 		 * bigger than the amount of data that can be returned. */
    811 		if (d.d_reclen > uio->uio_resid) {
    812 			error = -1;
    813 			break;
    814 		}
    815 
    816 		/* Copy the new dirent structure into the output buffer and
    817 		 * advance pointers. */
    818 		error = uiomove(&d, d.d_reclen, uio);
    819 
    820 		(*cntp)++;
    821 		de = TAILQ_NEXT(de, td_entries);
    822 	} while (error == 0 && uio->uio_resid > 0 && de != NULL);
    823 
    824 	/* Update the offset and cache. */
    825 	if (de == NULL) {
    826 		uio->uio_offset = TMPFS_DIRCOOKIE_EOF;
    827 		node->tn_spec.tn_dir.tn_readdir_lastn = 0;
    828 		node->tn_spec.tn_dir.tn_readdir_lastp = NULL;
    829 	} else {
    830 		node->tn_spec.tn_dir.tn_readdir_lastn = uio->uio_offset =
    831 		    TMPFS_DIRCOOKIE(de);
    832 		node->tn_spec.tn_dir.tn_readdir_lastp = de;
    833 	}
    834 
    835 	node->tn_status |= TMPFS_NODE_ACCESSED;
    836 
    837 	return error;
    838 }
    839 
    840 /* --------------------------------------------------------------------- */
    841 
    842 /*
    843  * Resizes the aobj associated to the regular file pointed to by vp to
    844  * the size newsize.  'vp' must point to a vnode that represents a regular
    845  * file.  'newsize' must be positive.
    846  *
    847  * Returns zero on success or an appropriate error code on failure.
    848  */
    849 int
    850 tmpfs_reg_resize(struct vnode *vp, off_t newsize)
    851 {
    852 	int error;
    853 	size_t newpages, oldpages;
    854 	struct tmpfs_mount *tmp;
    855 	struct tmpfs_node *node;
    856 	off_t oldsize;
    857 
    858 	KASSERT(vp->v_type == VREG);
    859 	KASSERT(newsize >= 0);
    860 
    861 	node = VP_TO_TMPFS_NODE(vp);
    862 	tmp = VFS_TO_TMPFS(vp->v_mount);
    863 
    864 	/* Convert the old and new sizes to the number of pages needed to
    865 	 * store them.  It may happen that we do not need to do anything
    866 	 * because the last allocated page can accommodate the change on
    867 	 * its own. */
    868 	oldsize = node->tn_size;
    869 	oldpages = round_page(oldsize) / PAGE_SIZE;
    870 	KASSERT(oldpages == node->tn_spec.tn_reg.tn_aobj_pages);
    871 	newpages = round_page(newsize) / PAGE_SIZE;
    872 
    873 	if (newpages > oldpages &&
    874 	    newpages - oldpages > TMPFS_PAGES_AVAIL(tmp)) {
    875 		error = ENOSPC;
    876 		goto out;
    877 	}
    878 
    879 	node->tn_spec.tn_reg.tn_aobj_pages = newpages;
    880 
    881 	tmp->tm_pages_used += (newpages - oldpages);
    882 	node->tn_size = newsize;
    883 	uvm_vnp_setsize(vp, newsize);
    884 	if (newsize < oldsize) {
    885 		int zerolen = MIN(round_page(newsize), node->tn_size) - newsize;
    886 
    887 		/*
    888 		 * free "backing store"
    889 		 */
    890 
    891 		if (newpages < oldpages) {
    892 			struct uvm_object *uobj;
    893 
    894 			uobj = node->tn_spec.tn_reg.tn_aobj;
    895 
    896 			simple_lock(&uobj->vmobjlock);
    897 			uao_dropswap_range(uobj, newpages, oldpages);
    898 			simple_unlock(&uobj->vmobjlock);
    899 		}
    900 
    901 		/*
    902 		 * zero out the truncated part of the last page.
    903 		 */
    904 
    905 		uvm_vnp_zerorange(vp, newsize, zerolen);
    906 	}
    907 
    908 	error = 0;
    909 
    910 out:
    911 	return error;
    912 }
    913 
    914 /* --------------------------------------------------------------------- */
    915 
    916 /*
    917  * Returns information about the number of available memory pages,
    918  * including physical and virtual ones.
    919  *
    920  * If 'total' is TRUE, the value returned is the total amount of memory
    921  * pages configured for the system (either in use or free).
    922  * If it is FALSE, the value returned is the amount of free memory pages.
    923  *
    924  * Remember to remove TMPFS_PAGES_RESERVED from the returned value to avoid
    925  * excessive memory usage.
    926  *
    927  */
    928 size_t
    929 tmpfs_mem_info(boolean_t total)
    930 {
    931 	size_t size;
    932 
    933 	size = 0;
    934 	size += uvmexp.swpgavail;
    935 	if (!total) {
    936 		size -= uvmexp.swpgonly;
    937 	}
    938 	size += uvmexp.free;
    939 	size += uvmexp.filepages;
    940 	if (size > uvmexp.wired) {
    941 		size -= uvmexp.wired;
    942 	} else {
    943 		size = 0;
    944 	}
    945 
    946 	return size;
    947 }
    948 
    949 /* --------------------------------------------------------------------- */
    950 
    951 /*
    952  * Change flags of the given vnode.
    953  * Caller should execute tmpfs_update on vp after a successful execution.
    954  * The vnode must be locked on entry and remain locked on exit.
    955  */
    956 int
    957 tmpfs_chflags(struct vnode *vp, int flags, kauth_cred_t cred, struct proc *p)
    958 {
    959 	int error;
    960 	struct tmpfs_node *node;
    961 
    962 	KASSERT(VOP_ISLOCKED(vp));
    963 
    964 	node = VP_TO_TMPFS_NODE(vp);
    965 
    966 	/* Disallow this operation if the file system is mounted read-only. */
    967 	if (vp->v_mount->mnt_flag & MNT_RDONLY)
    968 		return EROFS;
    969 
    970 	/* XXX: The following comes from UFS code, and can be found in
    971 	 * several other file systems.  Shouldn't this be centralized
    972 	 * somewhere? */
    973 	if (cred->cr_uid != node->tn_uid &&
    974 	    (error = kauth_authorize_generic(cred, KAUTH_GENERIC_ISSUSER,
    975 				       &p->p_acflag)))
    976 		return error;
    977 	if (cred->cr_uid == 0) {
    978 		/* The super-user is only allowed to change flags if the file
    979 		 * wasn't protected before and the securelevel is zero. */
    980 		if ((node->tn_flags & (SF_IMMUTABLE | SF_APPEND)) &&
    981 		    securelevel > 0)
    982 			return EPERM;
    983 		node->tn_flags = flags;
    984 	} else {
    985 		/* Regular users can change flags provided they only want to
    986 		 * change user-specific ones, not those reserved for the
    987 		 * super-user. */
    988 		if ((node->tn_flags & (SF_IMMUTABLE | SF_APPEND)) ||
    989 		    (flags & UF_SETTABLE) != flags)
    990 			return EPERM;
    991 		if ((node->tn_flags & SF_SETTABLE) != (flags & SF_SETTABLE))
    992 			return EPERM;
    993 		node->tn_flags &= SF_SETTABLE;
    994 		node->tn_flags |= (flags & UF_SETTABLE);
    995 	}
    996 
    997 	node->tn_status |= TMPFS_NODE_CHANGED;
    998 	VN_KNOTE(vp, NOTE_ATTRIB);
    999 
   1000 	KASSERT(VOP_ISLOCKED(vp));
   1001 
   1002 	return 0;
   1003 }
   1004 
   1005 /* --------------------------------------------------------------------- */
   1006 
   1007 /*
   1008  * Change access mode on the given vnode.
   1009  * Caller should execute tmpfs_update on vp after a successful execution.
   1010  * The vnode must be locked on entry and remain locked on exit.
   1011  */
   1012 int
   1013 tmpfs_chmod(struct vnode *vp, mode_t mode, kauth_cred_t cred, struct proc *p)
   1014 {
   1015 	int error;
   1016 	struct tmpfs_node *node;
   1017 
   1018 	KASSERT(VOP_ISLOCKED(vp));
   1019 
   1020 	node = VP_TO_TMPFS_NODE(vp);
   1021 
   1022 	/* Disallow this operation if the file system is mounted read-only. */
   1023 	if (vp->v_mount->mnt_flag & MNT_RDONLY)
   1024 		return EROFS;
   1025 
   1026 	/* Immutable or append-only files cannot be modified, either. */
   1027 	if (node->tn_flags & (IMMUTABLE | APPEND))
   1028 		return EPERM;
   1029 
   1030 	/* XXX: The following comes from UFS code, and can be found in
   1031 	 * several other file systems.  Shouldn't this be centralized
   1032 	 * somewhere? */
   1033 	if (cred->cr_uid != node->tn_uid &&
   1034 	    (error = kauth_authorize_generic(cred, KAUTH_GENERIC_ISSUSER,
   1035 				       &p->p_acflag)))
   1036 		return error;
   1037 	if (cred->cr_uid != 0) {
   1038 		if (vp->v_type != VDIR && (mode & S_ISTXT))
   1039 			return EFTYPE;
   1040 
   1041 		if (!groupmember(node->tn_gid, cred) && (mode & S_ISGID))
   1042 			return EPERM;
   1043 	}
   1044 
   1045 	node->tn_mode = (mode & ALLPERMS);
   1046 
   1047 	node->tn_status |= TMPFS_NODE_CHANGED;
   1048 	VN_KNOTE(vp, NOTE_ATTRIB);
   1049 
   1050 	KASSERT(VOP_ISLOCKED(vp));
   1051 
   1052 	return 0;
   1053 }
   1054 
   1055 /* --------------------------------------------------------------------- */
   1056 
   1057 /*
   1058  * Change ownership of the given vnode.  At least one of uid or gid must
   1059  * be different than VNOVAL.  If one is set to that value, the attribute
   1060  * is unchanged.
   1061  * Caller should execute tmpfs_update on vp after a successful execution.
   1062  * The vnode must be locked on entry and remain locked on exit.
   1063  */
   1064 int
   1065 tmpfs_chown(struct vnode *vp, uid_t uid, gid_t gid, kauth_cred_t cred,
   1066     struct proc *p)
   1067 {
   1068 	int error;
   1069 	struct tmpfs_node *node;
   1070 
   1071 	KASSERT(VOP_ISLOCKED(vp));
   1072 
   1073 	node = VP_TO_TMPFS_NODE(vp);
   1074 
   1075 	/* Assign default values if they are unknown. */
   1076 	KASSERT(uid != VNOVAL || gid != VNOVAL);
   1077 	if (uid == VNOVAL)
   1078 		uid = node->tn_uid;
   1079 	if (gid == VNOVAL)
   1080 		gid = node->tn_gid;
   1081 	KASSERT(uid != VNOVAL && gid != VNOVAL);
   1082 
   1083 	/* Disallow this operation if the file system is mounted read-only. */
   1084 	if (vp->v_mount->mnt_flag & MNT_RDONLY)
   1085 		return EROFS;
   1086 
   1087 	/* Immutable or append-only files cannot be modified, either. */
   1088 	if (node->tn_flags & (IMMUTABLE | APPEND))
   1089 		return EPERM;
   1090 
   1091 	/* XXX: The following comes from UFS code, and can be found in
   1092 	 * several other file systems.  Shouldn't this be centralized
   1093 	 * somewhere? */
   1094 	if ((cred->cr_uid != node->tn_uid || uid != node->tn_uid ||
   1095 	    (gid != node->tn_gid && !(cred->cr_gid == node->tn_gid ||
   1096 	     groupmember(gid, cred)))) &&
   1097 	    ((error = kauth_authorize_generic(cred, KAUTH_GENERIC_ISSUSER,
   1098 					&p->p_acflag)) != 0))
   1099 		return error;
   1100 
   1101 	node->tn_uid = uid;
   1102 	node->tn_gid = gid;
   1103 
   1104 	node->tn_status |= TMPFS_NODE_CHANGED;
   1105 	VN_KNOTE(vp, NOTE_ATTRIB);
   1106 
   1107 	KASSERT(VOP_ISLOCKED(vp));
   1108 
   1109 	return 0;
   1110 }
   1111 
   1112 /* --------------------------------------------------------------------- */
   1113 
   1114 /*
   1115  * Change size of the given vnode.
   1116  * Caller should execute tmpfs_update on vp after a successful execution.
   1117  * The vnode must be locked on entry and remain locked on exit.
   1118  */
   1119 int
   1120 tmpfs_chsize(struct vnode *vp, u_quad_t size, kauth_cred_t cred,
   1121     struct proc *p)
   1122 {
   1123 	int error;
   1124 	struct tmpfs_node *node;
   1125 
   1126 	KASSERT(VOP_ISLOCKED(vp));
   1127 
   1128 	node = VP_TO_TMPFS_NODE(vp);
   1129 
   1130 	/* Decide whether this is a valid operation based on the file type. */
   1131 	error = 0;
   1132 	switch (vp->v_type) {
   1133 	case VDIR:
   1134 		return EISDIR;
   1135 
   1136 	case VREG:
   1137 		if (vp->v_mount->mnt_flag & MNT_RDONLY)
   1138 			return EROFS;
   1139 		break;
   1140 
   1141 	case VBLK:
   1142 		/* FALLTHROUGH */
   1143 	case VCHR:
   1144 		/* FALLTHROUGH */
   1145 	case VFIFO:
   1146 		/* Allow modifications of special files even if in the file
   1147 		 * system is mounted read-only (we are not modifying the
   1148 		 * files themselves, but the objects they represent). */
   1149 		return 0;
   1150 
   1151 	default:
   1152 		/* Anything else is unsupported. */
   1153 		return EOPNOTSUPP;
   1154 	}
   1155 
   1156 	/* Immutable or append-only files cannot be modified, either. */
   1157 	if (node->tn_flags & (IMMUTABLE | APPEND))
   1158 		return EPERM;
   1159 
   1160 	error = tmpfs_truncate(vp, size);
   1161 	/* tmpfs_truncate will raise the NOTE_EXTEND and NOTE_ATTRIB kevents
   1162 	 * for us, as will update tn_status; no need to do that here. */
   1163 
   1164 	KASSERT(VOP_ISLOCKED(vp));
   1165 
   1166 	return error;
   1167 }
   1168 
   1169 /* --------------------------------------------------------------------- */
   1170 
   1171 /*
   1172  * Change access and modification times of the given vnode.
   1173  * Caller should execute tmpfs_update on vp after a successful execution.
   1174  * The vnode must be locked on entry and remain locked on exit.
   1175  */
   1176 int
   1177 tmpfs_chtimes(struct vnode *vp, struct timespec *atime, struct timespec *mtime,
   1178     int vaflags, kauth_cred_t cred, struct lwp *l)
   1179 {
   1180 	int error;
   1181 	struct tmpfs_node *node;
   1182 
   1183 	KASSERT(VOP_ISLOCKED(vp));
   1184 
   1185 	node = VP_TO_TMPFS_NODE(vp);
   1186 
   1187 	/* Disallow this operation if the file system is mounted read-only. */
   1188 	if (vp->v_mount->mnt_flag & MNT_RDONLY)
   1189 		return EROFS;
   1190 
   1191 	/* Immutable or append-only files cannot be modified, either. */
   1192 	if (node->tn_flags & (IMMUTABLE | APPEND))
   1193 		return EPERM;
   1194 
   1195 	/* XXX: The following comes from UFS code, and can be found in
   1196 	 * several other file systems.  Shouldn't this be centralized
   1197 	 * somewhere? */
   1198 	if (cred->cr_uid != node->tn_uid &&
   1199 	    (error = kauth_authorize_generic(cred, KAUTH_GENERIC_ISSUSER,
   1200 				       &l->l_proc->p_acflag)) &&
   1201 	    ((vaflags & VA_UTIMES_NULL) == 0 ||
   1202 	    (error = VOP_ACCESS(vp, VWRITE, cred, l))))
   1203 		return error;
   1204 
   1205 	if (atime->tv_sec != VNOVAL && atime->tv_nsec != VNOVAL)
   1206 		node->tn_status |= TMPFS_NODE_ACCESSED;
   1207 
   1208 	if (mtime->tv_sec != VNOVAL && mtime->tv_nsec != VNOVAL)
   1209 		node->tn_status |= TMPFS_NODE_MODIFIED;
   1210 
   1211 	tmpfs_update(vp, atime, mtime, 0);
   1212 
   1213 	KASSERT(VOP_ISLOCKED(vp));
   1214 
   1215 	return 0;
   1216 }
   1217 
   1218 /* --------------------------------------------------------------------- */
   1219 
   1220 /* Sync timestamps */
   1221 void
   1222 tmpfs_itimes(struct vnode *vp, const struct timespec *acc,
   1223     const struct timespec *mod)
   1224 {
   1225 	struct tmpfs_node *node;
   1226 	const struct timespec *ts = NULL;
   1227 	struct timespec tsb;
   1228 
   1229 	node = VP_TO_TMPFS_NODE(vp);
   1230 
   1231 	if ((node->tn_status & (TMPFS_NODE_ACCESSED | TMPFS_NODE_MODIFIED |
   1232 	    TMPFS_NODE_CHANGED)) == 0)
   1233 		return;
   1234 
   1235 	if (node->tn_status & TMPFS_NODE_ACCESSED) {
   1236 		if (acc == NULL)
   1237 			acc = ts == NULL ? (ts = nanotime(&tsb)) : ts;
   1238 		node->tn_atime = *acc;
   1239 	}
   1240 	if (node->tn_status & TMPFS_NODE_MODIFIED) {
   1241 		if (mod == NULL)
   1242 			mod = ts == NULL ? (ts = nanotime(&tsb)) : ts;
   1243 		node->tn_mtime = *mod;
   1244 	}
   1245 	if (node->tn_status & TMPFS_NODE_CHANGED) {
   1246 		if (ts == NULL)
   1247 			ts = nanotime(&tsb);
   1248 		node->tn_ctime = *ts;
   1249 	}
   1250 	node->tn_status &=
   1251 	    ~(TMPFS_NODE_ACCESSED | TMPFS_NODE_MODIFIED | TMPFS_NODE_CHANGED);
   1252 }
   1253 
   1254 /* --------------------------------------------------------------------- */
   1255 
   1256 void
   1257 tmpfs_update(struct vnode *vp, const struct timespec *acc,
   1258     const struct timespec *mod, int flags)
   1259 {
   1260 
   1261 	struct tmpfs_node *node;
   1262 
   1263 	KASSERT(VOP_ISLOCKED(vp));
   1264 
   1265 	node = VP_TO_TMPFS_NODE(vp);
   1266 
   1267 	if (flags & UPDATE_CLOSE)
   1268 		; /* XXX Need to do anything special? */
   1269 
   1270 	tmpfs_itimes(vp, acc, mod);
   1271 
   1272 	KASSERT(VOP_ISLOCKED(vp));
   1273 }
   1274 
   1275 /* --------------------------------------------------------------------- */
   1276 
   1277 int
   1278 tmpfs_truncate(struct vnode *vp, off_t length)
   1279 {
   1280 	boolean_t extended;
   1281 	int error;
   1282 	struct tmpfs_node *node;
   1283 
   1284 	node = VP_TO_TMPFS_NODE(vp);
   1285 	extended = length > node->tn_size;
   1286 
   1287 	if (length < 0) {
   1288 		error = EINVAL;
   1289 		goto out;
   1290 	}
   1291 
   1292 	if (node->tn_size == length) {
   1293 		error = 0;
   1294 		goto out;
   1295 	}
   1296 
   1297 	error = tmpfs_reg_resize(vp, length);
   1298 	if (error == 0) {
   1299 		VN_KNOTE(vp, NOTE_ATTRIB | (extended ? NOTE_EXTEND : 0));
   1300 		node->tn_status |= TMPFS_NODE_CHANGED | TMPFS_NODE_MODIFIED;
   1301 	}
   1302 
   1303 out:
   1304 	tmpfs_update(vp, NULL, NULL, 0);
   1305 
   1306 	return error;
   1307 }
   1308