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 * Copyright 2016 RackTop Systems. 26 */ 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 caller_context_t *ctp) 242 { 243 struct tdirent *tdp; 244 struct tmpnode *found = NULL; 245 int error = 0; 246 char *s; 247 248 /* 249 * tn_rwlock is held to serialize direnter and dirdeletes 250 */ 251 ASSERT(RW_WRITE_HELD(&dir->tn_rwlock)); 252 ASSERT(dir->tn_type == VDIR); 253 254 /* 255 * Don't allow '/' characters in pathname component 256 * (thus in ufs_direnter()). 257 */ 258 for (s = name; *s; s++) 259 if (*s == '/') 260 return (EACCES); 261 262 if (name[0] == '\0') 263 panic("tdirenter: NULL name"); 264 265 /* 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. 268 */ 269 if (op == DE_LINK || op == DE_RENAME) { 270 if (tp != dir) 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); 275 if (tp != dir) 276 rw_exit(&tp->tn_rwlock); 277 return (ENOENT); 278 } 279 280 if (tp->tn_nlink == MAXLINK) { 281 mutex_exit(&tp->tn_tlock); 282 if (tp != dir) 283 rw_exit(&tp->tn_rwlock); 284 return (EMLINK); 285 } 286 tp->tn_nlink++; 287 gethrestime(&tp->tn_ctime); 288 mutex_exit(&tp->tn_tlock); 289 if (tp != dir) 290 rw_exit(&tp->tn_rwlock); 291 } 292 293 /* 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. 300 */ 301 if (dir->tn_nlink == 0) { 302 error = ENOENT; 303 goto out; 304 } 305 306 /* 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. 312 */ 313 if (op == DE_RENAME) { 314 if (tp == dir) { 315 error = EINVAL; 316 goto out; 317 } 318 if (tp->tn_type == VDIR) { 319 if ((fromparent != dir) && 320 (error = tdircheckpath(tp, dir, cred))) { 321 goto out; 322 } 323 } 324 } 325 326 /* 327 * Search for the entry. Return "found" if it exists. 328 */ 329 tdp = tmpfs_hash_lookup(name, dir, 1, &found); 330 331 if (tdp) { 332 ASSERT(found); 333 switch (op) { 334 case DE_CREATE: 335 case DE_MKDIR: 336 if (tpp) { 337 *tpp = found; 338 error = EEXIST; 339 } else { 340 tmpnode_rele(found); 341 } 342 break; 343 344 case DE_RENAME: 345 error = tdirrename(fromparent, tp, 346 dir, name, found, tdp, cred); 347 if (error == 0) { 348 if (found != NULL) { 349 vnevent_rename_dest(TNTOV(found), 350 TNTOV(dir), name, ctp); 351 } 352 } 353 354 tmpnode_rele(found); 355 break; 356 357 case DE_LINK: 358 /* 359 * Can't link to an existing file. 360 */ 361 error = EEXIST; 362 tmpnode_rele(found); 363 break; 364 } 365 } else { 366 367 /* 368 * The entry does not exist. Check write permission in 369 * directory to see if entry can be created. 370 */ 371 if (error = tmp_taccess(dir, VWRITE, cred)) 372 goto out; 373 if (op == DE_CREATE || op == DE_MKDIR) { 374 /* 375 * Make new tmpnode and directory entry as required. 376 */ 377 error = tdirmaketnode(dir, tm, va, op, &tp, cred); 378 if (error) 379 goto out; 380 } 381 if (error = tdiraddentry(dir, tp, name, op, fromparent)) { 382 if (op == DE_CREATE || op == DE_MKDIR) { 383 /* 384 * Unmake the inode we just made. 385 */ 386 rw_enter(&tp->tn_rwlock, RW_WRITER); 387 if ((tp->tn_type) == VDIR) { 388 ASSERT(tdp == NULL); 389 /* 390 * cleanup allocs made by tdirinit() 391 */ 392 tdirtrunc(tp); 393 } 394 mutex_enter(&tp->tn_tlock); 395 tp->tn_nlink = 0; 396 mutex_exit(&tp->tn_tlock); 397 gethrestime(&tp->tn_ctime); 398 rw_exit(&tp->tn_rwlock); 399 tmpnode_rele(tp); 400 tp = NULL; 401 } 402 } else if (tpp) { 403 *tpp = tp; 404 } else if (op == DE_CREATE || op == DE_MKDIR) { 405 tmpnode_rele(tp); 406 } 407 } 408 409 out: 410 if (error && (op == DE_LINK || op == DE_RENAME)) { 411 /* 412 * Undo bumped link count. 413 */ 414 DECR_COUNT(&tp->tn_nlink, &tp->tn_tlock); 415 gethrestime(&tp->tn_ctime); 416 } 417 return (error); 418 } 419 420 /* 421 * Delete entry tp of name "nm" from dir. 422 * Free dir entry space and decrement link count on tmpnode(s). 423 * 424 * Return 0 on success. 425 */ 426 int 427 tdirdelete( 428 struct tmpnode *dir, 429 struct tmpnode *tp, 430 char *nm, 431 enum dr_op op, 432 struct cred *cred) 433 { 434 struct tdirent *tpdp; 435 int error; 436 size_t namelen; 437 struct tmpnode *tnp; 438 timestruc_t now; 439 440 ASSERT(RW_WRITE_HELD(&dir->tn_rwlock)); 441 ASSERT(RW_WRITE_HELD(&tp->tn_rwlock)); 442 ASSERT(dir->tn_type == VDIR); 443 444 if (nm[0] == '\0') 445 panic("tdirdelete: NULL name for %p", (void *)tp); 446 447 /* 448 * return error when removing . and .. 449 */ 450 if (nm[0] == '.') { 451 if (nm[1] == '\0') 452 return (EINVAL); 453 if (nm[1] == '.' && nm[2] == '\0') 454 return (EEXIST); /* thus in ufs */ 455 } 456 457 if (error = tmp_taccess(dir, VEXEC|VWRITE, cred)) 458 return (error); 459 460 /* 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. 466 */ 467 if ((error = tmp_sticky_remove_access(dir, tp, cred)) != 0) 468 return (error); 469 470 if (dir->tn_dir == NULL) 471 return (ENOENT); 472 473 tpdp = tmpfs_hash_lookup(nm, dir, 0, &tnp); 474 if (tpdp == NULL) { 475 /* 476 * If it is gone, some other thread got here first! 477 * Return error ENOENT. 478 */ 479 return (ENOENT); 480 } 481 482 /* 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). 486 */ 487 if (tp != tnp) 488 return (ENOENT); 489 490 /* 491 * If we are unlinking a directory which isn't empty (it has 492 * more than the minumum '.' and '..' entries) return EEXIST. 493 */ 494 if (tp->tn_type == VDIR && tp->tn_dirents > 2) 495 return (EEXIST); 496 497 tmpfs_hash_out(tpdp); 498 499 /* 500 * Take tpdp out of the directory list. 501 */ 502 ASSERT(tpdp->td_next != tpdp); 503 ASSERT(tpdp->td_prev != tpdp); 504 if (tpdp->td_prev) { 505 tpdp->td_prev->td_next = tpdp->td_next; 506 } 507 if (tpdp->td_next) { 508 tpdp->td_next->td_prev = tpdp->td_prev; 509 } 510 511 /* 512 * If the roving slot pointer happens to match tpdp, 513 * point it at the previous dirent. 514 */ 515 if (dir->tn_dir->td_prev == tpdp) { 516 dir->tn_dir->td_prev = tpdp->td_prev; 517 } 518 ASSERT(tpdp->td_next != tpdp); 519 ASSERT(tpdp->td_prev != tpdp); 520 521 /* 522 * tpdp points to the correct directory entry 523 */ 524 namelen = strlen(tpdp->td_name) + 1; 525 526 tmp_memfree(tpdp, sizeof (struct tdirent) + namelen); 527 dir->tn_size -= (sizeof (struct tdirent) + namelen); 528 dir->tn_dirents--; 529 530 gethrestime(&now); 531 dir->tn_mtime = now; 532 dir->tn_ctime = now; 533 tp->tn_ctime = now; 534 535 ASSERT(tp->tn_nlink > 0); 536 DECR_COUNT(&tp->tn_nlink, &tp->tn_tlock); 537 if (tp->tn_type == VDIR) { 538 tdirtrunc(tp); 539 ASSERT(tp->tn_nlink == 0); 540 } 541 return (0); 542 } 543 544 /* 545 * tdirinit is used internally to initialize a directory (dir) 546 * with '.' and '..' entries without checking permissions and locking 547 */ 548 void 549 tdirinit( 550 struct tmpnode *parent, /* parent of directory to initialize */ 551 struct tmpnode *dir) /* the new directory */ 552 { 553 struct tdirent *dot, *dotdot; 554 timestruc_t now; 555 556 ASSERT(RW_WRITE_HELD(&parent->tn_rwlock)); 557 ASSERT(dir->tn_type == VDIR); 558 559 dot = tmp_memalloc(sizeof (struct tdirent) + 2, TMP_MUSTHAVE); 560 dotdot = tmp_memalloc(sizeof (struct tdirent) + 3, TMP_MUSTHAVE); 561 562 /* 563 * Initialize the entries 564 */ 565 dot->td_tmpnode = dir; 566 dot->td_offset = 0; 567 dot->td_name = (char *)dot + sizeof (struct tdirent); 568 dot->td_name[0] = '.'; 569 dot->td_parent = dir; 570 tmpfs_hash_in(dot); 571 572 dotdot->td_tmpnode = parent; 573 dotdot->td_offset = 1; 574 dotdot->td_name = (char *)dotdot + sizeof (struct tdirent); 575 dotdot->td_name[0] = '.'; 576 dotdot->td_name[1] = '.'; 577 dotdot->td_parent = dir; 578 tmpfs_hash_in(dotdot); 579 580 /* 581 * Initialize directory entry list. 582 */ 583 dot->td_next = dotdot; 584 dot->td_prev = dotdot; /* dot's td_prev holds roving slot pointer */ 585 dotdot->td_next = NULL; 586 dotdot->td_prev = dot; 587 588 gethrestime(&now); 589 dir->tn_mtime = now; 590 dir->tn_ctime = now; 591 592 /* 593 * Link counts are special for the hidden attribute directory. 594 * The only explicit reference in the name space is "." and 595 * the reference through ".." is not counted on the parent 596 * file. The attrdir is created as a side effect to lookup, 597 * so don't change the ctime of the parent. 598 * Since tdirinit is called with both dir and parent being the 599 * same for the root vnode, we need to increment this before we set 600 * tn_nlink = 2 below. 601 */ 602 if (!(dir->tn_vnode->v_flag & V_XATTRDIR)) { 603 INCR_COUNT(&parent->tn_nlink, &parent->tn_tlock); 604 parent->tn_ctime = now; 605 } 606 607 dir->tn_dir = dot; 608 dir->tn_size = 2 * sizeof (struct tdirent) + 5; /* dot and dotdot */ 609 dir->tn_dirents = 2; 610 dir->tn_nlink = 2; 611 } 612 613 614 /* 615 * tdirtrunc is called to remove all directory entries under this directory. 616 */ 617 void 618 tdirtrunc(struct tmpnode *dir) 619 { 620 struct tdirent *tdp; 621 struct tmpnode *tp; 622 size_t namelen; 623 timestruc_t now; 624 int isvattrdir, isdotdot, skip_decr; 625 626 ASSERT(RW_WRITE_HELD(&dir->tn_rwlock)); 627 ASSERT(dir->tn_type == VDIR); 628 629 isvattrdir = (dir->tn_vnode->v_flag & V_XATTRDIR) ? 1 : 0; 630 for (tdp = dir->tn_dir; tdp; tdp = dir->tn_dir) { 631 ASSERT(tdp->td_next != tdp); 632 ASSERT(tdp->td_prev != tdp); 633 ASSERT(tdp->td_tmpnode); 634 635 dir->tn_dir = tdp->td_next; 636 namelen = strlen(tdp->td_name) + 1; 637 638 /* 639 * Adjust the link counts to account for this directory 640 * entry removal. Hidden attribute directories may 641 * not be empty as they may be truncated as a side- 642 * effect of removing the parent. We do hold/rele 643 * operations to free up these tmpnodes. 644 * 645 * Skip the link count adjustment for parents of 646 * attribute directories as those link counts 647 * do not include the ".." reference in the hidden 648 * directories. 649 */ 650 tp = tdp->td_tmpnode; 651 isdotdot = (strcmp("..", tdp->td_name) == 0); 652 skip_decr = (isvattrdir && isdotdot); 653 if (!skip_decr) { 654 ASSERT(tp->tn_nlink > 0); 655 DECR_COUNT(&tp->tn_nlink, &tp->tn_tlock); 656 } 657 658 tmpfs_hash_out(tdp); 659 660 tmp_memfree(tdp, sizeof (struct tdirent) + namelen); 661 dir->tn_size -= (sizeof (struct tdirent) + namelen); 662 dir->tn_dirents--; 663 } 664 665 gethrestime(&now); 666 dir->tn_mtime = now; 667 dir->tn_ctime = now; 668 669 ASSERT(dir->tn_dir == NULL); 670 ASSERT(dir->tn_size == 0); 671 ASSERT(dir->tn_dirents == 0); 672 } 673 674 /* 675 * Check if the source directory is in the path of the target directory. 676 * The target directory is locked by the caller. 677 * 678 * XXX - The source and target's should be different upon entry. 679 */ 680 static int 681 tdircheckpath( 682 struct tmpnode *fromtp, 683 struct tmpnode *toparent, 684 struct cred *cred) 685 { 686 int error = 0; 687 struct tmpnode *dir, *dotdot; 688 struct tdirent *tdp; 689 690 ASSERT(RW_WRITE_HELD(&toparent->tn_rwlock)); 691 692 tdp = tmpfs_hash_lookup("..", toparent, 1, &dotdot); 693 if (tdp == NULL) 694 return (ENOENT); 695 696 ASSERT(dotdot); 697 698 if (dotdot == toparent) { 699 /* root of fs. search trivially satisfied. */ 700 tmpnode_rele(dotdot); 701 return (0); 702 } 703 for (;;) { 704 /* 705 * Return error for cases like "mv c c/d", 706 * "mv c c/d/e" and so on. 707 */ 708 if (dotdot == fromtp) { 709 tmpnode_rele(dotdot); 710 error = EINVAL; 711 break; 712 } 713 dir = dotdot; 714 error = tdirlookup(dir, "..", &dotdot, cred); 715 if (error) { 716 tmpnode_rele(dir); 717 break; 718 } 719 /* 720 * We're okay if we traverse the directory tree up to 721 * the root directory and don't run into the 722 * parent directory. 723 */ 724 if (dir == dotdot) { 725 tmpnode_rele(dir); 726 tmpnode_rele(dotdot); 727 break; 728 } 729 tmpnode_rele(dir); 730 } 731 return (error); 732 } 733 734 static int 735 tdirrename( 736 struct tmpnode *fromparent, /* parent directory of source */ 737 struct tmpnode *fromtp, /* source tmpnode */ 738 struct tmpnode *toparent, /* parent directory of target */ 739 char *nm, /* entry we are trying to change */ 740 struct tmpnode *to, /* target tmpnode */ 741 struct tdirent *where, /* target tmpnode directory entry */ 742 struct cred *cred) /* credentials */ 743 { 744 int error = 0; 745 int doingdirectory; 746 timestruc_t now; 747 748 #if defined(lint) 749 nm = nm; 750 #endif 751 ASSERT(RW_WRITE_HELD(&toparent->tn_rwlock)); 752 753 /* 754 * Short circuit rename of something to itself. 755 */ 756 if (fromtp == to) 757 return (ESAME); /* special KLUDGE error code */ 758 759 rw_enter(&fromtp->tn_rwlock, RW_READER); 760 rw_enter(&to->tn_rwlock, RW_READER); 761 762 /* 763 * Check that everything is on the same filesystem. 764 */ 765 if (to->tn_vnode->v_vfsp != toparent->tn_vnode->v_vfsp || 766 to->tn_vnode->v_vfsp != fromtp->tn_vnode->v_vfsp) { 767 error = EXDEV; 768 goto out; 769 } 770 771 /* 772 * Must have write permission to rewrite target entry. 773 * Check for stickyness. 774 */ 775 if ((error = tmp_taccess(toparent, VWRITE, cred)) != 0 || 776 (error = tmp_sticky_remove_access(toparent, to, cred)) != 0) 777 goto out; 778 779 /* 780 * Ensure source and target are compatible (both directories 781 * or both not directories). If target is a directory it must 782 * be empty and have no links to it; in addition it must not 783 * be a mount point, and both the source and target must be 784 * writable. 785 */ 786 doingdirectory = (fromtp->tn_type == VDIR); 787 if (to->tn_type == VDIR) { 788 if (!doingdirectory) { 789 error = EISDIR; 790 goto out; 791 } 792 /* 793 * vn_vfswlock will prevent mounts from using the directory 794 * until we are done. 795 */ 796 if (vn_vfswlock(TNTOV(to))) { 797 error = EBUSY; 798 goto out; 799 } 800 if (vn_mountedvfs(TNTOV(to)) != NULL) { 801 vn_vfsunlock(TNTOV(to)); 802 error = EBUSY; 803 goto out; 804 } 805 806 mutex_enter(&to->tn_tlock); 807 if (to->tn_dirents > 2 || to->tn_nlink > 2) { 808 mutex_exit(&to->tn_tlock); 809 vn_vfsunlock(TNTOV(to)); 810 error = EEXIST; /* SIGH should be ENOTEMPTY */ 811 /* 812 * Update atime because checking tn_dirents is 813 * logically equivalent to reading the directory 814 */ 815 gethrestime(&to->tn_atime); 816 goto out; 817 } 818 mutex_exit(&to->tn_tlock); 819 } else if (doingdirectory) { 820 error = ENOTDIR; 821 goto out; 822 } 823 824 tmpfs_hash_change(where, fromtp); 825 gethrestime(&now); 826 toparent->tn_mtime = now; 827 toparent->tn_ctime = now; 828 829 /* 830 * Upgrade to write lock on "to" (i.e., the target tmpnode). 831 */ 832 rw_exit(&to->tn_rwlock); 833 rw_enter(&to->tn_rwlock, RW_WRITER); 834 835 /* 836 * Decrement the link count of the target tmpnode. 837 */ 838 DECR_COUNT(&to->tn_nlink, &to->tn_tlock); 839 to->tn_ctime = now; 840 841 if (doingdirectory) { 842 /* 843 * The entry for "to" no longer exists so release the vfslock. 844 */ 845 vn_vfsunlock(TNTOV(to)); 846 847 /* 848 * Decrement the target link count and delete all entires. 849 */ 850 tdirtrunc(to); 851 ASSERT(to->tn_nlink == 0); 852 853 /* 854 * Renaming a directory with the parent different 855 * requires that ".." be rewritten. The window is 856 * still there for ".." to be inconsistent, but this 857 * is unavoidable, and a lot shorter than when it was 858 * done in a user process. 859 */ 860 if (fromparent != toparent) 861 tdirfixdotdot(fromtp, fromparent, toparent); 862 } 863 out: 864 rw_exit(&to->tn_rwlock); 865 rw_exit(&fromtp->tn_rwlock); 866 return (error); 867 } 868 869 static void 870 tdirfixdotdot( 871 struct tmpnode *fromtp, /* child directory */ 872 struct tmpnode *fromparent, /* old parent directory */ 873 struct tmpnode *toparent) /* new parent directory */ 874 { 875 struct tdirent *dotdot; 876 877 ASSERT(RW_LOCK_HELD(&toparent->tn_rwlock)); 878 879 /* 880 * Increment the link count in the new parent tmpnode 881 */ 882 INCR_COUNT(&toparent->tn_nlink, &toparent->tn_tlock); 883 gethrestime(&toparent->tn_ctime); 884 885 dotdot = tmpfs_hash_lookup("..", fromtp, 0, NULL); 886 887 ASSERT(dotdot->td_tmpnode == fromparent); 888 dotdot->td_tmpnode = toparent; 889 890 /* 891 * Decrement the link count of the old parent tmpnode. 892 * If fromparent is NULL, then this is a new directory link; 893 * it has no parent, so we need not do anything. 894 */ 895 if (fromparent != NULL) { 896 mutex_enter(&fromparent->tn_tlock); 897 if (fromparent->tn_nlink != 0) { 898 fromparent->tn_nlink--; 899 gethrestime(&fromparent->tn_ctime); 900 } 901 mutex_exit(&fromparent->tn_tlock); 902 } 903 } 904 905 static int 906 tdiraddentry( 907 struct tmpnode *dir, /* target directory to make entry in */ 908 struct tmpnode *tp, /* new tmpnode */ 909 char *name, 910 enum de_op op, 911 struct tmpnode *fromtp) 912 { 913 struct tdirent *tdp, *tpdp; 914 size_t namelen, alloc_size; 915 timestruc_t now; 916 917 /* 918 * Make sure the parent directory wasn't removed from 919 * underneath the caller. 920 */ 921 if (dir->tn_dir == NULL) 922 return (ENOENT); 923 924 /* 925 * Check that everything is on the same filesystem. 926 */ 927 if (tp->tn_vnode->v_vfsp != dir->tn_vnode->v_vfsp) 928 return (EXDEV); 929 930 /* 931 * Allocate and initialize directory entry 932 */ 933 namelen = strlen(name) + 1; 934 alloc_size = namelen + sizeof (struct tdirent); 935 tdp = tmp_memalloc(alloc_size, 0); 936 if (tdp == NULL) 937 return (ENOSPC); 938 939 if ((op == DE_RENAME) && (tp->tn_type == VDIR)) 940 tdirfixdotdot(tp, fromtp, dir); 941 942 dir->tn_size += alloc_size; 943 dir->tn_dirents++; 944 tdp->td_tmpnode = tp; 945 tdp->td_parent = dir; 946 947 /* 948 * The directory entry and its name were allocated sequentially. 949 */ 950 tdp->td_name = (char *)tdp + sizeof (struct tdirent); 951 (void) strcpy(tdp->td_name, name); 952 953 tmpfs_hash_in(tdp); 954 955 /* 956 * Some utilities expect the size of a directory to remain 957 * somewhat static. For example, a routine which unlinks 958 * files between calls to readdir(); the size of the 959 * directory changes from underneath it and so the real 960 * directory offset in bytes is invalid. To circumvent 961 * this problem, we initialize a directory entry with an 962 * phony offset, and use this offset to determine end of 963 * file in tmp_readdir. 964 */ 965 tpdp = dir->tn_dir->td_prev; 966 /* 967 * Install at first empty "slot" in directory list. 968 */ 969 while (tpdp->td_next != NULL && (tpdp->td_next->td_offset - 970 tpdp->td_offset) <= 1) { 971 ASSERT(tpdp->td_next != tpdp); 972 ASSERT(tpdp->td_prev != tpdp); 973 ASSERT(tpdp->td_next->td_offset > tpdp->td_offset); 974 tpdp = tpdp->td_next; 975 } 976 tdp->td_offset = tpdp->td_offset + 1; 977 978 /* 979 * If we're at the end of the dirent list and the offset (which 980 * is necessarily the largest offset in this directory) is more 981 * than twice the number of dirents, that means the directory is 982 * 50% holes. At this point we reset the slot pointer back to 983 * the beginning of the directory so we start using the holes. 984 * The idea is that if there are N dirents, there must also be 985 * N holes, so we can satisfy the next N creates by walking at 986 * most 2N entries; thus the average cost of a create is constant. 987 * Note that we use the first dirent's td_prev as the roving 988 * slot pointer; it's ugly, but it saves a word in every dirent. 989 */ 990 if (tpdp->td_next == NULL && tpdp->td_offset > 2 * dir->tn_dirents) 991 dir->tn_dir->td_prev = dir->tn_dir->td_next; 992 else 993 dir->tn_dir->td_prev = tdp; 994 995 ASSERT(tpdp->td_next != tpdp); 996 ASSERT(tpdp->td_prev != tpdp); 997 998 tdp->td_next = tpdp->td_next; 999 if (tdp->td_next) { 1000 tdp->td_next->td_prev = tdp; 1001 } 1002 tdp->td_prev = tpdp; 1003 tpdp->td_next = tdp; 1004 1005 ASSERT(tdp->td_next != tdp); 1006 ASSERT(tdp->td_prev != tdp); 1007 ASSERT(tpdp->td_next != tpdp); 1008 ASSERT(tpdp->td_prev != tpdp); 1009 1010 gethrestime(&now); 1011 dir->tn_mtime = now; 1012 dir->tn_ctime = now; 1013 1014 return (0); 1015 } 1016 1017 static int 1018 tdirmaketnode( 1019 struct tmpnode *dir, 1020 struct tmount *tm, 1021 struct vattr *va, 1022 enum de_op op, 1023 struct tmpnode **newnode, 1024 struct cred *cred) 1025 { 1026 struct tmpnode *tp; 1027 enum vtype type; 1028 1029 ASSERT(va != NULL); 1030 ASSERT(op == DE_CREATE || op == DE_MKDIR); 1031 if (((va->va_mask & AT_ATIME) && TIMESPEC_OVERFLOW(&va->va_atime)) || 1032 ((va->va_mask & AT_MTIME) && TIMESPEC_OVERFLOW(&va->va_mtime))) 1033 return (EOVERFLOW); 1034 type = va->va_type; 1035 tp = tmp_memalloc(sizeof (struct tmpnode), TMP_MUSTHAVE); 1036 tmpnode_init(tm, tp, va, cred); 1037 1038 /* setup normal file/dir's extended attribute directory */ 1039 if (dir->tn_flags & ISXATTR) { 1040 /* parent dir is , mark file as xattr */ 1041 tp->tn_flags |= ISXATTR; 1042 } 1043 1044 1045 if (type == VBLK || type == VCHR) { 1046 tp->tn_vnode->v_rdev = tp->tn_rdev = va->va_rdev; 1047 } else { 1048 tp->tn_vnode->v_rdev = tp->tn_rdev = NODEV; 1049 } 1050 tp->tn_vnode->v_type = type; 1051 tp->tn_uid = crgetuid(cred); 1052 1053 /* 1054 * To determine the group-id of the created file: 1055 * 1) If the gid is set in the attribute list (non-Sun & pre-4.0 1056 * clients are not likely to set the gid), then use it if 1057 * the process is privileged, belongs to the target group, 1058 * or the group is the same as the parent directory. 1059 * 2) If the filesystem was not mounted with the Old-BSD-compatible 1060 * GRPID option, and the directory's set-gid bit is clear, 1061 * then use the process's gid. 1062 * 3) Otherwise, set the group-id to the gid of the parent directory. 1063 */ 1064 if ((va->va_mask & AT_GID) && 1065 ((va->va_gid == dir->tn_gid) || groupmember(va->va_gid, cred) || 1066 secpolicy_vnode_create_gid(cred) == 0)) { 1067 /* 1068 * XXX - is this only the case when a 4.0 NFS client, or a 1069 * client derived from that code, makes a call over the wire? 1070 */ 1071 tp->tn_gid = va->va_gid; 1072 } else { 1073 if (dir->tn_mode & VSGID) 1074 tp->tn_gid = dir->tn_gid; 1075 else 1076 tp->tn_gid = crgetgid(cred); 1077 } 1078 /* 1079 * If we're creating a directory, and the parent directory has the 1080 * set-GID bit set, set it on the new directory. 1081 * Otherwise, if the user is neither privileged nor a member of the 1082 * file's new group, clear the file's set-GID bit. 1083 */ 1084 if (dir->tn_mode & VSGID && type == VDIR) 1085 tp->tn_mode |= VSGID; 1086 else { 1087 if ((tp->tn_mode & VSGID) && 1088 secpolicy_vnode_setids_setgids(cred, tp->tn_gid) != 0) 1089 tp->tn_mode &= ~VSGID; 1090 } 1091 1092 if (va->va_mask & AT_ATIME) 1093 tp->tn_atime = va->va_atime; 1094 if (va->va_mask & AT_MTIME) 1095 tp->tn_mtime = va->va_mtime; 1096 1097 if (op == DE_MKDIR) 1098 tdirinit(dir, tp); 1099 1100 *newnode = tp; 1101 return (0); 1102 }