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