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