VFS messaging/interfacing work stage 6/99. Populate and maintain the
[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.1 2004/09/28 00:25:29 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 <vm/vm_zone.h>
67
68 #ifdef KTRACE
69 #include <sys/ktrace.h>
70 #endif
71
72 /*
73  * Initialize a nlookup() structure, early error return for copyin faults
74  * or a degenerate empty string (which is not allowed).
75  */
76 int
77 nlookup_init(struct nlookupdata *nd, const char *path, enum uio_seg seg, 
78                 int flags)
79 {
80     size_t pathlen;
81     struct proc *p;
82     thread_t td;
83     int error;
84
85     td = curthread;
86     p = td->td_proc;
87
88     /*
89      * note: the pathlen set by copy*str() includes the terminating \0.
90      */
91     bzero(nd, sizeof(struct nlookupdata));
92     nd->nl_path = zalloc(namei_zone);
93     nd->nl_flags |= NLC_HASBUF;
94     if (seg == UIO_SYSSPACE) 
95         error = copystr(path, nd->nl_path, MAXPATHLEN, &pathlen);
96     else
97         error = copyinstr(path, nd->nl_path, MAXPATHLEN, &pathlen);
98
99     /*
100      * Don't allow empty pathnames.
101      * POSIX.1 requirement: "" is not a vaild file name.
102      */
103     if (error == 0 && pathlen <= 1)
104         error = ENOENT;
105
106     if (error == 0) {
107         if (p->p_fd) {
108             nd->nl_ncp = cache_hold(p->p_fd->fd_ncdir);
109             nd->nl_rootncp = cache_hold(p->p_fd->fd_nrdir);
110             if (p->p_fd->fd_njdir)
111                 nd->nl_jailncp = cache_hold(p->p_fd->fd_njdir);
112         } else {
113             nd->nl_ncp = cache_vptoncp(rootvnode);
114             nd->nl_rootncp = cache_hold(nd->nl_ncp);
115             nd->nl_jailncp = cache_hold(nd->nl_ncp);
116         }
117         nd->nl_td = td;
118         nd->nl_cred = crhold(p->p_ucred);
119         nd->nl_flags |= flags;
120     } else {
121         nlookup_done(nd);
122     }
123     return(error);
124 }
125
126 /*
127  * Cleanup a nlookupdata structure after we are through with it.
128  */
129 void
130 nlookup_done(struct nlookupdata *nd)
131 {
132     if (nd->nl_ncp) {
133         cache_drop(nd->nl_ncp);
134         nd->nl_ncp = NULL;
135     }
136     if (nd->nl_rootncp) {
137         cache_drop(nd->nl_rootncp);
138         nd->nl_rootncp = NULL;
139     }
140     if (nd->nl_jailncp) {
141         cache_drop(nd->nl_jailncp);
142         nd->nl_jailncp = NULL;
143     }
144     if ((nd->nl_flags & NLC_HASBUF) && nd->nl_path) {
145         zfree(namei_zone, nd->nl_path);
146         nd->nl_path = NULL;
147     }
148     if (nd->nl_cred) {
149         crfree(nd->nl_cred);
150         nd->nl_cred = NULL;
151     }
152 }
153
154 /*
155  * Simple all-in-one nlookup
156  */
157 struct namecache *
158 nlookup_simple(const char *str, enum uio_seg seg, int niflags, int *error)
159 {
160     struct nlookupdata nd;
161     struct namecache *ncp;
162
163     *error = nlookup_init(&nd, str, seg, niflags);
164     if (*error == 0) {
165             *error = nlookup(&nd);
166             ncp = nd.nl_ncp;    /* keep hold ref from structure */
167             nd.nl_ncp = NULL;   /* and NULL out */
168             nlookup_done(&nd);
169     } else {
170             ncp = NULL;
171     }
172     return(ncp);
173 }
174
175 /*
176  * Do a generic nlookup.  Note that the passed nd is not nlookup_done()'d
177  * on return, even if an error occurs.  If no error occurs the returned
178  * nl_ncp is always referenced and locked, otherwise it may or may not be.
179  */
180 int
181 nlookup(struct nlookupdata *nd)
182 {
183     struct nlcomponent nlc;
184     struct namecache *ncp;
185     char *ptr;
186     int error;
187     int len;
188
189 #ifdef KTRACE
190     if (KTRPOINT(nd->nl_td, KTR_NAMEI))
191         ktrnamei(nd->nl_td->td_proc->p_tracep, nd->nl_path);
192 #endif
193     bzero(&nlc, sizeof(nlc));
194
195     /*
196      * Setup for the loop.  The current working namecache element must
197      * be in an unlocked state.  This typically the case on entry except
198      * when stringing nlookup()'s along in a chain, since nlookup(0 always
199      * returns nl_ncp in a locked state.
200      */
201     nd->nl_loopcnt = 0;
202     if (nd->nl_flags & NLC_NCPISLOCKED) {
203         nd->nl_flags &= ~NLC_NCPISLOCKED;
204         cache_unlock(nd->nl_ncp);
205     }
206     ptr = nd->nl_path;
207
208     /*
209      * Loop on the path compoenents
210      */
211     for (;;) {
212         /*
213          * Check if the root directory should replace the current
214          * directory.  This is done at the start of a translation
215          * or after a symbolic link has been found.  In other cases
216          * ptr will never be pointing at a '/'.
217          */
218         if (*ptr == '/') {
219             do {
220                 ++ptr;
221             } while (*ptr == '/');
222             cache_drop(nd->nl_ncp);
223             nd->nl_ncp = cache_hold(nd->nl_rootncp);
224             continue;
225         }
226
227         /*
228          * Extract the path component
229          */
230         nlc.nlc_nameptr = ptr;
231         while (*ptr && *ptr != '/')
232             ++ptr;
233         nlc.nlc_namelen = ptr - nlc.nlc_nameptr;
234
235         /*
236          * Resolve the namespace.  The ncp returned by cache_nlookup()
237          * is referenced and locked.
238          */
239         ncp = cache_nlookup(nd->nl_ncp, &nlc);
240         if (ncp->nc_flag & NCF_UNRESOLVED) {
241             error = cache_resolve(ncp);
242         } else {
243             error = ncp->nc_error;
244         }
245         if (error) {
246             cache_put(ncp);
247             break;
248         }
249
250         /*
251          * If the element is a symlink and it is either not the last
252          * element or it is the last element and we are allowed to
253          * follow symlinks, resolve the symlink.
254          */
255         if ((ncp->nc_flag & NCF_ISSYMLINK) &&
256             (*ptr || (nd->nl_flags & NLC_FOLLOW))
257         ) {
258             if (nd->nl_loopcnt++ >= MAXSYMLINKS) {
259                     error = ELOOP;
260                     cache_put(ncp);
261                     break;
262             }
263             error = nreadsymlink(nd, ncp, &nlc);
264             if (error) {
265                 cache_put(ncp);
266                 break;
267             }
268
269             /*
270              * Concatenate trailing path elements onto the returned symlink.
271              * Note that if the path component (ptr) is not exhausted, it
272              * will being with a '/', so we do not have to add another one.
273              *
274              * The symlink may not be empty.
275              */
276             len = strlen(ptr);
277             if (nlc.nlc_namelen == 0 || nlc.nlc_namelen + len >= MAXPATHLEN) {
278                 error = nlc.nlc_namelen ? ENAMETOOLONG : ENOENT;
279                 zfree(namei_zone, nlc.nlc_nameptr);
280                 cache_put(ncp);
281                 break;
282             }
283             bcopy(ptr, nlc.nlc_nameptr + nlc.nlc_namelen, len + 1);
284             if (nd->nl_flags & NLC_HASBUF)
285                 zfree(namei_zone, nd->nl_path);
286             nd->nl_path = nlc.nlc_nameptr;
287             nd->nl_flags |= NLC_HASBUF;
288             ptr = nd->nl_path;
289
290             /*
291              * Go back up to the top to resolve any initial '/'s in the
292              * symlink.
293              */
294             continue;
295         }
296
297         /*
298          * Skip any slashes to get to the next element.  If there 
299          * are any slashes at all the current element must be a
300          * directory.  If it isn't we break without incrementing
301          * ptr and fall through to the failure case below.
302          */
303         while (*ptr == '/') {
304             if ((ncp->nc_flag & NCF_ISDIR) == 0)
305                 break;
306             ++ptr;
307         }
308
309         /*
310          * Continuation case: additional elements and the current
311          * element is a directory.
312          */
313         if (*ptr && (ncp->nc_flag & NCF_ISDIR)) {
314             cache_drop(nd->nl_ncp);
315             cache_unlock(ncp);
316             nd->nl_ncp = ncp;
317             continue;
318         }
319
320         /*
321          * Failure case: additional elements and the current element
322          * is not a directory
323          */
324         if (*ptr) {
325             cache_put(ncp);
326             error = ENOTDIR;
327             break;
328         }
329
330         /*
331          * XXX vnode canvmio (test in mmap(), read(), and write())
332          */
333
334         /*
335          * Termination: no more elements.
336          */
337         cache_drop(nd->nl_ncp);
338         nd->nl_ncp = ncp;
339         nd->nl_flags |= NLC_NCPISLOCKED;
340         error = 0;
341         break;
342     }
343     return(error);
344 }
345
346 /*
347  * Read the contents of a symlink, allocate a path buffer out of the
348  * namei_zone and initialize the supplied nlcomponent with the result.
349  *
350  * If an error occurs no buffer will be allocated or returned in the nlc.
351  */
352 int
353 nreadsymlink(struct nlookupdata *nd, struct namecache *ncp, 
354                 struct nlcomponent *nlc)
355 {
356     struct iovec aiov;
357     struct uio auio;
358     int linklen;
359     int error;
360     char *cp;
361
362     nlc->nlc_nameptr = NULL;
363     nlc->nlc_namelen = 0;
364     if (ncp->nc_vp == NULL)
365         return(ENOENT);
366     if ((error = vget(ncp->nc_vp, NULL, LK_SHARED, nd->nl_td)) != 0)
367         return(error);
368     cp = zalloc(namei_zone);
369     aiov.iov_base = cp;
370     aiov.iov_len = MAXPATHLEN;
371     auio.uio_iov = &aiov;
372     auio.uio_iovcnt = 1;
373     auio.uio_offset = 0;
374     auio.uio_rw = UIO_READ;
375     auio.uio_segflg = UIO_SYSSPACE;
376     auio.uio_td = nd->nl_td;
377     auio.uio_resid = MAXPATHLEN - 1;
378     error = VOP_READLINK(ncp->nc_vp, &auio, nd->nl_cred);
379     if (error)
380         goto fail;
381     linklen = MAXPATHLEN - auio.uio_resid;
382     if (varsym_enable) {
383         linklen = varsymreplace(cp, linklen, MAXPATHLEN - 1);
384         if (linklen < 0) {
385             error = ENAMETOOLONG;
386             goto fail;
387         }
388     }
389     cp[linklen] = 0;
390     nlc->nlc_nameptr = cp;
391     nlc->nlc_namelen = linklen;
392     vput(ncp->nc_vp);
393     return(0);
394 fail:
395     zfree(namei_zone, cp);
396     vput(ncp->nc_vp);
397     return(error);
398 }
399