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 /* 23 * Copyright (c) 1988, 2010, Oracle and/or its affiliates. All rights reserved. 24 * Copyright 2013 Nexenta Systems, Inc. All rights reserved. 25 * Copyright 2022 RackTop Systems, Inc. 26 */ 27 28 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ 29 /* All Rights Reserved */ 30 31 /* 32 * University Copyright- Copyright (c) 1982, 1986, 1988 33 * The Regents of the University of California 34 * All Rights Reserved 35 * 36 * University Acknowledgment- Portions of this document are derived from 37 * software developed by the University of California, Berkeley, and its 38 * contributors. 39 */ 40 41 #include <sys/types.h> 42 #include <sys/param.h> 43 #include <sys/systm.h> 44 #include <sys/file.h> 45 #include <sys/errno.h> 46 #include <sys/cred.h> 47 #include <sys/user.h> 48 #include <sys/uio.h> 49 #include <sys/vfs.h> 50 #include <sys/vnode.h> 51 #include <sys/pathname.h> 52 #include <sys/proc.h> 53 #include <sys/vtrace.h> 54 #include <sys/sysmacros.h> 55 #include <sys/debug.h> 56 #include <sys/dirent.h> 57 #include <sys/zone.h> 58 #include <sys/dnlc.h> 59 #include <sys/fs/snode.h> 60 61 int 62 lookupname( 63 const char *fnamep, 64 enum uio_seg seg, 65 int followlink, 66 vnode_t **dirvpp, 67 vnode_t **compvpp) 68 { 69 return (lookupnameatcred(fnamep, seg, followlink, dirvpp, compvpp, NULL, 70 CRED())); 71 } 72 73 /* 74 * Lookup the user file name, 75 * Handle allocation and freeing of pathname buffer, return error. 76 */ 77 int 78 lookupnameatcred( 79 const char *fnamep, /* user pathname */ 80 enum uio_seg seg, /* addr space that name is in */ 81 int followlink, /* follow sym links */ 82 vnode_t **dirvpp, /* ret for ptr to parent dir vnode */ 83 vnode_t **compvpp, /* ret for ptr to component vnode */ 84 vnode_t *startvp, /* start path search from vp */ 85 cred_t *cr) /* credential */ 86 { 87 char namebuf[TYPICALMAXPATHLEN]; 88 struct pathname lookpn; 89 int error; 90 91 error = pn_get_buf(fnamep, seg, &lookpn, namebuf, sizeof (namebuf)); 92 if (error == 0) { 93 error = lookuppnatcred(&lookpn, NULL, followlink, 94 dirvpp, compvpp, startvp, cr); 95 } 96 if (error == ENAMETOOLONG) { 97 /* 98 * This thread used a pathname > TYPICALMAXPATHLEN bytes long. 99 */ 100 if ((error = pn_get(fnamep, seg, &lookpn)) != 0) 101 return (error); 102 error = lookuppnatcred(&lookpn, NULL, followlink, 103 dirvpp, compvpp, startvp, cr); 104 pn_free(&lookpn); 105 } 106 107 return (error); 108 } 109 110 /* 111 * Lookup the user file name from a given vp, using a specific credential. 112 */ 113 int 114 lookuppnatcred( 115 struct pathname *pnp, /* pathname to lookup */ 116 struct pathname *rpnp, /* if non-NULL, return resolved path */ 117 int followlink, /* (don't) follow sym links */ 118 vnode_t **dirvpp, /* ptr for parent vnode */ 119 vnode_t **compvpp, /* ptr for entry vnode */ 120 vnode_t *startvp, /* start search from this vp */ 121 cred_t *cr) /* user credential */ 122 { 123 vnode_t *vp; /* current directory vp */ 124 vnode_t *rootvp; 125 126 if (pnp->pn_pathlen == 0) 127 return (ENOENT); 128 129 /* Simplified for fake_... */ 130 vp = rootvp = rootdir; 131 132 /* 133 * Skip over leading slashes 134 */ 135 if (pnp->pn_path[0] == '/') { 136 do { 137 pnp->pn_path++; 138 pnp->pn_pathlen--; 139 } while (pnp->pn_path[0] == '/'); 140 } 141 142 return (lookuppnvp(pnp, rpnp, followlink, dirvpp, 143 compvpp, rootvp, vp, cr)); 144 } 145 146 /* 147 * Starting at current directory, translate pathname pnp to end. 148 * Leave pathname of final component in pnp, return the vnode 149 * for the final component in *compvpp, and return the vnode 150 * for the parent of the final component in dirvpp. 151 * 152 * This is the central routine in pathname translation and handles 153 * multiple components in pathnames, separating them at /'s. It also 154 * implements mounted file systems and processes symbolic links. 155 * 156 * vp is the vnode where the directory search should start. 157 * 158 * Reference counts: vp must be held prior to calling this function. rootvp 159 * should only be held if rootvp != rootdir. 160 */ 161 int 162 lookuppnvp( 163 struct pathname *pnp, /* pathname to lookup */ 164 struct pathname *rpnp, /* if non-NULL, return resolved path */ 165 int flags, /* follow symlinks */ 166 vnode_t **dirvpp, /* ptr for parent vnode */ 167 vnode_t **compvpp, /* ptr for entry vnode */ 168 vnode_t *rootvp, /* rootvp */ 169 vnode_t *vp, /* directory to start search at */ 170 cred_t *cr) /* user's credential */ 171 { 172 vnode_t *cvp; /* current component vp */ 173 vnode_t *tvp; /* addressable temp ptr */ 174 char component[MAXNAMELEN]; /* buffer for component (incl null) */ 175 int error; 176 int nlink; 177 int lookup_flags; 178 struct pathname presrvd; /* case preserved name */ 179 struct pathname *pp = NULL; 180 vnode_t *startvp; 181 int must_be_directory = 0; 182 boolean_t retry_with_kcred; 183 184 nlink = 0; 185 cvp = NULL; 186 if (rpnp) 187 rpnp->pn_pathlen = 0; 188 189 lookup_flags = dirvpp ? LOOKUP_DIR : 0; 190 if (flags & FIGNORECASE) { 191 lookup_flags |= FIGNORECASE; 192 pn_alloc(&presrvd); 193 pp = &presrvd; 194 } 195 196 /* 197 * Eliminate any trailing slashes in the pathname. 198 * If there are any, we must follow all symlinks. 199 * Also, we must guarantee that the last component is a directory. 200 */ 201 if (pn_fixslash(pnp)) { 202 flags |= FOLLOW; 203 must_be_directory = 1; 204 } 205 206 startvp = vp; 207 next: 208 retry_with_kcred = B_FALSE; 209 210 /* 211 * Make sure we have a directory. 212 */ 213 if (vp->v_type != VDIR) { 214 error = ENOTDIR; 215 goto bad; 216 } 217 218 if (rpnp && VN_CMP(vp, rootvp)) 219 (void) pn_set(rpnp, "/"); 220 221 /* 222 * Process the next component of the pathname. 223 */ 224 if ((error = pn_getcomponent(pnp, component)) != 0) { 225 goto bad; 226 } 227 228 /* 229 * Handle "..": two special cases. 230 * 1. If we're at the root directory (e.g. after chroot or 231 * zone_enter) then change ".." to "." so we can't get 232 * out of this subtree. 233 * 2. If this vnode is the root of a mounted file system, 234 * then replace it with the vnode that was mounted on 235 * so that we take the ".." in the other file system. 236 */ 237 if (component[0] == '.' && component[1] == '.' && component[2] == 0) { 238 checkforroot: 239 if (VN_CMP(vp, rootvp)) { 240 component[1] = '\0'; 241 } else if (vp->v_flag & VROOT) { 242 vfs_t *vfsp; 243 cvp = vp; 244 245 /* 246 * While we deal with the vfs pointer from the vnode 247 * the filesystem could have been forcefully unmounted 248 * and the vnode's v_vfsp could have been invalidated 249 * by VFS_UNMOUNT. Hence, we cache v_vfsp and use it 250 * with vfs_rlock_wait/vfs_unlock. 251 * It is safe to use the v_vfsp even it is freed by 252 * VFS_UNMOUNT because vfs_rlock_wait/vfs_unlock 253 * do not dereference v_vfsp. It is just used as a 254 * magic cookie. 255 * One more corner case here is the memory getting 256 * reused for another vfs structure. In this case 257 * lookuppnvp's vfs_rlock_wait will succeed, domount's 258 * vfs_lock will fail and domount will bail out with an 259 * error (EBUSY). 260 */ 261 vfsp = cvp->v_vfsp; 262 263 /* 264 * This lock is used to synchronize 265 * mounts/unmounts and lookups. 266 * Threads doing mounts/unmounts hold the 267 * writers version vfs_lock_wait(). 268 */ 269 270 vfs_rlock_wait(vfsp); 271 272 /* 273 * If this vnode is on a file system that 274 * has been forcibly unmounted, 275 * we can't proceed. Cancel this operation 276 * and return EIO. 277 * 278 * vfs_vnodecovered is NULL if unmounted. 279 * Currently, nfs uses VFS_UNMOUNTED to 280 * check if it's a forced-umount. Keep the 281 * same checking here as well even though it 282 * may not be needed. 283 */ 284 if (((vp = cvp->v_vfsp->vfs_vnodecovered) == NULL) || 285 (cvp->v_vfsp->vfs_flag & VFS_UNMOUNTED)) { 286 vfs_unlock(vfsp); 287 VN_RELE(cvp); 288 if (pp) 289 pn_free(pp); 290 return (EIO); 291 } 292 VN_HOLD(vp); 293 vfs_unlock(vfsp); 294 VN_RELE(cvp); 295 cvp = NULL; 296 /* 297 * Crossing mount points. For eg: We are doing 298 * a lookup of ".." for file systems root vnode 299 * mounted here, and VOP_LOOKUP() (with covered vnode) 300 * will be on underlying file systems mount point 301 * vnode. Set retry_with_kcred flag as we might end 302 * up doing VOP_LOOKUP() with kcred if required. 303 */ 304 retry_with_kcred = B_TRUE; 305 goto checkforroot; 306 } 307 } 308 309 /* 310 * Perform a lookup in the current directory. 311 */ 312 error = VOP_LOOKUP(vp, component, &tvp, pnp, lookup_flags, 313 rootvp, cr, NULL, NULL, pp); 314 315 /* 316 * Retry with kcred - If crossing mount points & error is EACCES. 317 * 318 * If we are crossing mount points here and doing ".." lookup, 319 * VOP_LOOKUP() might fail if the underlying file systems 320 * mount point has no execute permission. In cases like these, 321 * we retry VOP_LOOKUP() by giving as much privilage as possible 322 * by passing kcred credentials. 323 * 324 * In case of hierarchical file systems, passing kcred still may 325 * or may not work. 326 * For eg: UFS FS --> Mount NFS FS --> Again mount UFS on some 327 * directory inside NFS FS. 328 */ 329 if ((error == EACCES) && retry_with_kcred) 330 error = VOP_LOOKUP(vp, component, &tvp, pnp, lookup_flags, 331 rootvp, zone_kcred(), NULL, NULL, pp); 332 333 cvp = tvp; 334 if (error) { 335 cvp = NULL; 336 /* 337 * On error, return hard error if 338 * (a) we're not at the end of the pathname yet, or 339 * (b) the caller didn't want the parent directory, or 340 * (c) we failed for some reason other than a missing entry. 341 */ 342 if (pn_pathleft(pnp) || dirvpp == NULL || error != ENOENT) 343 goto bad; 344 345 pn_setlast(pnp); 346 /* 347 * We inform the caller that the desired entry must be 348 * a directory by adding a '/' to the component name. 349 */ 350 if (must_be_directory && (error = pn_addslash(pnp)) != 0) 351 goto bad; 352 *dirvpp = vp; 353 if (compvpp != NULL) 354 *compvpp = NULL; 355 if (rootvp != rootdir) 356 VN_RELE(rootvp); 357 if (pp) 358 pn_free(pp); 359 return (0); 360 } 361 362 /* 363 * Traverse mount points. 364 */ 365 if (vn_mountedvfs(cvp) != NULL) { 366 tvp = cvp; 367 if ((error = traverse(&tvp)) != 0) { 368 /* 369 * It is required to assign cvp here, because 370 * traverse() will return a held vnode which 371 * may different than the vnode that was passed 372 * in (even in the error case). If traverse() 373 * changes the vnode it releases the original, 374 * and holds the new one. 375 */ 376 cvp = tvp; 377 goto bad; 378 } 379 cvp = tvp; 380 } 381 382 /* 383 * If we hit a symbolic link and there is more path to be 384 * translated or this operation does not wish to apply 385 * to a link, then place the contents of the link at the 386 * front of the remaining pathname. 387 */ 388 if (cvp->v_type == VLNK && ((flags & FOLLOW) || pn_pathleft(pnp))) { 389 struct pathname linkpath; 390 391 if (++nlink > MAXSYMLINKS) { 392 error = ELOOP; 393 goto bad; 394 } 395 pn_alloc(&linkpath); 396 if ((error = pn_getsymlink(cvp, &linkpath, cr)) != 0) { 397 pn_free(&linkpath); 398 goto bad; 399 } 400 401 if (pn_pathleft(&linkpath) == 0) 402 (void) pn_set(&linkpath, "."); 403 error = pn_insert(pnp, &linkpath, strlen(component)); 404 pn_free(&linkpath); 405 if (error) 406 goto bad; 407 VN_RELE(cvp); 408 cvp = NULL; 409 if (pnp->pn_pathlen == 0) { 410 error = ENOENT; 411 goto bad; 412 } 413 if (pnp->pn_path[0] == '/') { 414 do { 415 pnp->pn_path++; 416 pnp->pn_pathlen--; 417 } while (pnp->pn_path[0] == '/'); 418 VN_RELE(vp); 419 vp = rootvp; 420 VN_HOLD(vp); 421 } 422 if (pn_fixslash(pnp)) { 423 flags |= FOLLOW; 424 must_be_directory = 1; 425 } 426 goto next; 427 } 428 429 /* 430 * If rpnp is non-NULL, remember the resolved path name therein. 431 * Do not include "." components. Collapse occurrences of 432 * "previous/..", so long as "previous" is not itself "..". 433 * Exhausting rpnp results in error ENAMETOOLONG. 434 */ 435 if (rpnp && strcmp(component, ".") != 0) { 436 size_t len; 437 438 if (strcmp(component, "..") == 0 && 439 rpnp->pn_pathlen != 0 && 440 !((rpnp->pn_pathlen > 2 && 441 strncmp(rpnp->pn_path+rpnp->pn_pathlen-3, "/..", 3) == 0) || 442 (rpnp->pn_pathlen == 2 && 443 strncmp(rpnp->pn_path, "..", 2) == 0))) { 444 while (rpnp->pn_pathlen && 445 rpnp->pn_path[rpnp->pn_pathlen-1] != '/') 446 rpnp->pn_pathlen--; 447 if (rpnp->pn_pathlen > 1) 448 rpnp->pn_pathlen--; 449 rpnp->pn_path[rpnp->pn_pathlen] = '\0'; 450 } else { 451 if (rpnp->pn_pathlen != 0 && 452 rpnp->pn_path[rpnp->pn_pathlen-1] != '/') 453 rpnp->pn_path[rpnp->pn_pathlen++] = '/'; 454 if (flags & FIGNORECASE) { 455 /* 456 * Return the case-preserved name 457 * within the resolved path. 458 */ 459 error = copystr(pp->pn_buf, 460 rpnp->pn_path + rpnp->pn_pathlen, 461 rpnp->pn_bufsize - rpnp->pn_pathlen, &len); 462 } else { 463 error = copystr(component, 464 rpnp->pn_path + rpnp->pn_pathlen, 465 rpnp->pn_bufsize - rpnp->pn_pathlen, &len); 466 } 467 if (error) /* copystr() returns ENAMETOOLONG */ 468 goto bad; 469 rpnp->pn_pathlen += (len - 1); 470 ASSERT(rpnp->pn_bufsize > rpnp->pn_pathlen); 471 } 472 } 473 474 /* 475 * If no more components, return last directory (if wanted) and 476 * last component (if wanted). 477 */ 478 if (pn_pathleft(pnp) == 0) { 479 /* 480 * If there was a trailing slash in the pathname, 481 * make sure the last component is a directory. 482 */ 483 if (must_be_directory && cvp->v_type != VDIR) { 484 error = ENOTDIR; 485 goto bad; 486 } 487 if (dirvpp != NULL) { 488 /* 489 * Check that we have the real parent and not 490 * an alias of the last component. 491 */ 492 if (vn_compare(vp, cvp)) { 493 pn_setlast(pnp); 494 VN_RELE(vp); 495 VN_RELE(cvp); 496 if (rootvp != rootdir) 497 VN_RELE(rootvp); 498 if (pp) 499 pn_free(pp); 500 return (EINVAL); 501 } 502 *dirvpp = vp; 503 } else 504 VN_RELE(vp); 505 if (pnp->pn_path == pnp->pn_buf) 506 (void) pn_set(pnp, "."); 507 else 508 pn_setlast(pnp); 509 if (rpnp) { 510 if (VN_CMP(cvp, rootvp)) 511 (void) pn_set(rpnp, "/"); 512 else if (rpnp->pn_pathlen == 0) 513 (void) pn_set(rpnp, "."); 514 } 515 516 if (compvpp != NULL) 517 *compvpp = cvp; 518 else 519 VN_RELE(cvp); 520 if (rootvp != rootdir) 521 VN_RELE(rootvp); 522 if (pp) 523 pn_free(pp); 524 return (0); 525 } 526 527 /* 528 * Skip over slashes from end of last component. 529 */ 530 while (pnp->pn_path[0] == '/') { 531 pnp->pn_path++; 532 pnp->pn_pathlen--; 533 } 534 535 /* 536 * Searched through another level of directory: 537 * release previous directory handle and save new (result 538 * of lookup) as current directory. 539 */ 540 VN_RELE(vp); 541 vp = cvp; 542 cvp = NULL; 543 goto next; 544 545 bad: 546 /* 547 * Error. Release vnodes and return. 548 */ 549 if (cvp) 550 VN_RELE(cvp); 551 /* 552 * If the error was ESTALE and the current directory to look in 553 * was the root for this lookup, the root for a mounted file 554 * system, or the starting directory for lookups, then 555 * return ENOENT instead of ESTALE. In this case, no recovery 556 * is possible by the higher level. If ESTALE was returned for 557 * some intermediate directory along the path, then recovery 558 * is potentially possible and retrying from the higher level 559 * will either correct the situation by purging stale cache 560 * entries or eventually get back to the point where no recovery 561 * is possible. 562 */ 563 if (error == ESTALE && 564 (VN_CMP(vp, rootvp) || (vp->v_flag & VROOT) || vp == startvp)) 565 error = ENOENT; 566 VN_RELE(vp); 567 if (rootvp != rootdir) 568 VN_RELE(rootvp); 569 if (pp) 570 pn_free(pp); 571 return (error); 572 } 573 574 /* 575 * Traverse a mount point. Routine accepts a vnode pointer as a reference 576 * parameter and performs the indirection, releasing the original vnode. 577 */ 578 int 579 traverse(vnode_t **cvpp) 580 { 581 int error = 0; 582 vnode_t *cvp; 583 vnode_t *tvp; 584 vfs_t *vfsp; 585 586 cvp = *cvpp; 587 588 /* 589 * If this vnode is mounted on, then we transparently indirect 590 * to the vnode which is the root of the mounted file system. 591 * Before we do this we must check that an unmount is not in 592 * progress on this vnode. 593 */ 594 595 for (;;) { 596 /* 597 * Used to try to read lock the vnode here. 598 */ 599 600 /* 601 * Reached the end of the mount chain? 602 */ 603 vfsp = vn_mountedvfs(cvp); 604 if (vfsp == NULL) { 605 break; 606 } 607 608 /* 609 * The read lock must be held across the call to VFS_ROOT() to 610 * prevent a concurrent unmount from destroying the vfs. 611 */ 612 error = VFS_ROOT(vfsp, &tvp); 613 if (error) 614 break; 615 616 VN_RELE(cvp); 617 618 cvp = tvp; 619 } 620 621 *cvpp = cvp; 622 return (error); 623 } 624 625 /* 626 * Get the vnode path, relative to the passed rootvp. 627 * Our vncache always fills in v_path, so this is easy. 628 */ 629 /* ARGSUSED */ 630 int 631 vnodetopath(vnode_t *vrootp, vnode_t *vp, char *buf, size_t buflen, cred_t *cr) 632 { 633 int len, rvp_len = 0; 634 const char *p = vp->v_path; 635 636 if (vrootp) 637 rvp_len = strlen(vrootp->v_path); 638 len = strlen(p); 639 if (rvp_len < len) 640 p += rvp_len; 641 else 642 p = "/"; 643 644 (void) strlcpy(buf, p, buflen); 645 return (0); 646 } 647