Initial import, 0.1 + pk 0.2.4-B1
[unix-history] / sys / kern / vfs_lookup.c
CommitLineData
15637ed4
RG
1/*
2 * Copyright (c) 1982, 1986, 1989 Regents of the University of California.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by the University of
16 * California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 *
33 * @(#)vfs_lookup.c 7.32 (Berkeley) 5/21/91
34 *
35 * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE
36 * -------------------- ----- ----------------------
37 * CURRENT PATCH LEVEL: 1 00006
38 * -------------------- ----- ----------------------
39 *
40 * 17 Aug 92 Christoph Robitschko Fixed parent of chroot panic
41 */
42
43#include "param.h"
44#include "syslimits.h"
45#include "time.h"
46#include "namei.h"
47#include "vnode.h"
48#include "mount.h"
49#include "errno.h"
50#include "malloc.h"
51#include "filedesc.h"
52#include "proc.h"
53
54#ifdef KTRACE
55#include "ktrace.h"
56#endif
57
58/*
59 * Convert a pathname into a pointer to a locked inode.
60 *
61 * The FOLLOW flag is set when symbolic links are to be followed
62 * when they occur at the end of the name translation process.
63 * Symbolic links are always followed for all other pathname
64 * components other than the last.
65 *
66 * The segflg defines whether the name is to be copied from user
67 * space or kernel space.
68 *
69 * Overall outline of namei:
70 *
71 * copy in name
72 * get starting directory
73 * while (!done && !error) {
74 * call lookup to search path.
75 * if symbolic link, massage name in buffer and continue
76 * }
77 */
78namei(ndp, p)
79 register struct nameidata *ndp;
80 struct proc *p;
81{
82 register struct filedesc *fdp; /* pointer to file descriptor state */
83 register char *cp; /* pointer into pathname argument */
84 register struct vnode *dp; /* the directory we are searching */
85 struct iovec aiov; /* uio for reading symbolic links */
86 struct uio auio;
87 int error, linklen;
88
89 ndp->ni_cred = p->p_ucred;
90 fdp = p->p_fd;
91
92 /*
93 * Get a buffer for the name to be translated, and copy the
94 * name into the buffer.
95 */
96 if ((ndp->ni_nameiop & HASBUF) == 0)
97 MALLOC(ndp->ni_pnbuf, caddr_t, MAXPATHLEN, M_NAMEI, M_WAITOK);
98 if (ndp->ni_segflg == UIO_SYSSPACE)
99 error = copystr(ndp->ni_dirp, ndp->ni_pnbuf,
100 MAXPATHLEN, &ndp->ni_pathlen);
101 else
102 error = copyinstr(ndp->ni_dirp, ndp->ni_pnbuf,
103 MAXPATHLEN, &ndp->ni_pathlen);
104 if (error) {
105 free(ndp->ni_pnbuf, M_NAMEI);
106 ndp->ni_vp = NULL;
107 return (error);
108 }
109 ndp->ni_loopcnt = 0;
110#ifdef KTRACE
111 if (KTRPOINT(p, KTR_NAMEI))
112 ktrnamei(p->p_tracep, ndp->ni_pnbuf);
113#endif
114
115 /*
116 * Get starting point for the translation.
117 */
118 if ((ndp->ni_rootdir = fdp->fd_rdir) == NULL)
119 ndp->ni_rootdir = rootdir;
120 dp = fdp->fd_cdir;
121 VREF(dp);
122 for (;;) {
123 /*
124 * Check if root directory should replace current directory.
125 * Done at start of translation and after symbolic link.
126 */
127 ndp->ni_ptr = ndp->ni_pnbuf;
128 if (*ndp->ni_ptr == '/') {
129 vrele(dp);
130 while (*ndp->ni_ptr == '/') {
131 ndp->ni_ptr++;
132 ndp->ni_pathlen--;
133 }
134 dp = ndp->ni_rootdir;
135 VREF(dp);
136 }
137 ndp->ni_startdir = dp;
138 if (error = lookup(ndp, p)) {
139 FREE(ndp->ni_pnbuf, M_NAMEI);
140 return (error);
141 }
142 /*
143 * Check for symbolic link
144 */
145 if (ndp->ni_more == 0) {
146 if ((ndp->ni_nameiop & (SAVENAME | SAVESTART)) == 0)
147 FREE(ndp->ni_pnbuf, M_NAMEI);
148 else
149 ndp->ni_nameiop |= HASBUF;
150 return (0);
151 }
152 if ((ndp->ni_nameiop & LOCKPARENT) && ndp->ni_pathlen == 1)
153 VOP_UNLOCK(ndp->ni_dvp);
154 if (ndp->ni_loopcnt++ >= MAXSYMLINKS) {
155 error = ELOOP;
156 break;
157 }
158 if (ndp->ni_pathlen > 1)
159 MALLOC(cp, char *, MAXPATHLEN, M_NAMEI, M_WAITOK);
160 else
161 cp = ndp->ni_pnbuf;
162 aiov.iov_base = cp;
163 aiov.iov_len = MAXPATHLEN;
164 auio.uio_iov = &aiov;
165 auio.uio_iovcnt = 1;
166 auio.uio_offset = 0;
167 auio.uio_rw = UIO_READ;
168 auio.uio_segflg = UIO_SYSSPACE;
169 auio.uio_procp = (struct proc *)0;
170 auio.uio_resid = MAXPATHLEN;
171 if (error = VOP_READLINK(ndp->ni_vp, &auio, p->p_ucred)) {
172 if (ndp->ni_pathlen > 1)
173 free(cp, M_NAMEI);
174 break;
175 }
176 linklen = MAXPATHLEN - auio.uio_resid;
177 if (linklen + ndp->ni_pathlen >= MAXPATHLEN) {
178 if (ndp->ni_pathlen > 1)
179 free(cp, M_NAMEI);
180 error = ENAMETOOLONG;
181 break;
182 }
183 if (ndp->ni_pathlen > 1) {
184 bcopy(ndp->ni_next, cp + linklen, ndp->ni_pathlen);
185 FREE(ndp->ni_pnbuf, M_NAMEI);
186 ndp->ni_pnbuf = cp;
187 } else
188 ndp->ni_pnbuf[linklen] = '\0';
189 ndp->ni_pathlen += linklen;
190 vput(ndp->ni_vp);
191 dp = ndp->ni_dvp;
192 }
193 FREE(ndp->ni_pnbuf, M_NAMEI);
194 vrele(ndp->ni_dvp);
195 vput(ndp->ni_vp);
196 ndp->ni_vp = NULL;
197 return (error);
198}
199
200/*
201 * Search a pathname.
202 * This is a very central and rather complicated routine.
203 *
204 * The pathname is pointed to by ni_ptr and is of length ni_pathlen.
205 * The starting directory is taken from ni_startdir. The pathname is
206 * descended until done, or a symbolic link is encountered. The variable
207 * ni_more is clear if the path is completed; it is set to one if a
208 * symbolic link needing interpretation is encountered.
209 *
210 * The flag argument is LOOKUP, CREATE, RENAME, or DELETE depending on
211 * whether the name is to be looked up, created, renamed, or deleted.
212 * When CREATE, RENAME, or DELETE is specified, information usable in
213 * creating, renaming, or deleting a directory entry may be calculated.
214 * If flag has LOCKPARENT or'ed into it, the parent directory is returned
215 * locked. If flag has WANTPARENT or'ed into it, the parent directory is
216 * returned unlocked. Otherwise the parent directory is not returned. If
217 * the target of the pathname exists and LOCKLEAF is or'ed into the flag
218 * the target is returned locked, otherwise it is returned unlocked.
219 * When creating or renaming and LOCKPARENT is specified, the target may not
220 * be ".". When deleting and LOCKPARENT is specified, the target may be ".".
221 * NOTE: (LOOKUP | LOCKPARENT) currently returns the parent vnode unlocked.
222 *
223 * Overall outline of lookup:
224 *
225 * dirloop:
226 * identify next component of name at ndp->ni_ptr
227 * handle degenerate case where name is null string
228 * if .. and crossing mount points and on mounted filesys, find parent
229 * call VOP_LOOKUP routine for next component name
230 * directory vnode returned in ni_dvp, unlocked unless LOCKPARENT set
231 * component vnode returned in ni_vp (if it exists), locked.
232 * if result vnode is mounted on and crossing mount points,
233 * find mounted on vnode
234 * if more components of name, do next level at dirloop
235 * return the answer in ni_vp, locked if LOCKLEAF set
236 * if LOCKPARENT set, return locked parent in ni_dvp
237 * if WANTPARENT set, return unlocked parent in ni_dvp
238 */
239lookup(ndp, p)
240 register struct nameidata *ndp;
241 struct proc *p;
242{
243 register char *cp; /* pointer into pathname argument */
244 register struct vnode *dp = 0; /* the directory we are searching */
245 struct vnode *tdp; /* saved dp */
246 struct mount *mp; /* mount table entry */
247 int docache; /* == 0 do not cache last component */
248 int flag; /* LOOKUP, CREATE, RENAME or DELETE */
249 int wantparent; /* 1 => wantparent or lockparent flag */
250 int rdonly; /* mounted read-only flag bit(s) */
251 int error = 0;
252
253 /*
254 * Setup: break out flag bits into variables.
255 */
256 flag = ndp->ni_nameiop & OPMASK;
257 wantparent = ndp->ni_nameiop & (LOCKPARENT|WANTPARENT);
258 docache = (ndp->ni_nameiop & NOCACHE) ^ NOCACHE;
259 if (flag == DELETE || (wantparent && flag != CREATE))
260 docache = 0;
261 rdonly = MNT_RDONLY;
262 if (ndp->ni_nameiop & REMOTE)
263 rdonly |= MNT_EXRDONLY;
264 ndp->ni_dvp = NULL;
265 ndp->ni_more = 0;
266 dp = ndp->ni_startdir;
267 ndp->ni_startdir = NULLVP;
268 VOP_LOCK(dp);
269
270dirloop:
271 /*
272 * Search a new directory.
273 *
274 * The ni_hash value is for use by vfs_cache.
275 * The last component of the filename is left accessible via
276 * ndp->ptr for callers that need the name. Callers needing
277 * the name set the SAVENAME flag. When done, they assume
278 * responsibility for freeing the pathname buffer.
279 */
280 ndp->ni_hash = 0;
281 for (cp = ndp->ni_ptr; *cp != 0 && *cp != '/'; cp++)
282 ndp->ni_hash += (unsigned char)*cp;
283 ndp->ni_namelen = cp - ndp->ni_ptr;
284 if (ndp->ni_namelen >= NAME_MAX) {
285 error = ENAMETOOLONG;
286 goto bad;
287 }
288#ifdef NAMEI_DIAGNOSTIC
289 { char c = *cp;
290 *cp = '\0';
291 printf("{%s}: ", ndp->ni_ptr);
292 *cp = c; }
293#endif
294 ndp->ni_pathlen -= ndp->ni_namelen;
295 ndp->ni_next = cp;
296 ndp->ni_makeentry = 1;
297 if (*cp == '\0' && docache == 0)
298 ndp->ni_makeentry = 0;
299 ndp->ni_isdotdot = (ndp->ni_namelen == 2 &&
300 ndp->ni_ptr[1] == '.' && ndp->ni_ptr[0] == '.');
301
302 /*
303 * Check for degenerate name (e.g. / or "")
304 * which is a way of talking about a directory,
305 * e.g. like "/." or ".".
306 */
307 if (ndp->ni_ptr[0] == '\0') {
308 if (flag != LOOKUP || wantparent) {
309 error = EISDIR;
310 goto bad;
311 }
312 if (dp->v_type != VDIR) {
313 error = ENOTDIR;
314 goto bad;
315 }
316 if (!(ndp->ni_nameiop & LOCKLEAF))
317 VOP_UNLOCK(dp);
318 ndp->ni_vp = dp;
319 if (ndp->ni_nameiop & SAVESTART)
320 panic("lookup: SAVESTART");
321 return (0);
322 }
323
324 /*
325 * Handle "..": two special cases.
326 * 1. If at root directory (e.g. after chroot)
327 * then ignore it so can't get out.
328 * 2. If this vnode is the root of a mounted
329 * filesystem, then replace it with the
330 * vnode which was mounted on so we take the
331 * .. in the other file system.
332 */
333 if (ndp->ni_isdotdot) {
334 for (;;) {
335/* 17 Aug 92*/ if ((dp == ndp->ni_rootdir) || (dp == rootdir)) {
336 ndp->ni_dvp = dp;
337 ndp->ni_vp = dp;
338 VREF(dp);
339 goto nextname;
340 }
341 if ((dp->v_flag & VROOT) == 0 ||
342 (ndp->ni_nameiop & NOCROSSMOUNT))
343 break;
344 tdp = dp;
345 dp = dp->v_mount->mnt_vnodecovered;
346 vput(tdp);
347 VREF(dp);
348 VOP_LOCK(dp);
349 }
350 }
351
352 /*
353 * We now have a segment name to search for, and a directory to search.
354 */
355 if (error = VOP_LOOKUP(dp, ndp, p)) {
356#ifdef DIAGNOSTIC
357 if (ndp->ni_vp != NULL)
358 panic("leaf should be empty");
359#endif
360#ifdef NAMEI_DIAGNOSTIC
361 printf("not found\n");
362#endif
363 if (flag == LOOKUP || flag == DELETE ||
364 error != ENOENT || *cp != 0)
365 goto bad;
366 /*
367 * If creating and at end of pathname, then can consider
368 * allowing file to be created.
369 */
370 if (ndp->ni_dvp->v_mount->mnt_flag & rdonly) {
371 error = EROFS;
372 goto bad;
373 }
374 /*
375 * We return with ni_vp NULL to indicate that the entry
376 * doesn't currently exist, leaving a pointer to the
377 * (possibly locked) directory inode in ndp->ni_dvp.
378 */
379 if (ndp->ni_nameiop & SAVESTART) {
380 ndp->ni_startdir = ndp->ni_dvp;
381 VREF(ndp->ni_startdir);
382 }
383 return (0);
384 }
385#ifdef NAMEI_DIAGNOSTIC
386 printf("found\n");
387#endif
388
389 dp = ndp->ni_vp;
390 /*
391 * Check for symbolic link
392 */
393 if ((dp->v_type == VLNK) &&
394 ((ndp->ni_nameiop & FOLLOW) || *ndp->ni_next == '/')) {
395 ndp->ni_more = 1;
396 return (0);
397 }
398
399 /*
400 * Check to see if the vnode has been mounted on;
401 * if so find the root of the mounted file system.
402 */
403mntloop:
404 while (dp->v_type == VDIR && (mp = dp->v_mountedhere) &&
405 (ndp->ni_nameiop & NOCROSSMOUNT) == 0) {
406 while(mp->mnt_flag & MNT_MLOCK) {
407 mp->mnt_flag |= MNT_MWAIT;
408 sleep((caddr_t)mp, PVFS);
409 goto mntloop;
410 }
411 if (error = VFS_ROOT(dp->v_mountedhere, &tdp))
412 goto bad2;
413 vput(dp);
414 ndp->ni_vp = dp = tdp;
415 }
416
417nextname:
418 /*
419 * Not a symbolic link. If more pathname,
420 * continue at next component, else return.
421 */
422 if (*ndp->ni_next == '/') {
423 ndp->ni_ptr = ndp->ni_next;
424 while (*ndp->ni_ptr == '/') {
425 ndp->ni_ptr++;
426 ndp->ni_pathlen--;
427 }
428 vrele(ndp->ni_dvp);
429 goto dirloop;
430 }
431 /*
432 * Check for read-only file systems.
433 */
434 if (flag == DELETE || flag == RENAME) {
435 /*
436 * Disallow directory write attempts on read-only
437 * file systems.
438 */
439 if ((dp->v_mount->mnt_flag & rdonly) ||
440 (wantparent && (ndp->ni_dvp->v_mount->mnt_flag & rdonly))) {
441 error = EROFS;
442 goto bad2;
443 }
444 }
445 if (ndp->ni_nameiop & SAVESTART) {
446 ndp->ni_startdir = ndp->ni_dvp;
447 VREF(ndp->ni_startdir);
448 }
449 if (!wantparent)
450 vrele(ndp->ni_dvp);
451 if ((ndp->ni_nameiop & LOCKLEAF) == 0)
452 VOP_UNLOCK(dp);
453 return (0);
454
455bad2:
456 if ((ndp->ni_nameiop & LOCKPARENT) && *ndp->ni_next == '\0')
457 VOP_UNLOCK(ndp->ni_dvp);
458 vrele(ndp->ni_dvp);
459bad:
460 vput(dp);
461 ndp->ni_vp = NULL;
462 return (error);
463}