xref: /titanic_51/usr/src/uts/common/fs/tmpfs/tmp_dir.c (revision da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5df2381bfSpraks  * Common Development and Distribution License (the "License").
6df2381bfSpraks  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
217c478bd9Sstevel@tonic-gate /*
22df2381bfSpraks  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
237c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
247c478bd9Sstevel@tonic-gate  */
257c478bd9Sstevel@tonic-gate 
267c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
277c478bd9Sstevel@tonic-gate 
287c478bd9Sstevel@tonic-gate #include <sys/types.h>
297c478bd9Sstevel@tonic-gate #include <sys/param.h>
307c478bd9Sstevel@tonic-gate #include <sys/sysmacros.h>
317c478bd9Sstevel@tonic-gate #include <sys/systm.h>
327c478bd9Sstevel@tonic-gate #include <sys/time.h>
337c478bd9Sstevel@tonic-gate #include <sys/vfs.h>
347c478bd9Sstevel@tonic-gate #include <sys/vnode.h>
357c478bd9Sstevel@tonic-gate #include <sys/errno.h>
367c478bd9Sstevel@tonic-gate #include <sys/cmn_err.h>
377c478bd9Sstevel@tonic-gate #include <sys/cred.h>
387c478bd9Sstevel@tonic-gate #include <sys/stat.h>
397c478bd9Sstevel@tonic-gate #include <sys/debug.h>
407c478bd9Sstevel@tonic-gate #include <sys/policy.h>
417c478bd9Sstevel@tonic-gate #include <sys/fs/tmpnode.h>
427c478bd9Sstevel@tonic-gate #include <sys/fs/tmp.h>
437c478bd9Sstevel@tonic-gate #include <sys/vtrace.h>
447c478bd9Sstevel@tonic-gate 
457c478bd9Sstevel@tonic-gate static int tdircheckpath(struct tmpnode *, struct tmpnode *, struct cred *);
467c478bd9Sstevel@tonic-gate static int tdirrename(struct tmpnode *, struct tmpnode *, struct tmpnode *,
477c478bd9Sstevel@tonic-gate 	char *, struct tmpnode *, struct tdirent *, struct cred *);
487c478bd9Sstevel@tonic-gate static void tdirfixdotdot(struct tmpnode *, struct tmpnode *, struct tmpnode *);
497c478bd9Sstevel@tonic-gate static int tdirmaketnode(struct tmpnode *, struct tmount *, struct vattr *,
507c478bd9Sstevel@tonic-gate 	enum de_op, struct tmpnode **, struct cred *);
517c478bd9Sstevel@tonic-gate static int tdiraddentry(struct tmpnode *, struct tmpnode *, char *,
527c478bd9Sstevel@tonic-gate 	enum de_op, struct tmpnode *);
537c478bd9Sstevel@tonic-gate 
547c478bd9Sstevel@tonic-gate 
557c478bd9Sstevel@tonic-gate #define	T_HASH_SIZE	8192		/* must be power of 2 */
567c478bd9Sstevel@tonic-gate #define	T_MUTEX_SIZE	64
577c478bd9Sstevel@tonic-gate 
587c478bd9Sstevel@tonic-gate static struct tdirent	*t_hashtable[T_HASH_SIZE];
597c478bd9Sstevel@tonic-gate static kmutex_t		 t_hashmutex[T_MUTEX_SIZE];
607c478bd9Sstevel@tonic-gate 
617c478bd9Sstevel@tonic-gate #define	T_HASH_INDEX(a)		((a) & (T_HASH_SIZE-1))
627c478bd9Sstevel@tonic-gate #define	T_MUTEX_INDEX(a)	((a) & (T_MUTEX_SIZE-1))
637c478bd9Sstevel@tonic-gate 
647c478bd9Sstevel@tonic-gate #define	TMPFS_HASH(tp, name, hash)				\
657c478bd9Sstevel@tonic-gate 	{							\
667c478bd9Sstevel@tonic-gate 		char Xc, *Xcp;					\
677c478bd9Sstevel@tonic-gate 		hash = (uint_t)(uintptr_t)(tp) >> 8;		\
687c478bd9Sstevel@tonic-gate 		for (Xcp = (name); (Xc = *Xcp) != 0; Xcp++)	\
697c478bd9Sstevel@tonic-gate 			hash = (hash << 4) + hash + (uint_t)Xc;	\
707c478bd9Sstevel@tonic-gate 	}
717c478bd9Sstevel@tonic-gate 
727c478bd9Sstevel@tonic-gate void
737c478bd9Sstevel@tonic-gate tmpfs_hash_init(void)
747c478bd9Sstevel@tonic-gate {
757c478bd9Sstevel@tonic-gate 	int	ix;
767c478bd9Sstevel@tonic-gate 
777c478bd9Sstevel@tonic-gate 	for (ix = 0; ix < T_MUTEX_SIZE; ix++)
787c478bd9Sstevel@tonic-gate 		mutex_init(&t_hashmutex[ix], NULL, MUTEX_DEFAULT, NULL);
797c478bd9Sstevel@tonic-gate }
807c478bd9Sstevel@tonic-gate 
817c478bd9Sstevel@tonic-gate /*
827c478bd9Sstevel@tonic-gate  * This routine is where the rubber meets the road for identities.
837c478bd9Sstevel@tonic-gate  */
847c478bd9Sstevel@tonic-gate static void
857c478bd9Sstevel@tonic-gate tmpfs_hash_in(struct tdirent *t)
867c478bd9Sstevel@tonic-gate {
877c478bd9Sstevel@tonic-gate 	uint_t		hash;
887c478bd9Sstevel@tonic-gate 	struct tdirent	**prevpp;
897c478bd9Sstevel@tonic-gate 	kmutex_t	*t_hmtx;
907c478bd9Sstevel@tonic-gate 
917c478bd9Sstevel@tonic-gate 	TMPFS_HASH(t->td_parent, t->td_name, hash);
927c478bd9Sstevel@tonic-gate 	t->td_hash = hash;
937c478bd9Sstevel@tonic-gate 	prevpp = &t_hashtable[T_HASH_INDEX(hash)];
947c478bd9Sstevel@tonic-gate 	t_hmtx = &t_hashmutex[T_MUTEX_INDEX(hash)];
957c478bd9Sstevel@tonic-gate 	mutex_enter(t_hmtx);
967c478bd9Sstevel@tonic-gate 	t->td_link = *prevpp;
977c478bd9Sstevel@tonic-gate 	*prevpp = t;
987c478bd9Sstevel@tonic-gate 	mutex_exit(t_hmtx);
997c478bd9Sstevel@tonic-gate }
1007c478bd9Sstevel@tonic-gate 
1017c478bd9Sstevel@tonic-gate /*
1027c478bd9Sstevel@tonic-gate  * Remove tdirent *t from the hash list.
1037c478bd9Sstevel@tonic-gate  */
1047c478bd9Sstevel@tonic-gate static void
1057c478bd9Sstevel@tonic-gate tmpfs_hash_out(struct tdirent *t)
1067c478bd9Sstevel@tonic-gate {
1077c478bd9Sstevel@tonic-gate 	uint_t		hash;
1087c478bd9Sstevel@tonic-gate 	struct tdirent	**prevpp;
1097c478bd9Sstevel@tonic-gate 	kmutex_t	*t_hmtx;
1107c478bd9Sstevel@tonic-gate 
1117c478bd9Sstevel@tonic-gate 	hash = t->td_hash;
1127c478bd9Sstevel@tonic-gate 	prevpp = &t_hashtable[T_HASH_INDEX(hash)];
1137c478bd9Sstevel@tonic-gate 	t_hmtx = &t_hashmutex[T_MUTEX_INDEX(hash)];
1147c478bd9Sstevel@tonic-gate 	mutex_enter(t_hmtx);
1157c478bd9Sstevel@tonic-gate 	while (*prevpp != t)
1167c478bd9Sstevel@tonic-gate 		prevpp = &(*prevpp)->td_link;
1177c478bd9Sstevel@tonic-gate 	*prevpp = t->td_link;
1187c478bd9Sstevel@tonic-gate 	mutex_exit(t_hmtx);
1197c478bd9Sstevel@tonic-gate }
1207c478bd9Sstevel@tonic-gate 
1217c478bd9Sstevel@tonic-gate /*
1227c478bd9Sstevel@tonic-gate  * Currently called by tdirrename() only.
1237c478bd9Sstevel@tonic-gate  * rename operation needs to be done with lock held, to ensure that
1247c478bd9Sstevel@tonic-gate  * no other operations can access the tmpnode at the same instance.
1257c478bd9Sstevel@tonic-gate  */
1267c478bd9Sstevel@tonic-gate static void
1277c478bd9Sstevel@tonic-gate tmpfs_hash_change(struct tdirent *tdp, struct tmpnode *fromtp)
1287c478bd9Sstevel@tonic-gate {
1297c478bd9Sstevel@tonic-gate 	uint_t		hash;
1307c478bd9Sstevel@tonic-gate 	kmutex_t	*t_hmtx;
1317c478bd9Sstevel@tonic-gate 
1327c478bd9Sstevel@tonic-gate 	hash = tdp->td_hash;
1337c478bd9Sstevel@tonic-gate 	t_hmtx = &t_hashmutex[T_MUTEX_INDEX(hash)];
1347c478bd9Sstevel@tonic-gate 	mutex_enter(t_hmtx);
1357c478bd9Sstevel@tonic-gate 	tdp->td_tmpnode = fromtp;
1367c478bd9Sstevel@tonic-gate 	mutex_exit(t_hmtx);
1377c478bd9Sstevel@tonic-gate }
1387c478bd9Sstevel@tonic-gate 
1397c478bd9Sstevel@tonic-gate static struct tdirent *
1407c478bd9Sstevel@tonic-gate tmpfs_hash_lookup(char *name, struct tmpnode *parent, uint_t hold,
1417c478bd9Sstevel@tonic-gate 	struct tmpnode **found)
1427c478bd9Sstevel@tonic-gate {
1437c478bd9Sstevel@tonic-gate 	struct tdirent	*l;
1447c478bd9Sstevel@tonic-gate 	uint_t		hash;
1457c478bd9Sstevel@tonic-gate 	kmutex_t	*t_hmtx;
1467c478bd9Sstevel@tonic-gate 	struct tmpnode	*tnp;
1477c478bd9Sstevel@tonic-gate 
1487c478bd9Sstevel@tonic-gate 	TMPFS_HASH(parent, name, hash);
1497c478bd9Sstevel@tonic-gate 	t_hmtx = &t_hashmutex[T_MUTEX_INDEX(hash)];
1507c478bd9Sstevel@tonic-gate 	mutex_enter(t_hmtx);
1517c478bd9Sstevel@tonic-gate 	l = t_hashtable[T_HASH_INDEX(hash)];
1527c478bd9Sstevel@tonic-gate 	while (l) {
1537c478bd9Sstevel@tonic-gate 		if ((l->td_hash == hash) &&
1547c478bd9Sstevel@tonic-gate 		    (l->td_parent == parent) &&
1557c478bd9Sstevel@tonic-gate 		    (strcmp(l->td_name, name) == 0)) {
1567c478bd9Sstevel@tonic-gate 			/*
1577c478bd9Sstevel@tonic-gate 			 * We need to make sure that the tmpnode that
1587c478bd9Sstevel@tonic-gate 			 * we put a hold on is the same one that we pass back.
1597c478bd9Sstevel@tonic-gate 			 * Hence, temporary variable tnp is necessary.
1607c478bd9Sstevel@tonic-gate 			 */
1617c478bd9Sstevel@tonic-gate 			tnp = l->td_tmpnode;
1627c478bd9Sstevel@tonic-gate 			if (hold) {
1637c478bd9Sstevel@tonic-gate 				ASSERT(tnp);
1647c478bd9Sstevel@tonic-gate 				tmpnode_hold(tnp);
1657c478bd9Sstevel@tonic-gate 			}
1667c478bd9Sstevel@tonic-gate 			if (found)
1677c478bd9Sstevel@tonic-gate 				*found = tnp;
1687c478bd9Sstevel@tonic-gate 			mutex_exit(t_hmtx);
1697c478bd9Sstevel@tonic-gate 			return (l);
1707c478bd9Sstevel@tonic-gate 		} else {
1717c478bd9Sstevel@tonic-gate 			l = l->td_link;
1727c478bd9Sstevel@tonic-gate 		}
1737c478bd9Sstevel@tonic-gate 	}
1747c478bd9Sstevel@tonic-gate 	mutex_exit(t_hmtx);
1757c478bd9Sstevel@tonic-gate 	return (NULL);
1767c478bd9Sstevel@tonic-gate }
1777c478bd9Sstevel@tonic-gate 
1787c478bd9Sstevel@tonic-gate /*
1797c478bd9Sstevel@tonic-gate  * Search directory 'parent' for entry 'name'.
1807c478bd9Sstevel@tonic-gate  *
1817c478bd9Sstevel@tonic-gate  * The calling thread can't hold the write version
1827c478bd9Sstevel@tonic-gate  * of the rwlock for the directory being searched
1837c478bd9Sstevel@tonic-gate  *
1847c478bd9Sstevel@tonic-gate  * 0 is returned on success and *foundtp points
1857c478bd9Sstevel@tonic-gate  * to the found tmpnode with its vnode held.
1867c478bd9Sstevel@tonic-gate  */
1877c478bd9Sstevel@tonic-gate int
1887c478bd9Sstevel@tonic-gate tdirlookup(
1897c478bd9Sstevel@tonic-gate 	struct tmpnode *parent,
1907c478bd9Sstevel@tonic-gate 	char *name,
1917c478bd9Sstevel@tonic-gate 	struct tmpnode **foundtp,
1927c478bd9Sstevel@tonic-gate 	struct cred *cred)
1937c478bd9Sstevel@tonic-gate {
1947c478bd9Sstevel@tonic-gate 	int error;
1957c478bd9Sstevel@tonic-gate 
1967c478bd9Sstevel@tonic-gate 	*foundtp = NULL;
1977c478bd9Sstevel@tonic-gate 	if (parent->tn_type != VDIR)
1987c478bd9Sstevel@tonic-gate 		return (ENOTDIR);
1997c478bd9Sstevel@tonic-gate 
2007c478bd9Sstevel@tonic-gate 	if ((error = tmp_taccess(parent, VEXEC, cred)))
2017c478bd9Sstevel@tonic-gate 		return (error);
2027c478bd9Sstevel@tonic-gate 
2037c478bd9Sstevel@tonic-gate 	if (*name == '\0') {
2047c478bd9Sstevel@tonic-gate 		tmpnode_hold(parent);
2057c478bd9Sstevel@tonic-gate 		*foundtp = parent;
2067c478bd9Sstevel@tonic-gate 		return (0);
2077c478bd9Sstevel@tonic-gate 	}
2087c478bd9Sstevel@tonic-gate 
2097c478bd9Sstevel@tonic-gate 	/*
2107c478bd9Sstevel@tonic-gate 	 * Search the directory for the matching name
2117c478bd9Sstevel@tonic-gate 	 * We need the lock protecting the tn_dir list
2127c478bd9Sstevel@tonic-gate 	 * so that it doesn't change out from underneath us.
2137c478bd9Sstevel@tonic-gate 	 * tmpfs_hash_lookup() will pass back the tmpnode
2147c478bd9Sstevel@tonic-gate 	 * with a hold on it.
2157c478bd9Sstevel@tonic-gate 	 */
2167c478bd9Sstevel@tonic-gate 
2177c478bd9Sstevel@tonic-gate 	if (tmpfs_hash_lookup(name, parent, 1, foundtp) != NULL) {
2187c478bd9Sstevel@tonic-gate 		ASSERT(*foundtp);
2197c478bd9Sstevel@tonic-gate 		return (0);
2207c478bd9Sstevel@tonic-gate 	}
2217c478bd9Sstevel@tonic-gate 
2227c478bd9Sstevel@tonic-gate 	return (ENOENT);
2237c478bd9Sstevel@tonic-gate }
2247c478bd9Sstevel@tonic-gate 
2257c478bd9Sstevel@tonic-gate /*
2267c478bd9Sstevel@tonic-gate  * Enter a directory entry for 'name' and 'tp' into directory 'dir'
2277c478bd9Sstevel@tonic-gate  *
2287c478bd9Sstevel@tonic-gate  * Returns 0 on success.
2297c478bd9Sstevel@tonic-gate  */
2307c478bd9Sstevel@tonic-gate int
2317c478bd9Sstevel@tonic-gate tdirenter(
2327c478bd9Sstevel@tonic-gate 	struct tmount	*tm,
2337c478bd9Sstevel@tonic-gate 	struct tmpnode	*dir,		/* target directory to make entry in */
2347c478bd9Sstevel@tonic-gate 	char		*name,		/* name of entry */
2357c478bd9Sstevel@tonic-gate 	enum de_op	op,		/* entry operation */
2367c478bd9Sstevel@tonic-gate 	struct tmpnode	*fromparent,	/* source directory if rename */
2377c478bd9Sstevel@tonic-gate 	struct tmpnode	*tp,		/* source tmpnode, if link/rename */
2387c478bd9Sstevel@tonic-gate 	struct vattr	*va,
2397c478bd9Sstevel@tonic-gate 	struct tmpnode	**tpp,		/* return tmpnode, if create/mkdir */
240*da6c28aaSamw 	struct cred	*cred,
241*da6c28aaSamw 	caller_context_t *ctp)
2427c478bd9Sstevel@tonic-gate {
2437c478bd9Sstevel@tonic-gate 	struct tdirent *tdp;
2447c478bd9Sstevel@tonic-gate 	struct tmpnode *found = NULL;
2457c478bd9Sstevel@tonic-gate 	int error = 0;
2467c478bd9Sstevel@tonic-gate 	char *s;
2477c478bd9Sstevel@tonic-gate 
2487c478bd9Sstevel@tonic-gate 	/*
2497c478bd9Sstevel@tonic-gate 	 * tn_rwlock is held to serialize direnter and dirdeletes
2507c478bd9Sstevel@tonic-gate 	 */
2517c478bd9Sstevel@tonic-gate 	ASSERT(RW_WRITE_HELD(&dir->tn_rwlock));
2527c478bd9Sstevel@tonic-gate 	ASSERT(dir->tn_type == VDIR);
2537c478bd9Sstevel@tonic-gate 
2547c478bd9Sstevel@tonic-gate 	/*
2557c478bd9Sstevel@tonic-gate 	 * Don't allow '/' characters in pathname component
2567c478bd9Sstevel@tonic-gate 	 * (thus in ufs_direnter()).
2577c478bd9Sstevel@tonic-gate 	 */
2587c478bd9Sstevel@tonic-gate 	for (s = name; *s; s++)
2597c478bd9Sstevel@tonic-gate 		if (*s == '/')
2607c478bd9Sstevel@tonic-gate 			return (EACCES);
2617c478bd9Sstevel@tonic-gate 
2627c478bd9Sstevel@tonic-gate 	if (name[0] == '\0')
2637c478bd9Sstevel@tonic-gate 		panic("tdirenter: NULL name");
2647c478bd9Sstevel@tonic-gate 
2657c478bd9Sstevel@tonic-gate 	/*
2667c478bd9Sstevel@tonic-gate 	 * For link and rename lock the source entry and check the link count
2677c478bd9Sstevel@tonic-gate 	 * to see if it has been removed while it was unlocked.
2687c478bd9Sstevel@tonic-gate 	 */
2697c478bd9Sstevel@tonic-gate 	if (op == DE_LINK || op == DE_RENAME) {
2707c478bd9Sstevel@tonic-gate 		if (tp != dir)
2717c478bd9Sstevel@tonic-gate 			rw_enter(&tp->tn_rwlock, RW_WRITER);
2727c478bd9Sstevel@tonic-gate 		mutex_enter(&tp->tn_tlock);
2737c478bd9Sstevel@tonic-gate 		if (tp->tn_nlink == 0) {
2747c478bd9Sstevel@tonic-gate 			mutex_exit(&tp->tn_tlock);
2757c478bd9Sstevel@tonic-gate 			if (tp != dir)
2767c478bd9Sstevel@tonic-gate 				rw_exit(&tp->tn_rwlock);
2777c478bd9Sstevel@tonic-gate 			return (ENOENT);
2787c478bd9Sstevel@tonic-gate 		}
2797c478bd9Sstevel@tonic-gate 
2807c478bd9Sstevel@tonic-gate 		if (tp->tn_nlink == MAXLINK) {
2817c478bd9Sstevel@tonic-gate 			mutex_exit(&tp->tn_tlock);
2827c478bd9Sstevel@tonic-gate 			if (tp != dir)
2837c478bd9Sstevel@tonic-gate 				rw_exit(&tp->tn_rwlock);
2847c478bd9Sstevel@tonic-gate 			return (EMLINK);
2857c478bd9Sstevel@tonic-gate 		}
2867c478bd9Sstevel@tonic-gate 		tp->tn_nlink++;
2877c478bd9Sstevel@tonic-gate 		gethrestime(&tp->tn_ctime);
2887c478bd9Sstevel@tonic-gate 		mutex_exit(&tp->tn_tlock);
2897c478bd9Sstevel@tonic-gate 		if (tp != dir)
2907c478bd9Sstevel@tonic-gate 			rw_exit(&tp->tn_rwlock);
2917c478bd9Sstevel@tonic-gate 	}
2927c478bd9Sstevel@tonic-gate 
2937c478bd9Sstevel@tonic-gate 	/*
2947c478bd9Sstevel@tonic-gate 	 * This might be a "dangling detached directory".
2957c478bd9Sstevel@tonic-gate 	 * it could have been removed, but a reference
2967c478bd9Sstevel@tonic-gate 	 * to it kept in u_cwd.  don't bother searching
2977c478bd9Sstevel@tonic-gate 	 * it, and with any luck the user will get tired
2987c478bd9Sstevel@tonic-gate 	 * of dealing with us and cd to some absolute
2997c478bd9Sstevel@tonic-gate 	 * pathway.  *sigh*, thus in ufs, too.
3007c478bd9Sstevel@tonic-gate 	 */
3017c478bd9Sstevel@tonic-gate 	if (dir->tn_nlink == 0) {
3027c478bd9Sstevel@tonic-gate 		error = ENOENT;
3037c478bd9Sstevel@tonic-gate 		goto out;
3047c478bd9Sstevel@tonic-gate 	}
3057c478bd9Sstevel@tonic-gate 
3067c478bd9Sstevel@tonic-gate 	/*
3077c478bd9Sstevel@tonic-gate 	 * If this is a rename of a directory and the parent is
3087c478bd9Sstevel@tonic-gate 	 * different (".." must be changed), then the source
3097c478bd9Sstevel@tonic-gate 	 * directory must not be in the directory hierarchy
3107c478bd9Sstevel@tonic-gate 	 * above the target, as this would orphan everything
3117c478bd9Sstevel@tonic-gate 	 * below the source directory.
3127c478bd9Sstevel@tonic-gate 	 */
3137c478bd9Sstevel@tonic-gate 	if (op == DE_RENAME) {
3147c478bd9Sstevel@tonic-gate 		if (tp == dir) {
3157c478bd9Sstevel@tonic-gate 			error = EINVAL;
3167c478bd9Sstevel@tonic-gate 			goto out;
3177c478bd9Sstevel@tonic-gate 		}
3187c478bd9Sstevel@tonic-gate 		if (tp->tn_type == VDIR) {
3197c478bd9Sstevel@tonic-gate 			if ((fromparent != dir) &&
3207c478bd9Sstevel@tonic-gate 			    (error = tdircheckpath(tp, dir, cred))) {
3217c478bd9Sstevel@tonic-gate 				goto out;
3227c478bd9Sstevel@tonic-gate 			}
3237c478bd9Sstevel@tonic-gate 		}
3247c478bd9Sstevel@tonic-gate 	}
3257c478bd9Sstevel@tonic-gate 
3267c478bd9Sstevel@tonic-gate 	/*
3277c478bd9Sstevel@tonic-gate 	 * Search for the entry.  Return "found" if it exists.
3287c478bd9Sstevel@tonic-gate 	 */
3297c478bd9Sstevel@tonic-gate 	tdp = tmpfs_hash_lookup(name, dir, 1, &found);
3307c478bd9Sstevel@tonic-gate 
3317c478bd9Sstevel@tonic-gate 	if (tdp) {
3327c478bd9Sstevel@tonic-gate 		ASSERT(found);
3337c478bd9Sstevel@tonic-gate 		switch (op) {
3347c478bd9Sstevel@tonic-gate 		case DE_CREATE:
3357c478bd9Sstevel@tonic-gate 		case DE_MKDIR:
3367c478bd9Sstevel@tonic-gate 			if (tpp) {
3377c478bd9Sstevel@tonic-gate 				*tpp = found;
3387c478bd9Sstevel@tonic-gate 				error = EEXIST;
3397c478bd9Sstevel@tonic-gate 			} else {
3407c478bd9Sstevel@tonic-gate 				tmpnode_rele(found);
3417c478bd9Sstevel@tonic-gate 			}
3427c478bd9Sstevel@tonic-gate 			break;
3437c478bd9Sstevel@tonic-gate 
3447c478bd9Sstevel@tonic-gate 		case DE_RENAME:
3457c478bd9Sstevel@tonic-gate 			error = tdirrename(fromparent, tp,
3467c478bd9Sstevel@tonic-gate 			    dir, name, found, tdp, cred);
3477c478bd9Sstevel@tonic-gate 			if (error == 0) {
348df2381bfSpraks 				if (found != NULL) {
349df2381bfSpraks 					vnevent_rename_dest(TNTOV(found),
350*da6c28aaSamw 					    TNTOV(dir), name, ctp);
3517c478bd9Sstevel@tonic-gate 				}
352df2381bfSpraks 			}
353df2381bfSpraks 
3547c478bd9Sstevel@tonic-gate 			tmpnode_rele(found);
3557c478bd9Sstevel@tonic-gate 			break;
3567c478bd9Sstevel@tonic-gate 
3577c478bd9Sstevel@tonic-gate 		case DE_LINK:
3587c478bd9Sstevel@tonic-gate 			/*
3597c478bd9Sstevel@tonic-gate 			 * Can't link to an existing file.
3607c478bd9Sstevel@tonic-gate 			 */
3617c478bd9Sstevel@tonic-gate 			error = EEXIST;
3627c478bd9Sstevel@tonic-gate 			tmpnode_rele(found);
3637c478bd9Sstevel@tonic-gate 			break;
3647c478bd9Sstevel@tonic-gate 		}
3657c478bd9Sstevel@tonic-gate 	} else {
3667c478bd9Sstevel@tonic-gate 
3677c478bd9Sstevel@tonic-gate 		/*
3687c478bd9Sstevel@tonic-gate 		 * The entry does not exist. Check write permission in
3697c478bd9Sstevel@tonic-gate 		 * directory to see if entry can be created.
3707c478bd9Sstevel@tonic-gate 		 */
3717c478bd9Sstevel@tonic-gate 		if (error = tmp_taccess(dir, VWRITE, cred))
3727c478bd9Sstevel@tonic-gate 			goto out;
3737c478bd9Sstevel@tonic-gate 		if (op == DE_CREATE || op == DE_MKDIR) {
3747c478bd9Sstevel@tonic-gate 			/*
3757c478bd9Sstevel@tonic-gate 			 * Make new tmpnode and directory entry as required.
3767c478bd9Sstevel@tonic-gate 			 */
3777c478bd9Sstevel@tonic-gate 			error = tdirmaketnode(dir, tm, va, op, &tp, cred);
3787c478bd9Sstevel@tonic-gate 			if (error)
3797c478bd9Sstevel@tonic-gate 				goto out;
3807c478bd9Sstevel@tonic-gate 		}
3817c478bd9Sstevel@tonic-gate 		if (error = tdiraddentry(dir, tp, name, op, fromparent)) {
3827c478bd9Sstevel@tonic-gate 			if (op == DE_CREATE || op == DE_MKDIR) {
3837c478bd9Sstevel@tonic-gate 				/*
3847c478bd9Sstevel@tonic-gate 				 * Unmake the inode we just made.
3857c478bd9Sstevel@tonic-gate 				 */
3867c478bd9Sstevel@tonic-gate 				rw_enter(&tp->tn_rwlock, RW_WRITER);
3877c478bd9Sstevel@tonic-gate 				if ((tp->tn_type) == VDIR) {
3887c478bd9Sstevel@tonic-gate 					ASSERT(tdp == NULL);
3897c478bd9Sstevel@tonic-gate 					/*
3907c478bd9Sstevel@tonic-gate 					 * cleanup allocs made by tdirinit()
3917c478bd9Sstevel@tonic-gate 					 */
3927c478bd9Sstevel@tonic-gate 					tdirtrunc(tp);
3937c478bd9Sstevel@tonic-gate 				}
3947c478bd9Sstevel@tonic-gate 				mutex_enter(&tp->tn_tlock);
3957c478bd9Sstevel@tonic-gate 				tp->tn_nlink = 0;
3967c478bd9Sstevel@tonic-gate 				mutex_exit(&tp->tn_tlock);
3977c478bd9Sstevel@tonic-gate 				gethrestime(&tp->tn_ctime);
3987c478bd9Sstevel@tonic-gate 				rw_exit(&tp->tn_rwlock);
3997c478bd9Sstevel@tonic-gate 				tmpnode_rele(tp);
4007c478bd9Sstevel@tonic-gate 				tp = NULL;
4017c478bd9Sstevel@tonic-gate 			}
4027c478bd9Sstevel@tonic-gate 		} else if (tpp) {
4037c478bd9Sstevel@tonic-gate 			*tpp = tp;
4047c478bd9Sstevel@tonic-gate 		} else if (op == DE_CREATE || op == DE_MKDIR) {
4057c478bd9Sstevel@tonic-gate 			tmpnode_rele(tp);
4067c478bd9Sstevel@tonic-gate 		}
4077c478bd9Sstevel@tonic-gate 	}
4087c478bd9Sstevel@tonic-gate 
4097c478bd9Sstevel@tonic-gate out:
4107c478bd9Sstevel@tonic-gate 	if (error && (op == DE_LINK || op == DE_RENAME)) {
4117c478bd9Sstevel@tonic-gate 		/*
4127c478bd9Sstevel@tonic-gate 		 * Undo bumped link count.
4137c478bd9Sstevel@tonic-gate 		 */
4147c478bd9Sstevel@tonic-gate 		DECR_COUNT(&tp->tn_nlink, &tp->tn_tlock);
4157c478bd9Sstevel@tonic-gate 		gethrestime(&tp->tn_ctime);
4167c478bd9Sstevel@tonic-gate 	}
4177c478bd9Sstevel@tonic-gate 	return (error);
4187c478bd9Sstevel@tonic-gate }
4197c478bd9Sstevel@tonic-gate 
4207c478bd9Sstevel@tonic-gate /*
4217c478bd9Sstevel@tonic-gate  * Delete entry tp of name "nm" from dir.
4227c478bd9Sstevel@tonic-gate  * Free dir entry space and decrement link count on tmpnode(s).
4237c478bd9Sstevel@tonic-gate  *
4247c478bd9Sstevel@tonic-gate  * Return 0 on success.
4257c478bd9Sstevel@tonic-gate  */
4267c478bd9Sstevel@tonic-gate int
4277c478bd9Sstevel@tonic-gate tdirdelete(
4287c478bd9Sstevel@tonic-gate 	struct tmpnode *dir,
4297c478bd9Sstevel@tonic-gate 	struct tmpnode *tp,
4307c478bd9Sstevel@tonic-gate 	char *nm,
4317c478bd9Sstevel@tonic-gate 	enum dr_op op,
4327c478bd9Sstevel@tonic-gate 	struct cred *cred)
4337c478bd9Sstevel@tonic-gate {
4347c478bd9Sstevel@tonic-gate 	struct tdirent *tpdp;
4357c478bd9Sstevel@tonic-gate 	int error;
4367c478bd9Sstevel@tonic-gate 	size_t namelen;
4377c478bd9Sstevel@tonic-gate 	struct tmpnode *tnp;
4387c478bd9Sstevel@tonic-gate 	timestruc_t now;
4397c478bd9Sstevel@tonic-gate 
4407c478bd9Sstevel@tonic-gate 	ASSERT(RW_WRITE_HELD(&dir->tn_rwlock));
4417c478bd9Sstevel@tonic-gate 	ASSERT(RW_WRITE_HELD(&tp->tn_rwlock));
4427c478bd9Sstevel@tonic-gate 	ASSERT(dir->tn_type == VDIR);
4437c478bd9Sstevel@tonic-gate 
4447c478bd9Sstevel@tonic-gate 	if (nm[0] == '\0')
4457c478bd9Sstevel@tonic-gate 		panic("tdirdelete: NULL name for %p", (void *)tp);
4467c478bd9Sstevel@tonic-gate 
4477c478bd9Sstevel@tonic-gate 	/*
4487c478bd9Sstevel@tonic-gate 	 * return error when removing . and ..
4497c478bd9Sstevel@tonic-gate 	 */
4507c478bd9Sstevel@tonic-gate 	if (nm[0] == '.') {
4517c478bd9Sstevel@tonic-gate 		if (nm[1] == '\0')
4527c478bd9Sstevel@tonic-gate 			return (EINVAL);
4537c478bd9Sstevel@tonic-gate 		if (nm[1] == '.' && nm[2] == '\0')
4547c478bd9Sstevel@tonic-gate 			return (EEXIST); /* thus in ufs */
4557c478bd9Sstevel@tonic-gate 	}
4567c478bd9Sstevel@tonic-gate 
4577c478bd9Sstevel@tonic-gate 	if (error = tmp_taccess(dir, VEXEC|VWRITE, cred))
4587c478bd9Sstevel@tonic-gate 		return (error);
4597c478bd9Sstevel@tonic-gate 
4607c478bd9Sstevel@tonic-gate 	/*
4617c478bd9Sstevel@tonic-gate 	 * If the parent directory is "sticky", then the user must
4627c478bd9Sstevel@tonic-gate 	 * own the parent directory or the file in it, or else must
4637c478bd9Sstevel@tonic-gate 	 * have permission to write the file.  Otherwise it may not
4647c478bd9Sstevel@tonic-gate 	 * be deleted (except by privileged users).
4657c478bd9Sstevel@tonic-gate 	 * Same as ufs_dirremove.
4667c478bd9Sstevel@tonic-gate 	 */
4677c478bd9Sstevel@tonic-gate 	if ((error = tmp_sticky_remove_access(dir, tp, cred)) != 0)
4687c478bd9Sstevel@tonic-gate 		return (error);
4697c478bd9Sstevel@tonic-gate 
4707c478bd9Sstevel@tonic-gate 	if (dir->tn_dir == NULL)
4717c478bd9Sstevel@tonic-gate 		return (ENOENT);
4727c478bd9Sstevel@tonic-gate 
4737c478bd9Sstevel@tonic-gate 	tpdp = tmpfs_hash_lookup(nm, dir, 0, &tnp);
4747c478bd9Sstevel@tonic-gate 	if (tpdp == NULL) {
4757c478bd9Sstevel@tonic-gate 		/*
4767c478bd9Sstevel@tonic-gate 		 * If it is gone, some other thread got here first!
4777c478bd9Sstevel@tonic-gate 		 * Return error ENOENT.
4787c478bd9Sstevel@tonic-gate 		 */
4797c478bd9Sstevel@tonic-gate 		return (ENOENT);
4807c478bd9Sstevel@tonic-gate 	}
4817c478bd9Sstevel@tonic-gate 
4827c478bd9Sstevel@tonic-gate 	/*
4837c478bd9Sstevel@tonic-gate 	 * If the tmpnode in the tdirent changed, we were probably
4847c478bd9Sstevel@tonic-gate 	 * the victim of a concurrent rename operation.  The original
4857c478bd9Sstevel@tonic-gate 	 * is gone, so return that status (same as UFS).
4867c478bd9Sstevel@tonic-gate 	 */
4877c478bd9Sstevel@tonic-gate 	if (tp != tnp)
4887c478bd9Sstevel@tonic-gate 		return (ENOENT);
4897c478bd9Sstevel@tonic-gate 
4907c478bd9Sstevel@tonic-gate 	tmpfs_hash_out(tpdp);
4917c478bd9Sstevel@tonic-gate 
4927c478bd9Sstevel@tonic-gate 	/*
4937c478bd9Sstevel@tonic-gate 	 * Take tpdp out of the directory list.
4947c478bd9Sstevel@tonic-gate 	 */
4957c478bd9Sstevel@tonic-gate 	ASSERT(tpdp->td_next != tpdp);
4967c478bd9Sstevel@tonic-gate 	ASSERT(tpdp->td_prev != tpdp);
4977c478bd9Sstevel@tonic-gate 	if (tpdp->td_prev) {
4987c478bd9Sstevel@tonic-gate 		tpdp->td_prev->td_next = tpdp->td_next;
4997c478bd9Sstevel@tonic-gate 	}
5007c478bd9Sstevel@tonic-gate 	if (tpdp->td_next) {
5017c478bd9Sstevel@tonic-gate 		tpdp->td_next->td_prev = tpdp->td_prev;
5027c478bd9Sstevel@tonic-gate 	}
5037c478bd9Sstevel@tonic-gate 
5047c478bd9Sstevel@tonic-gate 	/*
5057c478bd9Sstevel@tonic-gate 	 * If the roving slot pointer happens to match tpdp,
5067c478bd9Sstevel@tonic-gate 	 * point it at the previous dirent.
5077c478bd9Sstevel@tonic-gate 	 */
5087c478bd9Sstevel@tonic-gate 	if (dir->tn_dir->td_prev == tpdp) {
5097c478bd9Sstevel@tonic-gate 		dir->tn_dir->td_prev = tpdp->td_prev;
5107c478bd9Sstevel@tonic-gate 	}
5117c478bd9Sstevel@tonic-gate 	ASSERT(tpdp->td_next != tpdp);
5127c478bd9Sstevel@tonic-gate 	ASSERT(tpdp->td_prev != tpdp);
5137c478bd9Sstevel@tonic-gate 
5147c478bd9Sstevel@tonic-gate 	/*
5157c478bd9Sstevel@tonic-gate 	 * tpdp points to the correct directory entry
5167c478bd9Sstevel@tonic-gate 	 */
5177c478bd9Sstevel@tonic-gate 	namelen = strlen(tpdp->td_name) + 1;
5187c478bd9Sstevel@tonic-gate 
5197c478bd9Sstevel@tonic-gate 	tmp_memfree(tpdp, sizeof (struct tdirent) + namelen);
5207c478bd9Sstevel@tonic-gate 	dir->tn_size -= (sizeof (struct tdirent) + namelen);
5217c478bd9Sstevel@tonic-gate 	dir->tn_dirents--;
5227c478bd9Sstevel@tonic-gate 
5237c478bd9Sstevel@tonic-gate 	gethrestime(&now);
5247c478bd9Sstevel@tonic-gate 	dir->tn_mtime = now;
5257c478bd9Sstevel@tonic-gate 	dir->tn_ctime = now;
5267c478bd9Sstevel@tonic-gate 	tp->tn_ctime = now;
5277c478bd9Sstevel@tonic-gate 
5287c478bd9Sstevel@tonic-gate 	ASSERT(tp->tn_nlink > 0);
5297c478bd9Sstevel@tonic-gate 	DECR_COUNT(&tp->tn_nlink, &tp->tn_tlock);
5307c478bd9Sstevel@tonic-gate 	if (op == DR_RMDIR && tp->tn_type == VDIR) {
5317c478bd9Sstevel@tonic-gate 		tdirtrunc(tp);
5327c478bd9Sstevel@tonic-gate 		ASSERT(tp->tn_nlink == 0);
5337c478bd9Sstevel@tonic-gate 	}
5347c478bd9Sstevel@tonic-gate 	return (0);
5357c478bd9Sstevel@tonic-gate }
5367c478bd9Sstevel@tonic-gate 
5377c478bd9Sstevel@tonic-gate /*
5387c478bd9Sstevel@tonic-gate  * tdirinit is used internally to initialize a directory (dir)
5397c478bd9Sstevel@tonic-gate  * with '.' and '..' entries without checking permissions and locking
5407c478bd9Sstevel@tonic-gate  */
5417c478bd9Sstevel@tonic-gate void
5427c478bd9Sstevel@tonic-gate tdirinit(
5437c478bd9Sstevel@tonic-gate 	struct tmpnode *parent,		/* parent of directory to initialize */
5447c478bd9Sstevel@tonic-gate 	struct tmpnode *dir)		/* the new directory */
5457c478bd9Sstevel@tonic-gate {
5467c478bd9Sstevel@tonic-gate 	struct tdirent *dot, *dotdot;
5477c478bd9Sstevel@tonic-gate 	timestruc_t now;
5487c478bd9Sstevel@tonic-gate 
5497c478bd9Sstevel@tonic-gate 	ASSERT(RW_WRITE_HELD(&parent->tn_rwlock));
5507c478bd9Sstevel@tonic-gate 	ASSERT(dir->tn_type == VDIR);
5517c478bd9Sstevel@tonic-gate 
5527c478bd9Sstevel@tonic-gate 	dot = tmp_memalloc(sizeof (struct tdirent) + 2, TMP_MUSTHAVE);
5537c478bd9Sstevel@tonic-gate 	dotdot = tmp_memalloc(sizeof (struct tdirent) + 3, TMP_MUSTHAVE);
5547c478bd9Sstevel@tonic-gate 
5557c478bd9Sstevel@tonic-gate 	/*
5567c478bd9Sstevel@tonic-gate 	 * Initialize the entries
5577c478bd9Sstevel@tonic-gate 	 */
5587c478bd9Sstevel@tonic-gate 	dot->td_tmpnode = dir;
5597c478bd9Sstevel@tonic-gate 	dot->td_offset = 0;
5607c478bd9Sstevel@tonic-gate 	dot->td_name = (char *)dot + sizeof (struct tdirent);
5617c478bd9Sstevel@tonic-gate 	dot->td_name[0] = '.';
5627c478bd9Sstevel@tonic-gate 	dot->td_parent = dir;
5637c478bd9Sstevel@tonic-gate 	tmpfs_hash_in(dot);
5647c478bd9Sstevel@tonic-gate 
5657c478bd9Sstevel@tonic-gate 	dotdot->td_tmpnode = parent;
5667c478bd9Sstevel@tonic-gate 	dotdot->td_offset = 1;
5677c478bd9Sstevel@tonic-gate 	dotdot->td_name = (char *)dotdot + sizeof (struct tdirent);
5687c478bd9Sstevel@tonic-gate 	dotdot->td_name[0] = '.';
5697c478bd9Sstevel@tonic-gate 	dotdot->td_name[1] = '.';
5707c478bd9Sstevel@tonic-gate 	dotdot->td_parent = dir;
5717c478bd9Sstevel@tonic-gate 	tmpfs_hash_in(dotdot);
5727c478bd9Sstevel@tonic-gate 
5737c478bd9Sstevel@tonic-gate 	/*
5747c478bd9Sstevel@tonic-gate 	 * Initialize directory entry list.
5757c478bd9Sstevel@tonic-gate 	 */
5767c478bd9Sstevel@tonic-gate 	dot->td_next = dotdot;
5777c478bd9Sstevel@tonic-gate 	dot->td_prev = dotdot;	/* dot's td_prev holds roving slot pointer */
5787c478bd9Sstevel@tonic-gate 	dotdot->td_next = NULL;
5797c478bd9Sstevel@tonic-gate 	dotdot->td_prev = dot;
5807c478bd9Sstevel@tonic-gate 
5817c478bd9Sstevel@tonic-gate 	gethrestime(&now);
5827c478bd9Sstevel@tonic-gate 	dir->tn_mtime = now;
5837c478bd9Sstevel@tonic-gate 	dir->tn_ctime = now;
5847c478bd9Sstevel@tonic-gate 
5857c478bd9Sstevel@tonic-gate 	/*
5867c478bd9Sstevel@tonic-gate 	 * Link counts are special for the hidden attribute directory.
5877c478bd9Sstevel@tonic-gate 	 * The only explicit reference in the name space is "." and
5887c478bd9Sstevel@tonic-gate 	 * the reference through ".." is not counted on the parent
5897c478bd9Sstevel@tonic-gate 	 * file. The attrdir is created as a side effect to lookup,
5907c478bd9Sstevel@tonic-gate 	 * so don't change the ctime of the parent.
5917c478bd9Sstevel@tonic-gate 	 * Since tdirinit is called with both dir and parent being the
5927c478bd9Sstevel@tonic-gate 	 * same for the root vnode, we need to increment this before we set
5937c478bd9Sstevel@tonic-gate 	 * tn_nlink = 2 below.
5947c478bd9Sstevel@tonic-gate 	 */
5957c478bd9Sstevel@tonic-gate 	if (!(dir->tn_vnode->v_flag & V_XATTRDIR)) {
5967c478bd9Sstevel@tonic-gate 		INCR_COUNT(&parent->tn_nlink, &parent->tn_tlock);
5977c478bd9Sstevel@tonic-gate 		parent->tn_ctime = now;
5987c478bd9Sstevel@tonic-gate 	}
5997c478bd9Sstevel@tonic-gate 
6007c478bd9Sstevel@tonic-gate 	dir->tn_dir = dot;
6017c478bd9Sstevel@tonic-gate 	dir->tn_size = 2 * sizeof (struct tdirent) + 5;	/* dot and dotdot */
6027c478bd9Sstevel@tonic-gate 	dir->tn_dirents = 2;
6037c478bd9Sstevel@tonic-gate 	dir->tn_nlink = 2;
6047c478bd9Sstevel@tonic-gate }
6057c478bd9Sstevel@tonic-gate 
6067c478bd9Sstevel@tonic-gate 
6077c478bd9Sstevel@tonic-gate /*
6087c478bd9Sstevel@tonic-gate  * tdirtrunc is called to remove all directory entries under this directory.
6097c478bd9Sstevel@tonic-gate  */
6107c478bd9Sstevel@tonic-gate void
6117c478bd9Sstevel@tonic-gate tdirtrunc(struct tmpnode *dir)
6127c478bd9Sstevel@tonic-gate {
6137c478bd9Sstevel@tonic-gate 	struct tdirent *tdp;
6147c478bd9Sstevel@tonic-gate 	struct tmpnode *tp;
6157c478bd9Sstevel@tonic-gate 	size_t namelen;
6167c478bd9Sstevel@tonic-gate 	timestruc_t now;
6177c478bd9Sstevel@tonic-gate 	int isvattrdir, isdotdot, skip_decr;
6187c478bd9Sstevel@tonic-gate 
6197c478bd9Sstevel@tonic-gate 	ASSERT(RW_WRITE_HELD(&dir->tn_rwlock));
6207c478bd9Sstevel@tonic-gate 	ASSERT(dir->tn_type == VDIR);
6217c478bd9Sstevel@tonic-gate 
6227c478bd9Sstevel@tonic-gate 	isvattrdir = (dir->tn_vnode->v_flag & V_XATTRDIR) ? 1 : 0;
6237c478bd9Sstevel@tonic-gate 	for (tdp = dir->tn_dir; tdp; tdp = dir->tn_dir) {
6247c478bd9Sstevel@tonic-gate 		ASSERT(tdp->td_next != tdp);
6257c478bd9Sstevel@tonic-gate 		ASSERT(tdp->td_prev != tdp);
6267c478bd9Sstevel@tonic-gate 		ASSERT(tdp->td_tmpnode);
6277c478bd9Sstevel@tonic-gate 
6287c478bd9Sstevel@tonic-gate 		dir->tn_dir = tdp->td_next;
6297c478bd9Sstevel@tonic-gate 		namelen = strlen(tdp->td_name) + 1;
6307c478bd9Sstevel@tonic-gate 
6317c478bd9Sstevel@tonic-gate 		/*
6327c478bd9Sstevel@tonic-gate 		 * Adjust the link counts to account for this directory
6337c478bd9Sstevel@tonic-gate 		 * entry removal. Hidden attribute directories may
6347c478bd9Sstevel@tonic-gate 		 * not be empty as they may be truncated as a side-
6357c478bd9Sstevel@tonic-gate 		 * effect of removing the parent. We do hold/rele
6367c478bd9Sstevel@tonic-gate 		 * operations to free up these tmpnodes.
6377c478bd9Sstevel@tonic-gate 		 *
6387c478bd9Sstevel@tonic-gate 		 * Skip the link count adjustment for parents of
6397c478bd9Sstevel@tonic-gate 		 * attribute directories as those link counts
6407c478bd9Sstevel@tonic-gate 		 * do not include the ".." reference in the hidden
6417c478bd9Sstevel@tonic-gate 		 * directories.
6427c478bd9Sstevel@tonic-gate 		 */
6437c478bd9Sstevel@tonic-gate 		tp = tdp->td_tmpnode;
6447c478bd9Sstevel@tonic-gate 		isdotdot = (strcmp("..", tdp->td_name) == 0);
6457c478bd9Sstevel@tonic-gate 		skip_decr = (isvattrdir && isdotdot);
6467c478bd9Sstevel@tonic-gate 		if (!skip_decr) {
6477c478bd9Sstevel@tonic-gate 			ASSERT(tp->tn_nlink > 0);
6487c478bd9Sstevel@tonic-gate 			DECR_COUNT(&tp->tn_nlink, &tp->tn_tlock);
6497c478bd9Sstevel@tonic-gate 		}
6507c478bd9Sstevel@tonic-gate 
6517c478bd9Sstevel@tonic-gate 		tmpfs_hash_out(tdp);
6527c478bd9Sstevel@tonic-gate 
6537c478bd9Sstevel@tonic-gate 		tmp_memfree(tdp, sizeof (struct tdirent) + namelen);
6547c478bd9Sstevel@tonic-gate 		dir->tn_size -= (sizeof (struct tdirent) + namelen);
6557c478bd9Sstevel@tonic-gate 		dir->tn_dirents--;
6567c478bd9Sstevel@tonic-gate 	}
6577c478bd9Sstevel@tonic-gate 
6587c478bd9Sstevel@tonic-gate 	gethrestime(&now);
6597c478bd9Sstevel@tonic-gate 	dir->tn_mtime = now;
6607c478bd9Sstevel@tonic-gate 	dir->tn_ctime = now;
6617c478bd9Sstevel@tonic-gate 
6627c478bd9Sstevel@tonic-gate 	ASSERT(dir->tn_dir == NULL);
6637c478bd9Sstevel@tonic-gate 	ASSERT(dir->tn_size == 0);
6647c478bd9Sstevel@tonic-gate 	ASSERT(dir->tn_dirents == 0);
6657c478bd9Sstevel@tonic-gate }
6667c478bd9Sstevel@tonic-gate 
6677c478bd9Sstevel@tonic-gate /*
6687c478bd9Sstevel@tonic-gate  * Check if the source directory is in the path of the target directory.
6697c478bd9Sstevel@tonic-gate  * The target directory is locked by the caller.
6707c478bd9Sstevel@tonic-gate  *
6717c478bd9Sstevel@tonic-gate  * XXX - The source and target's should be different upon entry.
6727c478bd9Sstevel@tonic-gate  */
6737c478bd9Sstevel@tonic-gate static int
6747c478bd9Sstevel@tonic-gate tdircheckpath(
6757c478bd9Sstevel@tonic-gate 	struct tmpnode *fromtp,
6767c478bd9Sstevel@tonic-gate 	struct tmpnode	*toparent,
6777c478bd9Sstevel@tonic-gate 	struct cred	*cred)
6787c478bd9Sstevel@tonic-gate {
6797c478bd9Sstevel@tonic-gate 	int	error = 0;
6807c478bd9Sstevel@tonic-gate 	struct tmpnode *dir, *dotdot;
6817c478bd9Sstevel@tonic-gate 	struct tdirent *tdp;
6827c478bd9Sstevel@tonic-gate 
6837c478bd9Sstevel@tonic-gate 	ASSERT(RW_WRITE_HELD(&toparent->tn_rwlock));
6847c478bd9Sstevel@tonic-gate 
6857c478bd9Sstevel@tonic-gate 	tdp = tmpfs_hash_lookup("..", toparent, 1, &dotdot);
6867c478bd9Sstevel@tonic-gate 	if (tdp == NULL)
6877c478bd9Sstevel@tonic-gate 		return (ENOENT);
6887c478bd9Sstevel@tonic-gate 
6897c478bd9Sstevel@tonic-gate 	ASSERT(dotdot);
6907c478bd9Sstevel@tonic-gate 
6917c478bd9Sstevel@tonic-gate 	if (dotdot == toparent) {
6927c478bd9Sstevel@tonic-gate 		/* root of fs.  search trivially satisfied. */
6937c478bd9Sstevel@tonic-gate 		tmpnode_rele(dotdot);
6947c478bd9Sstevel@tonic-gate 		return (0);
6957c478bd9Sstevel@tonic-gate 	}
6967c478bd9Sstevel@tonic-gate 	for (;;) {
6977c478bd9Sstevel@tonic-gate 		/*
6987c478bd9Sstevel@tonic-gate 		 * Return error for cases like "mv c c/d",
6997c478bd9Sstevel@tonic-gate 		 * "mv c c/d/e" and so on.
7007c478bd9Sstevel@tonic-gate 		 */
7017c478bd9Sstevel@tonic-gate 		if (dotdot == fromtp) {
7027c478bd9Sstevel@tonic-gate 			tmpnode_rele(dotdot);
7037c478bd9Sstevel@tonic-gate 			error = EINVAL;
7047c478bd9Sstevel@tonic-gate 			break;
7057c478bd9Sstevel@tonic-gate 		}
7067c478bd9Sstevel@tonic-gate 		dir = dotdot;
7077c478bd9Sstevel@tonic-gate 		error = tdirlookup(dir, "..", &dotdot, cred);
7087c478bd9Sstevel@tonic-gate 		if (error) {
7097c478bd9Sstevel@tonic-gate 			tmpnode_rele(dir);
7107c478bd9Sstevel@tonic-gate 			break;
7117c478bd9Sstevel@tonic-gate 		}
7127c478bd9Sstevel@tonic-gate 		/*
7137c478bd9Sstevel@tonic-gate 		 * We're okay if we traverse the directory tree up to
7147c478bd9Sstevel@tonic-gate 		 * the root directory and don't run into the
7157c478bd9Sstevel@tonic-gate 		 * parent directory.
7167c478bd9Sstevel@tonic-gate 		 */
7177c478bd9Sstevel@tonic-gate 		if (dir == dotdot) {
7187c478bd9Sstevel@tonic-gate 			tmpnode_rele(dir);
7197c478bd9Sstevel@tonic-gate 			tmpnode_rele(dotdot);
7207c478bd9Sstevel@tonic-gate 			break;
7217c478bd9Sstevel@tonic-gate 		}
7227c478bd9Sstevel@tonic-gate 		tmpnode_rele(dir);
7237c478bd9Sstevel@tonic-gate 	}
7247c478bd9Sstevel@tonic-gate 	return (error);
7257c478bd9Sstevel@tonic-gate }
7267c478bd9Sstevel@tonic-gate 
7277c478bd9Sstevel@tonic-gate static int
7287c478bd9Sstevel@tonic-gate tdirrename(
7297c478bd9Sstevel@tonic-gate 	struct tmpnode *fromparent,	/* parent directory of source */
7307c478bd9Sstevel@tonic-gate 	struct tmpnode *fromtp,		/* source tmpnode */
7317c478bd9Sstevel@tonic-gate 	struct tmpnode *toparent,	/* parent directory of target */
7327c478bd9Sstevel@tonic-gate 	char *nm,			/* entry we are trying to change */
7337c478bd9Sstevel@tonic-gate 	struct tmpnode *to,		/* target tmpnode */
7347c478bd9Sstevel@tonic-gate 	struct tdirent *where,		/* target tmpnode directory entry */
7357c478bd9Sstevel@tonic-gate 	struct cred *cred)		/* credentials */
7367c478bd9Sstevel@tonic-gate {
7377c478bd9Sstevel@tonic-gate 	int error = 0;
7387c478bd9Sstevel@tonic-gate 	int doingdirectory;
7397c478bd9Sstevel@tonic-gate 	timestruc_t now;
7407c478bd9Sstevel@tonic-gate 
7417c478bd9Sstevel@tonic-gate #if defined(lint)
7427c478bd9Sstevel@tonic-gate 	nm = nm;
7437c478bd9Sstevel@tonic-gate #endif
7447c478bd9Sstevel@tonic-gate 	ASSERT(RW_WRITE_HELD(&toparent->tn_rwlock));
7457c478bd9Sstevel@tonic-gate 
7467c478bd9Sstevel@tonic-gate 	/*
7477c478bd9Sstevel@tonic-gate 	 * Short circuit rename of something to itself.
7487c478bd9Sstevel@tonic-gate 	 */
7497c478bd9Sstevel@tonic-gate 	if (fromtp == to)
7507c478bd9Sstevel@tonic-gate 		return (ESAME);		/* special KLUDGE error code */
7517c478bd9Sstevel@tonic-gate 
7527c478bd9Sstevel@tonic-gate 	rw_enter(&fromtp->tn_rwlock, RW_READER);
7537c478bd9Sstevel@tonic-gate 	rw_enter(&to->tn_rwlock, RW_READER);
7547c478bd9Sstevel@tonic-gate 
7557c478bd9Sstevel@tonic-gate 	/*
7567c478bd9Sstevel@tonic-gate 	 * Check that everything is on the same filesystem.
7577c478bd9Sstevel@tonic-gate 	 */
7587c478bd9Sstevel@tonic-gate 	if (to->tn_vnode->v_vfsp != toparent->tn_vnode->v_vfsp ||
7597c478bd9Sstevel@tonic-gate 	    to->tn_vnode->v_vfsp != fromtp->tn_vnode->v_vfsp) {
7607c478bd9Sstevel@tonic-gate 		error = EXDEV;
7617c478bd9Sstevel@tonic-gate 		goto out;
7627c478bd9Sstevel@tonic-gate 	}
7637c478bd9Sstevel@tonic-gate 
7647c478bd9Sstevel@tonic-gate 	/*
7657c478bd9Sstevel@tonic-gate 	 * Must have write permission to rewrite target entry.
7667c478bd9Sstevel@tonic-gate 	 * Check for stickyness.
7677c478bd9Sstevel@tonic-gate 	 */
7687c478bd9Sstevel@tonic-gate 	if ((error = tmp_taccess(toparent, VWRITE, cred)) != 0 ||
7697c478bd9Sstevel@tonic-gate 	    (error = tmp_sticky_remove_access(toparent, to, cred)) != 0)
7707c478bd9Sstevel@tonic-gate 		goto out;
7717c478bd9Sstevel@tonic-gate 
7727c478bd9Sstevel@tonic-gate 	/*
7737c478bd9Sstevel@tonic-gate 	 * Ensure source and target are compatible (both directories
7747c478bd9Sstevel@tonic-gate 	 * or both not directories).  If target is a directory it must
7757c478bd9Sstevel@tonic-gate 	 * be empty and have no links to it; in addition it must not
7767c478bd9Sstevel@tonic-gate 	 * be a mount point, and both the source and target must be
7777c478bd9Sstevel@tonic-gate 	 * writable.
7787c478bd9Sstevel@tonic-gate 	 */
7797c478bd9Sstevel@tonic-gate 	doingdirectory = (fromtp->tn_type == VDIR);
7807c478bd9Sstevel@tonic-gate 	if (to->tn_type == VDIR) {
7817c478bd9Sstevel@tonic-gate 		if (!doingdirectory) {
7827c478bd9Sstevel@tonic-gate 			error = EISDIR;
7837c478bd9Sstevel@tonic-gate 			goto out;
7847c478bd9Sstevel@tonic-gate 		}
7857c478bd9Sstevel@tonic-gate 		/*
786d5dbd18dSbatschul 		 * vn_vfswlock will prevent mounts from using the directory
7877c478bd9Sstevel@tonic-gate 		 * until we are done.
7887c478bd9Sstevel@tonic-gate 		 */
789d5dbd18dSbatschul 		if (vn_vfswlock(TNTOV(to))) {
7907c478bd9Sstevel@tonic-gate 			error = EBUSY;
7917c478bd9Sstevel@tonic-gate 			goto out;
7927c478bd9Sstevel@tonic-gate 		}
7937c478bd9Sstevel@tonic-gate 		if (vn_mountedvfs(TNTOV(to)) != NULL) {
7947c478bd9Sstevel@tonic-gate 			vn_vfsunlock(TNTOV(to));
7957c478bd9Sstevel@tonic-gate 			error = EBUSY;
7967c478bd9Sstevel@tonic-gate 			goto out;
7977c478bd9Sstevel@tonic-gate 		}
7987c478bd9Sstevel@tonic-gate 
7997c478bd9Sstevel@tonic-gate 		mutex_enter(&to->tn_tlock);
8007c478bd9Sstevel@tonic-gate 		if (to->tn_dirents > 2 || to->tn_nlink > 2) {
8017c478bd9Sstevel@tonic-gate 			mutex_exit(&to->tn_tlock);
8027c478bd9Sstevel@tonic-gate 			vn_vfsunlock(TNTOV(to));
8037c478bd9Sstevel@tonic-gate 			error = EEXIST; /* SIGH should be ENOTEMPTY */
8047c478bd9Sstevel@tonic-gate 			/*
8057c478bd9Sstevel@tonic-gate 			 * Update atime because checking tn_dirents is
8067c478bd9Sstevel@tonic-gate 			 * logically equivalent to reading the directory
8077c478bd9Sstevel@tonic-gate 			 */
8087c478bd9Sstevel@tonic-gate 			gethrestime(&to->tn_atime);
8097c478bd9Sstevel@tonic-gate 			goto out;
8107c478bd9Sstevel@tonic-gate 		}
8117c478bd9Sstevel@tonic-gate 		mutex_exit(&to->tn_tlock);
8127c478bd9Sstevel@tonic-gate 	} else if (doingdirectory) {
8137c478bd9Sstevel@tonic-gate 		error = ENOTDIR;
8147c478bd9Sstevel@tonic-gate 		goto out;
8157c478bd9Sstevel@tonic-gate 	}
8167c478bd9Sstevel@tonic-gate 
8177c478bd9Sstevel@tonic-gate 	tmpfs_hash_change(where, fromtp);
8187c478bd9Sstevel@tonic-gate 	gethrestime(&now);
8197c478bd9Sstevel@tonic-gate 	toparent->tn_mtime = now;
8207c478bd9Sstevel@tonic-gate 	toparent->tn_ctime = now;
8217c478bd9Sstevel@tonic-gate 
8227c478bd9Sstevel@tonic-gate 	/*
8237c478bd9Sstevel@tonic-gate 	 * Upgrade to write lock on "to" (i.e., the target tmpnode).
8247c478bd9Sstevel@tonic-gate 	 */
8257c478bd9Sstevel@tonic-gate 	rw_exit(&to->tn_rwlock);
8267c478bd9Sstevel@tonic-gate 	rw_enter(&to->tn_rwlock, RW_WRITER);
8277c478bd9Sstevel@tonic-gate 
8287c478bd9Sstevel@tonic-gate 	/*
8297c478bd9Sstevel@tonic-gate 	 * Decrement the link count of the target tmpnode.
8307c478bd9Sstevel@tonic-gate 	 */
8317c478bd9Sstevel@tonic-gate 	DECR_COUNT(&to->tn_nlink, &to->tn_tlock);
8327c478bd9Sstevel@tonic-gate 	to->tn_ctime = now;
8337c478bd9Sstevel@tonic-gate 
8347c478bd9Sstevel@tonic-gate 	if (doingdirectory) {
8357c478bd9Sstevel@tonic-gate 		/*
8367c478bd9Sstevel@tonic-gate 		 * The entry for "to" no longer exists so release the vfslock.
8377c478bd9Sstevel@tonic-gate 		 */
8387c478bd9Sstevel@tonic-gate 		vn_vfsunlock(TNTOV(to));
8397c478bd9Sstevel@tonic-gate 
8407c478bd9Sstevel@tonic-gate 		/*
8417c478bd9Sstevel@tonic-gate 		 * Decrement the target link count and delete all entires.
8427c478bd9Sstevel@tonic-gate 		 */
8437c478bd9Sstevel@tonic-gate 		tdirtrunc(to);
8447c478bd9Sstevel@tonic-gate 		ASSERT(to->tn_nlink == 0);
8457c478bd9Sstevel@tonic-gate 
8467c478bd9Sstevel@tonic-gate 		/*
8477c478bd9Sstevel@tonic-gate 		 * Renaming a directory with the parent different
8487c478bd9Sstevel@tonic-gate 		 * requires that ".." be rewritten.  The window is
8497c478bd9Sstevel@tonic-gate 		 * still there for ".." to be inconsistent, but this
8507c478bd9Sstevel@tonic-gate 		 * is unavoidable, and a lot shorter than when it was
8517c478bd9Sstevel@tonic-gate 		 * done in a user process.
8527c478bd9Sstevel@tonic-gate 		 */
8537c478bd9Sstevel@tonic-gate 		if (fromparent != toparent)
8547c478bd9Sstevel@tonic-gate 			tdirfixdotdot(fromtp, fromparent, toparent);
8557c478bd9Sstevel@tonic-gate 	}
8567c478bd9Sstevel@tonic-gate out:
8577c478bd9Sstevel@tonic-gate 	rw_exit(&to->tn_rwlock);
8587c478bd9Sstevel@tonic-gate 	rw_exit(&fromtp->tn_rwlock);
8597c478bd9Sstevel@tonic-gate 	return (error);
8607c478bd9Sstevel@tonic-gate }
8617c478bd9Sstevel@tonic-gate 
8627c478bd9Sstevel@tonic-gate static void
8637c478bd9Sstevel@tonic-gate tdirfixdotdot(
8647c478bd9Sstevel@tonic-gate 	struct tmpnode	*fromtp,	/* child directory */
8657c478bd9Sstevel@tonic-gate 	struct tmpnode	*fromparent,	/* old parent directory */
8667c478bd9Sstevel@tonic-gate 	struct tmpnode	*toparent)	/* new parent directory */
8677c478bd9Sstevel@tonic-gate {
8687c478bd9Sstevel@tonic-gate 	struct tdirent	*dotdot;
8697c478bd9Sstevel@tonic-gate 
8707c478bd9Sstevel@tonic-gate 	ASSERT(RW_LOCK_HELD(&toparent->tn_rwlock));
8717c478bd9Sstevel@tonic-gate 
8727c478bd9Sstevel@tonic-gate 	/*
8737c478bd9Sstevel@tonic-gate 	 * Increment the link count in the new parent tmpnode
8747c478bd9Sstevel@tonic-gate 	 */
8757c478bd9Sstevel@tonic-gate 	INCR_COUNT(&toparent->tn_nlink, &toparent->tn_tlock);
8767c478bd9Sstevel@tonic-gate 	gethrestime(&toparent->tn_ctime);
8777c478bd9Sstevel@tonic-gate 
8787c478bd9Sstevel@tonic-gate 	dotdot = tmpfs_hash_lookup("..", fromtp, 0, NULL);
8797c478bd9Sstevel@tonic-gate 
8807c478bd9Sstevel@tonic-gate 	ASSERT(dotdot->td_tmpnode == fromparent);
8817c478bd9Sstevel@tonic-gate 	dotdot->td_tmpnode = toparent;
8827c478bd9Sstevel@tonic-gate 
8837c478bd9Sstevel@tonic-gate 	/*
8847c478bd9Sstevel@tonic-gate 	 * Decrement the link count of the old parent tmpnode.
8857c478bd9Sstevel@tonic-gate 	 * If fromparent is NULL, then this is a new directory link;
8867c478bd9Sstevel@tonic-gate 	 * it has no parent, so we need not do anything.
8877c478bd9Sstevel@tonic-gate 	 */
8887c478bd9Sstevel@tonic-gate 	if (fromparent != NULL) {
8897c478bd9Sstevel@tonic-gate 		mutex_enter(&fromparent->tn_tlock);
8907c478bd9Sstevel@tonic-gate 		if (fromparent->tn_nlink != 0) {
8917c478bd9Sstevel@tonic-gate 			fromparent->tn_nlink--;
8927c478bd9Sstevel@tonic-gate 			gethrestime(&fromparent->tn_ctime);
8937c478bd9Sstevel@tonic-gate 		}
8947c478bd9Sstevel@tonic-gate 		mutex_exit(&fromparent->tn_tlock);
8957c478bd9Sstevel@tonic-gate 	}
8967c478bd9Sstevel@tonic-gate }
8977c478bd9Sstevel@tonic-gate 
8987c478bd9Sstevel@tonic-gate static int
8997c478bd9Sstevel@tonic-gate tdiraddentry(
9007c478bd9Sstevel@tonic-gate 	struct tmpnode	*dir,	/* target directory to make entry in */
9017c478bd9Sstevel@tonic-gate 	struct tmpnode	*tp,	/* new tmpnode */
9027c478bd9Sstevel@tonic-gate 	char		*name,
9037c478bd9Sstevel@tonic-gate 	enum de_op	op,
9047c478bd9Sstevel@tonic-gate 	struct tmpnode	*fromtp)
9057c478bd9Sstevel@tonic-gate {
9067c478bd9Sstevel@tonic-gate 	struct tdirent *tdp, *tpdp;
9077c478bd9Sstevel@tonic-gate 	size_t		namelen, alloc_size;
9087c478bd9Sstevel@tonic-gate 	timestruc_t	now;
9097c478bd9Sstevel@tonic-gate 
9107c478bd9Sstevel@tonic-gate 	/*
9117c478bd9Sstevel@tonic-gate 	 * Make sure the parent directory wasn't removed from
9127c478bd9Sstevel@tonic-gate 	 * underneath the caller.
9137c478bd9Sstevel@tonic-gate 	 */
9147c478bd9Sstevel@tonic-gate 	if (dir->tn_dir == NULL)
9157c478bd9Sstevel@tonic-gate 		return (ENOENT);
9167c478bd9Sstevel@tonic-gate 
9177c478bd9Sstevel@tonic-gate 	/*
9187c478bd9Sstevel@tonic-gate 	 * Check that everything is on the same filesystem.
9197c478bd9Sstevel@tonic-gate 	 */
9207c478bd9Sstevel@tonic-gate 	if (tp->tn_vnode->v_vfsp != dir->tn_vnode->v_vfsp)
9217c478bd9Sstevel@tonic-gate 		return (EXDEV);
9227c478bd9Sstevel@tonic-gate 
9237c478bd9Sstevel@tonic-gate 	/*
9247c478bd9Sstevel@tonic-gate 	 * Allocate and initialize directory entry
9257c478bd9Sstevel@tonic-gate 	 */
9267c478bd9Sstevel@tonic-gate 	namelen = strlen(name) + 1;
9277c478bd9Sstevel@tonic-gate 	alloc_size = namelen + sizeof (struct tdirent);
9287c478bd9Sstevel@tonic-gate 	tdp = tmp_memalloc(alloc_size, 0);
9297c478bd9Sstevel@tonic-gate 	if (tdp == NULL)
9307c478bd9Sstevel@tonic-gate 		return (ENOSPC);
9317c478bd9Sstevel@tonic-gate 
9327c478bd9Sstevel@tonic-gate 	if ((op == DE_RENAME) && (tp->tn_type == VDIR))
9337c478bd9Sstevel@tonic-gate 		tdirfixdotdot(tp, fromtp, dir);
9347c478bd9Sstevel@tonic-gate 
9357c478bd9Sstevel@tonic-gate 	dir->tn_size += alloc_size;
9367c478bd9Sstevel@tonic-gate 	dir->tn_dirents++;
9377c478bd9Sstevel@tonic-gate 	tdp->td_tmpnode = tp;
9387c478bd9Sstevel@tonic-gate 	tdp->td_parent = dir;
9397c478bd9Sstevel@tonic-gate 
9407c478bd9Sstevel@tonic-gate 	/*
9417c478bd9Sstevel@tonic-gate 	 * The directory entry and its name were allocated sequentially.
9427c478bd9Sstevel@tonic-gate 	 */
9437c478bd9Sstevel@tonic-gate 	tdp->td_name = (char *)tdp + sizeof (struct tdirent);
9447c478bd9Sstevel@tonic-gate 	(void) strcpy(tdp->td_name, name);
9457c478bd9Sstevel@tonic-gate 
9467c478bd9Sstevel@tonic-gate 	tmpfs_hash_in(tdp);
9477c478bd9Sstevel@tonic-gate 
9487c478bd9Sstevel@tonic-gate 	/*
9497c478bd9Sstevel@tonic-gate 	 * Some utilities expect the size of a directory to remain
9507c478bd9Sstevel@tonic-gate 	 * somewhat static.  For example, a routine which unlinks
9517c478bd9Sstevel@tonic-gate 	 * files between calls to readdir(); the size of the
9527c478bd9Sstevel@tonic-gate 	 * directory changes from underneath it and so the real
9537c478bd9Sstevel@tonic-gate 	 * directory offset in bytes is invalid.  To circumvent
9547c478bd9Sstevel@tonic-gate 	 * this problem, we initialize a directory entry with an
9557c478bd9Sstevel@tonic-gate 	 * phony offset, and use this offset to determine end of
9567c478bd9Sstevel@tonic-gate 	 * file in tmp_readdir.
9577c478bd9Sstevel@tonic-gate 	 */
9587c478bd9Sstevel@tonic-gate 	tpdp = dir->tn_dir->td_prev;
9597c478bd9Sstevel@tonic-gate 	/*
9607c478bd9Sstevel@tonic-gate 	 * Install at first empty "slot" in directory list.
9617c478bd9Sstevel@tonic-gate 	 */
9627c478bd9Sstevel@tonic-gate 	while (tpdp->td_next != NULL && (tpdp->td_next->td_offset -
9637c478bd9Sstevel@tonic-gate 	    tpdp->td_offset) <= 1) {
9647c478bd9Sstevel@tonic-gate 		ASSERT(tpdp->td_next != tpdp);
9657c478bd9Sstevel@tonic-gate 		ASSERT(tpdp->td_prev != tpdp);
9667c478bd9Sstevel@tonic-gate 		ASSERT(tpdp->td_next->td_offset > tpdp->td_offset);
9677c478bd9Sstevel@tonic-gate 		tpdp = tpdp->td_next;
9687c478bd9Sstevel@tonic-gate 	}
9697c478bd9Sstevel@tonic-gate 	tdp->td_offset = tpdp->td_offset + 1;
9707c478bd9Sstevel@tonic-gate 
9717c478bd9Sstevel@tonic-gate 	/*
9727c478bd9Sstevel@tonic-gate 	 * If we're at the end of the dirent list and the offset (which
9737c478bd9Sstevel@tonic-gate 	 * is necessarily the largest offset in this directory) is more
9747c478bd9Sstevel@tonic-gate 	 * than twice the number of dirents, that means the directory is
9757c478bd9Sstevel@tonic-gate 	 * 50% holes.  At this point we reset the slot pointer back to
9767c478bd9Sstevel@tonic-gate 	 * the beginning of the directory so we start using the holes.
9777c478bd9Sstevel@tonic-gate 	 * The idea is that if there are N dirents, there must also be
9787c478bd9Sstevel@tonic-gate 	 * N holes, so we can satisfy the next N creates by walking at
9797c478bd9Sstevel@tonic-gate 	 * most 2N entries; thus the average cost of a create is constant.
9807c478bd9Sstevel@tonic-gate 	 * Note that we use the first dirent's td_prev as the roving
9817c478bd9Sstevel@tonic-gate 	 * slot pointer; it's ugly, but it saves a word in every dirent.
9827c478bd9Sstevel@tonic-gate 	 */
9837c478bd9Sstevel@tonic-gate 	if (tpdp->td_next == NULL && tpdp->td_offset > 2 * dir->tn_dirents)
9847c478bd9Sstevel@tonic-gate 		dir->tn_dir->td_prev = dir->tn_dir->td_next;
9857c478bd9Sstevel@tonic-gate 	else
9867c478bd9Sstevel@tonic-gate 		dir->tn_dir->td_prev = tdp;
9877c478bd9Sstevel@tonic-gate 
9887c478bd9Sstevel@tonic-gate 	ASSERT(tpdp->td_next != tpdp);
9897c478bd9Sstevel@tonic-gate 	ASSERT(tpdp->td_prev != tpdp);
9907c478bd9Sstevel@tonic-gate 
9917c478bd9Sstevel@tonic-gate 	tdp->td_next = tpdp->td_next;
9927c478bd9Sstevel@tonic-gate 	if (tdp->td_next) {
9937c478bd9Sstevel@tonic-gate 		tdp->td_next->td_prev = tdp;
9947c478bd9Sstevel@tonic-gate 	}
9957c478bd9Sstevel@tonic-gate 	tdp->td_prev = tpdp;
9967c478bd9Sstevel@tonic-gate 	tpdp->td_next = tdp;
9977c478bd9Sstevel@tonic-gate 
9987c478bd9Sstevel@tonic-gate 	ASSERT(tdp->td_next != tdp);
9997c478bd9Sstevel@tonic-gate 	ASSERT(tdp->td_prev != tdp);
10007c478bd9Sstevel@tonic-gate 	ASSERT(tpdp->td_next != tpdp);
10017c478bd9Sstevel@tonic-gate 	ASSERT(tpdp->td_prev != tpdp);
10027c478bd9Sstevel@tonic-gate 
10037c478bd9Sstevel@tonic-gate 	gethrestime(&now);
10047c478bd9Sstevel@tonic-gate 	dir->tn_mtime = now;
10057c478bd9Sstevel@tonic-gate 	dir->tn_ctime = now;
10067c478bd9Sstevel@tonic-gate 
10077c478bd9Sstevel@tonic-gate 	return (0);
10087c478bd9Sstevel@tonic-gate }
10097c478bd9Sstevel@tonic-gate 
10107c478bd9Sstevel@tonic-gate static int
10117c478bd9Sstevel@tonic-gate tdirmaketnode(
10127c478bd9Sstevel@tonic-gate 	struct tmpnode *dir,
10137c478bd9Sstevel@tonic-gate 	struct tmount	*tm,
10147c478bd9Sstevel@tonic-gate 	struct vattr	*va,
10157c478bd9Sstevel@tonic-gate 	enum	de_op	op,
10167c478bd9Sstevel@tonic-gate 	struct tmpnode **newnode,
10177c478bd9Sstevel@tonic-gate 	struct cred	*cred)
10187c478bd9Sstevel@tonic-gate {
10197c478bd9Sstevel@tonic-gate 	struct tmpnode *tp;
10207c478bd9Sstevel@tonic-gate 	enum vtype	type;
10217c478bd9Sstevel@tonic-gate 
10227c478bd9Sstevel@tonic-gate 	ASSERT(va != NULL);
10237c478bd9Sstevel@tonic-gate 	ASSERT(op == DE_CREATE || op == DE_MKDIR);
10247c478bd9Sstevel@tonic-gate 	if (((va->va_mask & AT_ATIME) && TIMESPEC_OVERFLOW(&va->va_atime)) ||
10257c478bd9Sstevel@tonic-gate 	    ((va->va_mask & AT_MTIME) && TIMESPEC_OVERFLOW(&va->va_mtime)))
10267c478bd9Sstevel@tonic-gate 		return (EOVERFLOW);
10277c478bd9Sstevel@tonic-gate 	type = va->va_type;
10287c478bd9Sstevel@tonic-gate 	tp = tmp_memalloc(sizeof (struct tmpnode), TMP_MUSTHAVE);
10297c478bd9Sstevel@tonic-gate 	tmpnode_init(tm, tp, va, cred);
10307c478bd9Sstevel@tonic-gate 
10317c478bd9Sstevel@tonic-gate 	/* setup normal file/dir's extended attribute directory */
10327c478bd9Sstevel@tonic-gate 	if (dir->tn_flags & ISXATTR) {
10337c478bd9Sstevel@tonic-gate 		/* parent dir is , mark file as xattr */
10347c478bd9Sstevel@tonic-gate 		tp->tn_flags |= ISXATTR;
10357c478bd9Sstevel@tonic-gate 	}
10367c478bd9Sstevel@tonic-gate 
10377c478bd9Sstevel@tonic-gate 
10387c478bd9Sstevel@tonic-gate 	if (type == VBLK || type == VCHR) {
10397c478bd9Sstevel@tonic-gate 		tp->tn_vnode->v_rdev = tp->tn_rdev = va->va_rdev;
10407c478bd9Sstevel@tonic-gate 	} else {
10417c478bd9Sstevel@tonic-gate 		tp->tn_vnode->v_rdev = tp->tn_rdev = NODEV;
10427c478bd9Sstevel@tonic-gate 	}
10437c478bd9Sstevel@tonic-gate 	tp->tn_vnode->v_type = type;
10447c478bd9Sstevel@tonic-gate 	tp->tn_uid = crgetuid(cred);
10457c478bd9Sstevel@tonic-gate 
10467c478bd9Sstevel@tonic-gate 	/*
10477c478bd9Sstevel@tonic-gate 	 * To determine the group-id of the created file:
10487c478bd9Sstevel@tonic-gate 	 *   1) If the gid is set in the attribute list (non-Sun & pre-4.0
10497c478bd9Sstevel@tonic-gate 	 *	clients are not likely to set the gid), then use it if
10507c478bd9Sstevel@tonic-gate 	 *	the process is privileged, belongs to the target group,
10517c478bd9Sstevel@tonic-gate 	 *	or the group is the same as the parent directory.
10527c478bd9Sstevel@tonic-gate 	 *   2) If the filesystem was not mounted with the Old-BSD-compatible
10537c478bd9Sstevel@tonic-gate 	 *	GRPID option, and the directory's set-gid bit is clear,
10547c478bd9Sstevel@tonic-gate 	 *	then use the process's gid.
10557c478bd9Sstevel@tonic-gate 	 *   3) Otherwise, set the group-id to the gid of the parent directory.
10567c478bd9Sstevel@tonic-gate 	 */
10577c478bd9Sstevel@tonic-gate 	if ((va->va_mask & AT_GID) &&
10587c478bd9Sstevel@tonic-gate 	    ((va->va_gid == dir->tn_gid) || groupmember(va->va_gid, cred) ||
10597c478bd9Sstevel@tonic-gate 	    secpolicy_vnode_create_gid(cred) == 0)) {
10607c478bd9Sstevel@tonic-gate 		/*
10617c478bd9Sstevel@tonic-gate 		 * XXX - is this only the case when a 4.0 NFS client, or a
10627c478bd9Sstevel@tonic-gate 		 * client derived from that code, makes a call over the wire?
10637c478bd9Sstevel@tonic-gate 		 */
10647c478bd9Sstevel@tonic-gate 		tp->tn_gid = va->va_gid;
10657c478bd9Sstevel@tonic-gate 	} else {
10667c478bd9Sstevel@tonic-gate 		if (dir->tn_mode & VSGID)
10677c478bd9Sstevel@tonic-gate 			tp->tn_gid = dir->tn_gid;
10687c478bd9Sstevel@tonic-gate 		else
10697c478bd9Sstevel@tonic-gate 			tp->tn_gid = crgetgid(cred);
10707c478bd9Sstevel@tonic-gate 	}
10717c478bd9Sstevel@tonic-gate 	/*
10727c478bd9Sstevel@tonic-gate 	 * If we're creating a directory, and the parent directory has the
10737c478bd9Sstevel@tonic-gate 	 * set-GID bit set, set it on the new directory.
10747c478bd9Sstevel@tonic-gate 	 * Otherwise, if the user is neither privileged nor a member of the
10757c478bd9Sstevel@tonic-gate 	 * file's new group, clear the file's set-GID bit.
10767c478bd9Sstevel@tonic-gate 	 */
10777c478bd9Sstevel@tonic-gate 	if (dir->tn_mode & VSGID && type == VDIR)
10787c478bd9Sstevel@tonic-gate 		tp->tn_mode |= VSGID;
10797c478bd9Sstevel@tonic-gate 	else {
10807c478bd9Sstevel@tonic-gate 		if ((tp->tn_mode & VSGID) &&
10817c478bd9Sstevel@tonic-gate 		    secpolicy_vnode_setids_setgids(cred, tp->tn_gid) != 0)
10827c478bd9Sstevel@tonic-gate 			tp->tn_mode &= ~VSGID;
10837c478bd9Sstevel@tonic-gate 	}
10847c478bd9Sstevel@tonic-gate 
10857c478bd9Sstevel@tonic-gate 	if (va->va_mask & AT_ATIME)
10867c478bd9Sstevel@tonic-gate 		tp->tn_atime = va->va_atime;
10877c478bd9Sstevel@tonic-gate 	if (va->va_mask & AT_MTIME)
10887c478bd9Sstevel@tonic-gate 		tp->tn_mtime = va->va_mtime;
10897c478bd9Sstevel@tonic-gate 
10907c478bd9Sstevel@tonic-gate 	if (op == DE_MKDIR)
10917c478bd9Sstevel@tonic-gate 		tdirinit(dir, tp);
10927c478bd9Sstevel@tonic-gate 
10937c478bd9Sstevel@tonic-gate 	*newnode = tp;
10947c478bd9Sstevel@tonic-gate 	return (0);
10957c478bd9Sstevel@tonic-gate }
1096