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.8 2004/11/18 20:04:24 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 <vm/vm_zone.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 int 78 nlookup_init(struct nlookupdata *nd, 79 const char *path, enum uio_seg seg, int flags) 80 { 81 size_t pathlen; 82 struct proc *p; 83 thread_t td; 84 int error; 85 86 td = curthread; 87 p = td->td_proc; 88 89 /* 90 * note: the pathlen set by copy*str() includes the terminating \0. 91 */ 92 bzero(nd, sizeof(struct nlookupdata)); 93 nd->nl_path = zalloc(namei_zone); 94 nd->nl_flags |= NLC_HASBUF; 95 if (seg == UIO_SYSSPACE) 96 error = copystr(path, nd->nl_path, MAXPATHLEN, &pathlen); 97 else 98 error = copyinstr(path, nd->nl_path, MAXPATHLEN, &pathlen); 99 100 /* 101 * Don't allow empty pathnames. 102 * POSIX.1 requirement: "" is not a vaild file name. 103 */ 104 if (error == 0 && pathlen <= 1) 105 error = ENOENT; 106 107 if (error == 0) { 108 if (p && p->p_fd) { 109 nd->nl_ncp = cache_hold(p->p_fd->fd_ncdir); 110 nd->nl_rootncp = cache_hold(p->p_fd->fd_nrdir); 111 if (p->p_fd->fd_njdir) 112 nd->nl_jailncp = cache_hold(p->p_fd->fd_njdir); 113 nd->nl_cred = crhold(p->p_ucred); 114 } else { 115 nd->nl_ncp = cache_hold(rootncp); 116 nd->nl_rootncp = cache_hold(nd->nl_ncp); 117 nd->nl_jailncp = cache_hold(nd->nl_ncp); 118 nd->nl_cred = crhold(proc0.p_ucred); 119 } 120 nd->nl_td = td; 121 nd->nl_flags |= flags; 122 } else { 123 nlookup_done(nd); 124 } 125 return(error); 126 } 127 128 /* 129 * This works similarly to nlookup_init() but does not assume a process 130 * context. rootncp is always chosen for the root directory and the cred 131 * and starting directory are supplied in arguments. 132 */ 133 int 134 nlookup_init_raw(struct nlookupdata *nd, 135 const char *path, enum uio_seg seg, int flags, 136 struct ucred *cred, struct namecache *ncstart) 137 { 138 size_t pathlen; 139 thread_t td; 140 int error; 141 142 td = curthread; 143 144 bzero(nd, sizeof(struct nlookupdata)); 145 nd->nl_path = zalloc(namei_zone); 146 nd->nl_flags |= NLC_HASBUF; 147 if (seg == UIO_SYSSPACE) 148 error = copystr(path, nd->nl_path, MAXPATHLEN, &pathlen); 149 else 150 error = copyinstr(path, nd->nl_path, MAXPATHLEN, &pathlen); 151 152 /* 153 * Don't allow empty pathnames. 154 * POSIX.1 requirement: "" is not a vaild file name. 155 */ 156 if (error == 0 && pathlen <= 1) 157 error = ENOENT; 158 159 if (error == 0) { 160 nd->nl_ncp = cache_hold(ncstart); 161 nd->nl_rootncp = cache_hold(rootncp); 162 nd->nl_jailncp = cache_hold(rootncp); 163 nd->nl_cred = crhold(cred); 164 nd->nl_td = td; 165 nd->nl_flags |= flags; 166 } else { 167 nlookup_done(nd); 168 } 169 return(error); 170 } 171 172 /* 173 * Cleanup a nlookupdata structure after we are through with it. This may 174 * be called on any nlookupdata structure initialized with nlookup_init(). 175 * Calling nlookup_done() is mandatory in all cases except where nlookup_init() 176 * returns an error, even if as a consumer you believe you have taken all 177 * dynamic elements out of the nlookupdata structure. 178 */ 179 void 180 nlookup_done(struct nlookupdata *nd) 181 { 182 if (nd->nl_ncp) { 183 if (nd->nl_flags & NLC_NCPISLOCKED) { 184 nd->nl_flags &= ~NLC_NCPISLOCKED; 185 cache_unlock(nd->nl_ncp); 186 } 187 cache_drop(nd->nl_ncp); 188 nd->nl_ncp = NULL; 189 } 190 if (nd->nl_rootncp) { 191 cache_drop(nd->nl_rootncp); 192 nd->nl_rootncp = NULL; 193 } 194 if (nd->nl_jailncp) { 195 cache_drop(nd->nl_jailncp); 196 nd->nl_jailncp = NULL; 197 } 198 if ((nd->nl_flags & NLC_HASBUF) && nd->nl_path) { 199 zfree(namei_zone, nd->nl_path); 200 nd->nl_path = NULL; 201 } 202 if (nd->nl_cred) { 203 crfree(nd->nl_cred); 204 nd->nl_cred = NULL; 205 } 206 if (nd->nl_open_vp) { 207 if (nd->nl_flags & NLC_LOCKVP) { 208 VOP_UNLOCK(nd->nl_open_vp, 0, nd->nl_td); 209 nd->nl_flags &= ~NLC_LOCKVP; 210 } 211 vn_close(nd->nl_open_vp, nd->nl_vp_fmode, nd->nl_td); 212 nd->nl_open_vp = NULL; 213 } 214 nd->nl_flags = 0; /* clear remaining flags (just clear everything) */ 215 } 216 217 void 218 nlookup_zero(struct nlookupdata *nd) 219 { 220 bzero(nd, sizeof(struct nlookupdata)); 221 } 222 223 /* 224 * Simple all-in-one nlookup. Returns a locked namecache structure or NULL 225 * if an error occured. 226 * 227 * Note that the returned ncp is not checked for permissions, though VEXEC 228 * is checked on the directory path leading up to the result. The caller 229 * must call naccess() to check the permissions of the returned leaf. 230 */ 231 struct namecache * 232 nlookup_simple(const char *str, enum uio_seg seg, 233 int niflags, int *error) 234 { 235 struct nlookupdata nd; 236 struct namecache *ncp; 237 238 *error = nlookup_init(&nd, str, seg, niflags); 239 if (*error == 0) { 240 if ((*error = nlookup(&nd)) == 0) { 241 ncp = nd.nl_ncp; /* keep hold ref from structure */ 242 nd.nl_ncp = NULL; /* and NULL out */ 243 } else { 244 ncp = NULL; 245 } 246 nlookup_done(&nd); 247 } else { 248 ncp = NULL; 249 } 250 return(ncp); 251 } 252 253 /* 254 * Do a generic nlookup. Note that the passed nd is not nlookup_done()'d 255 * on return, even if an error occurs. If no error occurs the returned 256 * nl_ncp is always referenced and locked, otherwise it may or may not be. 257 * 258 * Intermediate directory elements, including the current directory, require 259 * execute (search) permission. nlookup does not examine the access 260 * permissions on the returned element. 261 * 262 * If NLC_CREATE or NLC_DELETE is set the last directory must allow node 263 * creation (VCREATE/VDELETE), and an error code of 0 will be returned for 264 * a non-existant target. Otherwise a non-existant target will cause 265 * ENOENT to be returned. 266 */ 267 int 268 nlookup(struct nlookupdata *nd) 269 { 270 struct nlcomponent nlc; 271 struct namecache *ncp; 272 char *ptr; 273 char *xptr; 274 int error; 275 int len; 276 277 #ifdef KTRACE 278 if (KTRPOINT(nd->nl_td, KTR_NAMEI)) 279 ktrnamei(nd->nl_td->td_proc->p_tracep, nd->nl_path); 280 #endif 281 bzero(&nlc, sizeof(nlc)); 282 283 /* 284 * Setup for the loop. The current working namecache element must 285 * be in a refd + unlocked state. This typically the case on entry except 286 * when stringing nlookup()'s along in a chain, since nlookup() always 287 * returns nl_ncp in a locked state. 288 */ 289 nd->nl_loopcnt = 0; 290 if (nd->nl_flags & NLC_NCPISLOCKED) { 291 nd->nl_flags &= ~NLC_NCPISLOCKED; 292 cache_unlock(nd->nl_ncp); 293 } 294 ptr = nd->nl_path; 295 296 /* 297 * Loop on the path components. At the top of the loop nd->nl_ncp 298 * is ref'd and unlocked and represents our current position. 299 */ 300 for (;;) { 301 /* 302 * Check if the root directory should replace the current 303 * directory. This is done at the start of a translation 304 * or after a symbolic link has been found. In other cases 305 * ptr will never be pointing at a '/'. 306 */ 307 if (*ptr == '/') { 308 do { 309 ++ptr; 310 } while (*ptr == '/'); 311 ncp = cache_hold(nd->nl_rootncp); 312 cache_drop(nd->nl_ncp); 313 nd->nl_ncp = ncp; 314 if (*ptr == 0) { 315 cache_lock(nd->nl_ncp); 316 nd->nl_flags |= NLC_NCPISLOCKED; 317 error = 0; 318 break; 319 } 320 continue; 321 } 322 323 /* 324 * Check directory search permissions 325 */ 326 if ((error = naccess(nd->nl_ncp, VEXEC, nd->nl_cred)) != 0) 327 break; 328 329 /* 330 * Extract the path component 331 */ 332 nlc.nlc_nameptr = ptr; 333 while (*ptr && *ptr != '/') 334 ++ptr; 335 nlc.nlc_namelen = ptr - nlc.nlc_nameptr; 336 337 /* 338 * Lookup the path component in the cache, creating an unresolved 339 * entry if necessary. We have to handle "." and ".." as special 340 * cases. 341 * 342 * When handling ".." we have to detect a traversal back through a 343 * mount point and skip the mount-under node. If we are at the root 344 * ".." just returns the root. 345 * 346 * This subsection returns a locked, refd 'ncp' unless it errors out. 347 * The namecache topology is not allowed to be disconnected, so 348 * encountering a NULL parent will generate EINVAL. This typically 349 * occurs when a directory is removed out from under a process. 350 */ 351 if (nlc.nlc_namelen == 1 && nlc.nlc_nameptr[0] == '.') { 352 ncp = cache_get(nd->nl_ncp); 353 } else if (nlc.nlc_namelen == 2 && 354 nlc.nlc_nameptr[0] == '.' && nlc.nlc_nameptr[1] == '.') { 355 ncp = nd->nl_ncp; 356 if (ncp == nd->nl_rootncp) { 357 ncp = cache_get(ncp); 358 } else { 359 while ((ncp->nc_flag & NCF_MOUNTPT) && ncp != nd->nl_rootncp) { 360 if (ncp->nc_parent->nc_flag & NCF_DESTROYED) 361 break; 362 ncp = ncp->nc_parent; /* get to underlying node */ 363 KKASSERT(ncp != NULL && 1); 364 } 365 if (ncp != nd->nl_rootncp) { 366 if (ncp->nc_parent->nc_flag & NCF_DESTROYED) { 367 error = EINVAL; 368 break; 369 } 370 ncp = ncp->nc_parent; 371 if (ncp == NULL) { 372 error = EINVAL; 373 break; 374 } 375 } 376 ncp = cache_get(ncp); 377 } 378 } else { 379 ncp = cache_nlookup(nd->nl_ncp, &nlc); 380 while ((error = cache_resolve(ncp, nd->nl_cred)) == EAGAIN) { 381 printf("[diagnostic] nlookup: relookup %*.*s\n", 382 ncp->nc_nlen, ncp->nc_nlen, ncp->nc_name); 383 cache_put(ncp); 384 ncp = cache_nlookup(nd->nl_ncp, &nlc); 385 } 386 } 387 /* 388 * [end of subsection] ncp is locked and ref'd. nd->nl_ncp is ref'd 389 */ 390 391 /* 392 * Resolve the namespace if necessary. The ncp returned by 393 * cache_nlookup() is referenced and locked. 394 * 395 * XXX neither '.' nor '..' should return EAGAIN since they were 396 * previously resolved and thus cannot be newly created ncp's. 397 */ 398 if (ncp->nc_flag & NCF_UNRESOLVED) { 399 error = cache_resolve(ncp, nd->nl_cred); 400 KKASSERT(error != EAGAIN); 401 } else { 402 error = ncp->nc_error; 403 } 404 405 /* 406 * Early completion. ENOENT is not an error if this is the last 407 * component and NLC_CREATE was requested. Note that ncp->nc_error 408 * is left as ENOENT in that case, which we check later on. 409 */ 410 for (xptr = ptr; *xptr == '/'; ++xptr) 411 ; 412 if (error == ENOENT && *xptr == 0 && (nd->nl_flags & NLC_CREATE)) { 413 error = naccess(ncp, VCREATE, nd->nl_cred); 414 } 415 416 /* 417 * Early completion on error. 418 */ 419 if (error) { 420 cache_put(ncp); 421 break; 422 } 423 424 /* 425 * If the element is a symlink and it is either not the last 426 * element or it is the last element and we are allowed to 427 * follow symlinks, resolve the symlink. 428 */ 429 if ((ncp->nc_flag & NCF_ISSYMLINK) && 430 (*ptr || (nd->nl_flags & NLC_FOLLOW)) 431 ) { 432 if (nd->nl_loopcnt++ >= MAXSYMLINKS) { 433 error = ELOOP; 434 cache_put(ncp); 435 break; 436 } 437 error = nreadsymlink(nd, ncp, &nlc); 438 cache_put(ncp); 439 if (error) 440 break; 441 442 /* 443 * Concatenate trailing path elements onto the returned symlink. 444 * Note that if the path component (ptr) is not exhausted, it 445 * will being with a '/', so we do not have to add another one. 446 * 447 * The symlink may not be empty. 448 */ 449 len = strlen(ptr); 450 if (nlc.nlc_namelen == 0 || nlc.nlc_namelen + len >= MAXPATHLEN) { 451 error = nlc.nlc_namelen ? ENAMETOOLONG : ENOENT; 452 zfree(namei_zone, nlc.nlc_nameptr); 453 break; 454 } 455 bcopy(ptr, nlc.nlc_nameptr + nlc.nlc_namelen, len + 1); 456 if (nd->nl_flags & NLC_HASBUF) 457 zfree(namei_zone, nd->nl_path); 458 nd->nl_path = nlc.nlc_nameptr; 459 nd->nl_flags |= NLC_HASBUF; 460 ptr = nd->nl_path; 461 462 /* 463 * Go back up to the top to resolve any initial '/'s in the 464 * symlink. 465 */ 466 continue; 467 } 468 469 /* 470 * If the element is a directory and we are crossing a mount point, 471 * retrieve the root of the mounted filesystem from mnt_ncp and 472 * resolve it if necessary. 473 * 474 * XXX mnt_ncp should really be resolved in the mount code. 475 * NOTE! the normal nresolve() code cannot resolve mount point ncp's! 476 * 477 * XXX NOCROSSMOUNT 478 */ 479 while ((ncp->nc_flag & NCF_ISDIR) && ncp->nc_vp->v_mountedhere && 480 (nd->nl_flags & NLC_NOCROSSMOUNT) == 0 481 ) { 482 struct mount *mp; 483 struct vnode *tdp; 484 485 mp = ncp->nc_vp->v_mountedhere; 486 cache_put(ncp); 487 ncp = cache_get(mp->mnt_ncp); 488 489 if (ncp->nc_flag & NCF_UNRESOLVED) { 490 while (vfs_busy(mp, 0, NULL, nd->nl_td)) 491 ; 492 error = VFS_ROOT(mp, &tdp); 493 vfs_unbusy(mp, nd->nl_td); 494 if (error) 495 break; 496 cache_setvp(ncp, tdp); 497 vput(tdp); 498 } 499 } 500 if (error) { 501 cache_put(ncp); 502 break; 503 } 504 505 /* 506 * Skip any slashes to get to the next element. If there 507 * are any slashes at all the current element must be a 508 * directory or, in the create case, intended to become a directory. 509 * If it isn't we break without incrementing ptr and fall through 510 * to the failure case below. 511 */ 512 while (*ptr == '/') { 513 if ((ncp->nc_flag & NCF_ISDIR) == 0 && 514 !(nd->nl_flags & NLC_WILLBEDIR) 515 ) { 516 break; 517 } 518 ++ptr; 519 } 520 521 /* 522 * Continuation case: additional elements and the current 523 * element is a directory. 524 */ 525 if (*ptr && (ncp->nc_flag & NCF_ISDIR)) { 526 cache_drop(nd->nl_ncp); 527 cache_unlock(ncp); 528 nd->nl_ncp = ncp; 529 continue; 530 } 531 532 /* 533 * Failure case: additional elements and the current element 534 * is not a directory 535 */ 536 if (*ptr) { 537 cache_put(ncp); 538 error = ENOTDIR; 539 break; 540 } 541 542 /* 543 * Successful lookup of last element. 544 * 545 * Check directory permissions if a deletion is specified. 546 */ 547 if (*ptr == 0 && (nd->nl_flags & NLC_DELETE)) { 548 if ((error = naccess(ncp, VDELETE, nd->nl_cred)) != 0) { 549 cache_put(ncp); 550 break; 551 } 552 } 553 554 /* 555 * XXX vnode canvmio (test in mmap(), read(), and write()) 556 */ 557 558 /* 559 * Termination: no more elements. If NLC_CREATE was set the 560 * ncp may represent a negative hit (ncp->nc_error will be ENOENT), 561 * but we still return an error code of 0. 562 */ 563 cache_drop(nd->nl_ncp); 564 nd->nl_ncp = ncp; 565 nd->nl_flags |= NLC_NCPISLOCKED; 566 error = 0; 567 break; 568 } 569 return(error); 570 } 571 572 /* 573 * Resolve a mount point's glue ncp. This ncp connects creates the illusion 574 * of continuity in the namecache tree by connecting the ncp related to the 575 * vnode under the mount to the ncp related to the mount's root vnode. 576 * 577 * If no error occured a locked, ref'd ncp is stored in *ncpp. 578 */ 579 int 580 nlookup_mp(struct mount *mp, struct namecache **ncpp) 581 { 582 struct namecache *ncp; 583 struct vnode *vp; 584 int error; 585 586 error = 0; 587 ncp = mp->mnt_ncp; 588 cache_get(ncp); 589 if (ncp->nc_flag & NCF_UNRESOLVED) { 590 while (vfs_busy(mp, 0, NULL, curthread)) 591 ; 592 error = VFS_ROOT(mp, &vp); 593 vfs_unbusy(mp, curthread); 594 if (error) { 595 cache_put(ncp); 596 ncp = NULL; 597 } else { 598 cache_setvp(ncp, vp); 599 vput(vp); 600 } 601 } 602 *ncpp = ncp; 603 return(error); 604 } 605 606 /* 607 * Read the contents of a symlink, allocate a path buffer out of the 608 * namei_zone and initialize the supplied nlcomponent with the result. 609 * 610 * If an error occurs no buffer will be allocated or returned in the nlc. 611 */ 612 int 613 nreadsymlink(struct nlookupdata *nd, struct namecache *ncp, 614 struct nlcomponent *nlc) 615 { 616 struct vnode *vp; 617 struct iovec aiov; 618 struct uio auio; 619 int linklen; 620 int error; 621 char *cp; 622 623 nlc->nlc_nameptr = NULL; 624 nlc->nlc_namelen = 0; 625 if (ncp->nc_vp == NULL) 626 return(ENOENT); 627 if ((error = cache_vget(ncp, nd->nl_cred, LK_SHARED, &vp)) != 0) 628 return(error); 629 cp = zalloc(namei_zone); 630 aiov.iov_base = cp; 631 aiov.iov_len = MAXPATHLEN; 632 auio.uio_iov = &aiov; 633 auio.uio_iovcnt = 1; 634 auio.uio_offset = 0; 635 auio.uio_rw = UIO_READ; 636 auio.uio_segflg = UIO_SYSSPACE; 637 auio.uio_td = nd->nl_td; 638 auio.uio_resid = MAXPATHLEN - 1; 639 error = VOP_READLINK(vp, &auio, nd->nl_cred); 640 if (error) 641 goto fail; 642 linklen = MAXPATHLEN - 1 - auio.uio_resid; 643 if (varsym_enable) { 644 linklen = varsymreplace(cp, linklen, MAXPATHLEN - 1); 645 if (linklen < 0) { 646 error = ENAMETOOLONG; 647 goto fail; 648 } 649 } 650 cp[linklen] = 0; 651 nlc->nlc_nameptr = cp; 652 nlc->nlc_namelen = linklen; 653 vput(vp); 654 return(0); 655 fail: 656 zfree(namei_zone, cp); 657 vput(vp); 658 return(error); 659 } 660 661 /* 662 * Check access [XXX cache vattr!] [XXX quota] 663 * 664 * Generally check the V* access bits from sys/vnode.h. All specified bits 665 * must pass for this function to return 0. 666 * 667 * If VCREATE is specified and the target ncp represents a non-existant 668 * file or dir, or if VDELETE is specified and the target exists, the parent 669 * directory is checked for VWRITE. If VEXCL is specified and the target 670 * ncp represents a positive hit, an error is returned. 671 * 672 * If VCREATE is not specified and the target does not exist (negative hit), 673 * ENOENT is returned. Note that nlookup() does not (and should not) return 674 * ENOENT for non-existant leafs. 675 * 676 * The passed ncp may or may not be locked. The caller should use a 677 * locked ncp on leaf lookups, especially for VCREATE, VDELETE, and VEXCL 678 * checks. 679 */ 680 int 681 naccess(struct namecache *ncp, int vmode, struct ucred *cred) 682 { 683 struct namecache *par; 684 struct vnode *vp; 685 struct vattr va; 686 int error; 687 688 if (ncp->nc_flag & NCF_UNRESOLVED) { 689 cache_lock(ncp); 690 cache_resolve(ncp, cred); 691 cache_unlock(ncp); 692 } 693 error = ncp->nc_error; 694 if (vmode & (VDELETE|VCREATE|VEXCL)) { 695 if (((vmode & VCREATE) && ncp->nc_vp == NULL) || 696 ((vmode & VDELETE) && ncp->nc_vp != NULL) 697 ) { 698 if ((par = ncp->nc_parent) == NULL) { 699 if (error != EAGAIN) 700 error = EINVAL; 701 } else { 702 cache_hold(par); 703 error = naccess(par, VWRITE, cred); 704 cache_drop(par); 705 } 706 } 707 if ((vmode & VEXCL) && ncp->nc_vp != NULL) 708 error = EEXIST; 709 } 710 if (error == 0) { 711 error = cache_vget(ncp, cred, LK_SHARED, &vp); 712 if (error == ENOENT) { 713 if (vmode & VCREATE) 714 error = 0; 715 } else if (error == 0) { 716 /* XXX cache the va in the namecache or in the vnode */ 717 if ((error = VOP_GETATTR(vp, &va, curthread)) == 0) { 718 if ((vmode & VWRITE) && vp->v_mount) { 719 if (vp->v_mount->mnt_flag & MNT_RDONLY) 720 error = EROFS; 721 } 722 } 723 vput(vp); 724 if (error == 0) 725 error = naccess_va(&va, vmode, cred); 726 } 727 } 728 return(error); 729 } 730 731 /* 732 * Check the requested access against the given vattr using cred. 733 */ 734 int 735 naccess_va(struct vattr *va, int vmode, struct ucred *cred) 736 { 737 int i; 738 739 /* 740 * Test the immutable bit for files, directories, and softlinks. 741 */ 742 if (vmode & (VWRITE|VDELETE)) { 743 if (va->va_type == VDIR || va->va_type == VLNK || va->va_type == VREG) { 744 if (va->va_flags & IMMUTABLE) 745 return (EPERM); 746 } 747 } 748 749 /* 750 * root gets universal access 751 */ 752 if (cred->cr_uid == 0) 753 return(0); 754 755 /* 756 * Check owner perms, group perms, and world perms 757 */ 758 vmode &= S_IRWXU; 759 if (cred->cr_uid == va->va_uid) { 760 if ((vmode & va->va_mode) != vmode) 761 return(EACCES); 762 return(0); 763 } 764 765 vmode >>= 3; 766 for (i = 0; i < cred->cr_ngroups; ++i) { 767 if (va->va_gid == cred->cr_groups[i]) { 768 if ((vmode & va->va_mode) != vmode) 769 return(EACCES); 770 return(0); 771 } 772 } 773 774 vmode >>= 3; 775 if ((vmode & va->va_mode) != vmode) 776 return(EACCES); 777 return(0); 778 } 779 780