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