xref: /onnv-gate/usr/src/uts/common/fs/tmpfs/tmp_dir.c (revision 5331:3047ad28a67b)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
54863Spraks  * Common Development and Distribution License (the "License").
64863Spraks  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
210Sstevel@tonic-gate /*
224863Spraks  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
230Sstevel@tonic-gate  * Use is subject to license terms.
240Sstevel@tonic-gate  */
250Sstevel@tonic-gate 
260Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
270Sstevel@tonic-gate 
280Sstevel@tonic-gate #include <sys/types.h>
290Sstevel@tonic-gate #include <sys/param.h>
300Sstevel@tonic-gate #include <sys/sysmacros.h>
310Sstevel@tonic-gate #include <sys/systm.h>
320Sstevel@tonic-gate #include <sys/time.h>
330Sstevel@tonic-gate #include <sys/vfs.h>
340Sstevel@tonic-gate #include <sys/vnode.h>
350Sstevel@tonic-gate #include <sys/errno.h>
360Sstevel@tonic-gate #include <sys/cmn_err.h>
370Sstevel@tonic-gate #include <sys/cred.h>
380Sstevel@tonic-gate #include <sys/stat.h>
390Sstevel@tonic-gate #include <sys/debug.h>
400Sstevel@tonic-gate #include <sys/policy.h>
410Sstevel@tonic-gate #include <sys/fs/tmpnode.h>
420Sstevel@tonic-gate #include <sys/fs/tmp.h>
430Sstevel@tonic-gate #include <sys/vtrace.h>
440Sstevel@tonic-gate 
450Sstevel@tonic-gate static int tdircheckpath(struct tmpnode *, struct tmpnode *, struct cred *);
460Sstevel@tonic-gate static int tdirrename(struct tmpnode *, struct tmpnode *, struct tmpnode *,
470Sstevel@tonic-gate 	char *, struct tmpnode *, struct tdirent *, struct cred *);
480Sstevel@tonic-gate static void tdirfixdotdot(struct tmpnode *, struct tmpnode *, struct tmpnode *);
490Sstevel@tonic-gate static int tdirmaketnode(struct tmpnode *, struct tmount *, struct vattr *,
500Sstevel@tonic-gate 	enum de_op, struct tmpnode **, struct cred *);
510Sstevel@tonic-gate static int tdiraddentry(struct tmpnode *, struct tmpnode *, char *,
520Sstevel@tonic-gate 	enum de_op, struct tmpnode *);
530Sstevel@tonic-gate 
540Sstevel@tonic-gate 
550Sstevel@tonic-gate #define	T_HASH_SIZE	8192		/* must be power of 2 */
560Sstevel@tonic-gate #define	T_MUTEX_SIZE	64
570Sstevel@tonic-gate 
580Sstevel@tonic-gate static struct tdirent	*t_hashtable[T_HASH_SIZE];
590Sstevel@tonic-gate static kmutex_t		 t_hashmutex[T_MUTEX_SIZE];
600Sstevel@tonic-gate 
610Sstevel@tonic-gate #define	T_HASH_INDEX(a)		((a) & (T_HASH_SIZE-1))
620Sstevel@tonic-gate #define	T_MUTEX_INDEX(a)	((a) & (T_MUTEX_SIZE-1))
630Sstevel@tonic-gate 
640Sstevel@tonic-gate #define	TMPFS_HASH(tp, name, hash)				\
650Sstevel@tonic-gate 	{							\
660Sstevel@tonic-gate 		char Xc, *Xcp;					\
670Sstevel@tonic-gate 		hash = (uint_t)(uintptr_t)(tp) >> 8;		\
680Sstevel@tonic-gate 		for (Xcp = (name); (Xc = *Xcp) != 0; Xcp++)	\
690Sstevel@tonic-gate 			hash = (hash << 4) + hash + (uint_t)Xc;	\
700Sstevel@tonic-gate 	}
710Sstevel@tonic-gate 
720Sstevel@tonic-gate void
tmpfs_hash_init(void)730Sstevel@tonic-gate tmpfs_hash_init(void)
740Sstevel@tonic-gate {
750Sstevel@tonic-gate 	int	ix;
760Sstevel@tonic-gate 
770Sstevel@tonic-gate 	for (ix = 0; ix < T_MUTEX_SIZE; ix++)
780Sstevel@tonic-gate 		mutex_init(&t_hashmutex[ix], NULL, MUTEX_DEFAULT, NULL);
790Sstevel@tonic-gate }
800Sstevel@tonic-gate 
810Sstevel@tonic-gate /*
820Sstevel@tonic-gate  * This routine is where the rubber meets the road for identities.
830Sstevel@tonic-gate  */
840Sstevel@tonic-gate static void
tmpfs_hash_in(struct tdirent * t)850Sstevel@tonic-gate tmpfs_hash_in(struct tdirent *t)
860Sstevel@tonic-gate {
870Sstevel@tonic-gate 	uint_t		hash;
880Sstevel@tonic-gate 	struct tdirent	**prevpp;
890Sstevel@tonic-gate 	kmutex_t	*t_hmtx;
900Sstevel@tonic-gate 
910Sstevel@tonic-gate 	TMPFS_HASH(t->td_parent, t->td_name, hash);
920Sstevel@tonic-gate 	t->td_hash = hash;
930Sstevel@tonic-gate 	prevpp = &t_hashtable[T_HASH_INDEX(hash)];
940Sstevel@tonic-gate 	t_hmtx = &t_hashmutex[T_MUTEX_INDEX(hash)];
950Sstevel@tonic-gate 	mutex_enter(t_hmtx);
960Sstevel@tonic-gate 	t->td_link = *prevpp;
970Sstevel@tonic-gate 	*prevpp = t;
980Sstevel@tonic-gate 	mutex_exit(t_hmtx);
990Sstevel@tonic-gate }
1000Sstevel@tonic-gate 
1010Sstevel@tonic-gate /*
1020Sstevel@tonic-gate  * Remove tdirent *t from the hash list.
1030Sstevel@tonic-gate  */
1040Sstevel@tonic-gate static void
tmpfs_hash_out(struct tdirent * t)1050Sstevel@tonic-gate tmpfs_hash_out(struct tdirent *t)
1060Sstevel@tonic-gate {
1070Sstevel@tonic-gate 	uint_t		hash;
1080Sstevel@tonic-gate 	struct tdirent	**prevpp;
1090Sstevel@tonic-gate 	kmutex_t	*t_hmtx;
1100Sstevel@tonic-gate 
1110Sstevel@tonic-gate 	hash = t->td_hash;
1120Sstevel@tonic-gate 	prevpp = &t_hashtable[T_HASH_INDEX(hash)];
1130Sstevel@tonic-gate 	t_hmtx = &t_hashmutex[T_MUTEX_INDEX(hash)];
1140Sstevel@tonic-gate 	mutex_enter(t_hmtx);
1150Sstevel@tonic-gate 	while (*prevpp != t)
1160Sstevel@tonic-gate 		prevpp = &(*prevpp)->td_link;
1170Sstevel@tonic-gate 	*prevpp = t->td_link;
1180Sstevel@tonic-gate 	mutex_exit(t_hmtx);
1190Sstevel@tonic-gate }
1200Sstevel@tonic-gate 
1210Sstevel@tonic-gate /*
1220Sstevel@tonic-gate  * Currently called by tdirrename() only.
1230Sstevel@tonic-gate  * rename operation needs to be done with lock held, to ensure that
1240Sstevel@tonic-gate  * no other operations can access the tmpnode at the same instance.
1250Sstevel@tonic-gate  */
1260Sstevel@tonic-gate static void
tmpfs_hash_change(struct tdirent * tdp,struct tmpnode * fromtp)1270Sstevel@tonic-gate tmpfs_hash_change(struct tdirent *tdp, struct tmpnode *fromtp)
1280Sstevel@tonic-gate {
1290Sstevel@tonic-gate 	uint_t		hash;
1300Sstevel@tonic-gate 	kmutex_t	*t_hmtx;
1310Sstevel@tonic-gate 
1320Sstevel@tonic-gate 	hash = tdp->td_hash;
1330Sstevel@tonic-gate 	t_hmtx = &t_hashmutex[T_MUTEX_INDEX(hash)];
1340Sstevel@tonic-gate 	mutex_enter(t_hmtx);
1350Sstevel@tonic-gate 	tdp->td_tmpnode = fromtp;
1360Sstevel@tonic-gate 	mutex_exit(t_hmtx);
1370Sstevel@tonic-gate }
1380Sstevel@tonic-gate 
1390Sstevel@tonic-gate static struct tdirent *
tmpfs_hash_lookup(char * name,struct tmpnode * parent,uint_t hold,struct tmpnode ** found)1400Sstevel@tonic-gate tmpfs_hash_lookup(char *name, struct tmpnode *parent, uint_t hold,
1410Sstevel@tonic-gate 	struct tmpnode **found)
1420Sstevel@tonic-gate {
1430Sstevel@tonic-gate 	struct tdirent	*l;
1440Sstevel@tonic-gate 	uint_t		hash;
1450Sstevel@tonic-gate 	kmutex_t	*t_hmtx;
1460Sstevel@tonic-gate 	struct tmpnode	*tnp;
1470Sstevel@tonic-gate 
1480Sstevel@tonic-gate 	TMPFS_HASH(parent, name, hash);
1490Sstevel@tonic-gate 	t_hmtx = &t_hashmutex[T_MUTEX_INDEX(hash)];
1500Sstevel@tonic-gate 	mutex_enter(t_hmtx);
1510Sstevel@tonic-gate 	l = t_hashtable[T_HASH_INDEX(hash)];
1520Sstevel@tonic-gate 	while (l) {
1530Sstevel@tonic-gate 		if ((l->td_hash == hash) &&
1540Sstevel@tonic-gate 		    (l->td_parent == parent) &&
1550Sstevel@tonic-gate 		    (strcmp(l->td_name, name) == 0)) {
1560Sstevel@tonic-gate 			/*
1570Sstevel@tonic-gate 			 * We need to make sure that the tmpnode that
1580Sstevel@tonic-gate 			 * we put a hold on is the same one that we pass back.
1590Sstevel@tonic-gate 			 * Hence, temporary variable tnp is necessary.
1600Sstevel@tonic-gate 			 */
1610Sstevel@tonic-gate 			tnp = l->td_tmpnode;
1620Sstevel@tonic-gate 			if (hold) {
1630Sstevel@tonic-gate 				ASSERT(tnp);
1640Sstevel@tonic-gate 				tmpnode_hold(tnp);
1650Sstevel@tonic-gate 			}
1660Sstevel@tonic-gate 			if (found)
1670Sstevel@tonic-gate 				*found = tnp;
1680Sstevel@tonic-gate 			mutex_exit(t_hmtx);
1690Sstevel@tonic-gate 			return (l);
1700Sstevel@tonic-gate 		} else {
1710Sstevel@tonic-gate 			l = l->td_link;
1720Sstevel@tonic-gate 		}
1730Sstevel@tonic-gate 	}
1740Sstevel@tonic-gate 	mutex_exit(t_hmtx);
1750Sstevel@tonic-gate 	return (NULL);
1760Sstevel@tonic-gate }
1770Sstevel@tonic-gate 
1780Sstevel@tonic-gate /*
1790Sstevel@tonic-gate  * Search directory 'parent' for entry 'name'.
1800Sstevel@tonic-gate  *
1810Sstevel@tonic-gate  * The calling thread can't hold the write version
1820Sstevel@tonic-gate  * of the rwlock for the directory being searched
1830Sstevel@tonic-gate  *
1840Sstevel@tonic-gate  * 0 is returned on success and *foundtp points
1850Sstevel@tonic-gate  * to the found tmpnode with its vnode held.
1860Sstevel@tonic-gate  */
1870Sstevel@tonic-gate int
tdirlookup(struct tmpnode * parent,char * name,struct tmpnode ** foundtp,struct cred * cred)1880Sstevel@tonic-gate tdirlookup(
1890Sstevel@tonic-gate 	struct tmpnode *parent,
1900Sstevel@tonic-gate 	char *name,
1910Sstevel@tonic-gate 	struct tmpnode **foundtp,
1920Sstevel@tonic-gate 	struct cred *cred)
1930Sstevel@tonic-gate {
1940Sstevel@tonic-gate 	int error;
1950Sstevel@tonic-gate 
1960Sstevel@tonic-gate 	*foundtp = NULL;
1970Sstevel@tonic-gate 	if (parent->tn_type != VDIR)
1980Sstevel@tonic-gate 		return (ENOTDIR);
1990Sstevel@tonic-gate 
2000Sstevel@tonic-gate 	if ((error = tmp_taccess(parent, VEXEC, cred)))
2010Sstevel@tonic-gate 		return (error);
2020Sstevel@tonic-gate 
2030Sstevel@tonic-gate 	if (*name == '\0') {
2040Sstevel@tonic-gate 		tmpnode_hold(parent);
2050Sstevel@tonic-gate 		*foundtp = parent;
2060Sstevel@tonic-gate 		return (0);
2070Sstevel@tonic-gate 	}
2080Sstevel@tonic-gate 
2090Sstevel@tonic-gate 	/*
2100Sstevel@tonic-gate 	 * Search the directory for the matching name
2110Sstevel@tonic-gate 	 * We need the lock protecting the tn_dir list
2120Sstevel@tonic-gate 	 * so that it doesn't change out from underneath us.
2130Sstevel@tonic-gate 	 * tmpfs_hash_lookup() will pass back the tmpnode
2140Sstevel@tonic-gate 	 * with a hold on it.
2150Sstevel@tonic-gate 	 */
2160Sstevel@tonic-gate 
2170Sstevel@tonic-gate 	if (tmpfs_hash_lookup(name, parent, 1, foundtp) != NULL) {
2180Sstevel@tonic-gate 		ASSERT(*foundtp);
2190Sstevel@tonic-gate 		return (0);
2200Sstevel@tonic-gate 	}
2210Sstevel@tonic-gate 
2220Sstevel@tonic-gate 	return (ENOENT);
2230Sstevel@tonic-gate }
2240Sstevel@tonic-gate 
2250Sstevel@tonic-gate /*
2260Sstevel@tonic-gate  * Enter a directory entry for 'name' and 'tp' into directory 'dir'
2270Sstevel@tonic-gate  *
2280Sstevel@tonic-gate  * Returns 0 on success.
2290Sstevel@tonic-gate  */
2300Sstevel@tonic-gate int
tdirenter(struct tmount * tm,struct tmpnode * dir,char * name,enum de_op op,struct tmpnode * fromparent,struct tmpnode * tp,struct vattr * va,struct tmpnode ** tpp,struct cred * cred,caller_context_t * ctp)2310Sstevel@tonic-gate tdirenter(
2320Sstevel@tonic-gate 	struct tmount	*tm,
2330Sstevel@tonic-gate 	struct tmpnode	*dir,		/* target directory to make entry in */
2340Sstevel@tonic-gate 	char		*name,		/* name of entry */
2350Sstevel@tonic-gate 	enum de_op	op,		/* entry operation */
2360Sstevel@tonic-gate 	struct tmpnode	*fromparent,	/* source directory if rename */
2370Sstevel@tonic-gate 	struct tmpnode	*tp,		/* source tmpnode, if link/rename */
2380Sstevel@tonic-gate 	struct vattr	*va,
2390Sstevel@tonic-gate 	struct tmpnode	**tpp,		/* return tmpnode, if create/mkdir */
240*5331Samw 	struct cred	*cred,
241*5331Samw 	caller_context_t *ctp)
2420Sstevel@tonic-gate {
2430Sstevel@tonic-gate 	struct tdirent *tdp;
2440Sstevel@tonic-gate 	struct tmpnode *found = NULL;
2450Sstevel@tonic-gate 	int error = 0;
2460Sstevel@tonic-gate 	char *s;
2470Sstevel@tonic-gate 
2480Sstevel@tonic-gate 	/*
2490Sstevel@tonic-gate 	 * tn_rwlock is held to serialize direnter and dirdeletes
2500Sstevel@tonic-gate 	 */
2510Sstevel@tonic-gate 	ASSERT(RW_WRITE_HELD(&dir->tn_rwlock));
2520Sstevel@tonic-gate 	ASSERT(dir->tn_type == VDIR);
2530Sstevel@tonic-gate 
2540Sstevel@tonic-gate 	/*
2550Sstevel@tonic-gate 	 * Don't allow '/' characters in pathname component
2560Sstevel@tonic-gate 	 * (thus in ufs_direnter()).
2570Sstevel@tonic-gate 	 */
2580Sstevel@tonic-gate 	for (s = name; *s; s++)
2590Sstevel@tonic-gate 		if (*s == '/')
2600Sstevel@tonic-gate 			return (EACCES);
2610Sstevel@tonic-gate 
2620Sstevel@tonic-gate 	if (name[0] == '\0')
2630Sstevel@tonic-gate 		panic("tdirenter: NULL name");
2640Sstevel@tonic-gate 
2650Sstevel@tonic-gate 	/*
2660Sstevel@tonic-gate 	 * For link and rename lock the source entry and check the link count
2670Sstevel@tonic-gate 	 * to see if it has been removed while it was unlocked.
2680Sstevel@tonic-gate 	 */
2690Sstevel@tonic-gate 	if (op == DE_LINK || op == DE_RENAME) {
2700Sstevel@tonic-gate 		if (tp != dir)
2710Sstevel@tonic-gate 			rw_enter(&tp->tn_rwlock, RW_WRITER);
2720Sstevel@tonic-gate 		mutex_enter(&tp->tn_tlock);
2730Sstevel@tonic-gate 		if (tp->tn_nlink == 0) {
2740Sstevel@tonic-gate 			mutex_exit(&tp->tn_tlock);
2750Sstevel@tonic-gate 			if (tp != dir)
2760Sstevel@tonic-gate 				rw_exit(&tp->tn_rwlock);
2770Sstevel@tonic-gate 			return (ENOENT);
2780Sstevel@tonic-gate 		}
2790Sstevel@tonic-gate 
2800Sstevel@tonic-gate 		if (tp->tn_nlink == MAXLINK) {
2810Sstevel@tonic-gate 			mutex_exit(&tp->tn_tlock);
2820Sstevel@tonic-gate 			if (tp != dir)
2830Sstevel@tonic-gate 				rw_exit(&tp->tn_rwlock);
2840Sstevel@tonic-gate 			return (EMLINK);
2850Sstevel@tonic-gate 		}
2860Sstevel@tonic-gate 		tp->tn_nlink++;
2870Sstevel@tonic-gate 		gethrestime(&tp->tn_ctime);
2880Sstevel@tonic-gate 		mutex_exit(&tp->tn_tlock);
2890Sstevel@tonic-gate 		if (tp != dir)
2900Sstevel@tonic-gate 			rw_exit(&tp->tn_rwlock);
2910Sstevel@tonic-gate 	}
2920Sstevel@tonic-gate 
2930Sstevel@tonic-gate 	/*
2940Sstevel@tonic-gate 	 * This might be a "dangling detached directory".
2950Sstevel@tonic-gate 	 * it could have been removed, but a reference
2960Sstevel@tonic-gate 	 * to it kept in u_cwd.  don't bother searching
2970Sstevel@tonic-gate 	 * it, and with any luck the user will get tired
2980Sstevel@tonic-gate 	 * of dealing with us and cd to some absolute
2990Sstevel@tonic-gate 	 * pathway.  *sigh*, thus in ufs, too.
3000Sstevel@tonic-gate 	 */
3010Sstevel@tonic-gate 	if (dir->tn_nlink == 0) {
3020Sstevel@tonic-gate 		error = ENOENT;
3030Sstevel@tonic-gate 		goto out;
3040Sstevel@tonic-gate 	}
3050Sstevel@tonic-gate 
3060Sstevel@tonic-gate 	/*
3070Sstevel@tonic-gate 	 * If this is a rename of a directory and the parent is
3080Sstevel@tonic-gate 	 * different (".." must be changed), then the source
3090Sstevel@tonic-gate 	 * directory must not be in the directory hierarchy
3100Sstevel@tonic-gate 	 * above the target, as this would orphan everything
3110Sstevel@tonic-gate 	 * below the source directory.
3120Sstevel@tonic-gate 	 */
3130Sstevel@tonic-gate 	if (op == DE_RENAME) {
3140Sstevel@tonic-gate 		if (tp == dir) {
3150Sstevel@tonic-gate 			error = EINVAL;
3160Sstevel@tonic-gate 			goto out;
3170Sstevel@tonic-gate 		}
3180Sstevel@tonic-gate 		if (tp->tn_type == VDIR) {
3190Sstevel@tonic-gate 			if ((fromparent != dir) &&
3200Sstevel@tonic-gate 			    (error = tdircheckpath(tp, dir, cred))) {
3210Sstevel@tonic-gate 				goto out;
3220Sstevel@tonic-gate 			}
3230Sstevel@tonic-gate 		}
3240Sstevel@tonic-gate 	}
3250Sstevel@tonic-gate 
3260Sstevel@tonic-gate 	/*
3270Sstevel@tonic-gate 	 * Search for the entry.  Return "found" if it exists.
3280Sstevel@tonic-gate 	 */
3290Sstevel@tonic-gate 	tdp = tmpfs_hash_lookup(name, dir, 1, &found);
3300Sstevel@tonic-gate 
3310Sstevel@tonic-gate 	if (tdp) {
3320Sstevel@tonic-gate 		ASSERT(found);
3330Sstevel@tonic-gate 		switch (op) {
3340Sstevel@tonic-gate 		case DE_CREATE:
3350Sstevel@tonic-gate 		case DE_MKDIR:
3360Sstevel@tonic-gate 			if (tpp) {
3370Sstevel@tonic-gate 				*tpp = found;
3380Sstevel@tonic-gate 				error = EEXIST;
3390Sstevel@tonic-gate 			} else {
3400Sstevel@tonic-gate 				tmpnode_rele(found);
3410Sstevel@tonic-gate 			}
3420Sstevel@tonic-gate 			break;
3430Sstevel@tonic-gate 
3440Sstevel@tonic-gate 		case DE_RENAME:
3450Sstevel@tonic-gate 			error = tdirrename(fromparent, tp,
3460Sstevel@tonic-gate 			    dir, name, found, tdp, cred);
3470Sstevel@tonic-gate 			if (error == 0) {
3484863Spraks 				if (found != NULL) {
3494863Spraks 					vnevent_rename_dest(TNTOV(found),
350*5331Samw 					    TNTOV(dir), name, ctp);
3514863Spraks 				}
3520Sstevel@tonic-gate 			}
3534863Spraks 
3540Sstevel@tonic-gate 			tmpnode_rele(found);
3550Sstevel@tonic-gate 			break;
3560Sstevel@tonic-gate 
3570Sstevel@tonic-gate 		case DE_LINK:
3580Sstevel@tonic-gate 			/*
3590Sstevel@tonic-gate 			 * Can't link to an existing file.
3600Sstevel@tonic-gate 			 */
3610Sstevel@tonic-gate 			error = EEXIST;
3620Sstevel@tonic-gate 			tmpnode_rele(found);
3630Sstevel@tonic-gate 			break;
3640Sstevel@tonic-gate 		}
3650Sstevel@tonic-gate 	} else {
3660Sstevel@tonic-gate 
3670Sstevel@tonic-gate 		/*
3680Sstevel@tonic-gate 		 * The entry does not exist. Check write permission in
3690Sstevel@tonic-gate 		 * directory to see if entry can be created.
3700Sstevel@tonic-gate 		 */
3710Sstevel@tonic-gate 		if (error = tmp_taccess(dir, VWRITE, cred))
3720Sstevel@tonic-gate 			goto out;
3730Sstevel@tonic-gate 		if (op == DE_CREATE || op == DE_MKDIR) {
3740Sstevel@tonic-gate 			/*
3750Sstevel@tonic-gate 			 * Make new tmpnode and directory entry as required.
3760Sstevel@tonic-gate 			 */
3770Sstevel@tonic-gate 			error = tdirmaketnode(dir, tm, va, op, &tp, cred);
3780Sstevel@tonic-gate 			if (error)
3790Sstevel@tonic-gate 				goto out;
3800Sstevel@tonic-gate 		}
3810Sstevel@tonic-gate 		if (error = tdiraddentry(dir, tp, name, op, fromparent)) {
3820Sstevel@tonic-gate 			if (op == DE_CREATE || op == DE_MKDIR) {
3830Sstevel@tonic-gate 				/*
3840Sstevel@tonic-gate 				 * Unmake the inode we just made.
3850Sstevel@tonic-gate 				 */
3860Sstevel@tonic-gate 				rw_enter(&tp->tn_rwlock, RW_WRITER);
3870Sstevel@tonic-gate 				if ((tp->tn_type) == VDIR) {
3880Sstevel@tonic-gate 					ASSERT(tdp == NULL);
3890Sstevel@tonic-gate 					/*
3900Sstevel@tonic-gate 					 * cleanup allocs made by tdirinit()
3910Sstevel@tonic-gate 					 */
3920Sstevel@tonic-gate 					tdirtrunc(tp);
3930Sstevel@tonic-gate 				}
3940Sstevel@tonic-gate 				mutex_enter(&tp->tn_tlock);
3950Sstevel@tonic-gate 				tp->tn_nlink = 0;
3960Sstevel@tonic-gate 				mutex_exit(&tp->tn_tlock);
3970Sstevel@tonic-gate 				gethrestime(&tp->tn_ctime);
3980Sstevel@tonic-gate 				rw_exit(&tp->tn_rwlock);
3990Sstevel@tonic-gate 				tmpnode_rele(tp);
4000Sstevel@tonic-gate 				tp = NULL;
4010Sstevel@tonic-gate 			}
4020Sstevel@tonic-gate 		} else if (tpp) {
4030Sstevel@tonic-gate 			*tpp = tp;
4040Sstevel@tonic-gate 		} else if (op == DE_CREATE || op == DE_MKDIR) {
4050Sstevel@tonic-gate 			tmpnode_rele(tp);
4060Sstevel@tonic-gate 		}
4070Sstevel@tonic-gate 	}
4080Sstevel@tonic-gate 
4090Sstevel@tonic-gate out:
4100Sstevel@tonic-gate 	if (error && (op == DE_LINK || op == DE_RENAME)) {
4110Sstevel@tonic-gate 		/*
4120Sstevel@tonic-gate 		 * Undo bumped link count.
4130Sstevel@tonic-gate 		 */
4140Sstevel@tonic-gate 		DECR_COUNT(&tp->tn_nlink, &tp->tn_tlock);
4150Sstevel@tonic-gate 		gethrestime(&tp->tn_ctime);
4160Sstevel@tonic-gate 	}
4170Sstevel@tonic-gate 	return (error);
4180Sstevel@tonic-gate }
4190Sstevel@tonic-gate 
4200Sstevel@tonic-gate /*
4210Sstevel@tonic-gate  * Delete entry tp of name "nm" from dir.
4220Sstevel@tonic-gate  * Free dir entry space and decrement link count on tmpnode(s).
4230Sstevel@tonic-gate  *
4240Sstevel@tonic-gate  * Return 0 on success.
4250Sstevel@tonic-gate  */
4260Sstevel@tonic-gate int
tdirdelete(struct tmpnode * dir,struct tmpnode * tp,char * nm,enum dr_op op,struct cred * cred)4270Sstevel@tonic-gate tdirdelete(
4280Sstevel@tonic-gate 	struct tmpnode *dir,
4290Sstevel@tonic-gate 	struct tmpnode *tp,
4300Sstevel@tonic-gate 	char *nm,
4310Sstevel@tonic-gate 	enum dr_op op,
4320Sstevel@tonic-gate 	struct cred *cred)
4330Sstevel@tonic-gate {
4340Sstevel@tonic-gate 	struct tdirent *tpdp;
4350Sstevel@tonic-gate 	int error;
4360Sstevel@tonic-gate 	size_t namelen;
4370Sstevel@tonic-gate 	struct tmpnode *tnp;
4380Sstevel@tonic-gate 	timestruc_t now;
4390Sstevel@tonic-gate 
4400Sstevel@tonic-gate 	ASSERT(RW_WRITE_HELD(&dir->tn_rwlock));
4410Sstevel@tonic-gate 	ASSERT(RW_WRITE_HELD(&tp->tn_rwlock));
4420Sstevel@tonic-gate 	ASSERT(dir->tn_type == VDIR);
4430Sstevel@tonic-gate 
4440Sstevel@tonic-gate 	if (nm[0] == '\0')
4450Sstevel@tonic-gate 		panic("tdirdelete: NULL name for %p", (void *)tp);
4460Sstevel@tonic-gate 
4470Sstevel@tonic-gate 	/*
4480Sstevel@tonic-gate 	 * return error when removing . and ..
4490Sstevel@tonic-gate 	 */
4500Sstevel@tonic-gate 	if (nm[0] == '.') {
4510Sstevel@tonic-gate 		if (nm[1] == '\0')
4520Sstevel@tonic-gate 			return (EINVAL);
4530Sstevel@tonic-gate 		if (nm[1] == '.' && nm[2] == '\0')
4540Sstevel@tonic-gate 			return (EEXIST); /* thus in ufs */
4550Sstevel@tonic-gate 	}
4560Sstevel@tonic-gate 
4570Sstevel@tonic-gate 	if (error = tmp_taccess(dir, VEXEC|VWRITE, cred))
4580Sstevel@tonic-gate 		return (error);
4590Sstevel@tonic-gate 
4600Sstevel@tonic-gate 	/*
4610Sstevel@tonic-gate 	 * If the parent directory is "sticky", then the user must
4620Sstevel@tonic-gate 	 * own the parent directory or the file in it, or else must
4630Sstevel@tonic-gate 	 * have permission to write the file.  Otherwise it may not
4640Sstevel@tonic-gate 	 * be deleted (except by privileged users).
4650Sstevel@tonic-gate 	 * Same as ufs_dirremove.
4660Sstevel@tonic-gate 	 */
4670Sstevel@tonic-gate 	if ((error = tmp_sticky_remove_access(dir, tp, cred)) != 0)
4680Sstevel@tonic-gate 		return (error);
4690Sstevel@tonic-gate 
4700Sstevel@tonic-gate 	if (dir->tn_dir == NULL)
4710Sstevel@tonic-gate 		return (ENOENT);
4720Sstevel@tonic-gate 
4730Sstevel@tonic-gate 	tpdp = tmpfs_hash_lookup(nm, dir, 0, &tnp);
4740Sstevel@tonic-gate 	if (tpdp == NULL) {
4750Sstevel@tonic-gate 		/*
4760Sstevel@tonic-gate 		 * If it is gone, some other thread got here first!
4770Sstevel@tonic-gate 		 * Return error ENOENT.
4780Sstevel@tonic-gate 		 */
4790Sstevel@tonic-gate 		return (ENOENT);
4800Sstevel@tonic-gate 	}
4810Sstevel@tonic-gate 
4820Sstevel@tonic-gate 	/*
4830Sstevel@tonic-gate 	 * If the tmpnode in the tdirent changed, we were probably
4840Sstevel@tonic-gate 	 * the victim of a concurrent rename operation.  The original
4850Sstevel@tonic-gate 	 * is gone, so return that status (same as UFS).
4860Sstevel@tonic-gate 	 */
4870Sstevel@tonic-gate 	if (tp != tnp)
4880Sstevel@tonic-gate 		return (ENOENT);
4890Sstevel@tonic-gate 
4900Sstevel@tonic-gate 	tmpfs_hash_out(tpdp);
4910Sstevel@tonic-gate 
4920Sstevel@tonic-gate 	/*
4930Sstevel@tonic-gate 	 * Take tpdp out of the directory list.
4940Sstevel@tonic-gate 	 */
4950Sstevel@tonic-gate 	ASSERT(tpdp->td_next != tpdp);
4960Sstevel@tonic-gate 	ASSERT(tpdp->td_prev != tpdp);
4970Sstevel@tonic-gate 	if (tpdp->td_prev) {
4980Sstevel@tonic-gate 		tpdp->td_prev->td_next = tpdp->td_next;
4990Sstevel@tonic-gate 	}
5000Sstevel@tonic-gate 	if (tpdp->td_next) {
5010Sstevel@tonic-gate 		tpdp->td_next->td_prev = tpdp->td_prev;
5020Sstevel@tonic-gate 	}
5030Sstevel@tonic-gate 
5040Sstevel@tonic-gate 	/*
5050Sstevel@tonic-gate 	 * If the roving slot pointer happens to match tpdp,
5060Sstevel@tonic-gate 	 * point it at the previous dirent.
5070Sstevel@tonic-gate 	 */
5080Sstevel@tonic-gate 	if (dir->tn_dir->td_prev == tpdp) {
5090Sstevel@tonic-gate 		dir->tn_dir->td_prev = tpdp->td_prev;
5100Sstevel@tonic-gate 	}
5110Sstevel@tonic-gate 	ASSERT(tpdp->td_next != tpdp);
5120Sstevel@tonic-gate 	ASSERT(tpdp->td_prev != tpdp);
5130Sstevel@tonic-gate 
5140Sstevel@tonic-gate 	/*
5150Sstevel@tonic-gate 	 * tpdp points to the correct directory entry
5160Sstevel@tonic-gate 	 */
5170Sstevel@tonic-gate 	namelen = strlen(tpdp->td_name) + 1;
5180Sstevel@tonic-gate 
5190Sstevel@tonic-gate 	tmp_memfree(tpdp, sizeof (struct tdirent) + namelen);
5200Sstevel@tonic-gate 	dir->tn_size -= (sizeof (struct tdirent) + namelen);
5210Sstevel@tonic-gate 	dir->tn_dirents--;
5220Sstevel@tonic-gate 
5230Sstevel@tonic-gate 	gethrestime(&now);
5240Sstevel@tonic-gate 	dir->tn_mtime = now;
5250Sstevel@tonic-gate 	dir->tn_ctime = now;
5260Sstevel@tonic-gate 	tp->tn_ctime = now;
5270Sstevel@tonic-gate 
5280Sstevel@tonic-gate 	ASSERT(tp->tn_nlink > 0);
5290Sstevel@tonic-gate 	DECR_COUNT(&tp->tn_nlink, &tp->tn_tlock);
5300Sstevel@tonic-gate 	if (op == DR_RMDIR && tp->tn_type == VDIR) {
5310Sstevel@tonic-gate 		tdirtrunc(tp);
5320Sstevel@tonic-gate 		ASSERT(tp->tn_nlink == 0);
5330Sstevel@tonic-gate 	}
5340Sstevel@tonic-gate 	return (0);
5350Sstevel@tonic-gate }
5360Sstevel@tonic-gate 
5370Sstevel@tonic-gate /*
5380Sstevel@tonic-gate  * tdirinit is used internally to initialize a directory (dir)
5390Sstevel@tonic-gate  * with '.' and '..' entries without checking permissions and locking
5400Sstevel@tonic-gate  */
5410Sstevel@tonic-gate void
tdirinit(struct tmpnode * parent,struct tmpnode * dir)5420Sstevel@tonic-gate tdirinit(
5430Sstevel@tonic-gate 	struct tmpnode *parent,		/* parent of directory to initialize */
5440Sstevel@tonic-gate 	struct tmpnode *dir)		/* the new directory */
5450Sstevel@tonic-gate {
5460Sstevel@tonic-gate 	struct tdirent *dot, *dotdot;
5470Sstevel@tonic-gate 	timestruc_t now;
5480Sstevel@tonic-gate 
5490Sstevel@tonic-gate 	ASSERT(RW_WRITE_HELD(&parent->tn_rwlock));
5500Sstevel@tonic-gate 	ASSERT(dir->tn_type == VDIR);
5510Sstevel@tonic-gate 
5520Sstevel@tonic-gate 	dot = tmp_memalloc(sizeof (struct tdirent) + 2, TMP_MUSTHAVE);
5530Sstevel@tonic-gate 	dotdot = tmp_memalloc(sizeof (struct tdirent) + 3, TMP_MUSTHAVE);
5540Sstevel@tonic-gate 
5550Sstevel@tonic-gate 	/*
5560Sstevel@tonic-gate 	 * Initialize the entries
5570Sstevel@tonic-gate 	 */
5580Sstevel@tonic-gate 	dot->td_tmpnode = dir;
5590Sstevel@tonic-gate 	dot->td_offset = 0;
5600Sstevel@tonic-gate 	dot->td_name = (char *)dot + sizeof (struct tdirent);
5610Sstevel@tonic-gate 	dot->td_name[0] = '.';
5620Sstevel@tonic-gate 	dot->td_parent = dir;
5630Sstevel@tonic-gate 	tmpfs_hash_in(dot);
5640Sstevel@tonic-gate 
5650Sstevel@tonic-gate 	dotdot->td_tmpnode = parent;
5660Sstevel@tonic-gate 	dotdot->td_offset = 1;
5670Sstevel@tonic-gate 	dotdot->td_name = (char *)dotdot + sizeof (struct tdirent);
5680Sstevel@tonic-gate 	dotdot->td_name[0] = '.';
5690Sstevel@tonic-gate 	dotdot->td_name[1] = '.';
5700Sstevel@tonic-gate 	dotdot->td_parent = dir;
5710Sstevel@tonic-gate 	tmpfs_hash_in(dotdot);
5720Sstevel@tonic-gate 
5730Sstevel@tonic-gate 	/*
5740Sstevel@tonic-gate 	 * Initialize directory entry list.
5750Sstevel@tonic-gate 	 */
5760Sstevel@tonic-gate 	dot->td_next = dotdot;
5770Sstevel@tonic-gate 	dot->td_prev = dotdot;	/* dot's td_prev holds roving slot pointer */
5780Sstevel@tonic-gate 	dotdot->td_next = NULL;
5790Sstevel@tonic-gate 	dotdot->td_prev = dot;
5800Sstevel@tonic-gate 
5810Sstevel@tonic-gate 	gethrestime(&now);
5820Sstevel@tonic-gate 	dir->tn_mtime = now;
5830Sstevel@tonic-gate 	dir->tn_ctime = now;
5840Sstevel@tonic-gate 
5850Sstevel@tonic-gate 	/*
5860Sstevel@tonic-gate 	 * Link counts are special for the hidden attribute directory.
5870Sstevel@tonic-gate 	 * The only explicit reference in the name space is "." and
5880Sstevel@tonic-gate 	 * the reference through ".." is not counted on the parent
5890Sstevel@tonic-gate 	 * file. The attrdir is created as a side effect to lookup,
5900Sstevel@tonic-gate 	 * so don't change the ctime of the parent.
5910Sstevel@tonic-gate 	 * Since tdirinit is called with both dir and parent being the
5920Sstevel@tonic-gate 	 * same for the root vnode, we need to increment this before we set
5930Sstevel@tonic-gate 	 * tn_nlink = 2 below.
5940Sstevel@tonic-gate 	 */
5950Sstevel@tonic-gate 	if (!(dir->tn_vnode->v_flag & V_XATTRDIR)) {
5960Sstevel@tonic-gate 		INCR_COUNT(&parent->tn_nlink, &parent->tn_tlock);
5970Sstevel@tonic-gate 		parent->tn_ctime = now;
5980Sstevel@tonic-gate 	}
5990Sstevel@tonic-gate 
6000Sstevel@tonic-gate 	dir->tn_dir = dot;
6010Sstevel@tonic-gate 	dir->tn_size = 2 * sizeof (struct tdirent) + 5;	/* dot and dotdot */
6020Sstevel@tonic-gate 	dir->tn_dirents = 2;
6030Sstevel@tonic-gate 	dir->tn_nlink = 2;
6040Sstevel@tonic-gate }
6050Sstevel@tonic-gate 
6060Sstevel@tonic-gate 
6070Sstevel@tonic-gate /*
6080Sstevel@tonic-gate  * tdirtrunc is called to remove all directory entries under this directory.
6090Sstevel@tonic-gate  */
6100Sstevel@tonic-gate void
tdirtrunc(struct tmpnode * dir)6110Sstevel@tonic-gate tdirtrunc(struct tmpnode *dir)
6120Sstevel@tonic-gate {
6130Sstevel@tonic-gate 	struct tdirent *tdp;
6140Sstevel@tonic-gate 	struct tmpnode *tp;
6150Sstevel@tonic-gate 	size_t namelen;
6160Sstevel@tonic-gate 	timestruc_t now;
6170Sstevel@tonic-gate 	int isvattrdir, isdotdot, skip_decr;
6180Sstevel@tonic-gate 
6190Sstevel@tonic-gate 	ASSERT(RW_WRITE_HELD(&dir->tn_rwlock));
6200Sstevel@tonic-gate 	ASSERT(dir->tn_type == VDIR);
6210Sstevel@tonic-gate 
6220Sstevel@tonic-gate 	isvattrdir = (dir->tn_vnode->v_flag & V_XATTRDIR) ? 1 : 0;
6230Sstevel@tonic-gate 	for (tdp = dir->tn_dir; tdp; tdp = dir->tn_dir) {
6240Sstevel@tonic-gate 		ASSERT(tdp->td_next != tdp);
6250Sstevel@tonic-gate 		ASSERT(tdp->td_prev != tdp);
6260Sstevel@tonic-gate 		ASSERT(tdp->td_tmpnode);
6270Sstevel@tonic-gate 
6280Sstevel@tonic-gate 		dir->tn_dir = tdp->td_next;
6290Sstevel@tonic-gate 		namelen = strlen(tdp->td_name) + 1;
6300Sstevel@tonic-gate 
6310Sstevel@tonic-gate 		/*
6320Sstevel@tonic-gate 		 * Adjust the link counts to account for this directory
6330Sstevel@tonic-gate 		 * entry removal. Hidden attribute directories may
6340Sstevel@tonic-gate 		 * not be empty as they may be truncated as a side-
6350Sstevel@tonic-gate 		 * effect of removing the parent. We do hold/rele
6360Sstevel@tonic-gate 		 * operations to free up these tmpnodes.
6370Sstevel@tonic-gate 		 *
6380Sstevel@tonic-gate 		 * Skip the link count adjustment for parents of
6390Sstevel@tonic-gate 		 * attribute directories as those link counts
6400Sstevel@tonic-gate 		 * do not include the ".." reference in the hidden
6410Sstevel@tonic-gate 		 * directories.
6420Sstevel@tonic-gate 		 */
6430Sstevel@tonic-gate 		tp = tdp->td_tmpnode;
6440Sstevel@tonic-gate 		isdotdot = (strcmp("..", tdp->td_name) == 0);
6450Sstevel@tonic-gate 		skip_decr = (isvattrdir && isdotdot);
6460Sstevel@tonic-gate 		if (!skip_decr) {
6470Sstevel@tonic-gate 			ASSERT(tp->tn_nlink > 0);
6480Sstevel@tonic-gate 			DECR_COUNT(&tp->tn_nlink, &tp->tn_tlock);
6490Sstevel@tonic-gate 		}
6500Sstevel@tonic-gate 
6510Sstevel@tonic-gate 		tmpfs_hash_out(tdp);
6520Sstevel@tonic-gate 
6530Sstevel@tonic-gate 		tmp_memfree(tdp, sizeof (struct tdirent) + namelen);
6540Sstevel@tonic-gate 		dir->tn_size -= (sizeof (struct tdirent) + namelen);
6550Sstevel@tonic-gate 		dir->tn_dirents--;
6560Sstevel@tonic-gate 	}
6570Sstevel@tonic-gate 
6580Sstevel@tonic-gate 	gethrestime(&now);
6590Sstevel@tonic-gate 	dir->tn_mtime = now;
6600Sstevel@tonic-gate 	dir->tn_ctime = now;
6610Sstevel@tonic-gate 
6620Sstevel@tonic-gate 	ASSERT(dir->tn_dir == NULL);
6630Sstevel@tonic-gate 	ASSERT(dir->tn_size == 0);
6640Sstevel@tonic-gate 	ASSERT(dir->tn_dirents == 0);
6650Sstevel@tonic-gate }
6660Sstevel@tonic-gate 
6670Sstevel@tonic-gate /*
6680Sstevel@tonic-gate  * Check if the source directory is in the path of the target directory.
6690Sstevel@tonic-gate  * The target directory is locked by the caller.
6700Sstevel@tonic-gate  *
6710Sstevel@tonic-gate  * XXX - The source and target's should be different upon entry.
6720Sstevel@tonic-gate  */
6730Sstevel@tonic-gate static int
tdircheckpath(struct tmpnode * fromtp,struct tmpnode * toparent,struct cred * cred)6740Sstevel@tonic-gate tdircheckpath(
6750Sstevel@tonic-gate 	struct tmpnode *fromtp,
6760Sstevel@tonic-gate 	struct tmpnode	*toparent,
6770Sstevel@tonic-gate 	struct cred	*cred)
6780Sstevel@tonic-gate {
6790Sstevel@tonic-gate 	int	error = 0;
6800Sstevel@tonic-gate 	struct tmpnode *dir, *dotdot;
6810Sstevel@tonic-gate 	struct tdirent *tdp;
6820Sstevel@tonic-gate 
6830Sstevel@tonic-gate 	ASSERT(RW_WRITE_HELD(&toparent->tn_rwlock));
6840Sstevel@tonic-gate 
6850Sstevel@tonic-gate 	tdp = tmpfs_hash_lookup("..", toparent, 1, &dotdot);
6860Sstevel@tonic-gate 	if (tdp == NULL)
6870Sstevel@tonic-gate 		return (ENOENT);
6880Sstevel@tonic-gate 
6890Sstevel@tonic-gate 	ASSERT(dotdot);
6900Sstevel@tonic-gate 
6910Sstevel@tonic-gate 	if (dotdot == toparent) {
6920Sstevel@tonic-gate 		/* root of fs.  search trivially satisfied. */
6930Sstevel@tonic-gate 		tmpnode_rele(dotdot);
6940Sstevel@tonic-gate 		return (0);
6950Sstevel@tonic-gate 	}
6960Sstevel@tonic-gate 	for (;;) {
6970Sstevel@tonic-gate 		/*
6980Sstevel@tonic-gate 		 * Return error for cases like "mv c c/d",
6990Sstevel@tonic-gate 		 * "mv c c/d/e" and so on.
7000Sstevel@tonic-gate 		 */
7010Sstevel@tonic-gate 		if (dotdot == fromtp) {
7020Sstevel@tonic-gate 			tmpnode_rele(dotdot);
7030Sstevel@tonic-gate 			error = EINVAL;
7040Sstevel@tonic-gate 			break;
7050Sstevel@tonic-gate 		}
7060Sstevel@tonic-gate 		dir = dotdot;
7070Sstevel@tonic-gate 		error = tdirlookup(dir, "..", &dotdot, cred);
7080Sstevel@tonic-gate 		if (error) {
7090Sstevel@tonic-gate 			tmpnode_rele(dir);
7100Sstevel@tonic-gate 			break;
7110Sstevel@tonic-gate 		}
7120Sstevel@tonic-gate 		/*
7130Sstevel@tonic-gate 		 * We're okay if we traverse the directory tree up to
7140Sstevel@tonic-gate 		 * the root directory and don't run into the
7150Sstevel@tonic-gate 		 * parent directory.
7160Sstevel@tonic-gate 		 */
7170Sstevel@tonic-gate 		if (dir == dotdot) {
7180Sstevel@tonic-gate 			tmpnode_rele(dir);
7190Sstevel@tonic-gate 			tmpnode_rele(dotdot);
7200Sstevel@tonic-gate 			break;
7210Sstevel@tonic-gate 		}
7220Sstevel@tonic-gate 		tmpnode_rele(dir);
7230Sstevel@tonic-gate 	}
7240Sstevel@tonic-gate 	return (error);
7250Sstevel@tonic-gate }
7260Sstevel@tonic-gate 
7270Sstevel@tonic-gate static int
tdirrename(struct tmpnode * fromparent,struct tmpnode * fromtp,struct tmpnode * toparent,char * nm,struct tmpnode * to,struct tdirent * where,struct cred * cred)7280Sstevel@tonic-gate tdirrename(
7290Sstevel@tonic-gate 	struct tmpnode *fromparent,	/* parent directory of source */
7300Sstevel@tonic-gate 	struct tmpnode *fromtp,		/* source tmpnode */
7310Sstevel@tonic-gate 	struct tmpnode *toparent,	/* parent directory of target */
7320Sstevel@tonic-gate 	char *nm,			/* entry we are trying to change */
7330Sstevel@tonic-gate 	struct tmpnode *to,		/* target tmpnode */
7340Sstevel@tonic-gate 	struct tdirent *where,		/* target tmpnode directory entry */
7350Sstevel@tonic-gate 	struct cred *cred)		/* credentials */
7360Sstevel@tonic-gate {
7370Sstevel@tonic-gate 	int error = 0;
7380Sstevel@tonic-gate 	int doingdirectory;
7390Sstevel@tonic-gate 	timestruc_t now;
7400Sstevel@tonic-gate 
7410Sstevel@tonic-gate #if defined(lint)
7420Sstevel@tonic-gate 	nm = nm;
7430Sstevel@tonic-gate #endif
7440Sstevel@tonic-gate 	ASSERT(RW_WRITE_HELD(&toparent->tn_rwlock));
7450Sstevel@tonic-gate 
7460Sstevel@tonic-gate 	/*
7470Sstevel@tonic-gate 	 * Short circuit rename of something to itself.
7480Sstevel@tonic-gate 	 */
7490Sstevel@tonic-gate 	if (fromtp == to)
7500Sstevel@tonic-gate 		return (ESAME);		/* special KLUDGE error code */
7510Sstevel@tonic-gate 
7520Sstevel@tonic-gate 	rw_enter(&fromtp->tn_rwlock, RW_READER);
7530Sstevel@tonic-gate 	rw_enter(&to->tn_rwlock, RW_READER);
7540Sstevel@tonic-gate 
7550Sstevel@tonic-gate 	/*
7560Sstevel@tonic-gate 	 * Check that everything is on the same filesystem.
7570Sstevel@tonic-gate 	 */
7580Sstevel@tonic-gate 	if (to->tn_vnode->v_vfsp != toparent->tn_vnode->v_vfsp ||
7590Sstevel@tonic-gate 	    to->tn_vnode->v_vfsp != fromtp->tn_vnode->v_vfsp) {
7600Sstevel@tonic-gate 		error = EXDEV;
7610Sstevel@tonic-gate 		goto out;
7620Sstevel@tonic-gate 	}
7630Sstevel@tonic-gate 
7640Sstevel@tonic-gate 	/*
7650Sstevel@tonic-gate 	 * Must have write permission to rewrite target entry.
7660Sstevel@tonic-gate 	 * Check for stickyness.
7670Sstevel@tonic-gate 	 */
7680Sstevel@tonic-gate 	if ((error = tmp_taccess(toparent, VWRITE, cred)) != 0 ||
7690Sstevel@tonic-gate 	    (error = tmp_sticky_remove_access(toparent, to, cred)) != 0)
7700Sstevel@tonic-gate 		goto out;
7710Sstevel@tonic-gate 
7720Sstevel@tonic-gate 	/*
7730Sstevel@tonic-gate 	 * Ensure source and target are compatible (both directories
7740Sstevel@tonic-gate 	 * or both not directories).  If target is a directory it must
7750Sstevel@tonic-gate 	 * be empty and have no links to it; in addition it must not
7760Sstevel@tonic-gate 	 * be a mount point, and both the source and target must be
7770Sstevel@tonic-gate 	 * writable.
7780Sstevel@tonic-gate 	 */
7790Sstevel@tonic-gate 	doingdirectory = (fromtp->tn_type == VDIR);
7800Sstevel@tonic-gate 	if (to->tn_type == VDIR) {
7810Sstevel@tonic-gate 		if (!doingdirectory) {
7820Sstevel@tonic-gate 			error = EISDIR;
7830Sstevel@tonic-gate 			goto out;
7840Sstevel@tonic-gate 		}
7850Sstevel@tonic-gate 		/*
786569Sbatschul 		 * vn_vfswlock will prevent mounts from using the directory
7870Sstevel@tonic-gate 		 * until we are done.
7880Sstevel@tonic-gate 		 */
789569Sbatschul 		if (vn_vfswlock(TNTOV(to))) {
7900Sstevel@tonic-gate 			error = EBUSY;
7910Sstevel@tonic-gate 			goto out;
7920Sstevel@tonic-gate 		}
7930Sstevel@tonic-gate 		if (vn_mountedvfs(TNTOV(to)) != NULL) {
7940Sstevel@tonic-gate 			vn_vfsunlock(TNTOV(to));
7950Sstevel@tonic-gate 			error = EBUSY;
7960Sstevel@tonic-gate 			goto out;
7970Sstevel@tonic-gate 		}
7980Sstevel@tonic-gate 
7990Sstevel@tonic-gate 		mutex_enter(&to->tn_tlock);
8000Sstevel@tonic-gate 		if (to->tn_dirents > 2 || to->tn_nlink > 2) {
8010Sstevel@tonic-gate 			mutex_exit(&to->tn_tlock);
8020Sstevel@tonic-gate 			vn_vfsunlock(TNTOV(to));
8030Sstevel@tonic-gate 			error = EEXIST; /* SIGH should be ENOTEMPTY */
8040Sstevel@tonic-gate 			/*
8050Sstevel@tonic-gate 			 * Update atime because checking tn_dirents is
8060Sstevel@tonic-gate 			 * logically equivalent to reading the directory
8070Sstevel@tonic-gate 			 */
8080Sstevel@tonic-gate 			gethrestime(&to->tn_atime);
8090Sstevel@tonic-gate 			goto out;
8100Sstevel@tonic-gate 		}
8110Sstevel@tonic-gate 		mutex_exit(&to->tn_tlock);
8120Sstevel@tonic-gate 	} else if (doingdirectory) {
8130Sstevel@tonic-gate 		error = ENOTDIR;
8140Sstevel@tonic-gate 		goto out;
8150Sstevel@tonic-gate 	}
8160Sstevel@tonic-gate 
8170Sstevel@tonic-gate 	tmpfs_hash_change(where, fromtp);
8180Sstevel@tonic-gate 	gethrestime(&now);
8190Sstevel@tonic-gate 	toparent->tn_mtime = now;
8200Sstevel@tonic-gate 	toparent->tn_ctime = now;
8210Sstevel@tonic-gate 
8220Sstevel@tonic-gate 	/*
8230Sstevel@tonic-gate 	 * Upgrade to write lock on "to" (i.e., the target tmpnode).
8240Sstevel@tonic-gate 	 */
8250Sstevel@tonic-gate 	rw_exit(&to->tn_rwlock);
8260Sstevel@tonic-gate 	rw_enter(&to->tn_rwlock, RW_WRITER);
8270Sstevel@tonic-gate 
8280Sstevel@tonic-gate 	/*
8290Sstevel@tonic-gate 	 * Decrement the link count of the target tmpnode.
8300Sstevel@tonic-gate 	 */
8310Sstevel@tonic-gate 	DECR_COUNT(&to->tn_nlink, &to->tn_tlock);
8320Sstevel@tonic-gate 	to->tn_ctime = now;
8330Sstevel@tonic-gate 
8340Sstevel@tonic-gate 	if (doingdirectory) {
8350Sstevel@tonic-gate 		/*
8360Sstevel@tonic-gate 		 * The entry for "to" no longer exists so release the vfslock.
8370Sstevel@tonic-gate 		 */
8380Sstevel@tonic-gate 		vn_vfsunlock(TNTOV(to));
8390Sstevel@tonic-gate 
8400Sstevel@tonic-gate 		/*
8410Sstevel@tonic-gate 		 * Decrement the target link count and delete all entires.
8420Sstevel@tonic-gate 		 */
8430Sstevel@tonic-gate 		tdirtrunc(to);
8440Sstevel@tonic-gate 		ASSERT(to->tn_nlink == 0);
8450Sstevel@tonic-gate 
8460Sstevel@tonic-gate 		/*
8470Sstevel@tonic-gate 		 * Renaming a directory with the parent different
8480Sstevel@tonic-gate 		 * requires that ".." be rewritten.  The window is
8490Sstevel@tonic-gate 		 * still there for ".." to be inconsistent, but this
8500Sstevel@tonic-gate 		 * is unavoidable, and a lot shorter than when it was
8510Sstevel@tonic-gate 		 * done in a user process.
8520Sstevel@tonic-gate 		 */
8530Sstevel@tonic-gate 		if (fromparent != toparent)
8540Sstevel@tonic-gate 			tdirfixdotdot(fromtp, fromparent, toparent);
8550Sstevel@tonic-gate 	}
8560Sstevel@tonic-gate out:
8570Sstevel@tonic-gate 	rw_exit(&to->tn_rwlock);
8580Sstevel@tonic-gate 	rw_exit(&fromtp->tn_rwlock);
8590Sstevel@tonic-gate 	return (error);
8600Sstevel@tonic-gate }
8610Sstevel@tonic-gate 
8620Sstevel@tonic-gate static void
tdirfixdotdot(struct tmpnode * fromtp,struct tmpnode * fromparent,struct tmpnode * toparent)8630Sstevel@tonic-gate tdirfixdotdot(
8640Sstevel@tonic-gate 	struct tmpnode	*fromtp,	/* child directory */
8650Sstevel@tonic-gate 	struct tmpnode	*fromparent,	/* old parent directory */
8660Sstevel@tonic-gate 	struct tmpnode	*toparent)	/* new parent directory */
8670Sstevel@tonic-gate {
8680Sstevel@tonic-gate 	struct tdirent	*dotdot;
8690Sstevel@tonic-gate 
8700Sstevel@tonic-gate 	ASSERT(RW_LOCK_HELD(&toparent->tn_rwlock));
8710Sstevel@tonic-gate 
8720Sstevel@tonic-gate 	/*
8730Sstevel@tonic-gate 	 * Increment the link count in the new parent tmpnode
8740Sstevel@tonic-gate 	 */
8750Sstevel@tonic-gate 	INCR_COUNT(&toparent->tn_nlink, &toparent->tn_tlock);
8760Sstevel@tonic-gate 	gethrestime(&toparent->tn_ctime);
8770Sstevel@tonic-gate 
8780Sstevel@tonic-gate 	dotdot = tmpfs_hash_lookup("..", fromtp, 0, NULL);
8790Sstevel@tonic-gate 
8800Sstevel@tonic-gate 	ASSERT(dotdot->td_tmpnode == fromparent);
8810Sstevel@tonic-gate 	dotdot->td_tmpnode = toparent;
8820Sstevel@tonic-gate 
8830Sstevel@tonic-gate 	/*
8840Sstevel@tonic-gate 	 * Decrement the link count of the old parent tmpnode.
8850Sstevel@tonic-gate 	 * If fromparent is NULL, then this is a new directory link;
8860Sstevel@tonic-gate 	 * it has no parent, so we need not do anything.
8870Sstevel@tonic-gate 	 */
8880Sstevel@tonic-gate 	if (fromparent != NULL) {
8890Sstevel@tonic-gate 		mutex_enter(&fromparent->tn_tlock);
8900Sstevel@tonic-gate 		if (fromparent->tn_nlink != 0) {
8910Sstevel@tonic-gate 			fromparent->tn_nlink--;
8920Sstevel@tonic-gate 			gethrestime(&fromparent->tn_ctime);
8930Sstevel@tonic-gate 		}
8940Sstevel@tonic-gate 		mutex_exit(&fromparent->tn_tlock);
8950Sstevel@tonic-gate 	}
8960Sstevel@tonic-gate }
8970Sstevel@tonic-gate 
8980Sstevel@tonic-gate static int
tdiraddentry(struct tmpnode * dir,struct tmpnode * tp,char * name,enum de_op op,struct tmpnode * fromtp)8990Sstevel@tonic-gate tdiraddentry(
9000Sstevel@tonic-gate 	struct tmpnode	*dir,	/* target directory to make entry in */
9010Sstevel@tonic-gate 	struct tmpnode	*tp,	/* new tmpnode */
9020Sstevel@tonic-gate 	char		*name,
9030Sstevel@tonic-gate 	enum de_op	op,
9040Sstevel@tonic-gate 	struct tmpnode	*fromtp)
9050Sstevel@tonic-gate {
9060Sstevel@tonic-gate 	struct tdirent *tdp, *tpdp;
9070Sstevel@tonic-gate 	size_t		namelen, alloc_size;
9080Sstevel@tonic-gate 	timestruc_t	now;
9090Sstevel@tonic-gate 
9100Sstevel@tonic-gate 	/*
9110Sstevel@tonic-gate 	 * Make sure the parent directory wasn't removed from
9120Sstevel@tonic-gate 	 * underneath the caller.
9130Sstevel@tonic-gate 	 */
9140Sstevel@tonic-gate 	if (dir->tn_dir == NULL)
9150Sstevel@tonic-gate 		return (ENOENT);
9160Sstevel@tonic-gate 
9170Sstevel@tonic-gate 	/*
9180Sstevel@tonic-gate 	 * Check that everything is on the same filesystem.
9190Sstevel@tonic-gate 	 */
9200Sstevel@tonic-gate 	if (tp->tn_vnode->v_vfsp != dir->tn_vnode->v_vfsp)
9210Sstevel@tonic-gate 		return (EXDEV);
9220Sstevel@tonic-gate 
9230Sstevel@tonic-gate 	/*
9240Sstevel@tonic-gate 	 * Allocate and initialize directory entry
9250Sstevel@tonic-gate 	 */
9260Sstevel@tonic-gate 	namelen = strlen(name) + 1;
9270Sstevel@tonic-gate 	alloc_size = namelen + sizeof (struct tdirent);
9280Sstevel@tonic-gate 	tdp = tmp_memalloc(alloc_size, 0);
9290Sstevel@tonic-gate 	if (tdp == NULL)
9300Sstevel@tonic-gate 		return (ENOSPC);
9310Sstevel@tonic-gate 
9320Sstevel@tonic-gate 	if ((op == DE_RENAME) && (tp->tn_type == VDIR))
9330Sstevel@tonic-gate 		tdirfixdotdot(tp, fromtp, dir);
9340Sstevel@tonic-gate 
9350Sstevel@tonic-gate 	dir->tn_size += alloc_size;
9360Sstevel@tonic-gate 	dir->tn_dirents++;
9370Sstevel@tonic-gate 	tdp->td_tmpnode = tp;
9380Sstevel@tonic-gate 	tdp->td_parent = dir;
9390Sstevel@tonic-gate 
9400Sstevel@tonic-gate 	/*
9410Sstevel@tonic-gate 	 * The directory entry and its name were allocated sequentially.
9420Sstevel@tonic-gate 	 */
9430Sstevel@tonic-gate 	tdp->td_name = (char *)tdp + sizeof (struct tdirent);
9440Sstevel@tonic-gate 	(void) strcpy(tdp->td_name, name);
9450Sstevel@tonic-gate 
9460Sstevel@tonic-gate 	tmpfs_hash_in(tdp);
9470Sstevel@tonic-gate 
9480Sstevel@tonic-gate 	/*
9490Sstevel@tonic-gate 	 * Some utilities expect the size of a directory to remain
9500Sstevel@tonic-gate 	 * somewhat static.  For example, a routine which unlinks
9510Sstevel@tonic-gate 	 * files between calls to readdir(); the size of the
9520Sstevel@tonic-gate 	 * directory changes from underneath it and so the real
9530Sstevel@tonic-gate 	 * directory offset in bytes is invalid.  To circumvent
9540Sstevel@tonic-gate 	 * this problem, we initialize a directory entry with an
9550Sstevel@tonic-gate 	 * phony offset, and use this offset to determine end of
9560Sstevel@tonic-gate 	 * file in tmp_readdir.
9570Sstevel@tonic-gate 	 */
9580Sstevel@tonic-gate 	tpdp = dir->tn_dir->td_prev;
9590Sstevel@tonic-gate 	/*
9600Sstevel@tonic-gate 	 * Install at first empty "slot" in directory list.
9610Sstevel@tonic-gate 	 */
9620Sstevel@tonic-gate 	while (tpdp->td_next != NULL && (tpdp->td_next->td_offset -
9630Sstevel@tonic-gate 	    tpdp->td_offset) <= 1) {
9640Sstevel@tonic-gate 		ASSERT(tpdp->td_next != tpdp);
9650Sstevel@tonic-gate 		ASSERT(tpdp->td_prev != tpdp);
9660Sstevel@tonic-gate 		ASSERT(tpdp->td_next->td_offset > tpdp->td_offset);
9670Sstevel@tonic-gate 		tpdp = tpdp->td_next;
9680Sstevel@tonic-gate 	}
9690Sstevel@tonic-gate 	tdp->td_offset = tpdp->td_offset + 1;
9700Sstevel@tonic-gate 
9710Sstevel@tonic-gate 	/*
9720Sstevel@tonic-gate 	 * If we're at the end of the dirent list and the offset (which
9730Sstevel@tonic-gate 	 * is necessarily the largest offset in this directory) is more
9740Sstevel@tonic-gate 	 * than twice the number of dirents, that means the directory is
9750Sstevel@tonic-gate 	 * 50% holes.  At this point we reset the slot pointer back to
9760Sstevel@tonic-gate 	 * the beginning of the directory so we start using the holes.
9770Sstevel@tonic-gate 	 * The idea is that if there are N dirents, there must also be
9780Sstevel@tonic-gate 	 * N holes, so we can satisfy the next N creates by walking at
9790Sstevel@tonic-gate 	 * most 2N entries; thus the average cost of a create is constant.
9800Sstevel@tonic-gate 	 * Note that we use the first dirent's td_prev as the roving
9810Sstevel@tonic-gate 	 * slot pointer; it's ugly, but it saves a word in every dirent.
9820Sstevel@tonic-gate 	 */
9830Sstevel@tonic-gate 	if (tpdp->td_next == NULL && tpdp->td_offset > 2 * dir->tn_dirents)
9840Sstevel@tonic-gate 		dir->tn_dir->td_prev = dir->tn_dir->td_next;
9850Sstevel@tonic-gate 	else
9860Sstevel@tonic-gate 		dir->tn_dir->td_prev = tdp;
9870Sstevel@tonic-gate 
9880Sstevel@tonic-gate 	ASSERT(tpdp->td_next != tpdp);
9890Sstevel@tonic-gate 	ASSERT(tpdp->td_prev != tpdp);
9900Sstevel@tonic-gate 
9910Sstevel@tonic-gate 	tdp->td_next = tpdp->td_next;
9920Sstevel@tonic-gate 	if (tdp->td_next) {
9930Sstevel@tonic-gate 		tdp->td_next->td_prev = tdp;
9940Sstevel@tonic-gate 	}
9950Sstevel@tonic-gate 	tdp->td_prev = tpdp;
9960Sstevel@tonic-gate 	tpdp->td_next = tdp;
9970Sstevel@tonic-gate 
9980Sstevel@tonic-gate 	ASSERT(tdp->td_next != tdp);
9990Sstevel@tonic-gate 	ASSERT(tdp->td_prev != tdp);
10000Sstevel@tonic-gate 	ASSERT(tpdp->td_next != tpdp);
10010Sstevel@tonic-gate 	ASSERT(tpdp->td_prev != tpdp);
10020Sstevel@tonic-gate 
10030Sstevel@tonic-gate 	gethrestime(&now);
10040Sstevel@tonic-gate 	dir->tn_mtime = now;
10050Sstevel@tonic-gate 	dir->tn_ctime = now;
10060Sstevel@tonic-gate 
10070Sstevel@tonic-gate 	return (0);
10080Sstevel@tonic-gate }
10090Sstevel@tonic-gate 
10100Sstevel@tonic-gate static int
tdirmaketnode(struct tmpnode * dir,struct tmount * tm,struct vattr * va,enum de_op op,struct tmpnode ** newnode,struct cred * cred)10110Sstevel@tonic-gate tdirmaketnode(
10120Sstevel@tonic-gate 	struct tmpnode *dir,
10130Sstevel@tonic-gate 	struct tmount	*tm,
10140Sstevel@tonic-gate 	struct vattr	*va,
10150Sstevel@tonic-gate 	enum	de_op	op,
10160Sstevel@tonic-gate 	struct tmpnode **newnode,
10170Sstevel@tonic-gate 	struct cred	*cred)
10180Sstevel@tonic-gate {
10190Sstevel@tonic-gate 	struct tmpnode *tp;
10200Sstevel@tonic-gate 	enum vtype	type;
10210Sstevel@tonic-gate 
10220Sstevel@tonic-gate 	ASSERT(va != NULL);
10230Sstevel@tonic-gate 	ASSERT(op == DE_CREATE || op == DE_MKDIR);
10240Sstevel@tonic-gate 	if (((va->va_mask & AT_ATIME) && TIMESPEC_OVERFLOW(&va->va_atime)) ||
10250Sstevel@tonic-gate 	    ((va->va_mask & AT_MTIME) && TIMESPEC_OVERFLOW(&va->va_mtime)))
10260Sstevel@tonic-gate 		return (EOVERFLOW);
10270Sstevel@tonic-gate 	type = va->va_type;
10280Sstevel@tonic-gate 	tp = tmp_memalloc(sizeof (struct tmpnode), TMP_MUSTHAVE);
10290Sstevel@tonic-gate 	tmpnode_init(tm, tp, va, cred);
10300Sstevel@tonic-gate 
10310Sstevel@tonic-gate 	/* setup normal file/dir's extended attribute directory */
10320Sstevel@tonic-gate 	if (dir->tn_flags & ISXATTR) {
10330Sstevel@tonic-gate 		/* parent dir is , mark file as xattr */
10340Sstevel@tonic-gate 		tp->tn_flags |= ISXATTR;
10350Sstevel@tonic-gate 	}
10360Sstevel@tonic-gate 
10370Sstevel@tonic-gate 
10380Sstevel@tonic-gate 	if (type == VBLK || type == VCHR) {
10390Sstevel@tonic-gate 		tp->tn_vnode->v_rdev = tp->tn_rdev = va->va_rdev;
10400Sstevel@tonic-gate 	} else {
10410Sstevel@tonic-gate 		tp->tn_vnode->v_rdev = tp->tn_rdev = NODEV;
10420Sstevel@tonic-gate 	}
10430Sstevel@tonic-gate 	tp->tn_vnode->v_type = type;
10440Sstevel@tonic-gate 	tp->tn_uid = crgetuid(cred);
10450Sstevel@tonic-gate 
10460Sstevel@tonic-gate 	/*
10470Sstevel@tonic-gate 	 * To determine the group-id of the created file:
10480Sstevel@tonic-gate 	 *   1) If the gid is set in the attribute list (non-Sun & pre-4.0
10490Sstevel@tonic-gate 	 *	clients are not likely to set the gid), then use it if
10500Sstevel@tonic-gate 	 *	the process is privileged, belongs to the target group,
10510Sstevel@tonic-gate 	 *	or the group is the same as the parent directory.
10520Sstevel@tonic-gate 	 *   2) If the filesystem was not mounted with the Old-BSD-compatible
10530Sstevel@tonic-gate 	 *	GRPID option, and the directory's set-gid bit is clear,
10540Sstevel@tonic-gate 	 *	then use the process's gid.
10550Sstevel@tonic-gate 	 *   3) Otherwise, set the group-id to the gid of the parent directory.
10560Sstevel@tonic-gate 	 */
10570Sstevel@tonic-gate 	if ((va->va_mask & AT_GID) &&
10580Sstevel@tonic-gate 	    ((va->va_gid == dir->tn_gid) || groupmember(va->va_gid, cred) ||
10590Sstevel@tonic-gate 	    secpolicy_vnode_create_gid(cred) == 0)) {
10600Sstevel@tonic-gate 		/*
10610Sstevel@tonic-gate 		 * XXX - is this only the case when a 4.0 NFS client, or a
10620Sstevel@tonic-gate 		 * client derived from that code, makes a call over the wire?
10630Sstevel@tonic-gate 		 */
10640Sstevel@tonic-gate 		tp->tn_gid = va->va_gid;
10650Sstevel@tonic-gate 	} else {
10660Sstevel@tonic-gate 		if (dir->tn_mode & VSGID)
10670Sstevel@tonic-gate 			tp->tn_gid = dir->tn_gid;
10680Sstevel@tonic-gate 		else
10690Sstevel@tonic-gate 			tp->tn_gid = crgetgid(cred);
10700Sstevel@tonic-gate 	}
10710Sstevel@tonic-gate 	/*
10720Sstevel@tonic-gate 	 * If we're creating a directory, and the parent directory has the
10730Sstevel@tonic-gate 	 * set-GID bit set, set it on the new directory.
10740Sstevel@tonic-gate 	 * Otherwise, if the user is neither privileged nor a member of the
10750Sstevel@tonic-gate 	 * file's new group, clear the file's set-GID bit.
10760Sstevel@tonic-gate 	 */
10770Sstevel@tonic-gate 	if (dir->tn_mode & VSGID && type == VDIR)
10780Sstevel@tonic-gate 		tp->tn_mode |= VSGID;
10790Sstevel@tonic-gate 	else {
10800Sstevel@tonic-gate 		if ((tp->tn_mode & VSGID) &&
10810Sstevel@tonic-gate 		    secpolicy_vnode_setids_setgids(cred, tp->tn_gid) != 0)
10820Sstevel@tonic-gate 			tp->tn_mode &= ~VSGID;
10830Sstevel@tonic-gate 	}
10840Sstevel@tonic-gate 
10850Sstevel@tonic-gate 	if (va->va_mask & AT_ATIME)
10860Sstevel@tonic-gate 		tp->tn_atime = va->va_atime;
10870Sstevel@tonic-gate 	if (va->va_mask & AT_MTIME)
10880Sstevel@tonic-gate 		tp->tn_mtime = va->va_mtime;
10890Sstevel@tonic-gate 
10900Sstevel@tonic-gate 	if (op == DE_MKDIR)
10910Sstevel@tonic-gate 		tdirinit(dir, tp);
10920Sstevel@tonic-gate 
10930Sstevel@tonic-gate 	*newnode = tp;
10940Sstevel@tonic-gate 	return (0);
10950Sstevel@tonic-gate }
1096