lookup converted to new form
[unix-history] / usr / src / sys / kern / vfs_lookup.c
CommitLineData
da7c5cc6 1/*
6d0f0ece
KM
2 * Copyright (c) 1982, 1986, 1989 Regents of the University of California.
3 * All rights reserved.
da7c5cc6 4 *
dbf0c423 5 * %sccs.include.redist.c%
6d0f0ece 6 *
743467cb 7 * @(#)vfs_lookup.c 7.36 (Berkeley) %G%
da7c5cc6 8 */
10873320 9
94368568 10#include "param.h"
955e7a55 11#include "syslimits.h"
6d0f0ece
KM
12#include "time.h"
13#include "namei.h"
14#include "vnode.h"
94368568 15#include "mount.h"
6d0f0ece 16#include "errno.h"
c3a74062 17#include "malloc.h"
5e00df3b 18#include "filedesc.h"
658f5fdc 19#include "proc.h"
0caff0ad
KM
20
21#ifdef KTRACE
658f5fdc
MT
22#include "ktrace.h"
23#endif
10873320
BJ
24
25/*
7f69b0e6 26 * Convert a pathname into a pointer to a locked inode.
4f083fd7 27 *
d870be74 28 * The FOLLOW flag is set when symbolic links are to be followed
4f083fd7 29 * when they occur at the end of the name translation process.
7f69b0e6
KM
30 * Symbolic links are always followed for all other pathname
31 * components other than the last.
32 *
33 * The segflg defines whether the name is to be copied from user
34 * space or kernel space.
10873320 35 *
f93197fc 36 * Overall outline of namei:
6bd0bb92
BJ
37 *
38 * copy in name
39 * get starting directory
955e7a55
KM
40 * while (!done && !error) {
41 * call lookup to search path.
42 * if symbolic link, massage name in buffer and continue
43 * }
10873320 44 */
743467cb
JH
45int
46namei(ndp)
d870be74 47 register struct nameidata *ndp;
10873320 48{
5e00df3b 49 register struct filedesc *fdp; /* pointer to file descriptor state */
6bd0bb92 50 register char *cp; /* pointer into pathname argument */
955e7a55
KM
51 register struct vnode *dp; /* the directory we are searching */
52 struct iovec aiov; /* uio for reading symbolic links */
53 struct uio auio;
54 int error, linklen;
743467cb 55 struct componentname *cnp = &ndp->ni_cnd;
10873320 56
743467cb
JH
57 ndp->ni_cnd.cn_cred = ndp->ni_cnd.cn_proc->p_ucred;
58#ifdef DIAGNOSTIC
59 if (!cnp->cn_cred || !cnp->cn_proc)
60 panic ("namei: bad cred/proc");
61 if (cnp->cn_nameiop & (~OPMASK))
62 panic ("namei: nameiop contaminated with flags");
63 if (cnp->cn_flags & OPMASK)
64 panic ("namei: flags contaminated with nameiops");
65#endif
66 fdp = cnp->cn_proc->p_fd;
955e7a55 67
f5039631 68 /*
6bd0bb92
BJ
69 * Get a buffer for the name to be translated, and copy the
70 * name into the buffer.
f5039631 71 */
743467cb
JH
72 if ((cnp->cn_flags & HASBUF) == 0)
73 MALLOC(cnp->cn_pnbuf, caddr_t, MAXPATHLEN, M_NAMEI, M_WAITOK);
955e7a55 74 if (ndp->ni_segflg == UIO_SYSSPACE)
743467cb 75 error = copystr(ndp->ni_dirp, cnp->cn_pnbuf,
955e7a55
KM
76 MAXPATHLEN, &ndp->ni_pathlen);
77 else
743467cb 78 error = copyinstr(ndp->ni_dirp, cnp->cn_pnbuf,
955e7a55
KM
79 MAXPATHLEN, &ndp->ni_pathlen);
80 if (error) {
743467cb 81 free(cnp->cn_pnbuf, M_NAMEI);
955e7a55
KM
82 ndp->ni_vp = NULL;
83 return (error);
d870be74 84 }
6d0f0ece 85 ndp->ni_loopcnt = 0;
658f5fdc 86#ifdef KTRACE
743467cb
JH
87 if (KTRPOINT(cnp->cn_proc, KTR_NAMEI))
88 ktrnamei(cnp->cn_proc->p_tracep, cnp->cn_pnbuf);
658f5fdc 89#endif
6bd0bb92 90
5f9e231a
KM
91 /*
92 * Get starting point for the translation.
93 */
955e7a55
KM
94 if ((ndp->ni_rootdir = fdp->fd_rdir) == NULL)
95 ndp->ni_rootdir = rootdir;
96 dp = fdp->fd_cdir;
5f9e231a 97 VREF(dp);
955e7a55
KM
98 for (;;) {
99 /*
100 * Check if root directory should replace current directory.
101 * Done at start of translation and after symbolic link.
102 */
743467cb
JH
103 cnp->cn_nameptr = cnp->cn_pnbuf;
104 if (*(cnp->cn_nameptr) == '/') {
955e7a55 105 vrele(dp);
743467cb
JH
106 while (*(cnp->cn_nameptr) == '/') {
107 cnp->cn_nameptr++;
955e7a55
KM
108 ndp->ni_pathlen--;
109 }
110 dp = ndp->ni_rootdir;
111 VREF(dp);
112 }
113 ndp->ni_startdir = dp;
743467cb
JH
114 if (error = lookup(ndp)) {
115 FREE(cnp->cn_pnbuf, M_NAMEI);
955e7a55
KM
116 return (error);
117 }
118 /*
119 * Check for symbolic link
120 */
743467cb
JH
121 if ((cnp->cn_flags & ISSYMLINK) == 0) {
122 if ((cnp->cn_flags & (SAVENAME | SAVESTART)) == 0)
123 FREE(cnp->cn_pnbuf, M_NAMEI);
955e7a55 124 else
743467cb 125 cnp->cn_flags |= HASBUF;
955e7a55
KM
126 return (0);
127 }
743467cb 128 if ((cnp->cn_flags & LOCKPARENT) && ndp->ni_pathlen == 1)
955e7a55
KM
129 VOP_UNLOCK(ndp->ni_dvp);
130 if (ndp->ni_loopcnt++ >= MAXSYMLINKS) {
131 error = ELOOP;
132 break;
133 }
134 if (ndp->ni_pathlen > 1)
135 MALLOC(cp, char *, MAXPATHLEN, M_NAMEI, M_WAITOK);
136 else
743467cb 137 cp = cnp->cn_pnbuf;
955e7a55
KM
138 aiov.iov_base = cp;
139 aiov.iov_len = MAXPATHLEN;
140 auio.uio_iov = &aiov;
141 auio.uio_iovcnt = 1;
142 auio.uio_offset = 0;
143 auio.uio_rw = UIO_READ;
144 auio.uio_segflg = UIO_SYSSPACE;
145 auio.uio_procp = (struct proc *)0;
146 auio.uio_resid = MAXPATHLEN;
743467cb 147 if (error = VOP_READLINK(ndp->ni_vp, &auio, cnp->cn_cred)) {
955e7a55
KM
148 if (ndp->ni_pathlen > 1)
149 free(cp, M_NAMEI);
150 break;
151 }
152 linklen = MAXPATHLEN - auio.uio_resid;
153 if (linklen + ndp->ni_pathlen >= MAXPATHLEN) {
154 if (ndp->ni_pathlen > 1)
155 free(cp, M_NAMEI);
156 error = ENAMETOOLONG;
157 break;
6d0f0ece 158 }
955e7a55
KM
159 if (ndp->ni_pathlen > 1) {
160 bcopy(ndp->ni_next, cp + linklen, ndp->ni_pathlen);
743467cb
JH
161 FREE(cnp->cn_pnbuf, M_NAMEI);
162 cnp->cn_pnbuf = cp;
955e7a55 163 } else
743467cb 164 cnp->cn_pnbuf[linklen] = '\0';
955e7a55
KM
165 ndp->ni_pathlen += linklen;
166 vput(ndp->ni_vp);
167 dp = ndp->ni_dvp;
6d0f0ece 168 }
743467cb 169 FREE(cnp->cn_pnbuf, M_NAMEI);
955e7a55
KM
170 vrele(ndp->ni_dvp);
171 vput(ndp->ni_vp);
172 ndp->ni_vp = NULL;
173 return (error);
174}
175
176/*
177 * Search a pathname.
178 * This is a very central and rather complicated routine.
179 *
180 * The pathname is pointed to by ni_ptr and is of length ni_pathlen.
181 * The starting directory is taken from ni_startdir. The pathname is
182 * descended until done, or a symbolic link is encountered. The variable
183 * ni_more is clear if the path is completed; it is set to one if a
184 * symbolic link needing interpretation is encountered.
185 *
186 * The flag argument is LOOKUP, CREATE, RENAME, or DELETE depending on
187 * whether the name is to be looked up, created, renamed, or deleted.
188 * When CREATE, RENAME, or DELETE is specified, information usable in
189 * creating, renaming, or deleting a directory entry may be calculated.
190 * If flag has LOCKPARENT or'ed into it, the parent directory is returned
191 * locked. If flag has WANTPARENT or'ed into it, the parent directory is
192 * returned unlocked. Otherwise the parent directory is not returned. If
193 * the target of the pathname exists and LOCKLEAF is or'ed into the flag
194 * the target is returned locked, otherwise it is returned unlocked.
195 * When creating or renaming and LOCKPARENT is specified, the target may not
196 * be ".". When deleting and LOCKPARENT is specified, the target may be ".".
197 * NOTE: (LOOKUP | LOCKPARENT) currently returns the parent vnode unlocked.
198 *
199 * Overall outline of lookup:
200 *
201 * dirloop:
202 * identify next component of name at ndp->ni_ptr
203 * handle degenerate case where name is null string
204 * if .. and crossing mount points and on mounted filesys, find parent
205 * call VOP_LOOKUP routine for next component name
206 * directory vnode returned in ni_dvp, unlocked unless LOCKPARENT set
207 * component vnode returned in ni_vp (if it exists), locked.
208 * if result vnode is mounted on and crossing mount points,
209 * find mounted on vnode
210 * if more components of name, do next level at dirloop
211 * return the answer in ni_vp, locked if LOCKLEAF set
212 * if LOCKPARENT set, return locked parent in ni_dvp
213 * if WANTPARENT set, return unlocked parent in ni_dvp
214 */
743467cb
JH
215int
216lookup(ndp)
955e7a55 217 register struct nameidata *ndp;
955e7a55
KM
218{
219 register char *cp; /* pointer into pathname argument */
220 register struct vnode *dp = 0; /* the directory we are searching */
221 struct vnode *tdp; /* saved dp */
222 struct mount *mp; /* mount table entry */
223 int docache; /* == 0 do not cache last component */
955e7a55 224 int wantparent; /* 1 => wantparent or lockparent flag */
6c235bc4 225 int rdonly; /* lookup read-only flag bit */
955e7a55 226 int error = 0;
cfef4373 227 struct componentname *cnp = &ndp->ni_cnd;
6bd0bb92 228
10873320 229 /*
955e7a55 230 * Setup: break out flag bits into variables.
10873320 231 */
743467cb 232 wantparent = cnp->cn_flags & (LOCKPARENT | WANTPARENT);
cfef4373 233 docache = (cnp->cn_flags & NOCACHE) ^ NOCACHE;
743467cb
JH
234 if (cnp->cn_nameiop == DELETE ||
235 (wantparent && cnp->cn_nameiop != CREATE))
955e7a55 236 docache = 0;
cfef4373 237 rdonly = cnp->cn_flags & RDONLY;
955e7a55 238 ndp->ni_dvp = NULL;
cfef4373 239 cnp->cn_flags &= ~ISSYMLINK;
955e7a55
KM
240 dp = ndp->ni_startdir;
241 ndp->ni_startdir = NULLVP;
242 VOP_LOCK(dp);
243
6bd0bb92 244dirloop:
6bd0bb92 245 /*
955e7a55
KM
246 * Search a new directory.
247 *
cfef4373 248 * The cn_hash value is for use by vfs_cache.
955e7a55 249 * The last component of the filename is left accessible via
cfef4373 250 * cnp->cn_nameptr for callers that need the name. Callers needing
955e7a55
KM
251 * the name set the SAVENAME flag. When done, they assume
252 * responsibility for freeing the pathname buffer.
6bd0bb92 253 */
cfef4373
JH
254 cnp->cn_hash = 0;
255 for (cp = cnp->cn_nameptr; *cp != 0 && *cp != '/'; cp++)
256 cnp->cn_hash += (unsigned char)*cp;
257 cnp->cn_namelen = cp - cnp->cn_nameptr;
258 if (cnp->cn_namelen >= NAME_MAX) {
955e7a55
KM
259 error = ENAMETOOLONG;
260 goto bad;
261 }
6d0f0ece 262#ifdef NAMEI_DIAGNOSTIC
955e7a55
KM
263 { char c = *cp;
264 *cp = '\0';
cfef4373 265 printf("{%s}: ", cnp->cn_nameptr);
955e7a55 266 *cp = c; }
6d0f0ece 267#endif
cfef4373 268 ndp->ni_pathlen -= cnp->cn_namelen;
955e7a55 269 ndp->ni_next = cp;
cfef4373 270 cnp->cn_flags |= MAKEENTRY;
87c05e6e 271 if (*cp == '\0' && docache == 0)
cfef4373
JH
272 cnp->cn_flags &= ~MAKEENTRY;
273 if (cnp->cn_namelen == 2 &&
274 cnp->cn_nameptr[1] == '.' && cnp->cn_nameptr[0] == '.')
275 cnp->cn_flags |= ISDOTDOT;
276 else cnp->cn_flags &= ~ISDOTDOT;
277 if (*ndp->ni_next == 0)
278 cnp->cn_flags |= ISLASTCN;
279 else cnp->cn_flags &= ~ISLASTCN;
280
6bd0bb92
BJ
281
282 /*
283 * Check for degenerate name (e.g. / or "")
284 * which is a way of talking about a directory,
285 * e.g. like "/." or ".".
286 */
cfef4373
JH
287 if (cnp->cn_nameptr[0] == '\0') {
288 if (cnp->cn_nameiop != LOOKUP || wantparent) {
6d0f0ece 289 error = EISDIR;
6bd0bb92 290 goto bad;
f5039631 291 }
955e7a55
KM
292 if (dp->v_type != VDIR) {
293 error = ENOTDIR;
294 goto bad;
295 }
cfef4373 296 if (!(cnp->cn_flags & LOCKLEAF))
6d0f0ece
KM
297 VOP_UNLOCK(dp);
298 ndp->ni_vp = dp;
cfef4373 299 if (cnp->cn_flags & SAVESTART)
955e7a55 300 panic("lookup: SAVESTART");
743467cb 301 return (0);
f5039631 302 }
6bd0bb92 303
e47da406 304 /*
6d0f0ece
KM
305 * Handle "..": two special cases.
306 * 1. If at root directory (e.g. after chroot)
307 * then ignore it so can't get out.
308 * 2. If this vnode is the root of a mounted
955e7a55 309 * filesystem, then replace it with the
6d0f0ece
KM
310 * vnode which was mounted on so we take the
311 * .. in the other file system.
e47da406 312 */
cfef4373 313 if (cnp->cn_flags & ISDOTDOT) {
e47da406 314 for (;;) {
955e7a55 315 if (dp == ndp->ni_rootdir) {
6d0f0ece 316 ndp->ni_dvp = dp;
7655c64a 317 ndp->ni_vp = dp;
8fe1c702 318 VREF(dp);
6d0f0ece 319 goto nextname;
e47da406 320 }
d5cea2aa 321 if ((dp->v_flag & VROOT) == 0 ||
cfef4373 322 (cnp->cn_flags & NOCROSSMOUNT))
e47da406 323 break;
6d0f0ece 324 tdp = dp;
54fb9dc2 325 dp = dp->v_mount->mnt_vnodecovered;
6d0f0ece 326 vput(tdp);
8fe1c702 327 VREF(dp);
7655c64a 328 VOP_LOCK(dp);
e47da406
KM
329 }
330 }
331
f93197fc
KM
332 /*
333 * We now have a segment name to search for, and a directory to search.
6459ebe0 334 */
cfef4373
JH
335 ndp->ni_dvp = dp;
336 if (error = VOP_LOOKUP(dp, &ndp->ni_vp, cnp)) {
955e7a55 337#ifdef DIAGNOSTIC
6d0f0ece
KM
338 if (ndp->ni_vp != NULL)
339 panic("leaf should be empty");
955e7a55 340#endif
658f5fdc 341#ifdef NAMEI_DIAGNOSTIC
6d0f0ece 342 printf("not found\n");
658f5fdc 343#endif
cfef4373 344 if (cnp->cn_nameiop == LOOKUP || cnp->cn_nameiop == DELETE ||
1f6ef9f5
KM
345 error != ENOENT || *cp != 0)
346 goto bad;
f5039631 347 /*
6d0f0ece
KM
348 * If creating and at end of pathname, then can consider
349 * allowing file to be created.
f5039631 350 */
6c235bc4 351 if (rdonly || (ndp->ni_dvp->v_mount->mnt_flag & MNT_RDONLY)) {
6d0f0ece 352 error = EROFS;
6bd0bb92 353 goto bad;
1f6ef9f5 354 }
f5039631 355 /*
6d0f0ece
KM
356 * We return with ni_vp NULL to indicate that the entry
357 * doesn't currently exist, leaving a pointer to the
358 * (possibly locked) directory inode in ndp->ni_dvp.
f5039631 359 */
cfef4373 360 if (cnp->cn_flags & SAVESTART) {
955e7a55
KM
361 ndp->ni_startdir = ndp->ni_dvp;
362 VREF(ndp->ni_startdir);
b8c8091e 363 p->p_spare[1]++;
955e7a55 364 }
743467cb 365 return (0);
6bd0bb92 366 }
658f5fdc 367#ifdef NAMEI_DIAGNOSTIC
6d0f0ece 368 printf("found\n");
658f5fdc 369#endif
6bd0bb92 370
955e7a55 371 dp = ndp->ni_vp;
6bd0bb92 372 /*
6d0f0ece 373 * Check for symbolic link
4f083fd7 374 */
6d0f0ece 375 if ((dp->v_type == VLNK) &&
cfef4373
JH
376 ((cnp->cn_flags & FOLLOW) || *ndp->ni_next == '/')) {
377 cnp->cn_flags |= ISSYMLINK;
743467cb 378 return (0);
f93197fc
KM
379 }
380
6bd0bb92 381 /*
6d0f0ece
KM
382 * Check to see if the vnode has been mounted on;
383 * if so find the root of the mounted file system.
6bd0bb92 384 */
6d0f0ece 385mntloop:
d5cea2aa 386 while (dp->v_type == VDIR && (mp = dp->v_mountedhere) &&
cfef4373 387 (cnp->cn_flags & NOCROSSMOUNT) == 0) {
17c70d83 388 if (mp->mnt_flag & MNT_MLOCK) {
54fb9dc2 389 mp->mnt_flag |= MNT_MWAIT;
6d0f0ece
KM
390 sleep((caddr_t)mp, PVFS);
391 goto mntloop;
6bd0bb92 392 }
955e7a55 393 if (error = VFS_ROOT(dp->v_mountedhere, &tdp))
6bd0bb92 394 goto bad2;
6d0f0ece
KM
395 vput(dp);
396 ndp->ni_vp = dp = tdp;
10873320 397 }
6bd0bb92 398
6d0f0ece 399nextname:
10873320 400 /*
6bd0bb92
BJ
401 * Not a symbolic link. If more pathname,
402 * continue at next component, else return.
10873320 403 */
955e7a55 404 if (*ndp->ni_next == '/') {
cfef4373
JH
405 cnp->cn_nameptr = ndp->ni_next;
406 while (*cnp->cn_nameptr == '/') {
407 cnp->cn_nameptr++;
6d0f0ece 408 ndp->ni_pathlen--;
6bd0bb92 409 }
6d0f0ece
KM
410 vrele(ndp->ni_dvp);
411 goto dirloop;
6bd0bb92
BJ
412 }
413 /*
01633fea 414 * Check for read-only file systems.
6bd0bb92 415 */
cfef4373 416 if (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME) {
dfc0e8dd 417 /*
01633fea
KM
418 * Disallow directory write attempts on read-only
419 * file systems.
dfc0e8dd 420 */
6c235bc4
KM
421 if (rdonly || (dp->v_mount->mnt_flag & MNT_RDONLY) ||
422 (wantparent &&
423 (ndp->ni_dvp->v_mount->mnt_flag & MNT_RDONLY))) {
dfc0e8dd
KM
424 error = EROFS;
425 goto bad2;
426 }
427 }
cfef4373 428 if (cnp->cn_flags & SAVESTART) {
955e7a55 429 ndp->ni_startdir = ndp->ni_dvp;
b8c8091e 430 p->p_spare[1]++;
955e7a55
KM
431 VREF(ndp->ni_startdir);
432 }
6d0f0ece
KM
433 if (!wantparent)
434 vrele(ndp->ni_dvp);
cfef4373 435 if ((cnp->cn_flags & LOCKLEAF) == 0)
6d0f0ece 436 VOP_UNLOCK(dp);
743467cb 437 return (0);
4f083fd7 438
6d0f0ece 439bad2:
cfef4373 440 if ((cnp->cn_flags & LOCKPARENT) && *ndp->ni_next == '\0')
206be3f9 441 VOP_UNLOCK(ndp->ni_dvp);
6d0f0ece
KM
442 vrele(ndp->ni_dvp);
443bad:
444 vput(dp);
445 ndp->ni_vp = NULL;
743467cb 446 return (error);
b1aa93b9 447}
cfef4373
JH
448
449