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.9 2004/12/27 20:30:03 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 int wasdotordotdot; 273 char *ptr; 274 char *xptr; 275 int error; 276 int len; 277 278 #ifdef KTRACE 279 if (KTRPOINT(nd->nl_td, KTR_NAMEI)) 280 ktrnamei(nd->nl_td->td_proc->p_tracep, nd->nl_path); 281 #endif 282 bzero(&nlc, sizeof(nlc)); 283 284 /* 285 * Setup for the loop. The current working namecache element must 286 * be in a refd + unlocked state. This typically the case on entry except 287 * when stringing nlookup()'s along in a chain, since nlookup() always 288 * returns nl_ncp in a locked state. 289 */ 290 nd->nl_loopcnt = 0; 291 if (nd->nl_flags & NLC_NCPISLOCKED) { 292 nd->nl_flags &= ~NLC_NCPISLOCKED; 293 cache_unlock(nd->nl_ncp); 294 } 295 ptr = nd->nl_path; 296 297 /* 298 * Loop on the path components. At the top of the loop nd->nl_ncp 299 * is ref'd and unlocked and represents our current position. 300 */ 301 for (;;) { 302 /* 303 * Check if the root directory should replace the current 304 * directory. This is done at the start of a translation 305 * or after a symbolic link has been found. In other cases 306 * ptr will never be pointing at a '/'. 307 */ 308 if (*ptr == '/') { 309 do { 310 ++ptr; 311 } while (*ptr == '/'); 312 ncp = cache_hold(nd->nl_rootncp); 313 cache_drop(nd->nl_ncp); 314 nd->nl_ncp = ncp; 315 if (*ptr == 0) { 316 cache_lock(nd->nl_ncp); 317 nd->nl_flags |= NLC_NCPISLOCKED; 318 error = 0; 319 break; 320 } 321 continue; 322 } 323 324 /* 325 * Check directory search permissions 326 */ 327 if ((error = naccess(nd->nl_ncp, VEXEC, nd->nl_cred)) != 0) 328 break; 329 330 /* 331 * Extract the path component 332 */ 333 nlc.nlc_nameptr = ptr; 334 while (*ptr && *ptr != '/') 335 ++ptr; 336 nlc.nlc_namelen = ptr - nlc.nlc_nameptr; 337 338 /* 339 * Lookup the path component in the cache, creating an unresolved 340 * entry if necessary. We have to handle "." and ".." as special 341 * cases. 342 * 343 * When handling ".." we have to detect a traversal back through a 344 * mount point and skip the mount-under node. If we are at the root 345 * ".." just returns the root. 346 * 347 * This subsection returns a locked, refd 'ncp' unless it errors out. 348 * The namecache topology is not allowed to be disconnected, so 349 * encountering a NULL parent will generate EINVAL. This typically 350 * occurs when a directory is removed out from under a process. 351 * 352 * If NLC_DELETE is set neither '.' or '..' can be the last component 353 * of a path. 354 */ 355 if (nlc.nlc_namelen == 1 && nlc.nlc_nameptr[0] == '.') { 356 ncp = cache_get(nd->nl_ncp); 357 wasdotordotdot = 1; 358 } else if (nlc.nlc_namelen == 2 && 359 nlc.nlc_nameptr[0] == '.' && nlc.nlc_nameptr[1] == '.') { 360 ncp = nd->nl_ncp; 361 if (ncp == nd->nl_rootncp) { 362 ncp = cache_get(ncp); 363 } else { 364 while ((ncp->nc_flag & NCF_MOUNTPT) && ncp != nd->nl_rootncp) { 365 if (ncp->nc_parent->nc_flag & NCF_DESTROYED) 366 break; 367 ncp = ncp->nc_parent; /* get to underlying node */ 368 KKASSERT(ncp != NULL && 1); 369 } 370 if (ncp != nd->nl_rootncp) { 371 if (ncp->nc_parent->nc_flag & NCF_DESTROYED) { 372 error = EINVAL; 373 break; 374 } 375 ncp = ncp->nc_parent; 376 if (ncp == NULL) { 377 error = EINVAL; 378 break; 379 } 380 } 381 ncp = cache_get(ncp); 382 } 383 wasdotordotdot = 1; 384 } else { 385 ncp = cache_nlookup(nd->nl_ncp, &nlc); 386 while ((error = cache_resolve(ncp, nd->nl_cred)) == EAGAIN) { 387 printf("[diagnostic] nlookup: relookup %*.*s\n", 388 ncp->nc_nlen, ncp->nc_nlen, ncp->nc_name); 389 cache_put(ncp); 390 ncp = cache_nlookup(nd->nl_ncp, &nlc); 391 } 392 wasdotordotdot = 0; 393 } 394 /* 395 * [end of subsection] ncp is locked and ref'd. nd->nl_ncp is ref'd 396 */ 397 398 /* 399 * Resolve the namespace if necessary. The ncp returned by 400 * cache_nlookup() is referenced and locked. 401 * 402 * XXX neither '.' nor '..' should return EAGAIN since they were 403 * previously resolved and thus cannot be newly created ncp's. 404 */ 405 if (ncp->nc_flag & NCF_UNRESOLVED) { 406 error = cache_resolve(ncp, nd->nl_cred); 407 KKASSERT(error != EAGAIN); 408 } else { 409 error = ncp->nc_error; 410 } 411 412 /* 413 * Early completion. ENOENT is not an error if this is the last 414 * component and NLC_CREATE was requested. Note that ncp->nc_error 415 * is left as ENOENT in that case, which we check later on. 416 * 417 * Also handle invalid '.' or '..' components terminating a path 418 * during removal. The standard requires this and pax pretty 419 *stupidly depends on it. 420 */ 421 for (xptr = ptr; *xptr == '/'; ++xptr) 422 ; 423 if (*xptr == 0) { 424 if (error == ENOENT && (nd->nl_flags & NLC_CREATE)) 425 error = naccess(ncp, VCREATE, nd->nl_cred); 426 if (error == 0 && wasdotordotdot && (nd->nl_flags & NLC_DELETE)) 427 error = EINVAL; 428 } 429 430 /* 431 * Early completion on error. 432 */ 433 if (error) { 434 cache_put(ncp); 435 break; 436 } 437 438 /* 439 * If the element is a symlink and it is either not the last 440 * element or it is the last element and we are allowed to 441 * follow symlinks, resolve the symlink. 442 */ 443 if ((ncp->nc_flag & NCF_ISSYMLINK) && 444 (*ptr || (nd->nl_flags & NLC_FOLLOW)) 445 ) { 446 if (nd->nl_loopcnt++ >= MAXSYMLINKS) { 447 error = ELOOP; 448 cache_put(ncp); 449 break; 450 } 451 error = nreadsymlink(nd, ncp, &nlc); 452 cache_put(ncp); 453 if (error) 454 break; 455 456 /* 457 * Concatenate trailing path elements onto the returned symlink. 458 * Note that if the path component (ptr) is not exhausted, it 459 * will being with a '/', so we do not have to add another one. 460 * 461 * The symlink may not be empty. 462 */ 463 len = strlen(ptr); 464 if (nlc.nlc_namelen == 0 || nlc.nlc_namelen + len >= MAXPATHLEN) { 465 error = nlc.nlc_namelen ? ENAMETOOLONG : ENOENT; 466 zfree(namei_zone, nlc.nlc_nameptr); 467 break; 468 } 469 bcopy(ptr, nlc.nlc_nameptr + nlc.nlc_namelen, len + 1); 470 if (nd->nl_flags & NLC_HASBUF) 471 zfree(namei_zone, nd->nl_path); 472 nd->nl_path = nlc.nlc_nameptr; 473 nd->nl_flags |= NLC_HASBUF; 474 ptr = nd->nl_path; 475 476 /* 477 * Go back up to the top to resolve any initial '/'s in the 478 * symlink. 479 */ 480 continue; 481 } 482 483 /* 484 * If the element is a directory and we are crossing a mount point, 485 * retrieve the root of the mounted filesystem from mnt_ncp and 486 * resolve it if necessary. 487 * 488 * XXX mnt_ncp should really be resolved in the mount code. 489 * NOTE! the normal nresolve() code cannot resolve mount point ncp's! 490 * 491 * XXX NOCROSSMOUNT 492 */ 493 while ((ncp->nc_flag & NCF_ISDIR) && ncp->nc_vp->v_mountedhere && 494 (nd->nl_flags & NLC_NOCROSSMOUNT) == 0 495 ) { 496 struct mount *mp; 497 struct vnode *tdp; 498 499 mp = ncp->nc_vp->v_mountedhere; 500 cache_put(ncp); 501 ncp = cache_get(mp->mnt_ncp); 502 503 if (ncp->nc_flag & NCF_UNRESOLVED) { 504 while (vfs_busy(mp, 0, NULL, nd->nl_td)) 505 ; 506 error = VFS_ROOT(mp, &tdp); 507 vfs_unbusy(mp, nd->nl_td); 508 if (error) 509 break; 510 cache_setvp(ncp, tdp); 511 vput(tdp); 512 } 513 } 514 if (error) { 515 cache_put(ncp); 516 break; 517 } 518 519 /* 520 * Skip any slashes to get to the next element. If there 521 * are any slashes at all the current element must be a 522 * directory or, in the create case, intended to become a directory. 523 * If it isn't we break without incrementing ptr and fall through 524 * to the failure case below. 525 */ 526 while (*ptr == '/') { 527 if ((ncp->nc_flag & NCF_ISDIR) == 0 && 528 !(nd->nl_flags & NLC_WILLBEDIR) 529 ) { 530 break; 531 } 532 ++ptr; 533 } 534 535 /* 536 * Continuation case: additional elements and the current 537 * element is a directory. 538 */ 539 if (*ptr && (ncp->nc_flag & NCF_ISDIR)) { 540 cache_drop(nd->nl_ncp); 541 cache_unlock(ncp); 542 nd->nl_ncp = ncp; 543 continue; 544 } 545 546 /* 547 * Failure case: additional elements and the current element 548 * is not a directory 549 */ 550 if (*ptr) { 551 cache_put(ncp); 552 error = ENOTDIR; 553 break; 554 } 555 556 /* 557 * Successful lookup of last element. 558 * 559 * Check directory permissions if a deletion is specified. 560 */ 561 if (*ptr == 0 && (nd->nl_flags & NLC_DELETE)) { 562 if ((error = naccess(ncp, VDELETE, nd->nl_cred)) != 0) { 563 cache_put(ncp); 564 break; 565 } 566 } 567 568 /* 569 * XXX vnode canvmio (test in mmap(), read(), and write()) 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_ncp); 578 nd->nl_ncp = ncp; 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 namecache **ncpp) 595 { 596 struct namecache *ncp; 597 struct vnode *vp; 598 int error; 599 600 error = 0; 601 ncp = mp->mnt_ncp; 602 cache_get(ncp); 603 if (ncp->nc_flag & NCF_UNRESOLVED) { 604 while (vfs_busy(mp, 0, NULL, curthread)) 605 ; 606 error = VFS_ROOT(mp, &vp); 607 vfs_unbusy(mp, curthread); 608 if (error) { 609 cache_put(ncp); 610 ncp = NULL; 611 } else { 612 cache_setvp(ncp, vp); 613 vput(vp); 614 } 615 } 616 *ncpp = ncp; 617 return(error); 618 } 619 620 /* 621 * Read the contents of a symlink, allocate a path buffer out of the 622 * namei_zone and initialize the supplied nlcomponent with the result. 623 * 624 * If an error occurs no buffer will be allocated or returned in the nlc. 625 */ 626 int 627 nreadsymlink(struct nlookupdata *nd, struct namecache *ncp, 628 struct nlcomponent *nlc) 629 { 630 struct vnode *vp; 631 struct iovec aiov; 632 struct uio auio; 633 int linklen; 634 int error; 635 char *cp; 636 637 nlc->nlc_nameptr = NULL; 638 nlc->nlc_namelen = 0; 639 if (ncp->nc_vp == NULL) 640 return(ENOENT); 641 if ((error = cache_vget(ncp, nd->nl_cred, LK_SHARED, &vp)) != 0) 642 return(error); 643 cp = zalloc(namei_zone); 644 aiov.iov_base = cp; 645 aiov.iov_len = MAXPATHLEN; 646 auio.uio_iov = &aiov; 647 auio.uio_iovcnt = 1; 648 auio.uio_offset = 0; 649 auio.uio_rw = UIO_READ; 650 auio.uio_segflg = UIO_SYSSPACE; 651 auio.uio_td = nd->nl_td; 652 auio.uio_resid = MAXPATHLEN - 1; 653 error = VOP_READLINK(vp, &auio, nd->nl_cred); 654 if (error) 655 goto fail; 656 linklen = MAXPATHLEN - 1 - auio.uio_resid; 657 if (varsym_enable) { 658 linklen = varsymreplace(cp, linklen, MAXPATHLEN - 1); 659 if (linklen < 0) { 660 error = ENAMETOOLONG; 661 goto fail; 662 } 663 } 664 cp[linklen] = 0; 665 nlc->nlc_nameptr = cp; 666 nlc->nlc_namelen = linklen; 667 vput(vp); 668 return(0); 669 fail: 670 zfree(namei_zone, cp); 671 vput(vp); 672 return(error); 673 } 674 675 /* 676 * Check access [XXX cache vattr!] [XXX quota] 677 * 678 * Generally check the V* access bits from sys/vnode.h. All specified bits 679 * must pass for this function to return 0. 680 * 681 * If VCREATE is specified and the target ncp represents a non-existant 682 * file or dir, or if VDELETE is specified and the target exists, the parent 683 * directory is checked for VWRITE. If VEXCL is specified and the target 684 * ncp represents a positive hit, an error is returned. 685 * 686 * If VCREATE is not specified and the target does not exist (negative hit), 687 * ENOENT is returned. Note that nlookup() does not (and should not) return 688 * ENOENT for non-existant leafs. 689 * 690 * The passed ncp may or may not be locked. The caller should use a 691 * locked ncp on leaf lookups, especially for VCREATE, VDELETE, and VEXCL 692 * checks. 693 */ 694 int 695 naccess(struct namecache *ncp, int vmode, struct ucred *cred) 696 { 697 struct namecache *par; 698 struct vnode *vp; 699 struct vattr va; 700 int error; 701 702 if (ncp->nc_flag & NCF_UNRESOLVED) { 703 cache_lock(ncp); 704 cache_resolve(ncp, cred); 705 cache_unlock(ncp); 706 } 707 error = ncp->nc_error; 708 if (vmode & (VDELETE|VCREATE|VEXCL)) { 709 if (((vmode & VCREATE) && ncp->nc_vp == NULL) || 710 ((vmode & VDELETE) && ncp->nc_vp != NULL) 711 ) { 712 if ((par = ncp->nc_parent) == NULL) { 713 if (error != EAGAIN) 714 error = EINVAL; 715 } else { 716 cache_hold(par); 717 error = naccess(par, VWRITE, cred); 718 cache_drop(par); 719 } 720 } 721 if ((vmode & VEXCL) && ncp->nc_vp != NULL) 722 error = EEXIST; 723 } 724 if (error == 0) { 725 error = cache_vget(ncp, cred, LK_SHARED, &vp); 726 if (error == ENOENT) { 727 if (vmode & VCREATE) 728 error = 0; 729 } else if (error == 0) { 730 /* XXX cache the va in the namecache or in the vnode */ 731 if ((error = VOP_GETATTR(vp, &va, curthread)) == 0) { 732 if ((vmode & VWRITE) && vp->v_mount) { 733 if (vp->v_mount->mnt_flag & MNT_RDONLY) 734 error = EROFS; 735 } 736 } 737 vput(vp); 738 if (error == 0) 739 error = naccess_va(&va, vmode, cred); 740 } 741 } 742 return(error); 743 } 744 745 /* 746 * Check the requested access against the given vattr using cred. 747 */ 748 int 749 naccess_va(struct vattr *va, int vmode, struct ucred *cred) 750 { 751 int i; 752 753 /* 754 * Test the immutable bit for files, directories, and softlinks. 755 */ 756 if (vmode & (VWRITE|VDELETE)) { 757 if (va->va_type == VDIR || va->va_type == VLNK || va->va_type == VREG) { 758 if (va->va_flags & IMMUTABLE) 759 return (EPERM); 760 } 761 } 762 763 /* 764 * root gets universal access 765 */ 766 if (cred->cr_uid == 0) 767 return(0); 768 769 /* 770 * Check owner perms, group perms, and world perms 771 */ 772 vmode &= S_IRWXU; 773 if (cred->cr_uid == va->va_uid) { 774 if ((vmode & va->va_mode) != vmode) 775 return(EACCES); 776 return(0); 777 } 778 779 vmode >>= 3; 780 for (i = 0; i < cred->cr_ngroups; ++i) { 781 if (va->va_gid == cred->cr_groups[i]) { 782 if ((vmode & va->va_mode) != vmode) 783 return(EACCES); 784 return(0); 785 } 786 } 787 788 vmode >>= 3; 789 if ((vmode & va->va_mode) != vmode) 790 return(EACCES); 791 return(0); 792 } 793 794