Remove now-unused loop_cnt variable.
[dragonfly.git] / sys / kern / vfs_nlookup.c
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