1 /* 2 * CDDL HEADER START 3 * 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. 7 * 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. 12 * 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] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 #include <sys/types.h> 29 #include <sys/param.h> 30 #include <sys/sysmacros.h> 31 #include <sys/systm.h> 32 #include <sys/time.h> 33 #include <sys/vfs.h> 34 #include <sys/vnode.h> 35 #include <sys/errno.h> 36 #include <sys/cmn_err.h> 37 #include <sys/cred.h> 38 #include <sys/stat.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> 44 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 *); 53 54 55 #define T_HASH_SIZE 8192 /* must be power of 2 */ 56 #define T_MUTEX_SIZE 64 57 58 static struct tdirent *t_hashtable[T_HASH_SIZE]; 59 static kmutex_t t_hashmutex[T_MUTEX_SIZE]; 60 61 #define T_HASH_INDEX(a) ((a) & (T_HASH_SIZE-1)) 62 #define T_MUTEX_INDEX(a) ((a) & (T_MUTEX_SIZE-1)) 63 64 #define TMPFS_HASH(tp, name, hash) \ 65 { \ 66 char Xc, *Xcp; \ 67 hash = (uint_t)(uintptr_t)(tp) >> 8; \ 68 for (Xcp = (name); (Xc = *Xcp) != 0; Xcp++) \ 69 hash = (hash << 4) + hash + (uint_t)Xc; \ 70 } 71 72 void 73 tmpfs_hash_init(void) 74 { 75 int ix; 76 77 for (ix = 0; ix < T_MUTEX_SIZE; ix++) 78 mutex_init(&t_hashmutex[ix], NULL, MUTEX_DEFAULT, NULL); 79 } 80 81 /* 82 * This routine is where the rubber meets the road for identities. 83 */ 84 static void 85 tmpfs_hash_in(struct tdirent *t) 86 { 87 uint_t hash; 88 struct tdirent **prevpp; 89 kmutex_t *t_hmtx; 90 91 TMPFS_HASH(t->td_parent, t->td_name, hash); 92 t->td_hash = hash; 93 prevpp = &t_hashtable[T_HASH_INDEX(hash)]; 94 t_hmtx = &t_hashmutex[T_MUTEX_INDEX(hash)]; 95 mutex_enter(t_hmtx); 96 t->td_link = *prevpp; 97 *prevpp = t; 98 mutex_exit(t_hmtx); 99 } 100 101 /* 102 * Remove tdirent *t from the hash list. 103 */ 104 static void 105 tmpfs_hash_out(struct tdirent *t) 106 { 107 uint_t hash; 108 struct tdirent **prevpp; 109 kmutex_t *t_hmtx; 110 111 hash = t->td_hash; 112 prevpp = &t_hashtable[T_HASH_INDEX(hash)]; 113 t_hmtx = &t_hashmutex[T_MUTEX_INDEX(hash)]; 114 mutex_enter(t_hmtx); 115 while (*prevpp != t) 116 prevpp = &(*prevpp)->td_link; 117 *prevpp = t->td_link; 118 mutex_exit(t_hmtx); 119 } 120 121 /* 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. 125 */ 126 static void 127 tmpfs_hash_change(struct tdirent *tdp, struct tmpnode *fromtp) 128 { 129 uint_t hash; 130 kmutex_t *t_hmtx; 131 132 hash = tdp->td_hash; 133 t_hmtx = &t_hashmutex[T_MUTEX_INDEX(hash)]; 134 mutex_enter(t_hmtx); 135 tdp->td_tmpnode = fromtp; 136 mutex_exit(t_hmtx); 137 } 138 139 static struct tdirent * 140 tmpfs_hash_lookup(char *name, struct tmpnode *parent, uint_t hold, 141 struct tmpnode **found) 142 { 143 struct tdirent *l; 144 uint_t hash; 145 kmutex_t *t_hmtx; 146 struct tmpnode *tnp; 147 148 TMPFS_HASH(parent, name, hash); 149 t_hmtx = &t_hashmutex[T_MUTEX_INDEX(hash)]; 150 mutex_enter(t_hmtx); 151 l = t_hashtable[T_HASH_INDEX(hash)]; 152 while (l) { 153 if ((l->td_hash == hash) && 154 (l->td_parent == parent) && 155 (strcmp(l->td_name, name) == 0)) { 156 /* 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. 160 */ 161 tnp = l->td_tmpnode; 162 if (hold) { 163 ASSERT(tnp); 164 tmpnode_hold(tnp); 165 } 166 if (found) 167 *found = tnp; 168 mutex_exit(t_hmtx); 169 return (l); 170 } else { 171 l = l->td_link; 172 } 173 } 174 mutex_exit(t_hmtx); 175 return (NULL); 176 } 177 178 /* 179 * Search directory 'parent' for entry 'name'. 180 * 181 * The calling thread can't hold the write version 182 * of the rwlock for the directory being searched 183 * 184 * 0 is returned on success and *foundtp points 185 * to the found tmpnode with its vnode held. 186 */ 187 int 188 tdirlookup( 189 struct tmpnode *parent, 190 char *name, 191 struct tmpnode **foundtp, 192 struct cred *cred) 193 { 194 int error; 195 196 *foundtp = NULL; 197 if (parent->tn_type != VDIR) 198 return (ENOTDIR); 199 200 if ((error = tmp_taccess(parent, VEXEC, cred))) 201 return (error); 202 203 if (*name == '\0') { 204 tmpnode_hold(parent); 205 *foundtp = parent; 206 return (0); 207 } 208 209 /* 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 214 * with a hold on it. 215 */ 216 217 if (tmpfs_hash_lookup(name, parent, 1, foundtp) != NULL) { 218 ASSERT(*foundtp); 219 return (0); 220 } 221 222 return (ENOENT); 223 } 224 225 /* 226 * Enter a directory entry for 'name' and 'tp' into directory 'dir' 227 * 228 * Returns 0 on success. 229 */ 230 int 231 tdirenter( 232 struct tmount *tm, 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 */ 238 struct vattr *va, 239 struct tmpnode **tpp, /* return tmpnode, if create/mkdir */ 240 struct cred *cred) 241 { 242 struct tdirent *tdp; 243 struct tmpnode *found = NULL; 244 int error = 0; 245 char *s; 246 247 /* 248 * tn_rwlock is held to serialize direnter and dirdeletes 249 */ 250 ASSERT(RW_WRITE_HELD(&dir->tn_rwlock)); 251 ASSERT(dir->tn_type == VDIR); 252 253 /* 254 * Don't allow '/' characters in pathname component 255 * (thus in ufs_direnter()). 256 */ 257 for (s = name; *s; s++) 258 if (*s == '/') 259 return (EACCES); 260 261 if (name[0] == '\0') 262 panic("tdirenter: NULL name"); 263 264 /* 265 * For link and rename lock the source entry and check the link count 266 * to see if it has been removed while it was unlocked. 267 */ 268 if (op == DE_LINK || op == DE_RENAME) { 269 if (tp != dir) 270 rw_enter(&tp->tn_rwlock, RW_WRITER); 271 mutex_enter(&tp->tn_tlock); 272 if (tp->tn_nlink == 0) { 273 mutex_exit(&tp->tn_tlock); 274 if (tp != dir) 275 rw_exit(&tp->tn_rwlock); 276 return (ENOENT); 277 } 278 279 if (tp->tn_nlink == MAXLINK) { 280 mutex_exit(&tp->tn_tlock); 281 if (tp != dir) 282 rw_exit(&tp->tn_rwlock); 283 return (EMLINK); 284 } 285 tp->tn_nlink++; 286 gethrestime(&tp->tn_ctime); 287 mutex_exit(&tp->tn_tlock); 288 if (tp != dir) 289 rw_exit(&tp->tn_rwlock); 290 } 291 292 /* 293 * This might be a "dangling detached directory". 294 * it could have been removed, but a reference 295 * to it kept in u_cwd. don't bother searching 296 * it, and with any luck the user will get tired 297 * of dealing with us and cd to some absolute 298 * pathway. *sigh*, thus in ufs, too. 299 */ 300 if (dir->tn_nlink == 0) { 301 error = ENOENT; 302 goto out; 303 } 304 305 /* 306 * If this is a rename of a directory and the parent is 307 * different (".." must be changed), then the source 308 * directory must not be in the directory hierarchy 309 * above the target, as this would orphan everything 310 * below the source directory. 311 */ 312 if (op == DE_RENAME) { 313 if (tp == dir) { 314 error = EINVAL; 315 goto out; 316 } 317 if (tp->tn_type == VDIR) { 318 if ((fromparent != dir) && 319 (error = tdircheckpath(tp, dir, cred))) { 320 goto out; 321 } 322 } 323 } 324 325 /* 326 * Search for the entry. Return "found" if it exists. 327 */ 328 tdp = tmpfs_hash_lookup(name, dir, 1, &found); 329 330 if (tdp) { 331 ASSERT(found); 332 switch (op) { 333 case DE_CREATE: 334 case DE_MKDIR: 335 if (tpp) { 336 *tpp = found; 337 error = EEXIST; 338 } else { 339 tmpnode_rele(found); 340 } 341 break; 342 343 case DE_RENAME: 344 error = tdirrename(fromparent, tp, 345 dir, name, found, tdp, cred); 346 if (error == 0) { 347 if (found != NULL) { 348 vnevent_rename_dest(TNTOV(found), 349 TNTOV(dir), name); 350 } 351 } 352 353 tmpnode_rele(found); 354 break; 355 356 case DE_LINK: 357 /* 358 * Can't link to an existing file. 359 */ 360 error = EEXIST; 361 tmpnode_rele(found); 362 break; 363 } 364 } else { 365 366 /* 367 * The entry does not exist. Check write permission in 368 * directory to see if entry can be created. 369 */ 370 if (error = tmp_taccess(dir, VWRITE, cred)) 371 goto out; 372 if (op == DE_CREATE || op == DE_MKDIR) { 373 /* 374 * Make new tmpnode and directory entry as required. 375 */ 376 error = tdirmaketnode(dir, tm, va, op, &tp, cred); 377 if (error) 378 goto out; 379 } 380 if (error = tdiraddentry(dir, tp, name, op, fromparent)) { 381 if (op == DE_CREATE || op == DE_MKDIR) { 382 /* 383 * Unmake the inode we just made. 384 */ 385 rw_enter(&tp->tn_rwlock, RW_WRITER); 386 if ((tp->tn_type) == VDIR) { 387 ASSERT(tdp == NULL); 388 /* 389 * cleanup allocs made by tdirinit() 390 */ 391 tdirtrunc(tp); 392 } 393 mutex_enter(&tp->tn_tlock); 394 tp->tn_nlink = 0; 395 mutex_exit(&tp->tn_tlock); 396 gethrestime(&tp->tn_ctime); 397 rw_exit(&tp->tn_rwlock); 398 tmpnode_rele(tp); 399 tp = NULL; 400 } 401 } else if (tpp) { 402 *tpp = tp; 403 } else if (op == DE_CREATE || op == DE_MKDIR) { 404 tmpnode_rele(tp); 405 } 406 } 407 408 out: 409 if (error && (op == DE_LINK || op == DE_RENAME)) { 410 /* 411 * Undo bumped link count. 412 */ 413 DECR_COUNT(&tp->tn_nlink, &tp->tn_tlock); 414 gethrestime(&tp->tn_ctime); 415 } 416 return (error); 417 } 418 419 /* 420 * Delete entry tp of name "nm" from dir. 421 * Free dir entry space and decrement link count on tmpnode(s). 422 * 423 * Return 0 on success. 424 */ 425 int 426 tdirdelete( 427 struct tmpnode *dir, 428 struct tmpnode *tp, 429 char *nm, 430 enum dr_op op, 431 struct cred *cred) 432 { 433 struct tdirent *tpdp; 434 int error; 435 size_t namelen; 436 struct tmpnode *tnp; 437 timestruc_t now; 438 439 ASSERT(RW_WRITE_HELD(&dir->tn_rwlock)); 440 ASSERT(RW_WRITE_HELD(&tp->tn_rwlock)); 441 ASSERT(dir->tn_type == VDIR); 442 443 if (nm[0] == '\0') 444 panic("tdirdelete: NULL name for %p", (void *)tp); 445 446 /* 447 * return error when removing . and .. 448 */ 449 if (nm[0] == '.') { 450 if (nm[1] == '\0') 451 return (EINVAL); 452 if (nm[1] == '.' && nm[2] == '\0') 453 return (EEXIST); /* thus in ufs */ 454 } 455 456 if (error = tmp_taccess(dir, VEXEC|VWRITE, cred)) 457 return (error); 458 459 /* 460 * If the parent directory is "sticky", then the user must 461 * own the parent directory or the file in it, or else must 462 * have permission to write the file. Otherwise it may not 463 * be deleted (except by privileged users). 464 * Same as ufs_dirremove. 465 */ 466 if ((error = tmp_sticky_remove_access(dir, tp, cred)) != 0) 467 return (error); 468 469 if (dir->tn_dir == NULL) 470 return (ENOENT); 471 472 tpdp = tmpfs_hash_lookup(nm, dir, 0, &tnp); 473 if (tpdp == NULL) { 474 /* 475 * If it is gone, some other thread got here first! 476 * Return error ENOENT. 477 */ 478 return (ENOENT); 479 } 480 481 /* 482 * If the tmpnode in the tdirent changed, we were probably 483 * the victim of a concurrent rename operation. The original 484 * is gone, so return that status (same as UFS). 485 */ 486 if (tp != tnp) 487 return (ENOENT); 488 489 tmpfs_hash_out(tpdp); 490 491 /* 492 * Take tpdp out of the directory list. 493 */ 494 ASSERT(tpdp->td_next != tpdp); 495 ASSERT(tpdp->td_prev != tpdp); 496 if (tpdp->td_prev) { 497 tpdp->td_prev->td_next = tpdp->td_next; 498 } 499 if (tpdp->td_next) { 500 tpdp->td_next->td_prev = tpdp->td_prev; 501 } 502 503 /* 504 * If the roving slot pointer happens to match tpdp, 505 * point it at the previous dirent. 506 */ 507 if (dir->tn_dir->td_prev == tpdp) { 508 dir->tn_dir->td_prev = tpdp->td_prev; 509 } 510 ASSERT(tpdp->td_next != tpdp); 511 ASSERT(tpdp->td_prev != tpdp); 512 513 /* 514 * tpdp points to the correct directory entry 515 */ 516 namelen = strlen(tpdp->td_name) + 1; 517 518 tmp_memfree(tpdp, sizeof (struct tdirent) + namelen); 519 dir->tn_size -= (sizeof (struct tdirent) + namelen); 520 dir->tn_dirents--; 521 522 gethrestime(&now); 523 dir->tn_mtime = now; 524 dir->tn_ctime = now; 525 tp->tn_ctime = now; 526 527 ASSERT(tp->tn_nlink > 0); 528 DECR_COUNT(&tp->tn_nlink, &tp->tn_tlock); 529 if (op == DR_RMDIR && tp->tn_type == VDIR) { 530 tdirtrunc(tp); 531 ASSERT(tp->tn_nlink == 0); 532 } 533 return (0); 534 } 535 536 /* 537 * tdirinit is used internally to initialize a directory (dir) 538 * with '.' and '..' entries without checking permissions and locking 539 */ 540 void 541 tdirinit( 542 struct tmpnode *parent, /* parent of directory to initialize */ 543 struct tmpnode *dir) /* the new directory */ 544 { 545 struct tdirent *dot, *dotdot; 546 timestruc_t now; 547 548 ASSERT(RW_WRITE_HELD(&parent->tn_rwlock)); 549 ASSERT(dir->tn_type == VDIR); 550 551 dot = tmp_memalloc(sizeof (struct tdirent) + 2, TMP_MUSTHAVE); 552 dotdot = tmp_memalloc(sizeof (struct tdirent) + 3, TMP_MUSTHAVE); 553 554 /* 555 * Initialize the entries 556 */ 557 dot->td_tmpnode = dir; 558 dot->td_offset = 0; 559 dot->td_name = (char *)dot + sizeof (struct tdirent); 560 dot->td_name[0] = '.'; 561 dot->td_parent = dir; 562 tmpfs_hash_in(dot); 563 564 dotdot->td_tmpnode = parent; 565 dotdot->td_offset = 1; 566 dotdot->td_name = (char *)dotdot + sizeof (struct tdirent); 567 dotdot->td_name[0] = '.'; 568 dotdot->td_name[1] = '.'; 569 dotdot->td_parent = dir; 570 tmpfs_hash_in(dotdot); 571 572 /* 573 * Initialize directory entry list. 574 */ 575 dot->td_next = dotdot; 576 dot->td_prev = dotdot; /* dot's td_prev holds roving slot pointer */ 577 dotdot->td_next = NULL; 578 dotdot->td_prev = dot; 579 580 gethrestime(&now); 581 dir->tn_mtime = now; 582 dir->tn_ctime = now; 583 584 /* 585 * Link counts are special for the hidden attribute directory. 586 * The only explicit reference in the name space is "." and 587 * the reference through ".." is not counted on the parent 588 * file. The attrdir is created as a side effect to lookup, 589 * so don't change the ctime of the parent. 590 * Since tdirinit is called with both dir and parent being the 591 * same for the root vnode, we need to increment this before we set 592 * tn_nlink = 2 below. 593 */ 594 if (!(dir->tn_vnode->v_flag & V_XATTRDIR)) { 595 INCR_COUNT(&parent->tn_nlink, &parent->tn_tlock); 596 parent->tn_ctime = now; 597 } 598 599 dir->tn_dir = dot; 600 dir->tn_size = 2 * sizeof (struct tdirent) + 5; /* dot and dotdot */ 601 dir->tn_dirents = 2; 602 dir->tn_nlink = 2; 603 } 604 605 606 /* 607 * tdirtrunc is called to remove all directory entries under this directory. 608 */ 609 void 610 tdirtrunc(struct tmpnode *dir) 611 { 612 struct tdirent *tdp; 613 struct tmpnode *tp; 614 size_t namelen; 615 timestruc_t now; 616 int isvattrdir, isdotdot, skip_decr; 617 618 ASSERT(RW_WRITE_HELD(&dir->tn_rwlock)); 619 ASSERT(dir->tn_type == VDIR); 620 621 isvattrdir = (dir->tn_vnode->v_flag & V_XATTRDIR) ? 1 : 0; 622 for (tdp = dir->tn_dir; tdp; tdp = dir->tn_dir) { 623 ASSERT(tdp->td_next != tdp); 624 ASSERT(tdp->td_prev != tdp); 625 ASSERT(tdp->td_tmpnode); 626 627 dir->tn_dir = tdp->td_next; 628 namelen = strlen(tdp->td_name) + 1; 629 630 /* 631 * Adjust the link counts to account for this directory 632 * entry removal. Hidden attribute directories may 633 * not be empty as they may be truncated as a side- 634 * effect of removing the parent. We do hold/rele 635 * operations to free up these tmpnodes. 636 * 637 * Skip the link count adjustment for parents of 638 * attribute directories as those link counts 639 * do not include the ".." reference in the hidden 640 * directories. 641 */ 642 tp = tdp->td_tmpnode; 643 isdotdot = (strcmp("..", tdp->td_name) == 0); 644 skip_decr = (isvattrdir && isdotdot); 645 if (!skip_decr) { 646 ASSERT(tp->tn_nlink > 0); 647 DECR_COUNT(&tp->tn_nlink, &tp->tn_tlock); 648 } 649 650 tmpfs_hash_out(tdp); 651 652 tmp_memfree(tdp, sizeof (struct tdirent) + namelen); 653 dir->tn_size -= (sizeof (struct tdirent) + namelen); 654 dir->tn_dirents--; 655 } 656 657 gethrestime(&now); 658 dir->tn_mtime = now; 659 dir->tn_ctime = now; 660 661 ASSERT(dir->tn_dir == NULL); 662 ASSERT(dir->tn_size == 0); 663 ASSERT(dir->tn_dirents == 0); 664 } 665 666 /* 667 * Check if the source directory is in the path of the target directory. 668 * The target directory is locked by the caller. 669 * 670 * XXX - The source and target's should be different upon entry. 671 */ 672 static int 673 tdircheckpath( 674 struct tmpnode *fromtp, 675 struct tmpnode *toparent, 676 struct cred *cred) 677 { 678 int error = 0; 679 struct tmpnode *dir, *dotdot; 680 struct tdirent *tdp; 681 682 ASSERT(RW_WRITE_HELD(&toparent->tn_rwlock)); 683 684 tdp = tmpfs_hash_lookup("..", toparent, 1, &dotdot); 685 if (tdp == NULL) 686 return (ENOENT); 687 688 ASSERT(dotdot); 689 690 if (dotdot == toparent) { 691 /* root of fs. search trivially satisfied. */ 692 tmpnode_rele(dotdot); 693 return (0); 694 } 695 for (;;) { 696 /* 697 * Return error for cases like "mv c c/d", 698 * "mv c c/d/e" and so on. 699 */ 700 if (dotdot == fromtp) { 701 tmpnode_rele(dotdot); 702 error = EINVAL; 703 break; 704 } 705 dir = dotdot; 706 error = tdirlookup(dir, "..", &dotdot, cred); 707 if (error) { 708 tmpnode_rele(dir); 709 break; 710 } 711 /* 712 * We're okay if we traverse the directory tree up to 713 * the root directory and don't run into the 714 * parent directory. 715 */ 716 if (dir == dotdot) { 717 tmpnode_rele(dir); 718 tmpnode_rele(dotdot); 719 break; 720 } 721 tmpnode_rele(dir); 722 } 723 return (error); 724 } 725 726 static int 727 tdirrename( 728 struct tmpnode *fromparent, /* parent directory of source */ 729 struct tmpnode *fromtp, /* source tmpnode */ 730 struct tmpnode *toparent, /* parent directory of target */ 731 char *nm, /* entry we are trying to change */ 732 struct tmpnode *to, /* target tmpnode */ 733 struct tdirent *where, /* target tmpnode directory entry */ 734 struct cred *cred) /* credentials */ 735 { 736 int error = 0; 737 int doingdirectory; 738 timestruc_t now; 739 740 #if defined(lint) 741 nm = nm; 742 #endif 743 ASSERT(RW_WRITE_HELD(&toparent->tn_rwlock)); 744 745 /* 746 * Short circuit rename of something to itself. 747 */ 748 if (fromtp == to) 749 return (ESAME); /* special KLUDGE error code */ 750 751 rw_enter(&fromtp->tn_rwlock, RW_READER); 752 rw_enter(&to->tn_rwlock, RW_READER); 753 754 /* 755 * Check that everything is on the same filesystem. 756 */ 757 if (to->tn_vnode->v_vfsp != toparent->tn_vnode->v_vfsp || 758 to->tn_vnode->v_vfsp != fromtp->tn_vnode->v_vfsp) { 759 error = EXDEV; 760 goto out; 761 } 762 763 /* 764 * Must have write permission to rewrite target entry. 765 * Check for stickyness. 766 */ 767 if ((error = tmp_taccess(toparent, VWRITE, cred)) != 0 || 768 (error = tmp_sticky_remove_access(toparent, to, cred)) != 0) 769 goto out; 770 771 /* 772 * Ensure source and target are compatible (both directories 773 * or both not directories). If target is a directory it must 774 * be empty and have no links to it; in addition it must not 775 * be a mount point, and both the source and target must be 776 * writable. 777 */ 778 doingdirectory = (fromtp->tn_type == VDIR); 779 if (to->tn_type == VDIR) { 780 if (!doingdirectory) { 781 error = EISDIR; 782 goto out; 783 } 784 /* 785 * vn_vfswlock will prevent mounts from using the directory 786 * until we are done. 787 */ 788 if (vn_vfswlock(TNTOV(to))) { 789 error = EBUSY; 790 goto out; 791 } 792 if (vn_mountedvfs(TNTOV(to)) != NULL) { 793 vn_vfsunlock(TNTOV(to)); 794 error = EBUSY; 795 goto out; 796 } 797 798 mutex_enter(&to->tn_tlock); 799 if (to->tn_dirents > 2 || to->tn_nlink > 2) { 800 mutex_exit(&to->tn_tlock); 801 vn_vfsunlock(TNTOV(to)); 802 error = EEXIST; /* SIGH should be ENOTEMPTY */ 803 /* 804 * Update atime because checking tn_dirents is 805 * logically equivalent to reading the directory 806 */ 807 gethrestime(&to->tn_atime); 808 goto out; 809 } 810 mutex_exit(&to->tn_tlock); 811 } else if (doingdirectory) { 812 error = ENOTDIR; 813 goto out; 814 } 815 816 tmpfs_hash_change(where, fromtp); 817 gethrestime(&now); 818 toparent->tn_mtime = now; 819 toparent->tn_ctime = now; 820 821 /* 822 * Upgrade to write lock on "to" (i.e., the target tmpnode). 823 */ 824 rw_exit(&to->tn_rwlock); 825 rw_enter(&to->tn_rwlock, RW_WRITER); 826 827 /* 828 * Decrement the link count of the target tmpnode. 829 */ 830 DECR_COUNT(&to->tn_nlink, &to->tn_tlock); 831 to->tn_ctime = now; 832 833 if (doingdirectory) { 834 /* 835 * The entry for "to" no longer exists so release the vfslock. 836 */ 837 vn_vfsunlock(TNTOV(to)); 838 839 /* 840 * Decrement the target link count and delete all entires. 841 */ 842 tdirtrunc(to); 843 ASSERT(to->tn_nlink == 0); 844 845 /* 846 * Renaming a directory with the parent different 847 * requires that ".." be rewritten. The window is 848 * still there for ".." to be inconsistent, but this 849 * is unavoidable, and a lot shorter than when it was 850 * done in a user process. 851 */ 852 if (fromparent != toparent) 853 tdirfixdotdot(fromtp, fromparent, toparent); 854 } 855 out: 856 rw_exit(&to->tn_rwlock); 857 rw_exit(&fromtp->tn_rwlock); 858 return (error); 859 } 860 861 static void 862 tdirfixdotdot( 863 struct tmpnode *fromtp, /* child directory */ 864 struct tmpnode *fromparent, /* old parent directory */ 865 struct tmpnode *toparent) /* new parent directory */ 866 { 867 struct tdirent *dotdot; 868 869 ASSERT(RW_LOCK_HELD(&toparent->tn_rwlock)); 870 871 /* 872 * Increment the link count in the new parent tmpnode 873 */ 874 INCR_COUNT(&toparent->tn_nlink, &toparent->tn_tlock); 875 gethrestime(&toparent->tn_ctime); 876 877 dotdot = tmpfs_hash_lookup("..", fromtp, 0, NULL); 878 879 ASSERT(dotdot->td_tmpnode == fromparent); 880 dotdot->td_tmpnode = toparent; 881 882 /* 883 * Decrement the link count of the old parent tmpnode. 884 * If fromparent is NULL, then this is a new directory link; 885 * it has no parent, so we need not do anything. 886 */ 887 if (fromparent != NULL) { 888 mutex_enter(&fromparent->tn_tlock); 889 if (fromparent->tn_nlink != 0) { 890 fromparent->tn_nlink--; 891 gethrestime(&fromparent->tn_ctime); 892 } 893 mutex_exit(&fromparent->tn_tlock); 894 } 895 } 896 897 static int 898 tdiraddentry( 899 struct tmpnode *dir, /* target directory to make entry in */ 900 struct tmpnode *tp, /* new tmpnode */ 901 char *name, 902 enum de_op op, 903 struct tmpnode *fromtp) 904 { 905 struct tdirent *tdp, *tpdp; 906 size_t namelen, alloc_size; 907 timestruc_t now; 908 909 /* 910 * Make sure the parent directory wasn't removed from 911 * underneath the caller. 912 */ 913 if (dir->tn_dir == NULL) 914 return (ENOENT); 915 916 /* 917 * Check that everything is on the same filesystem. 918 */ 919 if (tp->tn_vnode->v_vfsp != dir->tn_vnode->v_vfsp) 920 return (EXDEV); 921 922 /* 923 * Allocate and initialize directory entry 924 */ 925 namelen = strlen(name) + 1; 926 alloc_size = namelen + sizeof (struct tdirent); 927 tdp = tmp_memalloc(alloc_size, 0); 928 if (tdp == NULL) 929 return (ENOSPC); 930 931 if ((op == DE_RENAME) && (tp->tn_type == VDIR)) 932 tdirfixdotdot(tp, fromtp, dir); 933 934 dir->tn_size += alloc_size; 935 dir->tn_dirents++; 936 tdp->td_tmpnode = tp; 937 tdp->td_parent = dir; 938 939 /* 940 * The directory entry and its name were allocated sequentially. 941 */ 942 tdp->td_name = (char *)tdp + sizeof (struct tdirent); 943 (void) strcpy(tdp->td_name, name); 944 945 tmpfs_hash_in(tdp); 946 947 /* 948 * Some utilities expect the size of a directory to remain 949 * somewhat static. For example, a routine which unlinks 950 * files between calls to readdir(); the size of the 951 * directory changes from underneath it and so the real 952 * directory offset in bytes is invalid. To circumvent 953 * this problem, we initialize a directory entry with an 954 * phony offset, and use this offset to determine end of 955 * file in tmp_readdir. 956 */ 957 tpdp = dir->tn_dir->td_prev; 958 /* 959 * Install at first empty "slot" in directory list. 960 */ 961 while (tpdp->td_next != NULL && (tpdp->td_next->td_offset - 962 tpdp->td_offset) <= 1) { 963 ASSERT(tpdp->td_next != tpdp); 964 ASSERT(tpdp->td_prev != tpdp); 965 ASSERT(tpdp->td_next->td_offset > tpdp->td_offset); 966 tpdp = tpdp->td_next; 967 } 968 tdp->td_offset = tpdp->td_offset + 1; 969 970 /* 971 * If we're at the end of the dirent list and the offset (which 972 * is necessarily the largest offset in this directory) is more 973 * than twice the number of dirents, that means the directory is 974 * 50% holes. At this point we reset the slot pointer back to 975 * the beginning of the directory so we start using the holes. 976 * The idea is that if there are N dirents, there must also be 977 * N holes, so we can satisfy the next N creates by walking at 978 * most 2N entries; thus the average cost of a create is constant. 979 * Note that we use the first dirent's td_prev as the roving 980 * slot pointer; it's ugly, but it saves a word in every dirent. 981 */ 982 if (tpdp->td_next == NULL && tpdp->td_offset > 2 * dir->tn_dirents) 983 dir->tn_dir->td_prev = dir->tn_dir->td_next; 984 else 985 dir->tn_dir->td_prev = tdp; 986 987 ASSERT(tpdp->td_next != tpdp); 988 ASSERT(tpdp->td_prev != tpdp); 989 990 tdp->td_next = tpdp->td_next; 991 if (tdp->td_next) { 992 tdp->td_next->td_prev = tdp; 993 } 994 tdp->td_prev = tpdp; 995 tpdp->td_next = tdp; 996 997 ASSERT(tdp->td_next != tdp); 998 ASSERT(tdp->td_prev != tdp); 999 ASSERT(tpdp->td_next != tpdp); 1000 ASSERT(tpdp->td_prev != tpdp); 1001 1002 gethrestime(&now); 1003 dir->tn_mtime = now; 1004 dir->tn_ctime = now; 1005 1006 return (0); 1007 } 1008 1009 static int 1010 tdirmaketnode( 1011 struct tmpnode *dir, 1012 struct tmount *tm, 1013 struct vattr *va, 1014 enum de_op op, 1015 struct tmpnode **newnode, 1016 struct cred *cred) 1017 { 1018 struct tmpnode *tp; 1019 enum vtype type; 1020 1021 ASSERT(va != NULL); 1022 ASSERT(op == DE_CREATE || op == DE_MKDIR); 1023 if (((va->va_mask & AT_ATIME) && TIMESPEC_OVERFLOW(&va->va_atime)) || 1024 ((va->va_mask & AT_MTIME) && TIMESPEC_OVERFLOW(&va->va_mtime))) 1025 return (EOVERFLOW); 1026 type = va->va_type; 1027 tp = tmp_memalloc(sizeof (struct tmpnode), TMP_MUSTHAVE); 1028 tmpnode_init(tm, tp, va, cred); 1029 1030 /* setup normal file/dir's extended attribute directory */ 1031 if (dir->tn_flags & ISXATTR) { 1032 /* parent dir is , mark file as xattr */ 1033 tp->tn_flags |= ISXATTR; 1034 } 1035 1036 1037 if (type == VBLK || type == VCHR) { 1038 tp->tn_vnode->v_rdev = tp->tn_rdev = va->va_rdev; 1039 } else { 1040 tp->tn_vnode->v_rdev = tp->tn_rdev = NODEV; 1041 } 1042 tp->tn_vnode->v_type = type; 1043 tp->tn_uid = crgetuid(cred); 1044 1045 /* 1046 * To determine the group-id of the created file: 1047 * 1) If the gid is set in the attribute list (non-Sun & pre-4.0 1048 * clients are not likely to set the gid), then use it if 1049 * the process is privileged, belongs to the target group, 1050 * or the group is the same as the parent directory. 1051 * 2) If the filesystem was not mounted with the Old-BSD-compatible 1052 * GRPID option, and the directory's set-gid bit is clear, 1053 * then use the process's gid. 1054 * 3) Otherwise, set the group-id to the gid of the parent directory. 1055 */ 1056 if ((va->va_mask & AT_GID) && 1057 ((va->va_gid == dir->tn_gid) || groupmember(va->va_gid, cred) || 1058 secpolicy_vnode_create_gid(cred) == 0)) { 1059 /* 1060 * XXX - is this only the case when a 4.0 NFS client, or a 1061 * client derived from that code, makes a call over the wire? 1062 */ 1063 tp->tn_gid = va->va_gid; 1064 } else { 1065 if (dir->tn_mode & VSGID) 1066 tp->tn_gid = dir->tn_gid; 1067 else 1068 tp->tn_gid = crgetgid(cred); 1069 } 1070 /* 1071 * If we're creating a directory, and the parent directory has the 1072 * set-GID bit set, set it on the new directory. 1073 * Otherwise, if the user is neither privileged nor a member of the 1074 * file's new group, clear the file's set-GID bit. 1075 */ 1076 if (dir->tn_mode & VSGID && type == VDIR) 1077 tp->tn_mode |= VSGID; 1078 else { 1079 if ((tp->tn_mode & VSGID) && 1080 secpolicy_vnode_setids_setgids(cred, tp->tn_gid) != 0) 1081 tp->tn_mode &= ~VSGID; 1082 } 1083 1084 if (va->va_mask & AT_ATIME) 1085 tp->tn_atime = va->va_atime; 1086 if (va->va_mask & AT_MTIME) 1087 tp->tn_mtime = va->va_mtime; 1088 1089 if (op == DE_MKDIR) 1090 tdirinit(dir, tp); 1091 1092 *newnode = tp; 1093 return (0); 1094 } 1095