4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
22 * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
26 #pragma ident "%Z%%M% %I% %E% SMI"
28 #include <sys/types.h>
29 #include <sys/param.h>
30 #include <sys/sysmacros.h>
31 #include <sys/systm.h>
34 #include <sys/vnode.h>
35 #include <sys/errno.h>
36 #include <sys/cmn_err.h>
39 #include <sys/debug.h>
40 #include <sys/policy.h>
41 #include <sys/fs/tmpnode.h>
42 #include <sys/fs/tmp.h>
43 #include <sys/vtrace.h>
45 static int tdircheckpath(struct tmpnode
*, struct tmpnode
*, struct cred
*);
46 static int tdirrename(struct tmpnode
*, struct tmpnode
*, struct tmpnode
*,
47 char *, struct tmpnode
*, struct tdirent
*, struct cred
*);
48 static void tdirfixdotdot(struct tmpnode
*, struct tmpnode
*, struct tmpnode
*);
49 static int tdirmaketnode(struct tmpnode
*, struct tmount
*, struct vattr
*,
50 enum de_op
, struct tmpnode
**, struct cred
*);
51 static int tdiraddentry(struct tmpnode
*, struct tmpnode
*, char *,
52 enum de_op
, struct tmpnode
*);
55 #define T_HASH_SIZE 8192 /* must be power of 2 */
56 #define T_MUTEX_SIZE 64
58 static struct tdirent
*t_hashtable
[T_HASH_SIZE
];
59 static kmutex_t t_hashmutex
[T_MUTEX_SIZE
];
61 #define T_HASH_INDEX(a) ((a) & (T_HASH_SIZE-1))
62 #define T_MUTEX_INDEX(a) ((a) & (T_MUTEX_SIZE-1))
64 #define TMPFS_HASH(tp, name, hash) \
67 hash = (uint_t)(uintptr_t)(tp) >> 8; \
68 for (Xcp = (name); (Xc = *Xcp) != 0; Xcp++) \
69 hash = (hash << 4) + hash + (uint_t)Xc; \
77 for (ix
= 0; ix
< T_MUTEX_SIZE
; ix
++)
78 mutex_init(&t_hashmutex
[ix
], NULL
, MUTEX_DEFAULT
, NULL
);
82 * This routine is where the rubber meets the road for identities.
85 tmpfs_hash_in(struct tdirent
*t
)
88 struct tdirent
**prevpp
;
91 TMPFS_HASH(t
->td_parent
, t
->td_name
, hash
);
93 prevpp
= &t_hashtable
[T_HASH_INDEX(hash
)];
94 t_hmtx
= &t_hashmutex
[T_MUTEX_INDEX(hash
)];
102 * Remove tdirent *t from the hash list.
105 tmpfs_hash_out(struct tdirent
*t
)
108 struct tdirent
**prevpp
;
112 prevpp
= &t_hashtable
[T_HASH_INDEX(hash
)];
113 t_hmtx
= &t_hashmutex
[T_MUTEX_INDEX(hash
)];
116 prevpp
= &(*prevpp
)->td_link
;
117 *prevpp
= t
->td_link
;
122 * Currently called by tdirrename() only.
123 * rename operation needs to be done with lock held, to ensure that
124 * no other operations can access the tmpnode at the same instance.
127 tmpfs_hash_change(struct tdirent
*tdp
, struct tmpnode
*fromtp
)
133 t_hmtx
= &t_hashmutex
[T_MUTEX_INDEX(hash
)];
135 tdp
->td_tmpnode
= fromtp
;
139 static struct tdirent
*
140 tmpfs_hash_lookup(char *name
, struct tmpnode
*parent
, uint_t hold
,
141 struct tmpnode
**found
)
148 TMPFS_HASH(parent
, name
, hash
);
149 t_hmtx
= &t_hashmutex
[T_MUTEX_INDEX(hash
)];
151 l
= t_hashtable
[T_HASH_INDEX(hash
)];
153 if ((l
->td_hash
== hash
) &&
154 (l
->td_parent
== parent
) &&
155 (strcmp(l
->td_name
, name
) == 0)) {
157 * We need to make sure that the tmpnode that
158 * we put a hold on is the same one that we pass back.
159 * Hence, temporary variable tnp is necessary.
179 * Search directory 'parent' for entry 'name'.
181 * The calling thread can't hold the write version
182 * of the rwlock for the directory being searched
184 * 0 is returned on success and *foundtp points
185 * to the found tmpnode with its vnode held.
189 struct tmpnode
*parent
,
191 struct tmpnode
**foundtp
,
197 if (parent
->tn_type
!= VDIR
)
200 if ((error
= tmp_taccess(parent
, VEXEC
, cred
)))
204 tmpnode_hold(parent
);
210 * Search the directory for the matching name
211 * We need the lock protecting the tn_dir list
212 * so that it doesn't change out from underneath us.
213 * tmpfs_hash_lookup() will pass back the tmpnode
217 if (tmpfs_hash_lookup(name
, parent
, 1, foundtp
) != NULL
) {
226 * Enter a directory entry for 'name' and 'tp' into directory 'dir'
228 * Returns 0 on success.
233 struct tmpnode
*dir
, /* target directory to make entry in */
234 char *name
, /* name of entry */
235 enum de_op op
, /* entry operation */
236 struct tmpnode
*fromparent
, /* source directory if rename */
237 struct tmpnode
*tp
, /* source tmpnode, if link/rename */
239 struct tmpnode
**tpp
, /* return tmpnode, if create/mkdir */
241 caller_context_t
*ctp
)
244 struct tmpnode
*found
= NULL
;
249 * tn_rwlock is held to serialize direnter and dirdeletes
251 ASSERT(RW_WRITE_HELD(&dir
->tn_rwlock
));
252 ASSERT(dir
->tn_type
== VDIR
);
255 * Don't allow '/' characters in pathname component
256 * (thus in ufs_direnter()).
258 for (s
= name
; *s
; s
++)
263 panic("tdirenter: NULL name");
266 * For link and rename lock the source entry and check the link count
267 * to see if it has been removed while it was unlocked.
269 if (op
== DE_LINK
|| op
== DE_RENAME
) {
271 rw_enter(&tp
->tn_rwlock
, RW_WRITER
);
272 mutex_enter(&tp
->tn_tlock
);
273 if (tp
->tn_nlink
== 0) {
274 mutex_exit(&tp
->tn_tlock
);
276 rw_exit(&tp
->tn_rwlock
);
280 if (tp
->tn_nlink
== MAXLINK
) {
281 mutex_exit(&tp
->tn_tlock
);
283 rw_exit(&tp
->tn_rwlock
);
287 gethrestime(&tp
->tn_ctime
);
288 mutex_exit(&tp
->tn_tlock
);
290 rw_exit(&tp
->tn_rwlock
);
294 * This might be a "dangling detached directory".
295 * it could have been removed, but a reference
296 * to it kept in u_cwd. don't bother searching
297 * it, and with any luck the user will get tired
298 * of dealing with us and cd to some absolute
299 * pathway. *sigh*, thus in ufs, too.
301 if (dir
->tn_nlink
== 0) {
307 * If this is a rename of a directory and the parent is
308 * different (".." must be changed), then the source
309 * directory must not be in the directory hierarchy
310 * above the target, as this would orphan everything
311 * below the source directory.
313 if (op
== DE_RENAME
) {
318 if (tp
->tn_type
== VDIR
) {
319 if ((fromparent
!= dir
) &&
320 (error
= tdircheckpath(tp
, dir
, cred
))) {
327 * Search for the entry. Return "found" if it exists.
329 tdp
= tmpfs_hash_lookup(name
, dir
, 1, &found
);
345 error
= tdirrename(fromparent
, tp
,
346 dir
, name
, found
, tdp
, cred
);
349 vnevent_rename_dest(TNTOV(found
),
350 TNTOV(dir
), name
, ctp
);
359 * Can't link to an existing file.
368 * The entry does not exist. Check write permission in
369 * directory to see if entry can be created.
371 if (error
= tmp_taccess(dir
, VWRITE
, cred
))
373 if (op
== DE_CREATE
|| op
== DE_MKDIR
) {
375 * Make new tmpnode and directory entry as required.
377 error
= tdirmaketnode(dir
, tm
, va
, op
, &tp
, cred
);
381 if (error
= tdiraddentry(dir
, tp
, name
, op
, fromparent
)) {
382 if (op
== DE_CREATE
|| op
== DE_MKDIR
) {
384 * Unmake the inode we just made.
386 rw_enter(&tp
->tn_rwlock
, RW_WRITER
);
387 if ((tp
->tn_type
) == VDIR
) {
390 * cleanup allocs made by tdirinit()
394 mutex_enter(&tp
->tn_tlock
);
396 mutex_exit(&tp
->tn_tlock
);
397 gethrestime(&tp
->tn_ctime
);
398 rw_exit(&tp
->tn_rwlock
);
404 } else if (op
== DE_CREATE
|| op
== DE_MKDIR
) {
410 if (error
&& (op
== DE_LINK
|| op
== DE_RENAME
)) {
412 * Undo bumped link count.
414 DECR_COUNT(&tp
->tn_nlink
, &tp
->tn_tlock
);
415 gethrestime(&tp
->tn_ctime
);
421 * Delete entry tp of name "nm" from dir.
422 * Free dir entry space and decrement link count on tmpnode(s).
424 * Return 0 on success.
434 struct tdirent
*tpdp
;
440 ASSERT(RW_WRITE_HELD(&dir
->tn_rwlock
));
441 ASSERT(RW_WRITE_HELD(&tp
->tn_rwlock
));
442 ASSERT(dir
->tn_type
== VDIR
);
445 panic("tdirdelete: NULL name for %p", (void *)tp
);
448 * return error when removing . and ..
453 if (nm
[1] == '.' && nm
[2] == '\0')
454 return (EEXIST
); /* thus in ufs */
457 if (error
= tmp_taccess(dir
, VEXEC
|VWRITE
, cred
))
461 * If the parent directory is "sticky", then the user must
462 * own the parent directory or the file in it, or else must
463 * have permission to write the file. Otherwise it may not
464 * be deleted (except by privileged users).
465 * Same as ufs_dirremove.
467 if ((error
= tmp_sticky_remove_access(dir
, tp
, cred
)) != 0)
470 if (dir
->tn_dir
== NULL
)
473 tpdp
= tmpfs_hash_lookup(nm
, dir
, 0, &tnp
);
476 * If it is gone, some other thread got here first!
477 * Return error ENOENT.
483 * If the tmpnode in the tdirent changed, we were probably
484 * the victim of a concurrent rename operation. The original
485 * is gone, so return that status (same as UFS).
490 tmpfs_hash_out(tpdp
);
493 * Take tpdp out of the directory list.
495 ASSERT(tpdp
->td_next
!= tpdp
);
496 ASSERT(tpdp
->td_prev
!= tpdp
);
498 tpdp
->td_prev
->td_next
= tpdp
->td_next
;
501 tpdp
->td_next
->td_prev
= tpdp
->td_prev
;
505 * If the roving slot pointer happens to match tpdp,
506 * point it at the previous dirent.
508 if (dir
->tn_dir
->td_prev
== tpdp
) {
509 dir
->tn_dir
->td_prev
= tpdp
->td_prev
;
511 ASSERT(tpdp
->td_next
!= tpdp
);
512 ASSERT(tpdp
->td_prev
!= tpdp
);
515 * tpdp points to the correct directory entry
517 namelen
= strlen(tpdp
->td_name
) + 1;
519 tmp_memfree(tpdp
, sizeof (struct tdirent
) + namelen
);
520 dir
->tn_size
-= (sizeof (struct tdirent
) + namelen
);
528 ASSERT(tp
->tn_nlink
> 0);
529 DECR_COUNT(&tp
->tn_nlink
, &tp
->tn_tlock
);
530 if (op
== DR_RMDIR
&& tp
->tn_type
== VDIR
) {
532 ASSERT(tp
->tn_nlink
== 0);
538 * tdirinit is used internally to initialize a directory (dir)
539 * with '.' and '..' entries without checking permissions and locking
543 struct tmpnode
*parent
, /* parent of directory to initialize */
544 struct tmpnode
*dir
) /* the new directory */
546 struct tdirent
*dot
, *dotdot
;
549 ASSERT(RW_WRITE_HELD(&parent
->tn_rwlock
));
550 ASSERT(dir
->tn_type
== VDIR
);
552 dot
= tmp_memalloc(sizeof (struct tdirent
) + 2, TMP_MUSTHAVE
);
553 dotdot
= tmp_memalloc(sizeof (struct tdirent
) + 3, TMP_MUSTHAVE
);
556 * Initialize the entries
558 dot
->td_tmpnode
= dir
;
560 dot
->td_name
= (char *)dot
+ sizeof (struct tdirent
);
561 dot
->td_name
[0] = '.';
562 dot
->td_parent
= dir
;
565 dotdot
->td_tmpnode
= parent
;
566 dotdot
->td_offset
= 1;
567 dotdot
->td_name
= (char *)dotdot
+ sizeof (struct tdirent
);
568 dotdot
->td_name
[0] = '.';
569 dotdot
->td_name
[1] = '.';
570 dotdot
->td_parent
= dir
;
571 tmpfs_hash_in(dotdot
);
574 * Initialize directory entry list.
576 dot
->td_next
= dotdot
;
577 dot
->td_prev
= dotdot
; /* dot's td_prev holds roving slot pointer */
578 dotdot
->td_next
= NULL
;
579 dotdot
->td_prev
= dot
;
586 * Link counts are special for the hidden attribute directory.
587 * The only explicit reference in the name space is "." and
588 * the reference through ".." is not counted on the parent
589 * file. The attrdir is created as a side effect to lookup,
590 * so don't change the ctime of the parent.
591 * Since tdirinit is called with both dir and parent being the
592 * same for the root vnode, we need to increment this before we set
593 * tn_nlink = 2 below.
595 if (!(dir
->tn_vnode
->v_flag
& V_XATTRDIR
)) {
596 INCR_COUNT(&parent
->tn_nlink
, &parent
->tn_tlock
);
597 parent
->tn_ctime
= now
;
601 dir
->tn_size
= 2 * sizeof (struct tdirent
) + 5; /* dot and dotdot */
608 * tdirtrunc is called to remove all directory entries under this directory.
611 tdirtrunc(struct tmpnode
*dir
)
617 int isvattrdir
, isdotdot
, skip_decr
;
619 ASSERT(RW_WRITE_HELD(&dir
->tn_rwlock
));
620 ASSERT(dir
->tn_type
== VDIR
);
622 isvattrdir
= (dir
->tn_vnode
->v_flag
& V_XATTRDIR
) ? 1 : 0;
623 for (tdp
= dir
->tn_dir
; tdp
; tdp
= dir
->tn_dir
) {
624 ASSERT(tdp
->td_next
!= tdp
);
625 ASSERT(tdp
->td_prev
!= tdp
);
626 ASSERT(tdp
->td_tmpnode
);
628 dir
->tn_dir
= tdp
->td_next
;
629 namelen
= strlen(tdp
->td_name
) + 1;
632 * Adjust the link counts to account for this directory
633 * entry removal. Hidden attribute directories may
634 * not be empty as they may be truncated as a side-
635 * effect of removing the parent. We do hold/rele
636 * operations to free up these tmpnodes.
638 * Skip the link count adjustment for parents of
639 * attribute directories as those link counts
640 * do not include the ".." reference in the hidden
643 tp
= tdp
->td_tmpnode
;
644 isdotdot
= (strcmp("..", tdp
->td_name
) == 0);
645 skip_decr
= (isvattrdir
&& isdotdot
);
647 ASSERT(tp
->tn_nlink
> 0);
648 DECR_COUNT(&tp
->tn_nlink
, &tp
->tn_tlock
);
653 tmp_memfree(tdp
, sizeof (struct tdirent
) + namelen
);
654 dir
->tn_size
-= (sizeof (struct tdirent
) + namelen
);
662 ASSERT(dir
->tn_dir
== NULL
);
663 ASSERT(dir
->tn_size
== 0);
664 ASSERT(dir
->tn_dirents
== 0);
668 * Check if the source directory is in the path of the target directory.
669 * The target directory is locked by the caller.
671 * XXX - The source and target's should be different upon entry.
675 struct tmpnode
*fromtp
,
676 struct tmpnode
*toparent
,
680 struct tmpnode
*dir
, *dotdot
;
683 ASSERT(RW_WRITE_HELD(&toparent
->tn_rwlock
));
685 tdp
= tmpfs_hash_lookup("..", toparent
, 1, &dotdot
);
691 if (dotdot
== toparent
) {
692 /* root of fs. search trivially satisfied. */
693 tmpnode_rele(dotdot
);
698 * Return error for cases like "mv c c/d",
699 * "mv c c/d/e" and so on.
701 if (dotdot
== fromtp
) {
702 tmpnode_rele(dotdot
);
707 error
= tdirlookup(dir
, "..", &dotdot
, cred
);
713 * We're okay if we traverse the directory tree up to
714 * the root directory and don't run into the
719 tmpnode_rele(dotdot
);
729 struct tmpnode
*fromparent
, /* parent directory of source */
730 struct tmpnode
*fromtp
, /* source tmpnode */
731 struct tmpnode
*toparent
, /* parent directory of target */
732 char *nm
, /* entry we are trying to change */
733 struct tmpnode
*to
, /* target tmpnode */
734 struct tdirent
*where
, /* target tmpnode directory entry */
735 struct cred
*cred
) /* credentials */
741 ASSERT(RW_WRITE_HELD(&toparent
->tn_rwlock
));
744 * Short circuit rename of something to itself.
747 return (ESAME
); /* special KLUDGE error code */
749 rw_enter(&fromtp
->tn_rwlock
, RW_READER
);
750 rw_enter(&to
->tn_rwlock
, RW_READER
);
753 * Check that everything is on the same filesystem.
755 if (to
->tn_vnode
->v_vfsp
!= toparent
->tn_vnode
->v_vfsp
||
756 to
->tn_vnode
->v_vfsp
!= fromtp
->tn_vnode
->v_vfsp
) {
762 * Must have write permission to rewrite target entry.
763 * Check for stickyness.
765 if ((error
= tmp_taccess(toparent
, VWRITE
, cred
)) != 0 ||
766 (error
= tmp_sticky_remove_access(toparent
, to
, cred
)) != 0)
770 * Ensure source and target are compatible (both directories
771 * or both not directories). If target is a directory it must
772 * be empty and have no links to it; in addition it must not
773 * be a mount point, and both the source and target must be
776 doingdirectory
= (fromtp
->tn_type
== VDIR
);
777 if (to
->tn_type
== VDIR
) {
778 if (!doingdirectory
) {
783 * vn_vfswlock will prevent mounts from using the directory
786 if (vn_vfswlock(TNTOV(to
))) {
790 if (vn_mountedvfs(TNTOV(to
)) != NULL
) {
791 vn_vfsunlock(TNTOV(to
));
796 mutex_enter(&to
->tn_tlock
);
797 if (to
->tn_dirents
> 2 || to
->tn_nlink
> 2) {
798 mutex_exit(&to
->tn_tlock
);
799 vn_vfsunlock(TNTOV(to
));
800 error
= EEXIST
; /* SIGH should be ENOTEMPTY */
802 * Update atime because checking tn_dirents is
803 * logically equivalent to reading the directory
805 gethrestime(&to
->tn_atime
);
808 mutex_exit(&to
->tn_tlock
);
809 } else if (doingdirectory
) {
814 tmpfs_hash_change(where
, fromtp
);
816 toparent
->tn_mtime
= now
;
817 toparent
->tn_ctime
= now
;
820 * Upgrade to write lock on "to" (i.e., the target tmpnode).
822 rw_exit(&to
->tn_rwlock
);
823 rw_enter(&to
->tn_rwlock
, RW_WRITER
);
826 * Decrement the link count of the target tmpnode.
828 DECR_COUNT(&to
->tn_nlink
, &to
->tn_tlock
);
831 if (doingdirectory
) {
833 * The entry for "to" no longer exists so release the vfslock.
835 vn_vfsunlock(TNTOV(to
));
838 * Decrement the target link count and delete all entires.
841 ASSERT(to
->tn_nlink
== 0);
844 * Renaming a directory with the parent different
845 * requires that ".." be rewritten. The window is
846 * still there for ".." to be inconsistent, but this
847 * is unavoidable, and a lot shorter than when it was
848 * done in a user process.
850 if (fromparent
!= toparent
)
851 tdirfixdotdot(fromtp
, fromparent
, toparent
);
854 rw_exit(&to
->tn_rwlock
);
855 rw_exit(&fromtp
->tn_rwlock
);
861 struct tmpnode
*fromtp
, /* child directory */
862 struct tmpnode
*fromparent
, /* old parent directory */
863 struct tmpnode
*toparent
) /* new parent directory */
865 struct tdirent
*dotdot
;
867 ASSERT(RW_LOCK_HELD(&toparent
->tn_rwlock
));
870 * Increment the link count in the new parent tmpnode
872 INCR_COUNT(&toparent
->tn_nlink
, &toparent
->tn_tlock
);
873 gethrestime(&toparent
->tn_ctime
);
875 dotdot
= tmpfs_hash_lookup("..", fromtp
, 0, NULL
);
877 ASSERT(dotdot
->td_tmpnode
== fromparent
);
878 dotdot
->td_tmpnode
= toparent
;
881 * Decrement the link count of the old parent tmpnode.
882 * If fromparent is NULL, then this is a new directory link;
883 * it has no parent, so we need not do anything.
885 if (fromparent
!= NULL
) {
886 mutex_enter(&fromparent
->tn_tlock
);
887 if (fromparent
->tn_nlink
!= 0) {
888 fromparent
->tn_nlink
--;
889 gethrestime(&fromparent
->tn_ctime
);
891 mutex_exit(&fromparent
->tn_tlock
);
897 struct tmpnode
*dir
, /* target directory to make entry in */
898 struct tmpnode
*tp
, /* new tmpnode */
901 struct tmpnode
*fromtp
)
903 struct tdirent
*tdp
, *tpdp
;
904 size_t namelen
, alloc_size
;
908 * Make sure the parent directory wasn't removed from
909 * underneath the caller.
911 if (dir
->tn_dir
== NULL
)
915 * Check that everything is on the same filesystem.
917 if (tp
->tn_vnode
->v_vfsp
!= dir
->tn_vnode
->v_vfsp
)
921 * Allocate and initialize directory entry
923 namelen
= strlen(name
) + 1;
924 alloc_size
= namelen
+ sizeof (struct tdirent
);
925 tdp
= tmp_memalloc(alloc_size
, 0);
929 if ((op
== DE_RENAME
) && (tp
->tn_type
== VDIR
))
930 tdirfixdotdot(tp
, fromtp
, dir
);
932 dir
->tn_size
+= alloc_size
;
934 tdp
->td_tmpnode
= tp
;
935 tdp
->td_parent
= dir
;
938 * The directory entry and its name were allocated sequentially.
940 tdp
->td_name
= (char *)tdp
+ sizeof (struct tdirent
);
941 (void) strcpy(tdp
->td_name
, name
);
946 * Some utilities expect the size of a directory to remain
947 * somewhat static. For example, a routine which unlinks
948 * files between calls to readdir(); the size of the
949 * directory changes from underneath it and so the real
950 * directory offset in bytes is invalid. To circumvent
951 * this problem, we initialize a directory entry with an
952 * phony offset, and use this offset to determine end of
953 * file in tmp_readdir.
955 tpdp
= dir
->tn_dir
->td_prev
;
957 * Install at first empty "slot" in directory list.
959 while (tpdp
->td_next
!= NULL
&& (tpdp
->td_next
->td_offset
-
960 tpdp
->td_offset
) <= 1) {
961 ASSERT(tpdp
->td_next
!= tpdp
);
962 ASSERT(tpdp
->td_prev
!= tpdp
);
963 ASSERT(tpdp
->td_next
->td_offset
> tpdp
->td_offset
);
964 tpdp
= tpdp
->td_next
;
966 tdp
->td_offset
= tpdp
->td_offset
+ 1;
969 * If we're at the end of the dirent list and the offset (which
970 * is necessarily the largest offset in this directory) is more
971 * than twice the number of dirents, that means the directory is
972 * 50% holes. At this point we reset the slot pointer back to
973 * the beginning of the directory so we start using the holes.
974 * The idea is that if there are N dirents, there must also be
975 * N holes, so we can satisfy the next N creates by walking at
976 * most 2N entries; thus the average cost of a create is constant.
977 * Note that we use the first dirent's td_prev as the roving
978 * slot pointer; it's ugly, but it saves a word in every dirent.
980 if (tpdp
->td_next
== NULL
&& tpdp
->td_offset
> 2 * dir
->tn_dirents
)
981 dir
->tn_dir
->td_prev
= dir
->tn_dir
->td_next
;
983 dir
->tn_dir
->td_prev
= tdp
;
985 ASSERT(tpdp
->td_next
!= tpdp
);
986 ASSERT(tpdp
->td_prev
!= tpdp
);
988 tdp
->td_next
= tpdp
->td_next
;
990 tdp
->td_next
->td_prev
= tdp
;
995 ASSERT(tdp
->td_next
!= tdp
);
996 ASSERT(tdp
->td_prev
!= tdp
);
997 ASSERT(tpdp
->td_next
!= tpdp
);
998 ASSERT(tpdp
->td_prev
!= tpdp
);
1001 dir
->tn_mtime
= now
;
1002 dir
->tn_ctime
= now
;
1009 struct tmpnode
*dir
,
1013 struct tmpnode
**newnode
,
1020 ASSERT(op
== DE_CREATE
|| op
== DE_MKDIR
);
1021 if (((va
->va_mask
& AT_ATIME
) && TIMESPEC_OVERFLOW(&va
->va_atime
)) ||
1022 ((va
->va_mask
& AT_MTIME
) && TIMESPEC_OVERFLOW(&va
->va_mtime
)))
1025 tp
= tmp_memalloc(sizeof (struct tmpnode
), TMP_MUSTHAVE
);
1026 tmpnode_init(tm
, tp
, va
, cred
);
1028 /* setup normal file/dir's extended attribute directory */
1029 if (dir
->tn_flags
& ISXATTR
) {
1030 /* parent dir is , mark file as xattr */
1031 tp
->tn_flags
|= ISXATTR
;
1035 if (type
== VBLK
|| type
== VCHR
) {
1036 tp
->tn_vnode
->v_rdev
= tp
->tn_rdev
= va
->va_rdev
;
1038 tp
->tn_vnode
->v_rdev
= tp
->tn_rdev
= NODEV
;
1040 tp
->tn_vnode
->v_type
= type
;
1041 tp
->tn_uid
= crgetuid(cred
);
1044 * To determine the group-id of the created file:
1045 * 1) If the gid is set in the attribute list (non-Sun & pre-4.0
1046 * clients are not likely to set the gid), then use it if
1047 * the process is privileged, belongs to the target group,
1048 * or the group is the same as the parent directory.
1049 * 2) If the filesystem was not mounted with the Old-BSD-compatible
1050 * GRPID option, and the directory's set-gid bit is clear,
1051 * then use the process's gid.
1052 * 3) Otherwise, set the group-id to the gid of the parent directory.
1054 if ((va
->va_mask
& AT_GID
) &&
1055 ((va
->va_gid
== dir
->tn_gid
) || groupmember(va
->va_gid
, cred
) ||
1056 secpolicy_vnode_create_gid(cred
) == 0)) {
1058 * XXX - is this only the case when a 4.0 NFS client, or a
1059 * client derived from that code, makes a call over the wire?
1061 tp
->tn_gid
= va
->va_gid
;
1063 if (dir
->tn_mode
& VSGID
)
1064 tp
->tn_gid
= dir
->tn_gid
;
1066 tp
->tn_gid
= crgetgid(cred
);
1069 * If we're creating a directory, and the parent directory has the
1070 * set-GID bit set, set it on the new directory.
1071 * Otherwise, if the user is neither privileged nor a member of the
1072 * file's new group, clear the file's set-GID bit.
1074 if (dir
->tn_mode
& VSGID
&& type
== VDIR
)
1075 tp
->tn_mode
|= VSGID
;
1077 if ((tp
->tn_mode
& VSGID
) &&
1078 secpolicy_vnode_setids_setgids(cred
, tp
->tn_gid
) != 0)
1079 tp
->tn_mode
&= ~VSGID
;
1082 if (va
->va_mask
& AT_ATIME
)
1083 tp
->tn_atime
= va
->va_atime
;
1084 if (va
->va_mask
& AT_MTIME
)
1085 tp
->tn_mtime
= va
->va_mtime
;