Home | History | Annotate | Line # | Download | only in tmpfs
tmpfs_subr.c revision 1.97
      1 /*	$NetBSD: tmpfs_subr.c,v 1.97 2014/09/08 14:49:46 gson Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 2005-2013 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, and by Mindaugas Rasiukevicius.
     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  *
     20  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     21  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     22  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     23  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     24  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     25  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     26  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     27  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     28  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     29  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     30  * POSSIBILITY OF SUCH DAMAGE.
     31  */
     32 
     33 /*
     34  * Efficient memory file system: interfaces for inode and directory entry
     35  * construction, destruction and manipulation.
     36  *
     37  * Reference counting
     38  *
     39  *	The link count of inode (tmpfs_node_t::tn_links) is used as a
     40  *	reference counter.  However, it has slightly different semantics.
     41  *
     42  *	For directories - link count represents directory entries, which
     43  *	refer to the directories.  In other words, it represents the count
     44  *	of sub-directories.  It also takes into account the virtual '.'
     45  *	entry (which has no real entry in the list).  For files - link count
     46  *	represents the hard links.  Since only empty directories can be
     47  *	removed - link count aligns the reference counting requirements
     48  *	enough.  Note: to check whether directory is not empty, the inode
     49  *	size (tmpfs_node_t::tn_size) can be used.
     50  *
     51  *	The inode itself, as an object, gathers its first reference when
     52  *	directory entry is attached via tmpfs_dir_attach(9).  For instance,
     53  *	after regular tmpfs_create(), a file would have a link count of 1,
     54  *	while directory after tmpfs_mkdir() would have 2 (due to '.').
     55  *
     56  * Reclamation
     57  *
     58  *	It should be noted that tmpfs inodes rely on a combination of vnode
     59  *	reference counting and link counting.  That is, an inode can only be
     60  *	destroyed if its associated vnode is inactive.  The destruction is
     61  *	done on vnode reclamation i.e. tmpfs_reclaim().  It should be noted
     62  *	that tmpfs_node_t::tn_links being 0 is a destruction criterion.
     63  *
     64  *	If an inode has references within the file system (tn_links > 0) and
     65  *	its inactive vnode gets reclaimed/recycled - then the association is
     66  *	broken in tmpfs_reclaim().  In such case, an inode will always pass
     67  *	tmpfs_lookup() and thus tmpfs_vnode_get() to associate a new vnode.
     68  *
     69  * Lock order
     70  *
     71  *	tmpfs_node_t::tn_vlock ->
     72  *		vnode_t::v_vlock ->
     73  *			vnode_t::v_interlock
     74  */
     75 
     76 #include <sys/cdefs.h>
     77 __KERNEL_RCSID(0, "$NetBSD: tmpfs_subr.c,v 1.97 2014/09/08 14:49:46 gson Exp $");
     78 
     79 #include <sys/param.h>
     80 #include <sys/cprng.h>
     81 #include <sys/dirent.h>
     82 #include <sys/event.h>
     83 #include <sys/kmem.h>
     84 #include <sys/mount.h>
     85 #include <sys/namei.h>
     86 #include <sys/time.h>
     87 #include <sys/stat.h>
     88 #include <sys/systm.h>
     89 #include <sys/vnode.h>
     90 #include <sys/kauth.h>
     91 #include <sys/atomic.h>
     92 
     93 #include <uvm/uvm.h>
     94 
     95 #include <miscfs/specfs/specdev.h>
     96 #include <miscfs/genfs/genfs.h>
     97 #include <fs/tmpfs/tmpfs.h>
     98 #include <fs/tmpfs/tmpfs_fifoops.h>
     99 #include <fs/tmpfs/tmpfs_specops.h>
    100 #include <fs/tmpfs/tmpfs_vnops.h>
    101 
    102 static void	tmpfs_dir_putseq(tmpfs_node_t *, tmpfs_dirent_t *);
    103 
    104 /*
    105  * tmpfs_alloc_node: allocate a new inode of a specified type and
    106  * insert it into the list of specified mount point.
    107  */
    108 int
    109 tmpfs_alloc_node(tmpfs_mount_t *tmp, enum vtype type, uid_t uid, gid_t gid,
    110     mode_t mode, char *target, dev_t rdev, tmpfs_node_t **node)
    111 {
    112 	tmpfs_node_t *nnode;
    113 
    114 	nnode = tmpfs_node_get(tmp);
    115 	if (nnode == NULL) {
    116 		return ENOSPC;
    117 	}
    118 
    119 	/* Initially, no references and no associations. */
    120 	nnode->tn_links = 0;
    121 	nnode->tn_vnode = NULL;
    122 	nnode->tn_dirent_hint = NULL;
    123 
    124 	/*
    125 	 * XXX Where the pool is backed by a map larger than (4GB *
    126 	 * sizeof(*nnode)), this may produce duplicate inode numbers
    127 	 * for applications that do not understand 64-bit ino_t.
    128 	 */
    129 	nnode->tn_id = (ino_t)((uintptr_t)nnode / sizeof(*nnode));
    130 	/*
    131 	 * Make sure the generation number is not zero.
    132 	 * tmpfs_inactive() uses generation zero to mark dead nodes.
    133 	 */
    134 	do {
    135 		nnode->tn_gen = TMPFS_NODE_GEN_MASK & cprng_fast32();
    136 	} while (nnode->tn_gen == 0);
    137 
    138 	/* Generic initialization. */
    139 	nnode->tn_type = type;
    140 	nnode->tn_size = 0;
    141 	nnode->tn_flags = 0;
    142 	nnode->tn_lockf = NULL;
    143 
    144 	vfs_timestamp(&nnode->tn_atime);
    145 	nnode->tn_birthtime = nnode->tn_atime;
    146 	nnode->tn_ctime = nnode->tn_atime;
    147 	nnode->tn_mtime = nnode->tn_atime;
    148 
    149 	KASSERT(uid != VNOVAL && gid != VNOVAL && mode != VNOVAL);
    150 	nnode->tn_uid = uid;
    151 	nnode->tn_gid = gid;
    152 	nnode->tn_mode = mode;
    153 
    154 	/* Type-specific initialization. */
    155 	switch (nnode->tn_type) {
    156 	case VBLK:
    157 	case VCHR:
    158 		/* Character/block special device. */
    159 		KASSERT(rdev != VNOVAL);
    160 		nnode->tn_spec.tn_dev.tn_rdev = rdev;
    161 		break;
    162 	case VDIR:
    163 		/* Directory. */
    164 		TAILQ_INIT(&nnode->tn_spec.tn_dir.tn_dir);
    165 		nnode->tn_spec.tn_dir.tn_parent = NULL;
    166 		nnode->tn_spec.tn_dir.tn_seq_arena = NULL;
    167 		nnode->tn_spec.tn_dir.tn_next_seq = TMPFS_DIRSEQ_START;
    168 		nnode->tn_spec.tn_dir.tn_readdir_lastp = NULL;
    169 
    170 		/* Extra link count for the virtual '.' entry. */
    171 		nnode->tn_links++;
    172 		break;
    173 	case VFIFO:
    174 	case VSOCK:
    175 		break;
    176 	case VLNK:
    177 		/* Symbolic link.  Target specifies the file name. */
    178 		KASSERT(target != NULL);
    179 		nnode->tn_size = strlen(target);
    180 
    181 		if (nnode->tn_size == 0) {
    182 			/* Zero-length targets are supported. */
    183 			nnode->tn_spec.tn_lnk.tn_link = NULL;
    184 			break;
    185 		}
    186 
    187 		KASSERT(nnode->tn_size < MAXPATHLEN);
    188 
    189 		nnode->tn_spec.tn_lnk.tn_link =
    190 		    tmpfs_strname_alloc(tmp, nnode->tn_size);
    191 		if (nnode->tn_spec.tn_lnk.tn_link == NULL) {
    192 			tmpfs_node_put(tmp, nnode);
    193 			return ENOSPC;
    194 		}
    195 		memcpy(nnode->tn_spec.tn_lnk.tn_link, target, nnode->tn_size);
    196 		break;
    197 	case VREG:
    198 		/* Regular file.  Create an underlying UVM object. */
    199 		nnode->tn_spec.tn_reg.tn_aobj =
    200 		    uao_create(INT32_MAX - PAGE_SIZE, 0);
    201 		nnode->tn_spec.tn_reg.tn_aobj_pages = 0;
    202 		break;
    203 	default:
    204 		KASSERT(false);
    205 	}
    206 
    207 	mutex_init(&nnode->tn_vlock, MUTEX_DEFAULT, IPL_NONE);
    208 
    209 	mutex_enter(&tmp->tm_lock);
    210 	LIST_INSERT_HEAD(&tmp->tm_nodes, nnode, tn_entries);
    211 	mutex_exit(&tmp->tm_lock);
    212 
    213 	*node = nnode;
    214 	return 0;
    215 }
    216 
    217 /*
    218  * tmpfs_free_node: remove the inode from a list in the mount point and
    219  * destroy the inode structures.
    220  */
    221 void
    222 tmpfs_free_node(tmpfs_mount_t *tmp, tmpfs_node_t *node)
    223 {
    224 	size_t objsz;
    225 
    226 	mutex_enter(&tmp->tm_lock);
    227 	LIST_REMOVE(node, tn_entries);
    228 	mutex_exit(&tmp->tm_lock);
    229 
    230 	switch (node->tn_type) {
    231 	case VLNK:
    232 		if (node->tn_size > 0) {
    233 			tmpfs_strname_free(tmp, node->tn_spec.tn_lnk.tn_link,
    234 			    node->tn_size);
    235 		}
    236 		break;
    237 	case VREG:
    238 		/*
    239 		 * Calculate the size of inode data, decrease the used-memory
    240 		 * counter, and destroy the unerlying UVM object (if any).
    241 		 */
    242 		objsz = PAGE_SIZE * node->tn_spec.tn_reg.tn_aobj_pages;
    243 		if (objsz != 0) {
    244 			tmpfs_mem_decr(tmp, objsz);
    245 		}
    246 		if (node->tn_spec.tn_reg.tn_aobj != NULL) {
    247 			uao_detach(node->tn_spec.tn_reg.tn_aobj);
    248 		}
    249 		break;
    250 	case VDIR:
    251 		KASSERT(node->tn_size == 0);
    252 		KASSERT(node->tn_spec.tn_dir.tn_seq_arena == NULL);
    253 		KASSERT(TAILQ_EMPTY(&node->tn_spec.tn_dir.tn_dir));
    254 		KASSERT(node->tn_spec.tn_dir.tn_parent == NULL ||
    255 		    node == tmp->tm_root);
    256 		break;
    257 	default:
    258 		break;
    259 	}
    260 	KASSERT(node->tn_vnode == NULL);
    261 	KASSERT(node->tn_links == 0);
    262 
    263 	mutex_destroy(&node->tn_vlock);
    264 	tmpfs_node_put(tmp, node);
    265 }
    266 
    267 /*
    268  * tmpfs_vnode_get: allocate or reclaim a vnode for a specified inode.
    269  *
    270  * => Must be called with tmpfs_node_t::tn_vlock held.
    271  * => Returns vnode (*vpp) locked.
    272  */
    273 int
    274 tmpfs_vnode_get(struct mount *mp, tmpfs_node_t *node, vnode_t **vpp)
    275 {
    276 	vnode_t *vp;
    277 	kmutex_t *slock;
    278 	int error;
    279 again:
    280 	/* If there is already a vnode, try to reclaim it. */
    281 	if ((vp = node->tn_vnode) != NULL) {
    282 		atomic_or_32(&node->tn_gen, TMPFS_RECLAIMING_BIT);
    283 		mutex_enter(vp->v_interlock);
    284 		mutex_exit(&node->tn_vlock);
    285 		error = vget(vp, LK_EXCLUSIVE);
    286 		if (error == ENOENT) {
    287 			mutex_enter(&node->tn_vlock);
    288 			goto again;
    289 		}
    290 		atomic_and_32(&node->tn_gen, ~TMPFS_RECLAIMING_BIT);
    291 		*vpp = vp;
    292 		return error;
    293 	}
    294 	if (TMPFS_NODE_RECLAIMING(node)) {
    295 		atomic_and_32(&node->tn_gen, ~TMPFS_RECLAIMING_BIT);
    296 	}
    297 
    298 	/*
    299 	 * Get a new vnode and associate it with our inode.  Share the
    300 	 * lock with underlying UVM object, if there is one (VREG case).
    301 	 */
    302 	if (node->tn_type == VREG) {
    303 		struct uvm_object *uobj = node->tn_spec.tn_reg.tn_aobj;
    304 		slock = uobj->vmobjlock;
    305 	} else {
    306 		slock = NULL;
    307 	}
    308 	error = getnewvnode(VT_TMPFS, mp, tmpfs_vnodeop_p, slock, &vp);
    309 	if (error) {
    310 		mutex_exit(&node->tn_vlock);
    311 		return error;
    312 	}
    313 
    314 	vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
    315 	vp->v_type = node->tn_type;
    316 
    317 	/* Type-specific initialization. */
    318 	switch (node->tn_type) {
    319 	case VBLK:
    320 	case VCHR:
    321 		vp->v_op = tmpfs_specop_p;
    322 		spec_node_init(vp, node->tn_spec.tn_dev.tn_rdev);
    323 		break;
    324 	case VDIR:
    325 		vp->v_vflag |= node->tn_spec.tn_dir.tn_parent == node ?
    326 		    VV_ROOT : 0;
    327 		break;
    328 	case VFIFO:
    329 		vp->v_op = tmpfs_fifoop_p;
    330 		break;
    331 	case VLNK:
    332 	case VREG:
    333 	case VSOCK:
    334 		break;
    335 	default:
    336 		KASSERT(false);
    337 	}
    338 
    339 	uvm_vnp_setsize(vp, node->tn_size);
    340 	vp->v_data = node;
    341 	node->tn_vnode = vp;
    342 	mutex_exit(&node->tn_vlock);
    343 
    344 	KASSERT(VOP_ISLOCKED(vp));
    345 	*vpp = vp;
    346 	return 0;
    347 }
    348 
    349 /*
    350  * tmpfs_construct_node: allocate a new file of specified type and adds it
    351  * into the parent directory.
    352  *
    353  * => Credentials of the caller are used.
    354  */
    355 int
    356 tmpfs_construct_node(vnode_t *dvp, vnode_t **vpp, struct vattr *vap,
    357     struct componentname *cnp, char *target)
    358 {
    359 	tmpfs_mount_t *tmp = VFS_TO_TMPFS(dvp->v_mount);
    360 	tmpfs_node_t *dnode = VP_TO_TMPFS_DIR(dvp), *node;
    361 	tmpfs_dirent_t *de, *wde;
    362 	int error;
    363 
    364 	KASSERT(VOP_ISLOCKED(dvp));
    365 	*vpp = NULL;
    366 
    367 	/*
    368 	 * If directory was removed, prevent from node creation.  The vnode
    369 	 * might still be referenced, but it is about to be reclaimed.
    370 	 */
    371 	if (dnode->tn_links == 0) {
    372 		error = ENOENT;
    373 		goto out;
    374 	}
    375 
    376 	/* Check for the maximum number of links limit. */
    377 	if (vap->va_type == VDIR) {
    378 		/* Check for maximum links limit. */
    379 		if (dnode->tn_links == LINK_MAX) {
    380 			error = EMLINK;
    381 			goto out;
    382 		}
    383 		KASSERT(dnode->tn_links < LINK_MAX);
    384 	}
    385 
    386 	/* Allocate a node that represents the new file. */
    387 	error = tmpfs_alloc_node(tmp, vap->va_type, kauth_cred_geteuid(cnp->cn_cred),
    388 	    dnode->tn_gid, vap->va_mode, target, vap->va_rdev, &node);
    389 	if (error)
    390 		goto out;
    391 
    392 	/* Allocate a directory entry that points to the new file. */
    393 	error = tmpfs_alloc_dirent(tmp, cnp->cn_nameptr, cnp->cn_namelen, &de);
    394 	if (error) {
    395 		tmpfs_free_node(tmp, node);
    396 		goto out;
    397 	}
    398 
    399 	/* Get a vnode for the new file. */
    400 	mutex_enter(&node->tn_vlock);
    401 	error = tmpfs_vnode_get(dvp->v_mount, node, vpp);
    402 	if (error) {
    403 		tmpfs_free_dirent(tmp, de);
    404 		tmpfs_free_node(tmp, node);
    405 		goto out;
    406 	}
    407 
    408 	/* Remove whiteout before adding the new entry. */
    409 	if (cnp->cn_flags & ISWHITEOUT) {
    410 		wde = tmpfs_dir_lookup(dnode, cnp);
    411 		KASSERT(wde != NULL && wde->td_node == TMPFS_NODE_WHITEOUT);
    412 		tmpfs_dir_detach(dnode, wde);
    413 		tmpfs_free_dirent(tmp, wde);
    414 	}
    415 
    416 	/* Associate inode and attach the entry into the directory. */
    417 	tmpfs_dir_attach(dnode, de, node);
    418 
    419 	/* Make node opaque if requested. */
    420 	if (cnp->cn_flags & ISWHITEOUT)
    421 		node->tn_flags |= UF_OPAQUE;
    422 
    423 	/* Update the parent's timestamps. */
    424 	tmpfs_update(dvp, TMPFS_UPDATE_MTIME | TMPFS_UPDATE_CTIME);
    425 out:
    426 	if (error == 0)
    427 		VOP_UNLOCK(*vpp);
    428 
    429 	return error;
    430 }
    431 
    432 /*
    433  * tmpfs_alloc_dirent: allocates a new directory entry for the inode.
    434  * The directory entry contains a path name component.
    435  */
    436 int
    437 tmpfs_alloc_dirent(tmpfs_mount_t *tmp, const char *name, uint16_t len,
    438     tmpfs_dirent_t **de)
    439 {
    440 	tmpfs_dirent_t *nde;
    441 
    442 	nde = tmpfs_dirent_get(tmp);
    443 	if (nde == NULL)
    444 		return ENOSPC;
    445 
    446 	nde->td_name = tmpfs_strname_alloc(tmp, len);
    447 	if (nde->td_name == NULL) {
    448 		tmpfs_dirent_put(tmp, nde);
    449 		return ENOSPC;
    450 	}
    451 	nde->td_namelen = len;
    452 	memcpy(nde->td_name, name, len);
    453 	nde->td_seq = TMPFS_DIRSEQ_NONE;
    454 
    455 	*de = nde;
    456 	return 0;
    457 }
    458 
    459 /*
    460  * tmpfs_free_dirent: free a directory entry.
    461  */
    462 void
    463 tmpfs_free_dirent(tmpfs_mount_t *tmp, tmpfs_dirent_t *de)
    464 {
    465 	KASSERT(de->td_node == NULL);
    466 	KASSERT(de->td_seq == TMPFS_DIRSEQ_NONE);
    467 	tmpfs_strname_free(tmp, de->td_name, de->td_namelen);
    468 	tmpfs_dirent_put(tmp, de);
    469 }
    470 
    471 /*
    472  * tmpfs_dir_attach: associate directory entry with a specified inode,
    473  * and attach the entry into the directory, specified by vnode.
    474  *
    475  * => Increases link count on the associated node.
    476  * => Increases link count on directory node if our node is VDIR.
    477  * => It is caller's responsibility to check for the LINK_MAX limit.
    478  * => Triggers kqueue events here.
    479  */
    480 void
    481 tmpfs_dir_attach(tmpfs_node_t *dnode, tmpfs_dirent_t *de, tmpfs_node_t *node)
    482 {
    483 	vnode_t *dvp = dnode->tn_vnode;
    484 	int events = NOTE_WRITE;
    485 
    486 	KASSERT(dvp != NULL);
    487 	KASSERT(VOP_ISLOCKED(dvp));
    488 
    489 	/* Get a new sequence number. */
    490 	KASSERT(de->td_seq == TMPFS_DIRSEQ_NONE);
    491 	de->td_seq = tmpfs_dir_getseq(dnode, de);
    492 
    493 	/* Associate directory entry and the inode. */
    494 	de->td_node = node;
    495 	if (node != TMPFS_NODE_WHITEOUT) {
    496 		KASSERT(node->tn_links < LINK_MAX);
    497 		node->tn_links++;
    498 
    499 		/* Save the hint (might overwrite). */
    500 		node->tn_dirent_hint = de;
    501 	} else if ((dnode->tn_gen & TMPFS_WHITEOUT_BIT) == 0) {
    502 		/* Flag that there are whiteout entries. */
    503 		atomic_or_32(&dnode->tn_gen, TMPFS_WHITEOUT_BIT);
    504 	}
    505 
    506 	/* Insert the entry to the directory (parent of inode). */
    507 	TAILQ_INSERT_TAIL(&dnode->tn_spec.tn_dir.tn_dir, de, td_entries);
    508 	dnode->tn_size += sizeof(tmpfs_dirent_t);
    509 	uvm_vnp_setsize(dvp, dnode->tn_size);
    510 
    511 	if (node != TMPFS_NODE_WHITEOUT && node->tn_type == VDIR) {
    512 		/* Set parent. */
    513 		KASSERT(node->tn_spec.tn_dir.tn_parent == NULL);
    514 		node->tn_spec.tn_dir.tn_parent = dnode;
    515 
    516 		/* Increase the link count of parent. */
    517 		KASSERT(dnode->tn_links < LINK_MAX);
    518 		dnode->tn_links++;
    519 		events |= NOTE_LINK;
    520 
    521 		TMPFS_VALIDATE_DIR(node);
    522 	}
    523 	VN_KNOTE(dvp, events);
    524 }
    525 
    526 /*
    527  * tmpfs_dir_detach: disassociate directory entry and its inode,
    528  * and detach the entry from the directory, specified by vnode.
    529  *
    530  * => Decreases link count on the associated node.
    531  * => Decreases the link count on directory node, if our node is VDIR.
    532  * => Triggers kqueue events here.
    533  *
    534  * => Note: dvp and vp may be NULL only if called by tmpfs_unmount().
    535  */
    536 void
    537 tmpfs_dir_detach(tmpfs_node_t *dnode, tmpfs_dirent_t *de)
    538 {
    539 	tmpfs_node_t *node = de->td_node;
    540 	vnode_t *vp, *dvp = dnode->tn_vnode;
    541 	int events = NOTE_WRITE;
    542 
    543 	KASSERT(dvp == NULL || VOP_ISLOCKED(dvp));
    544 
    545 	if (__predict_true(node != TMPFS_NODE_WHITEOUT)) {
    546 		/* Deassociate the inode and entry. */
    547 		node->tn_dirent_hint = NULL;
    548 
    549 		KASSERT(node->tn_links > 0);
    550 		node->tn_links--;
    551 
    552 		if ((vp = node->tn_vnode) != NULL) {
    553 			KASSERT(VOP_ISLOCKED(vp));
    554 			VN_KNOTE(vp, node->tn_links ? NOTE_LINK : NOTE_DELETE);
    555 		}
    556 
    557 		/* If directory - decrease the link count of parent. */
    558 		if (node->tn_type == VDIR) {
    559 			KASSERT(node->tn_spec.tn_dir.tn_parent == dnode);
    560 			node->tn_spec.tn_dir.tn_parent = NULL;
    561 
    562 			KASSERT(dnode->tn_links > 0);
    563 			dnode->tn_links--;
    564 			events |= NOTE_LINK;
    565 		}
    566 	}
    567 	de->td_node = NULL;
    568 
    569 	/* Remove the entry from the directory. */
    570 	if (dnode->tn_spec.tn_dir.tn_readdir_lastp == de) {
    571 		dnode->tn_spec.tn_dir.tn_readdir_lastp = NULL;
    572 	}
    573 	TAILQ_REMOVE(&dnode->tn_spec.tn_dir.tn_dir, de, td_entries);
    574 	dnode->tn_size -= sizeof(tmpfs_dirent_t);
    575 	tmpfs_dir_putseq(dnode, de);
    576 
    577 	if (dvp) {
    578 		uvm_vnp_setsize(dvp, dnode->tn_size);
    579 		VN_KNOTE(dvp, events);
    580 	}
    581 }
    582 
    583 /*
    584  * tmpfs_dir_lookup: find a directory entry in the specified inode.
    585  *
    586  * Note that the . and .. components are not allowed as they do not
    587  * physically exist within directories.
    588  */
    589 tmpfs_dirent_t *
    590 tmpfs_dir_lookup(tmpfs_node_t *node, struct componentname *cnp)
    591 {
    592 	const char *name = cnp->cn_nameptr;
    593 	const uint16_t nlen = cnp->cn_namelen;
    594 	tmpfs_dirent_t *de;
    595 
    596 	KASSERT(VOP_ISLOCKED(node->tn_vnode));
    597 	KASSERT(nlen != 1 || !(name[0] == '.'));
    598 	KASSERT(nlen != 2 || !(name[0] == '.' && name[1] == '.'));
    599 	TMPFS_VALIDATE_DIR(node);
    600 
    601 	TAILQ_FOREACH(de, &node->tn_spec.tn_dir.tn_dir, td_entries) {
    602 		if (de->td_namelen != nlen)
    603 			continue;
    604 		if (memcmp(de->td_name, name, nlen) != 0)
    605 			continue;
    606 		break;
    607 	}
    608 	return de;
    609 }
    610 
    611 /*
    612  * tmpfs_dir_cached: get a cached directory entry if it is valid.  Used to
    613  * avoid unnecessary tmpfs_dir_lookup().
    614  *
    615  * => The vnode must be locked.
    616  */
    617 tmpfs_dirent_t *
    618 tmpfs_dir_cached(tmpfs_node_t *node)
    619 {
    620 	tmpfs_dirent_t *de = node->tn_dirent_hint;
    621 
    622 	KASSERT(VOP_ISLOCKED(node->tn_vnode));
    623 
    624 	if (de == NULL) {
    625 		return NULL;
    626 	}
    627 	KASSERT(de->td_node == node);
    628 
    629 	/*
    630 	 * Directories always have a valid hint.  For files, check if there
    631 	 * are any hard links.  If there are - hint might be invalid.
    632 	 */
    633 	return (node->tn_type != VDIR && node->tn_links > 1) ? NULL : de;
    634 }
    635 
    636 /*
    637  * tmpfs_dir_getseq: get a per-directory sequence number for the entry.
    638  *
    639  * => Shall not be larger than 2^31 for linux32 compatibility.
    640  */
    641 uint32_t
    642 tmpfs_dir_getseq(tmpfs_node_t *dnode, tmpfs_dirent_t *de)
    643 {
    644 	uint32_t seq = de->td_seq;
    645 	vmem_t *seq_arena;
    646 	vmem_addr_t off;
    647 	int error __diagused;
    648 
    649 	TMPFS_VALIDATE_DIR(dnode);
    650 
    651 	if (__predict_true(seq != TMPFS_DIRSEQ_NONE)) {
    652 		/* Already set. */
    653 		KASSERT(seq >= TMPFS_DIRSEQ_START);
    654 		return seq;
    655 	}
    656 
    657 	/*
    658 	 * The "." and ".." and the end-of-directory have reserved numbers.
    659 	 * The other sequence numbers are allocated as following:
    660 	 *
    661 	 * - The first half of the 2^31 is assigned incrementally.
    662 	 *
    663 	 * - If that range is exceeded, then the second half of 2^31
    664 	 * is used, but managed by vmem(9).
    665 	 */
    666 
    667 	seq = dnode->tn_spec.tn_dir.tn_next_seq;
    668 	KASSERT(seq >= TMPFS_DIRSEQ_START);
    669 
    670 	if (__predict_true(seq < TMPFS_DIRSEQ_END)) {
    671 		/* First half: just increment and return. */
    672 		dnode->tn_spec.tn_dir.tn_next_seq++;
    673 		return seq;
    674 	}
    675 
    676 	/*
    677 	 * First half exceeded, use the second half.  May need to create
    678 	 * vmem(9) arena for the directory first.
    679 	 */
    680 	if ((seq_arena = dnode->tn_spec.tn_dir.tn_seq_arena) == NULL) {
    681 		seq_arena = vmem_create("tmpfscoo", 0,
    682 		    TMPFS_DIRSEQ_END - 1, 1, NULL, NULL, NULL, 0,
    683 		    VM_SLEEP, IPL_NONE);
    684 		dnode->tn_spec.tn_dir.tn_seq_arena = seq_arena;
    685 		KASSERT(seq_arena != NULL);
    686 	}
    687 	error = vmem_alloc(seq_arena, 1, VM_SLEEP | VM_BESTFIT, &off);
    688 	KASSERT(error == 0);
    689 
    690 	KASSERT(off < TMPFS_DIRSEQ_END);
    691 	seq = off | TMPFS_DIRSEQ_END;
    692 	return seq;
    693 }
    694 
    695 static void
    696 tmpfs_dir_putseq(tmpfs_node_t *dnode, tmpfs_dirent_t *de)
    697 {
    698 	vmem_t *seq_arena = dnode->tn_spec.tn_dir.tn_seq_arena;
    699 	uint32_t seq = de->td_seq;
    700 
    701 	TMPFS_VALIDATE_DIR(dnode);
    702 
    703 	if (seq == TMPFS_DIRSEQ_NONE || seq < TMPFS_DIRSEQ_END) {
    704 		/* First half (or no sequence number set yet). */
    705 		KASSERT(de->td_seq >= TMPFS_DIRSEQ_START);
    706 	} else {
    707 		/* Second half. */
    708 		KASSERT(seq_arena != NULL);
    709 		KASSERT(seq >= TMPFS_DIRSEQ_END);
    710 		seq &= ~TMPFS_DIRSEQ_END;
    711 		vmem_free(seq_arena, seq, 1);
    712 	}
    713 	de->td_seq = TMPFS_DIRSEQ_NONE;
    714 
    715 	/* Empty?  We can reset. */
    716 	if (seq_arena && dnode->tn_size == 0) {
    717 		dnode->tn_spec.tn_dir.tn_seq_arena = NULL;
    718 		dnode->tn_spec.tn_dir.tn_next_seq = TMPFS_DIRSEQ_START;
    719 		vmem_destroy(seq_arena);
    720 	}
    721 }
    722 
    723 /*
    724  * tmpfs_dir_lookupbyseq: lookup a directory entry by the sequence number.
    725  */
    726 tmpfs_dirent_t *
    727 tmpfs_dir_lookupbyseq(tmpfs_node_t *node, off_t seq)
    728 {
    729 	tmpfs_dirent_t *de = node->tn_spec.tn_dir.tn_readdir_lastp;
    730 
    731 	TMPFS_VALIDATE_DIR(node);
    732 
    733 	/*
    734 	 * First, check the cache.  If does not match - perform a lookup.
    735 	 */
    736 	if (de && de->td_seq == seq) {
    737 		KASSERT(de->td_seq >= TMPFS_DIRSEQ_START);
    738 		KASSERT(de->td_seq != TMPFS_DIRSEQ_NONE);
    739 		return de;
    740 	}
    741 	TAILQ_FOREACH(de, &node->tn_spec.tn_dir.tn_dir, td_entries) {
    742 		KASSERT(de->td_seq >= TMPFS_DIRSEQ_START);
    743 		KASSERT(de->td_seq != TMPFS_DIRSEQ_NONE);
    744 		if (de->td_seq == seq)
    745 			return de;
    746 	}
    747 	return NULL;
    748 }
    749 
    750 /*
    751  * tmpfs_dir_getdotents: helper function for tmpfs_readdir() to get the
    752  * dot meta entries, that is, "." or "..".  Copy it to the UIO space.
    753  */
    754 static int
    755 tmpfs_dir_getdotents(tmpfs_node_t *node, struct dirent *dp, struct uio *uio)
    756 {
    757 	tmpfs_dirent_t *de;
    758 	off_t next = 0;
    759 	int error;
    760 
    761 	switch (uio->uio_offset) {
    762 	case TMPFS_DIRSEQ_DOT:
    763 		dp->d_fileno = node->tn_id;
    764 		strlcpy(dp->d_name, ".", sizeof(dp->d_name));
    765 		next = TMPFS_DIRSEQ_DOTDOT;
    766 		break;
    767 	case TMPFS_DIRSEQ_DOTDOT:
    768 		dp->d_fileno = node->tn_spec.tn_dir.tn_parent->tn_id;
    769 		strlcpy(dp->d_name, "..", sizeof(dp->d_name));
    770 		de = TAILQ_FIRST(&node->tn_spec.tn_dir.tn_dir);
    771 		next = de ? tmpfs_dir_getseq(node, de) : TMPFS_DIRSEQ_EOF;
    772 		break;
    773 	default:
    774 		KASSERT(false);
    775 	}
    776 	dp->d_type = DT_DIR;
    777 	dp->d_namlen = strlen(dp->d_name);
    778 	dp->d_reclen = _DIRENT_SIZE(dp);
    779 
    780 	if (dp->d_reclen > uio->uio_resid) {
    781 		return EJUSTRETURN;
    782 	}
    783 	if ((error = uiomove(dp, dp->d_reclen, uio)) != 0) {
    784 		return error;
    785 	}
    786 
    787 	uio->uio_offset = next;
    788 	return error;
    789 }
    790 
    791 /*
    792  * tmpfs_dir_getdents: helper function for tmpfs_readdir.
    793  *
    794  * => Returns as much directory entries as can fit in the uio space.
    795  * => The read starts at uio->uio_offset.
    796  */
    797 int
    798 tmpfs_dir_getdents(tmpfs_node_t *node, struct uio *uio, off_t *cntp)
    799 {
    800 	tmpfs_dirent_t *de;
    801 	struct dirent dent;
    802 	int error = 0;
    803 
    804 	KASSERT(VOP_ISLOCKED(node->tn_vnode));
    805 	TMPFS_VALIDATE_DIR(node);
    806 
    807 	/*
    808 	 * First check for the "." and ".." cases.
    809 	 * Note: tmpfs_dir_getdotents() will "seek" for us.
    810 	 */
    811 	memset(&dent, 0, sizeof(dent));
    812 
    813 	if (uio->uio_offset == TMPFS_DIRSEQ_DOT) {
    814 		if ((error = tmpfs_dir_getdotents(node, &dent, uio)) != 0) {
    815 			goto done;
    816 		}
    817 		(*cntp)++;
    818 	}
    819 	if (uio->uio_offset == TMPFS_DIRSEQ_DOTDOT) {
    820 		if ((error = tmpfs_dir_getdotents(node, &dent, uio)) != 0) {
    821 			goto done;
    822 		}
    823 		(*cntp)++;
    824 	}
    825 
    826 	/* Done if we reached the end. */
    827 	if (uio->uio_offset == TMPFS_DIRSEQ_EOF) {
    828 		goto done;
    829 	}
    830 
    831 	/* Locate the directory entry given by the given sequence number. */
    832 	de = tmpfs_dir_lookupbyseq(node, uio->uio_offset);
    833 	if (de == NULL) {
    834 		error = EINVAL;
    835 		goto done;
    836 	}
    837 
    838 	/*
    839 	 * Read as many entries as possible; i.e., until we reach the end
    840 	 * of the directory or we exhaust UIO space.
    841 	 */
    842 	do {
    843 		if (de->td_node == TMPFS_NODE_WHITEOUT) {
    844 			dent.d_fileno = 1;
    845 			dent.d_type = DT_WHT;
    846 		} else {
    847 			dent.d_fileno = de->td_node->tn_id;
    848 			dent.d_type = vtype2dt(de->td_node->tn_type);
    849 		}
    850 		dent.d_namlen = de->td_namelen;
    851 		KASSERT(de->td_namelen < sizeof(dent.d_name));
    852 		memcpy(dent.d_name, de->td_name, de->td_namelen);
    853 		dent.d_name[de->td_namelen] = '\0';
    854 		dent.d_reclen = _DIRENT_SIZE(&dent);
    855 
    856 		if (dent.d_reclen > uio->uio_resid) {
    857 			/* Exhausted UIO space. */
    858 			error = EJUSTRETURN;
    859 			break;
    860 		}
    861 
    862 		/* Copy out the directory entry and continue. */
    863 		error = uiomove(&dent, dent.d_reclen, uio);
    864 		if (error) {
    865 			break;
    866 		}
    867 		(*cntp)++;
    868 		de = TAILQ_NEXT(de, td_entries);
    869 
    870 	} while (uio->uio_resid > 0 && de);
    871 
    872 	/* Cache the last entry or clear and mark EOF. */
    873 	uio->uio_offset = de ? tmpfs_dir_getseq(node, de) : TMPFS_DIRSEQ_EOF;
    874 	node->tn_spec.tn_dir.tn_readdir_lastp = de;
    875 done:
    876 	tmpfs_update(node->tn_vnode, TMPFS_UPDATE_ATIME);
    877 
    878 	if (error == EJUSTRETURN) {
    879 		/* Exhausted UIO space - just return. */
    880 		error = 0;
    881 	}
    882 	KASSERT(error >= 0);
    883 	return error;
    884 }
    885 
    886 /*
    887  * tmpfs_reg_resize: resize the underlying UVM object associated with the
    888  * specified regular file.
    889  */
    890 int
    891 tmpfs_reg_resize(struct vnode *vp, off_t newsize)
    892 {
    893 	tmpfs_mount_t *tmp = VFS_TO_TMPFS(vp->v_mount);
    894 	tmpfs_node_t *node = VP_TO_TMPFS_NODE(vp);
    895 	struct uvm_object *uobj = node->tn_spec.tn_reg.tn_aobj;
    896 	size_t newpages, oldpages;
    897 	off_t oldsize;
    898 
    899 	KASSERT(vp->v_type == VREG);
    900 	KASSERT(newsize >= 0);
    901 
    902 	oldsize = node->tn_size;
    903 	oldpages = round_page(oldsize) >> PAGE_SHIFT;
    904 	newpages = round_page(newsize) >> PAGE_SHIFT;
    905 	KASSERT(oldpages == node->tn_spec.tn_reg.tn_aobj_pages);
    906 
    907 	if (newpages > oldpages) {
    908 		/* Increase the used-memory counter if getting extra pages. */
    909 		if (!tmpfs_mem_incr(tmp, (newpages - oldpages) << PAGE_SHIFT)) {
    910 			return ENOSPC;
    911 		}
    912 	} else if (newsize < oldsize) {
    913 		size_t zerolen;
    914 
    915 		zerolen = MIN(round_page(newsize), node->tn_size) - newsize;
    916 		ubc_zerorange(uobj, newsize, zerolen, UBC_UNMAP_FLAG(vp));
    917 	}
    918 
    919 	node->tn_spec.tn_reg.tn_aobj_pages = newpages;
    920 	node->tn_size = newsize;
    921 	uvm_vnp_setsize(vp, newsize);
    922 
    923 	/*
    924 	 * Free "backing store".
    925 	 */
    926 	if (newpages < oldpages) {
    927 		KASSERT(uobj->vmobjlock == vp->v_interlock);
    928 
    929 		mutex_enter(uobj->vmobjlock);
    930 		uao_dropswap_range(uobj, newpages, oldpages);
    931 		mutex_exit(uobj->vmobjlock);
    932 
    933 		/* Decrease the used-memory counter. */
    934 		tmpfs_mem_decr(tmp, (oldpages - newpages) << PAGE_SHIFT);
    935 	}
    936 	if (newsize > oldsize) {
    937 		VN_KNOTE(vp, NOTE_EXTEND);
    938 	}
    939 	return 0;
    940 }
    941 
    942 /*
    943  * tmpfs_chflags: change flags of the given vnode.
    944  */
    945 int
    946 tmpfs_chflags(vnode_t *vp, int flags, kauth_cred_t cred, lwp_t *l)
    947 {
    948 	tmpfs_node_t *node = VP_TO_TMPFS_NODE(vp);
    949 	kauth_action_t action = KAUTH_VNODE_WRITE_FLAGS;
    950 	int error;
    951 	bool changing_sysflags = false;
    952 
    953 	KASSERT(VOP_ISLOCKED(vp));
    954 
    955 	/* Disallow this operation if the file system is mounted read-only. */
    956 	if (vp->v_mount->mnt_flag & MNT_RDONLY)
    957 		return EROFS;
    958 
    959 	/*
    960 	 * If the new flags have non-user flags that are different than
    961 	 * those on the node, we need special permission to change them.
    962 	 */
    963 	if ((flags & SF_SETTABLE) != (node->tn_flags & SF_SETTABLE)) {
    964 		action |= KAUTH_VNODE_WRITE_SYSFLAGS;
    965 		changing_sysflags = true;
    966 	}
    967 
    968 	/*
    969 	 * Indicate that this node's flags have system attributes in them if
    970 	 * that's the case.
    971 	 */
    972 	if (node->tn_flags & (SF_IMMUTABLE | SF_APPEND)) {
    973 		action |= KAUTH_VNODE_HAS_SYSFLAGS;
    974 	}
    975 
    976 	error = kauth_authorize_vnode(cred, action, vp, NULL,
    977 	    genfs_can_chflags(cred, vp->v_type, node->tn_uid,
    978 	    changing_sysflags));
    979 	if (error)
    980 		return error;
    981 
    982 	/*
    983 	 * Set the flags. If we're not setting non-user flags, be careful not
    984 	 * to overwrite them.
    985 	 *
    986 	 * XXX: Can't we always assign here? if the system flags are different,
    987 	 *      the code above should catch attempts to change them without
    988 	 *      proper permissions, and if we're here it means it's okay to
    989 	 *      change them...
    990 	 */
    991 	if (!changing_sysflags) {
    992 		/* Clear all user-settable flags and re-set them. */
    993 		node->tn_flags &= SF_SETTABLE;
    994 		node->tn_flags |= (flags & UF_SETTABLE);
    995 	} else {
    996 		node->tn_flags = flags;
    997 	}
    998 	tmpfs_update(vp, TMPFS_UPDATE_CTIME);
    999 	VN_KNOTE(vp, NOTE_ATTRIB);
   1000 	return 0;
   1001 }
   1002 
   1003 /*
   1004  * tmpfs_chmod: change access mode on the given vnode.
   1005  */
   1006 int
   1007 tmpfs_chmod(vnode_t *vp, mode_t mode, kauth_cred_t cred, lwp_t *l)
   1008 {
   1009 	tmpfs_node_t *node = VP_TO_TMPFS_NODE(vp);
   1010 	int error;
   1011 
   1012 	KASSERT(VOP_ISLOCKED(vp));
   1013 
   1014 	/* Disallow this operation if the file system is mounted read-only. */
   1015 	if (vp->v_mount->mnt_flag & MNT_RDONLY)
   1016 		return EROFS;
   1017 
   1018 	/* Immutable or append-only files cannot be modified, either. */
   1019 	if (node->tn_flags & (IMMUTABLE | APPEND))
   1020 		return EPERM;
   1021 
   1022 	error = kauth_authorize_vnode(cred, KAUTH_VNODE_WRITE_SECURITY, vp,
   1023 	    NULL, genfs_can_chmod(vp->v_type, cred, node->tn_uid, node->tn_gid, mode));
   1024 	if (error) {
   1025 		return error;
   1026 	}
   1027 	node->tn_mode = (mode & ALLPERMS);
   1028 	tmpfs_update(vp, TMPFS_UPDATE_CTIME);
   1029 	VN_KNOTE(vp, NOTE_ATTRIB);
   1030 	return 0;
   1031 }
   1032 
   1033 /*
   1034  * tmpfs_chown: change ownership of the given vnode.
   1035  *
   1036  * => At least one of uid or gid must be different than VNOVAL.
   1037  * => Attribute is unchanged for VNOVAL case.
   1038  */
   1039 int
   1040 tmpfs_chown(vnode_t *vp, uid_t uid, gid_t gid, kauth_cred_t cred, lwp_t *l)
   1041 {
   1042 	tmpfs_node_t *node = VP_TO_TMPFS_NODE(vp);
   1043 	int error;
   1044 
   1045 	KASSERT(VOP_ISLOCKED(vp));
   1046 
   1047 	/* Assign default values if they are unknown. */
   1048 	KASSERT(uid != VNOVAL || gid != VNOVAL);
   1049 	if (uid == VNOVAL) {
   1050 		uid = node->tn_uid;
   1051 	}
   1052 	if (gid == VNOVAL) {
   1053 		gid = node->tn_gid;
   1054 	}
   1055 
   1056 	/* Disallow this operation if the file system is mounted read-only. */
   1057 	if (vp->v_mount->mnt_flag & MNT_RDONLY)
   1058 		return EROFS;
   1059 
   1060 	/* Immutable or append-only files cannot be modified, either. */
   1061 	if (node->tn_flags & (IMMUTABLE | APPEND))
   1062 		return EPERM;
   1063 
   1064 	error = kauth_authorize_vnode(cred, KAUTH_VNODE_CHANGE_OWNERSHIP, vp,
   1065 	    NULL, genfs_can_chown(cred, node->tn_uid, node->tn_gid, uid,
   1066 	    gid));
   1067 	if (error) {
   1068 		return error;
   1069 	}
   1070 	node->tn_uid = uid;
   1071 	node->tn_gid = gid;
   1072 	tmpfs_update(vp, TMPFS_UPDATE_CTIME);
   1073 	VN_KNOTE(vp, NOTE_ATTRIB);
   1074 	return 0;
   1075 }
   1076 
   1077 /*
   1078  * tmpfs_chsize: change size of the given vnode.
   1079  */
   1080 int
   1081 tmpfs_chsize(vnode_t *vp, u_quad_t size, kauth_cred_t cred, lwp_t *l)
   1082 {
   1083 	tmpfs_node_t *node = VP_TO_TMPFS_NODE(vp);
   1084 	const off_t length = size;
   1085 	int error;
   1086 
   1087 	KASSERT(VOP_ISLOCKED(vp));
   1088 
   1089 	/* Decide whether this is a valid operation based on the file type. */
   1090 	switch (vp->v_type) {
   1091 	case VDIR:
   1092 		return EISDIR;
   1093 	case VREG:
   1094 		if (vp->v_mount->mnt_flag & MNT_RDONLY) {
   1095 			return EROFS;
   1096 		}
   1097 		break;
   1098 	case VBLK:
   1099 	case VCHR:
   1100 	case VFIFO:
   1101 		/*
   1102 		 * Allow modifications of special files even if in the file
   1103 		 * system is mounted read-only (we are not modifying the
   1104 		 * files themselves, but the objects they represent).
   1105 		 */
   1106 		return 0;
   1107 	default:
   1108 		return EOPNOTSUPP;
   1109 	}
   1110 
   1111 	/* Immutable or append-only files cannot be modified, either. */
   1112 	if (node->tn_flags & (IMMUTABLE | APPEND)) {
   1113 		return EPERM;
   1114 	}
   1115 
   1116 	if (length < 0) {
   1117 		return EINVAL;
   1118 	}
   1119 	if (node->tn_size == length) {
   1120 		return 0;
   1121 	}
   1122 
   1123 	/* Note: tmpfs_reg_resize() will raise NOTE_EXTEND and NOTE_ATTRIB. */
   1124 	if ((error = tmpfs_reg_resize(vp, length)) != 0) {
   1125 		return error;
   1126 	}
   1127 	tmpfs_update(vp, TMPFS_UPDATE_CTIME | TMPFS_UPDATE_MTIME);
   1128 	return 0;
   1129 }
   1130 
   1131 /*
   1132  * tmpfs_chtimes: change access and modification times for vnode.
   1133  */
   1134 int
   1135 tmpfs_chtimes(vnode_t *vp, const struct timespec *atime,
   1136     const struct timespec *mtime, const struct timespec *btime,
   1137     int vaflags, kauth_cred_t cred, lwp_t *l)
   1138 {
   1139 	tmpfs_node_t *node = VP_TO_TMPFS_NODE(vp);
   1140 	int error;
   1141 
   1142 	KASSERT(VOP_ISLOCKED(vp));
   1143 
   1144 	/* Disallow this operation if the file system is mounted read-only. */
   1145 	if (vp->v_mount->mnt_flag & MNT_RDONLY)
   1146 		return EROFS;
   1147 
   1148 	/* Immutable or append-only files cannot be modified, either. */
   1149 	if (node->tn_flags & (IMMUTABLE | APPEND))
   1150 		return EPERM;
   1151 
   1152 	error = kauth_authorize_vnode(cred, KAUTH_VNODE_WRITE_TIMES, vp, NULL,
   1153 	    genfs_can_chtimes(vp, vaflags, node->tn_uid, cred));
   1154 	if (error)
   1155 		return error;
   1156 
   1157 	if (atime->tv_sec != VNOVAL) {
   1158 		node->tn_atime = *atime;
   1159 	}
   1160 	if (mtime->tv_sec != VNOVAL) {
   1161 		node->tn_mtime = *mtime;
   1162 	}
   1163 	if (btime->tv_sec != VNOVAL) {
   1164 		node->tn_birthtime = *btime;
   1165 	}
   1166 	VN_KNOTE(vp, NOTE_ATTRIB);
   1167 	return 0;
   1168 }
   1169 
   1170 /*
   1171  * tmpfs_update: update the timestamps as indicated by the flags.
   1172  */
   1173 void
   1174 tmpfs_update(vnode_t *vp, unsigned tflags)
   1175 {
   1176 	tmpfs_node_t *node = VP_TO_TMPFS_NODE(vp);
   1177 	struct timespec nowtm;
   1178 
   1179 	if (tflags == 0) {
   1180 		return;
   1181 	}
   1182 	vfs_timestamp(&nowtm);
   1183 
   1184 	if (tflags & TMPFS_UPDATE_ATIME) {
   1185 		node->tn_atime = nowtm;
   1186 	}
   1187 	if (tflags & TMPFS_UPDATE_MTIME) {
   1188 		node->tn_mtime = nowtm;
   1189 	}
   1190 	if (tflags & TMPFS_UPDATE_CTIME) {
   1191 		node->tn_ctime = nowtm;
   1192 	}
   1193 }
   1194