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