1 /* 2 * Copyright (c) 2004 The DragonFly Project. All rights reserved. 3 * 4 * This code is derived from software contributed to The DragonFly Project 5 * by Matthew Dillon <dillon@backplane.com> 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in 15 * the documentation and/or other materials provided with the 16 * distribution. 17 * 3. Neither the name of The DragonFly Project nor the names of its 18 * contributors may be used to endorse or promote products derived 19 * from this software without specific, prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 25 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 26 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, 27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 30 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 31 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 * 34 * $DragonFly: src/sys/kern/vfs_nlookup.c,v 1.22 2007/05/09 05:12:45 dillon Exp $ 35 */ 36 /* 37 * nlookup() is the 'new' namei interface. Rather then return directory and 38 * leaf vnodes (in various lock states) the new interface instead deals in 39 * namecache records. Namecache records may represent both a positive or 40 * a negative hit. The namespace is locked via the namecache record instead 41 * of via the vnode, and only the leaf namecache record (representing the 42 * filename) needs to be locked. 43 * 44 * This greatly improves filesystem parallelism and is a huge simplification 45 * of the API verses the old vnode locking / namei scheme. 46 * 47 * Filesystems must actively control the caching aspects of the namecache, 48 * and since namecache pointers are used as handles they are non-optional 49 * even for filesystems which do not generally wish to cache things. It is 50 * intended that a separate cache coherency API will be constructed to handle 51 * these issues. 52 */ 53 54 #include "opt_ktrace.h" 55 56 #include <sys/param.h> 57 #include <sys/systm.h> 58 #include <sys/kernel.h> 59 #include <sys/vnode.h> 60 #include <sys/mount.h> 61 #include <sys/filedesc.h> 62 #include <sys/proc.h> 63 #include <sys/namei.h> 64 #include <sys/nlookup.h> 65 #include <sys/malloc.h> 66 #include <sys/stat.h> 67 #include <sys/objcache.h> 68 69 #ifdef KTRACE 70 #include <sys/ktrace.h> 71 #endif 72 73 /* 74 * Initialize a nlookup() structure, early error return for copyin faults 75 * or a degenerate empty string (which is not allowed). 76 * 77 * The first process proc0's credentials are used if the calling thread 78 * is not associated with a process context. 79 */ 80 int 81 nlookup_init(struct nlookupdata *nd, 82 const char *path, enum uio_seg seg, int flags) 83 { 84 size_t pathlen; 85 struct proc *p; 86 thread_t td; 87 int error; 88 89 td = curthread; 90 p = td->td_proc; 91 92 /* 93 * note: the pathlen set by copy*str() includes the terminating \0. 94 */ 95 bzero(nd, sizeof(struct nlookupdata)); 96 nd->nl_path = objcache_get(namei_oc, M_WAITOK); 97 nd->nl_flags |= NLC_HASBUF; 98 if (seg == UIO_SYSSPACE) 99 error = copystr(path, nd->nl_path, MAXPATHLEN, &pathlen); 100 else 101 error = copyinstr(path, nd->nl_path, MAXPATHLEN, &pathlen); 102 103 /* 104 * Don't allow empty pathnames. 105 * POSIX.1 requirement: "" is not a vaild file name. 106 */ 107 if (error == 0 && pathlen <= 1) 108 error = ENOENT; 109 110 if (error == 0) { 111 if (p && p->p_fd) { 112 cache_copy(&p->p_fd->fd_ncdir, &nd->nl_nch); 113 cache_copy(&p->p_fd->fd_nrdir, &nd->nl_rootnch); 114 if (p->p_fd->fd_njdir.ncp) 115 cache_copy(&p->p_fd->fd_njdir, &nd->nl_jailnch); 116 nd->nl_cred = crhold(p->p_ucred); 117 } else { 118 cache_copy(&rootnch, &nd->nl_nch); 119 cache_copy(&nd->nl_nch, &nd->nl_rootnch); 120 cache_copy(&nd->nl_nch, &nd->nl_jailnch); 121 nd->nl_cred = crhold(proc0.p_ucred); 122 } 123 nd->nl_td = td; 124 nd->nl_flags |= flags; 125 } else { 126 nlookup_done(nd); 127 } 128 return(error); 129 } 130 131 /* 132 * This works similarly to nlookup_init() but does not assume a process 133 * context. rootnch is always chosen for the root directory and the cred 134 * and starting directory are supplied in arguments. 135 */ 136 int 137 nlookup_init_raw(struct nlookupdata *nd, 138 const char *path, enum uio_seg seg, int flags, 139 struct ucred *cred, struct nchandle *ncstart) 140 { 141 size_t pathlen; 142 thread_t td; 143 int error; 144 145 td = curthread; 146 147 bzero(nd, sizeof(struct nlookupdata)); 148 nd->nl_path = objcache_get(namei_oc, M_WAITOK); 149 nd->nl_flags |= NLC_HASBUF; 150 if (seg == UIO_SYSSPACE) 151 error = copystr(path, nd->nl_path, MAXPATHLEN, &pathlen); 152 else 153 error = copyinstr(path, nd->nl_path, MAXPATHLEN, &pathlen); 154 155 /* 156 * Don't allow empty pathnames. 157 * POSIX.1 requirement: "" is not a vaild file name. 158 */ 159 if (error == 0 && pathlen <= 1) 160 error = ENOENT; 161 162 if (error == 0) { 163 cache_copy(ncstart, &nd->nl_nch); 164 cache_copy(&rootnch, &nd->nl_rootnch); 165 cache_copy(&rootnch, &nd->nl_jailnch); 166 nd->nl_cred = crhold(cred); 167 nd->nl_td = td; 168 nd->nl_flags |= flags; 169 } else { 170 nlookup_done(nd); 171 } 172 return(error); 173 } 174 175 /* 176 * Set a different credential; this credential will be used by future 177 * operations performed on nd.nl_open_vp and nlookupdata structure. 178 */ 179 void 180 nlookup_set_cred(struct nlookupdata *nd, struct ucred *cred) 181 { 182 KKASSERT(nd->nl_cred != NULL); 183 184 if (nd->nl_cred != cred) { 185 cred = crhold(cred); 186 crfree(nd->nl_cred); 187 nd->nl_cred = cred; 188 } 189 } 190 191 /* 192 * Cleanup a nlookupdata structure after we are through with it. This may 193 * be called on any nlookupdata structure initialized with nlookup_init(). 194 * Calling nlookup_done() is mandatory in all cases except where nlookup_init() 195 * returns an error, even if as a consumer you believe you have taken all 196 * dynamic elements out of the nlookupdata structure. 197 */ 198 void 199 nlookup_done(struct nlookupdata *nd) 200 { 201 if (nd->nl_nch.ncp) { 202 if (nd->nl_flags & NLC_NCPISLOCKED) { 203 nd->nl_flags &= ~NLC_NCPISLOCKED; 204 cache_unlock(&nd->nl_nch); 205 } 206 cache_drop(&nd->nl_nch); 207 } 208 if (nd->nl_rootnch.ncp) 209 cache_drop(&nd->nl_rootnch); 210 if (nd->nl_jailnch.ncp) 211 cache_drop(&nd->nl_jailnch); 212 if ((nd->nl_flags & NLC_HASBUF) && nd->nl_path) { 213 objcache_put(namei_oc, nd->nl_path); 214 nd->nl_path = NULL; 215 } 216 if (nd->nl_cred) { 217 crfree(nd->nl_cred); 218 nd->nl_cred = NULL; 219 } 220 if (nd->nl_open_vp) { 221 if (nd->nl_flags & NLC_LOCKVP) { 222 vn_unlock(nd->nl_open_vp); 223 nd->nl_flags &= ~NLC_LOCKVP; 224 } 225 vn_close(nd->nl_open_vp, nd->nl_vp_fmode); 226 nd->nl_open_vp = NULL; 227 } 228 nd->nl_flags = 0; /* clear remaining flags (just clear everything) */ 229 } 230 231 void 232 nlookup_zero(struct nlookupdata *nd) 233 { 234 bzero(nd, sizeof(struct nlookupdata)); 235 } 236 237 /* 238 * Simple all-in-one nlookup. Returns a locked namecache structure or NULL 239 * if an error occured. 240 * 241 * Note that the returned ncp is not checked for permissions, though VEXEC 242 * is checked on the directory path leading up to the result. The caller 243 * must call naccess() to check the permissions of the returned leaf. 244 */ 245 struct nchandle 246 nlookup_simple(const char *str, enum uio_seg seg, 247 int niflags, int *error) 248 { 249 struct nlookupdata nd; 250 struct nchandle nch; 251 252 *error = nlookup_init(&nd, str, seg, niflags); 253 if (*error == 0) { 254 if ((*error = nlookup(&nd)) == 0) { 255 nch = nd.nl_nch; /* keep hold ref from structure */ 256 cache_zero(&nd.nl_nch); /* and NULL out */ 257 } else { 258 cache_zero(&nch); 259 } 260 nlookup_done(&nd); 261 } else { 262 cache_zero(&nch); 263 } 264 return(nch); 265 } 266 267 /* 268 * Do a generic nlookup. Note that the passed nd is not nlookup_done()'d 269 * on return, even if an error occurs. If no error occurs the returned 270 * nl_nch is always referenced and locked, otherwise it may or may not be. 271 * 272 * Intermediate directory elements, including the current directory, require 273 * execute (search) permission. nlookup does not examine the access 274 * permissions on the returned element. 275 * 276 * If NLC_CREATE or NLC_DELETE is set the last directory must allow node 277 * creation (VCREATE/VDELETE), and an error code of 0 will be returned for 278 * a non-existant target. Otherwise a non-existant target will cause 279 * ENOENT to be returned. 280 */ 281 int 282 nlookup(struct nlookupdata *nd) 283 { 284 struct nlcomponent nlc; 285 struct nchandle nch; 286 struct mount *mp; 287 int wasdotordotdot; 288 char *ptr; 289 char *xptr; 290 int error; 291 int len; 292 293 #ifdef KTRACE 294 if (KTRPOINT(nd->nl_td, KTR_NAMEI)) 295 ktrnamei(nd->nl_td->td_proc, nd->nl_path); 296 #endif 297 bzero(&nlc, sizeof(nlc)); 298 299 /* 300 * Setup for the loop. The current working namecache element must 301 * be in a refd + unlocked state. This typically the case on entry except 302 * when stringing nlookup()'s along in a chain, since nlookup() always 303 * returns nl_nch in a locked state. 304 */ 305 nd->nl_loopcnt = 0; 306 if (nd->nl_flags & NLC_NCPISLOCKED) { 307 nd->nl_flags &= ~NLC_NCPISLOCKED; 308 cache_unlock(&nd->nl_nch); 309 } 310 ptr = nd->nl_path; 311 312 /* 313 * Loop on the path components. At the top of the loop nd->nl_nch 314 * is ref'd and unlocked and represents our current position. 315 */ 316 for (;;) { 317 /* 318 * Check if the root directory should replace the current 319 * directory. This is done at the start of a translation 320 * or after a symbolic link has been found. In other cases 321 * ptr will never be pointing at a '/'. 322 */ 323 if (*ptr == '/') { 324 do { 325 ++ptr; 326 } while (*ptr == '/'); 327 cache_copy(&nd->nl_rootnch, &nch); 328 cache_drop(&nd->nl_nch); 329 nd->nl_nch = nch; 330 if (*ptr == 0) { 331 cache_lock(&nd->nl_nch); 332 nd->nl_flags |= NLC_NCPISLOCKED; 333 error = 0; 334 break; 335 } 336 continue; 337 } 338 339 /* 340 * Check directory search permissions. 341 */ 342 if ((error = naccess(&nd->nl_nch, VEXEC, nd->nl_cred)) != 0) 343 break; 344 345 /* 346 * Extract the path component 347 */ 348 nlc.nlc_nameptr = ptr; 349 while (*ptr && *ptr != '/') 350 ++ptr; 351 nlc.nlc_namelen = ptr - nlc.nlc_nameptr; 352 353 /* 354 * Lookup the path component in the cache, creating an unresolved 355 * entry if necessary. We have to handle "." and ".." as special 356 * cases. 357 * 358 * When handling ".." we have to detect a traversal back through a 359 * mount point. If we are at the root, ".." just returns the root. 360 * 361 * This subsection returns a locked, refd 'nch' unless it errors out. 362 * The namecache topology is not allowed to be disconnected, so 363 * encountering a NULL parent will generate EINVAL. This typically 364 * occurs when a directory is removed out from under a process. 365 * 366 * If NLC_DELETE is set neither '.' or '..' can be the last component 367 * of a path. 368 */ 369 if (nlc.nlc_namelen == 1 && nlc.nlc_nameptr[0] == '.') { 370 cache_get(&nd->nl_nch, &nch); 371 wasdotordotdot = 1; 372 } else if (nlc.nlc_namelen == 2 && 373 nlc.nlc_nameptr[0] == '.' && nlc.nlc_nameptr[1] == '.') { 374 if (nd->nl_nch.mount == nd->nl_rootnch.mount && 375 nd->nl_nch.ncp == nd->nl_rootnch.ncp 376 ) { 377 /* 378 * ".." at the root returns the root 379 */ 380 cache_get(&nd->nl_nch, &nch); 381 } else { 382 /* 383 * Locate the parent ncp. If we are at the root of a 384 * filesystem mount we have to skip to the mounted-on 385 * point in the underlying filesystem. 386 */ 387 nch = nd->nl_nch; 388 while (nch.ncp == nch.mount->mnt_ncmountpt.ncp) 389 nch = nch.mount->mnt_ncmounton; 390 nch.ncp = nch.ncp->nc_parent; 391 KKASSERT(nch.ncp != NULL); 392 cache_get(&nch, &nch); 393 } 394 wasdotordotdot = 1; 395 } else { 396 nch = cache_nlookup(&nd->nl_nch, &nlc); 397 while ((error = cache_resolve(&nch, nd->nl_cred)) == EAGAIN) { 398 kprintf("[diagnostic] nlookup: relookup %*.*s\n", 399 nch.ncp->nc_nlen, nch.ncp->nc_nlen, nch.ncp->nc_name); 400 cache_put(&nch); 401 nch = cache_nlookup(&nd->nl_nch, &nlc); 402 } 403 wasdotordotdot = 0; 404 } 405 /* 406 * [end of subsection] ncp is locked and ref'd. nd->nl_nch is ref'd 407 */ 408 409 /* 410 * Resolve the namespace if necessary. The ncp returned by 411 * cache_nlookup() is referenced and locked. 412 * 413 * XXX neither '.' nor '..' should return EAGAIN since they were 414 * previously resolved and thus cannot be newly created ncp's. 415 */ 416 if (nch.ncp->nc_flag & NCF_UNRESOLVED) { 417 error = cache_resolve(&nch, nd->nl_cred); 418 KKASSERT(error != EAGAIN); 419 } else { 420 error = nch.ncp->nc_error; 421 } 422 423 /* 424 * Early completion. ENOENT is not an error if this is the last 425 * component and NLC_CREATE was requested. Note that ncp->nc_error 426 * is left as ENOENT in that case, which we check later on. 427 * 428 * Also handle invalid '.' or '..' components terminating a path 429 * during removal. The standard requires this and pax pretty 430 *stupidly depends on it. 431 */ 432 for (xptr = ptr; *xptr == '/'; ++xptr) 433 ; 434 if (*xptr == 0) { 435 if (error == ENOENT && (nd->nl_flags & NLC_CREATE)) 436 error = naccess(&nch, VCREATE, nd->nl_cred); 437 if (error == 0 && wasdotordotdot && (nd->nl_flags & NLC_DELETE)) 438 error = EINVAL; 439 } 440 441 /* 442 * Early completion on error. 443 */ 444 if (error) { 445 cache_put(&nch); 446 break; 447 } 448 449 /* 450 * If the element is a symlink and it is either not the last 451 * element or it is the last element and we are allowed to 452 * follow symlinks, resolve the symlink. 453 */ 454 if ((nch.ncp->nc_flag & NCF_ISSYMLINK) && 455 (*ptr || (nd->nl_flags & NLC_FOLLOW)) 456 ) { 457 if (nd->nl_loopcnt++ >= MAXSYMLINKS) { 458 error = ELOOP; 459 cache_put(&nch); 460 break; 461 } 462 error = nreadsymlink(nd, &nch, &nlc); 463 cache_put(&nch); 464 if (error) 465 break; 466 467 /* 468 * Concatenate trailing path elements onto the returned symlink. 469 * Note that if the path component (ptr) is not exhausted, it 470 * will being with a '/', so we do not have to add another one. 471 * 472 * The symlink may not be empty. 473 */ 474 len = strlen(ptr); 475 if (nlc.nlc_namelen == 0 || nlc.nlc_namelen + len >= MAXPATHLEN) { 476 error = nlc.nlc_namelen ? ENAMETOOLONG : ENOENT; 477 objcache_put(namei_oc, nlc.nlc_nameptr); 478 break; 479 } 480 bcopy(ptr, nlc.nlc_nameptr + nlc.nlc_namelen, len + 1); 481 if (nd->nl_flags & NLC_HASBUF) 482 objcache_put(namei_oc, nd->nl_path); 483 nd->nl_path = nlc.nlc_nameptr; 484 nd->nl_flags |= NLC_HASBUF; 485 ptr = nd->nl_path; 486 487 /* 488 * Go back up to the top to resolve any initial '/'s in the 489 * symlink. 490 */ 491 continue; 492 } 493 494 /* 495 * If the element is a directory and we are crossing a mount point, 496 * Locate the mount. 497 */ 498 while ((nch.ncp->nc_flag & NCF_ISMOUNTPT) && 499 (nd->nl_flags & NLC_NOCROSSMOUNT) == 0 && 500 (mp = cache_findmount(&nch)) != NULL 501 ) { 502 struct vnode *tdp; 503 504 cache_put(&nch); 505 cache_get(&mp->mnt_ncmountpt, &nch); 506 507 if (nch.ncp->nc_flag & NCF_UNRESOLVED) { 508 while (vfs_busy(mp, 0)) 509 ; 510 error = VFS_ROOT(mp, &tdp); 511 vfs_unbusy(mp); 512 if (error) 513 break; 514 cache_setvp(&nch, tdp); 515 vput(tdp); 516 } 517 } 518 if (error) { 519 cache_put(&nch); 520 break; 521 } 522 523 /* 524 * Skip any slashes to get to the next element. If there 525 * are any slashes at all the current element must be a 526 * directory or, in the create case, intended to become a directory. 527 * If it isn't we break without incrementing ptr and fall through 528 * to the failure case below. 529 */ 530 while (*ptr == '/') { 531 if ((nch.ncp->nc_flag & NCF_ISDIR) == 0 && 532 !(nd->nl_flags & NLC_WILLBEDIR) 533 ) { 534 break; 535 } 536 ++ptr; 537 } 538 539 /* 540 * Continuation case: additional elements and the current 541 * element is a directory. 542 */ 543 if (*ptr && (nch.ncp->nc_flag & NCF_ISDIR)) { 544 cache_drop(&nd->nl_nch); 545 cache_unlock(&nch); 546 nd->nl_nch = nch; 547 continue; 548 } 549 550 /* 551 * Failure case: additional elements and the current element 552 * is not a directory 553 */ 554 if (*ptr) { 555 cache_put(&nch); 556 error = ENOTDIR; 557 break; 558 } 559 560 /* 561 * Successful lookup of last element. 562 * 563 * Check directory permissions if a deletion is specified. 564 */ 565 if (*ptr == 0 && (nd->nl_flags & NLC_DELETE)) { 566 if ((error = naccess(&nch, VDELETE, nd->nl_cred)) != 0) { 567 cache_put(&nch); 568 break; 569 } 570 } 571 572 /* 573 * Termination: no more elements. If NLC_CREATE was set the 574 * ncp may represent a negative hit (ncp->nc_error will be ENOENT), 575 * but we still return an error code of 0. 576 */ 577 cache_drop(&nd->nl_nch); 578 nd->nl_nch = nch; 579 nd->nl_flags |= NLC_NCPISLOCKED; 580 error = 0; 581 break; 582 } 583 return(error); 584 } 585 586 /* 587 * Resolve a mount point's glue ncp. This ncp connects creates the illusion 588 * of continuity in the namecache tree by connecting the ncp related to the 589 * vnode under the mount to the ncp related to the mount's root vnode. 590 * 591 * If no error occured a locked, ref'd ncp is stored in *ncpp. 592 */ 593 int 594 nlookup_mp(struct mount *mp, struct nchandle *nch) 595 { 596 struct vnode *vp; 597 int error; 598 599 error = 0; 600 cache_get(&mp->mnt_ncmountpt, nch); 601 if (nch->ncp->nc_flag & NCF_UNRESOLVED) { 602 while (vfs_busy(mp, 0)) 603 ; 604 error = VFS_ROOT(mp, &vp); 605 vfs_unbusy(mp); 606 if (error) { 607 cache_put(nch); 608 } else { 609 cache_setvp(nch, vp); 610 vput(vp); 611 } 612 } 613 return(error); 614 } 615 616 /* 617 * Read the contents of a symlink, allocate a path buffer out of the 618 * namei_oc and initialize the supplied nlcomponent with the result. 619 * 620 * If an error occurs no buffer will be allocated or returned in the nlc. 621 */ 622 int 623 nreadsymlink(struct nlookupdata *nd, struct nchandle *nch, 624 struct nlcomponent *nlc) 625 { 626 struct vnode *vp; 627 struct iovec aiov; 628 struct uio auio; 629 int linklen; 630 int error; 631 char *cp; 632 633 nlc->nlc_nameptr = NULL; 634 nlc->nlc_namelen = 0; 635 if (nch->ncp->nc_vp == NULL) 636 return(ENOENT); 637 if ((error = cache_vget(nch, nd->nl_cred, LK_SHARED, &vp)) != 0) 638 return(error); 639 cp = objcache_get(namei_oc, M_WAITOK); 640 aiov.iov_base = cp; 641 aiov.iov_len = MAXPATHLEN; 642 auio.uio_iov = &aiov; 643 auio.uio_iovcnt = 1; 644 auio.uio_offset = 0; 645 auio.uio_rw = UIO_READ; 646 auio.uio_segflg = UIO_SYSSPACE; 647 auio.uio_td = nd->nl_td; 648 auio.uio_resid = MAXPATHLEN - 1; 649 error = VOP_READLINK(vp, &auio, nd->nl_cred); 650 if (error) 651 goto fail; 652 linklen = MAXPATHLEN - 1 - auio.uio_resid; 653 if (varsym_enable) { 654 linklen = varsymreplace(cp, linklen, MAXPATHLEN - 1); 655 if (linklen < 0) { 656 error = ENAMETOOLONG; 657 goto fail; 658 } 659 } 660 cp[linklen] = 0; 661 nlc->nlc_nameptr = cp; 662 nlc->nlc_namelen = linklen; 663 vput(vp); 664 return(0); 665 fail: 666 objcache_put(namei_oc, cp); 667 vput(vp); 668 return(error); 669 } 670 671 /* 672 * Check access [XXX cache vattr!] [XXX quota] 673 * 674 * Generally check the V* access bits from sys/vnode.h. All specified bits 675 * must pass for this function to return 0. 676 * 677 * If VCREATE is specified and the target ncp represents a non-existant 678 * file or dir, or if VDELETE is specified and the target exists, the parent 679 * directory is checked for VWRITE. If VEXCL is specified and the target 680 * ncp represents a positive hit, an error is returned. 681 * 682 * If VCREATE is not specified and the target does not exist (negative hit), 683 * ENOENT is returned. Note that nlookup() does not (and should not) return 684 * ENOENT for non-existant leafs. 685 * 686 * The passed ncp may or may not be locked. The caller should use a 687 * locked ncp on leaf lookups, especially for VCREATE, VDELETE, and VEXCL 688 * checks. 689 */ 690 int 691 naccess(struct nchandle *nch, int vmode, struct ucred *cred) 692 { 693 struct nchandle par; 694 struct vnode *vp; 695 struct vattr va; 696 int error; 697 698 if (nch->ncp->nc_flag & NCF_UNRESOLVED) { 699 cache_lock(nch); 700 cache_resolve(nch, cred); 701 cache_unlock(nch); 702 } 703 error = nch->ncp->nc_error; 704 if (vmode & (VDELETE|VCREATE|VEXCL)) { 705 if (((vmode & VCREATE) && nch->ncp->nc_vp == NULL) || 706 ((vmode & VDELETE) && nch->ncp->nc_vp != NULL) 707 ) { 708 if ((par.ncp = nch->ncp->nc_parent) == NULL) { 709 if (error != EAGAIN) 710 error = EINVAL; 711 } else { 712 par.mount = nch->mount; 713 cache_hold(&par); 714 error = naccess(&par, VWRITE, cred); 715 cache_drop(&par); 716 } 717 } 718 if ((vmode & VEXCL) && nch->ncp->nc_vp != NULL) 719 error = EEXIST; 720 } 721 if (error == 0) { 722 error = cache_vget(nch, cred, LK_SHARED, &vp); 723 if (error == ENOENT) { 724 if (vmode & VCREATE) 725 error = 0; 726 } else if (error == 0) { 727 /* XXX cache the va in the namecache or in the vnode */ 728 if ((error = VOP_GETATTR(vp, &va)) == 0) { 729 if ((vmode & VWRITE) && vp->v_mount) { 730 if (vp->v_mount->mnt_flag & MNT_RDONLY) 731 error = EROFS; 732 } 733 } 734 vput(vp); 735 if (error == 0) 736 error = naccess_va(&va, vmode, cred); 737 } 738 } 739 return(error); 740 } 741 742 /* 743 * Check the requested access against the given vattr using cred. 744 */ 745 int 746 naccess_va(struct vattr *va, int vmode, struct ucred *cred) 747 { 748 int i; 749 750 /* 751 * Test the immutable bit for files, directories, and softlinks. 752 */ 753 if (vmode & (VWRITE|VDELETE)) { 754 if (va->va_type == VDIR || va->va_type == VLNK || va->va_type == VREG) { 755 if (va->va_flags & IMMUTABLE) 756 return (EPERM); 757 } 758 } 759 760 /* 761 * root gets universal access 762 */ 763 if (cred->cr_uid == 0) 764 return(0); 765 766 /* 767 * Check owner perms, group perms, and world perms 768 */ 769 vmode &= S_IRWXU; 770 if (cred->cr_uid == va->va_uid) { 771 if ((vmode & va->va_mode) != vmode) 772 return(EACCES); 773 return(0); 774 } 775 776 vmode >>= 3; 777 for (i = 0; i < cred->cr_ngroups; ++i) { 778 if (va->va_gid == cred->cr_groups[i]) { 779 if ((vmode & va->va_mode) != vmode) 780 return(EACCES); 781 return(0); 782 } 783 } 784 785 vmode >>= 3; 786 if ((vmode & va->va_mode) != vmode) 787 return(EACCES); 788 return(0); 789 } 790 791