Line data Source code
1 : /* $OpenBSD: vfs_lookup.c,v 1.74 2018/08/13 23:11:44 deraadt Exp $ */
2 : /* $NetBSD: vfs_lookup.c,v 1.17 1996/02/09 19:00:59 christos Exp $ */
3 :
4 : /*
5 : * Copyright (c) 1982, 1986, 1989, 1993
6 : * The Regents of the University of California. All rights reserved.
7 : * (c) UNIX System Laboratories, Inc.
8 : * All or some portions of this file are derived from material licensed
9 : * to the University of California by American Telephone and Telegraph
10 : * Co. or Unix System Laboratories, Inc. and are reproduced herein with
11 : * the permission of UNIX System Laboratories, Inc.
12 : *
13 : * Redistribution and use in source and binary forms, with or without
14 : * modification, are permitted provided that the following conditions
15 : * are met:
16 : * 1. Redistributions of source code must retain the above copyright
17 : * notice, this list of conditions and the following disclaimer.
18 : * 2. Redistributions in binary form must reproduce the above copyright
19 : * notice, this list of conditions and the following disclaimer in the
20 : * documentation and/or other materials provided with the distribution.
21 : * 3. Neither the name of the University nor the names of its contributors
22 : * may be used to endorse or promote products derived from this software
23 : * without specific prior written permission.
24 : *
25 : * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
26 : * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27 : * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28 : * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
29 : * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30 : * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31 : * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32 : * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33 : * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34 : * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35 : * SUCH DAMAGE.
36 : *
37 : * @(#)vfs_lookup.c 8.6 (Berkeley) 11/21/94
38 : */
39 :
40 : #include <sys/param.h>
41 : #include <sys/systm.h>
42 : #include <sys/syslimits.h>
43 : #include <sys/time.h>
44 : #include <sys/namei.h>
45 : #include <sys/vnode.h>
46 : #include <sys/lock.h>
47 : #include <sys/mount.h>
48 : #include <sys/errno.h>
49 : #include <sys/pool.h>
50 : #include <sys/filedesc.h>
51 : #include <sys/proc.h>
52 : #include <sys/pledge.h>
53 : #include <sys/file.h>
54 : #include <sys/fcntl.h>
55 :
56 : #ifdef KTRACE
57 : #include <sys/ktrace.h>
58 : #endif
59 :
60 : void unveil_check_component(struct proc *p, struct nameidata *ni, struct vnode *dp );
61 : int unveil_check_final(struct proc *p, struct nameidata *ni);
62 :
63 : void
64 0 : ndinitat(struct nameidata *ndp, u_long op, u_long flags,
65 : enum uio_seg segflg, int dirfd, const char *namep, struct proc *p)
66 : {
67 0 : memset(ndp, 0, sizeof(*ndp));
68 0 : ndp->ni_cnd.cn_nameiop = op;
69 0 : ndp->ni_cnd.cn_flags = flags;
70 0 : ndp->ni_segflg = segflg;
71 0 : ndp->ni_dirfd = dirfd;
72 0 : ndp->ni_dirp = namep;
73 0 : ndp->ni_cnd.cn_proc = p;
74 0 : }
75 :
76 : /*
77 : * Convert a pathname into a pointer to a vnode.
78 : *
79 : * The FOLLOW flag is set when symbolic links are to be followed
80 : * when they occur at the end of the name translation process.
81 : * Symbolic links are always followed for all other pathname
82 : * components other than the last.
83 : *
84 : * If the LOCKLEAF flag is set, a locked vnode is returned.
85 : *
86 : * The segflg defines whether the name is to be copied from user
87 : * space or kernel space.
88 : *
89 : * Overall outline of namei:
90 : *
91 : * copy in name
92 : * get starting directory
93 : * while (!done && !error) {
94 : * call lookup to search path.
95 : * if symbolic link, massage name in buffer and continue
96 : * }
97 : */
98 : int
99 0 : namei(struct nameidata *ndp)
100 : {
101 : struct filedesc *fdp; /* pointer to file descriptor state */
102 : char *cp; /* pointer into pathname argument */
103 : struct vnode *dp; /* the directory we are searching */
104 0 : struct iovec aiov; /* uio for reading symbolic links */
105 0 : struct uio auio;
106 : int error, linklen;
107 0 : struct componentname *cnp = &ndp->ni_cnd;
108 0 : struct proc *p = cnp->cn_proc;
109 :
110 0 : ndp->ni_cnd.cn_cred = ndp->ni_cnd.cn_proc->p_ucred;
111 : #ifdef DIAGNOSTIC
112 0 : if (!cnp->cn_cred || !cnp->cn_proc)
113 0 : panic ("namei: bad cred/proc");
114 0 : if (cnp->cn_nameiop & (~OPMASK))
115 0 : panic ("namei: nameiop contaminated with flags");
116 0 : if (cnp->cn_flags & OPMASK)
117 0 : panic ("namei: flags contaminated with nameiops");
118 : #endif
119 0 : fdp = cnp->cn_proc->p_fd;
120 :
121 : /*
122 : * Get a buffer for the name to be translated, and copy the
123 : * name into the buffer.
124 : */
125 0 : if ((cnp->cn_flags & HASBUF) == 0)
126 0 : cnp->cn_pnbuf = pool_get(&namei_pool, PR_WAITOK);
127 0 : if (ndp->ni_segflg == UIO_SYSSPACE)
128 0 : error = copystr(ndp->ni_dirp, cnp->cn_pnbuf,
129 : MAXPATHLEN, &ndp->ni_pathlen);
130 : else
131 0 : error = copyinstr(ndp->ni_dirp, cnp->cn_pnbuf,
132 : MAXPATHLEN, &ndp->ni_pathlen);
133 :
134 : /*
135 : * Fail on null pathnames
136 : */
137 0 : if (error == 0 && ndp->ni_pathlen == 1)
138 0 : error = ENOENT;
139 :
140 0 : if (error) {
141 : fail:
142 0 : pool_put(&namei_pool, cnp->cn_pnbuf);
143 0 : ndp->ni_vp = NULL;
144 0 : return (error);
145 : }
146 :
147 : #ifdef KTRACE
148 0 : if (KTRPOINT(cnp->cn_proc, KTR_NAMEI))
149 0 : ktrnamei(cnp->cn_proc, cnp->cn_pnbuf);
150 : #endif
151 :
152 : /*
153 : * Strip trailing slashes, as requested
154 : */
155 0 : if (cnp->cn_flags & STRIPSLASHES) {
156 0 : char *end = cnp->cn_pnbuf + ndp->ni_pathlen - 2;
157 :
158 : cp = end;
159 0 : while (cp >= cnp->cn_pnbuf && (*cp == '/'))
160 0 : cp--;
161 :
162 : /* Still some remaining characters in the buffer */
163 0 : if (cp >= cnp->cn_pnbuf) {
164 0 : ndp->ni_pathlen -= (end - cp);
165 0 : *(cp + 1) = '\0';
166 0 : }
167 0 : }
168 :
169 0 : ndp->ni_loopcnt = 0;
170 :
171 : /*
172 : * Get starting point for the translation.
173 : */
174 0 : if ((ndp->ni_rootdir = fdp->fd_rdir) == NULL ||
175 0 : (ndp->ni_cnd.cn_flags & KERNELPATH))
176 0 : ndp->ni_rootdir = rootvnode;
177 :
178 0 : if (ndp->ni_cnd.cn_flags & KERNELPATH) {
179 0 : ndp->ni_cnd.cn_flags |= BYPASSUNVEIL;
180 0 : } else {
181 0 : error = pledge_namei(p, ndp, cnp->cn_pnbuf);
182 0 : if (error)
183 : goto fail;
184 : }
185 :
186 : /*
187 : * Check if starting from root directory or current directory.
188 : */
189 0 : if (cnp->cn_pnbuf[0] == '/') {
190 0 : curproc->p_p->ps_uvpcwd = NULL;
191 0 : curproc->p_p->ps_uvpcwdgone = 0;
192 0 : dp = ndp->ni_rootdir;
193 0 : vref(dp);
194 0 : } else if (ndp->ni_dirfd == AT_FDCWD) {
195 0 : dp = fdp->fd_cdir;
196 0 : vref(dp);
197 0 : unveil_check_component(p, ndp, dp);
198 0 : } else {
199 0 : struct file *fp = fd_getfile(fdp, ndp->ni_dirfd);
200 0 : if (fp == NULL) {
201 0 : pool_put(&namei_pool, cnp->cn_pnbuf);
202 0 : return (EBADF);
203 : }
204 0 : dp = (struct vnode *)fp->f_data;
205 0 : if (fp->f_type != DTYPE_VNODE || dp->v_type != VDIR) {
206 0 : FRELE(fp, p);
207 0 : pool_put(&namei_pool, cnp->cn_pnbuf);
208 0 : return (ENOTDIR);
209 : }
210 0 : vref(dp);
211 0 : unveil_check_component(p, ndp, dp);
212 0 : FRELE(fp, p);
213 0 : }
214 0 : for (;;) {
215 0 : if (!dp->v_mount) {
216 : /* Give up if the directory is no longer mounted */
217 0 : pool_put(&namei_pool, cnp->cn_pnbuf);
218 0 : vrele(dp);
219 0 : ndp->ni_vp = NULL;
220 0 : return (ENOENT);
221 : }
222 0 : cnp->cn_nameptr = cnp->cn_pnbuf;
223 0 : ndp->ni_startdir = dp;
224 0 : if ((error = vfs_lookup(ndp)) != 0) {
225 0 : pool_put(&namei_pool, cnp->cn_pnbuf);
226 0 : return (error);
227 : }
228 : /*
229 : * If not a symbolic link, return search result.
230 : */
231 0 : if ((cnp->cn_flags & ISSYMLINK) == 0) {
232 0 : if ((error = unveil_check_final(p, ndp))) {
233 0 : pool_put(&namei_pool, cnp->cn_pnbuf);
234 0 : if ((cnp->cn_flags & LOCKPARENT) &&
235 0 : (cnp->cn_flags & ISLASTCN) &&
236 0 : (ndp->ni_vp != ndp->ni_dvp))
237 0 : VOP_UNLOCK(ndp->ni_dvp);
238 0 : if (ndp->ni_vp) {
239 0 : if ((cnp->cn_flags & LOCKLEAF))
240 0 : vput(ndp->ni_vp);
241 : else
242 0 : vrele(ndp->ni_vp);
243 : }
244 0 : ndp->ni_vp = NULL;
245 0 : return (error);
246 : }
247 0 : if ((cnp->cn_flags & (SAVENAME | SAVESTART)) == 0)
248 0 : pool_put(&namei_pool, cnp->cn_pnbuf);
249 : else
250 0 : cnp->cn_flags |= HASBUF;
251 0 : return(0);
252 : }
253 0 : if ((cnp->cn_flags & LOCKPARENT) && (cnp->cn_flags & ISLASTCN))
254 0 : VOP_UNLOCK(ndp->ni_dvp);
255 0 : if (ndp->ni_loopcnt++ >= SYMLOOP_MAX) {
256 : error = ELOOP;
257 0 : break;
258 : }
259 0 : if (ndp->ni_pathlen > 1)
260 0 : cp = pool_get(&namei_pool, PR_WAITOK);
261 : else
262 0 : cp = cnp->cn_pnbuf;
263 0 : aiov.iov_base = cp;
264 0 : aiov.iov_len = MAXPATHLEN;
265 0 : auio.uio_iov = &aiov;
266 0 : auio.uio_iovcnt = 1;
267 0 : auio.uio_offset = 0;
268 0 : auio.uio_rw = UIO_READ;
269 0 : auio.uio_segflg = UIO_SYSSPACE;
270 0 : auio.uio_procp = cnp->cn_proc;
271 0 : auio.uio_resid = MAXPATHLEN;
272 0 : error = VOP_READLINK(ndp->ni_vp, &auio, cnp->cn_cred);
273 0 : if (error) {
274 : badlink:
275 0 : if (ndp->ni_pathlen > 1)
276 0 : pool_put(&namei_pool, cp);
277 : break;
278 : }
279 0 : linklen = MAXPATHLEN - auio.uio_resid;
280 0 : if (linklen == 0) {
281 : error = ENOENT;
282 0 : goto badlink;
283 : }
284 0 : if (linklen + ndp->ni_pathlen >= MAXPATHLEN) {
285 : error = ENAMETOOLONG;
286 0 : goto badlink;
287 : }
288 0 : if (ndp->ni_pathlen > 1) {
289 0 : memcpy(cp + linklen, ndp->ni_next, ndp->ni_pathlen);
290 0 : pool_put(&namei_pool, cnp->cn_pnbuf);
291 0 : cnp->cn_pnbuf = cp;
292 0 : } else
293 0 : cnp->cn_pnbuf[linklen] = '\0';
294 0 : ndp->ni_pathlen += linklen;
295 0 : vput(ndp->ni_vp);
296 0 : dp = ndp->ni_dvp;
297 : /*
298 : * Check if root directory should replace current directory.
299 : */
300 0 : if (cnp->cn_pnbuf[0] == '/') {
301 0 : vrele(dp);
302 0 : dp = ndp->ni_rootdir;
303 0 : vref(dp);
304 0 : ndp->ni_unveil_match = NULL;
305 0 : curproc->p_p->ps_uvpcwd = NULL;
306 0 : unveil_check_component(p, ndp, dp);
307 0 : } else {
308 : /*
309 : * this is a relative link, so remember our
310 : * unveil match from this point
311 : */
312 0 : curproc->p_p->ps_uvpcwd = ndp->ni_unveil_match;
313 : }
314 :
315 : }
316 0 : pool_put(&namei_pool, cnp->cn_pnbuf);
317 0 : vrele(ndp->ni_dvp);
318 0 : vput(ndp->ni_vp);
319 0 : ndp->ni_vp = NULL;
320 0 : return (error);
321 0 : }
322 :
323 : /*
324 : * Search a pathname.
325 : * This is a very central and rather complicated routine.
326 : *
327 : * The pathname is pointed to by ni_cnd.cn_nameptr and is of length
328 : * ni_pathlen. The starting directory is taken from ni_startdir. The
329 : * pathname is descended until done, or a symbolic link is encountered.
330 : * If the path is completed the flag ISLASTCN is set in ni_cnd.cn_flags.
331 : * If a symbolic link need interpretation is encountered, the flag ISSYMLINK
332 : * is set in ni_cnd.cn_flags.
333 : *
334 : * The flag argument is LOOKUP, CREATE, RENAME, or DELETE depending on
335 : * whether the name is to be looked up, created, renamed, or deleted.
336 : * When CREATE, RENAME, or DELETE is specified, information usable in
337 : * creating, renaming, or deleting a directory entry may be calculated.
338 : * If flag has LOCKPARENT or'ed into it, the parent directory is returned
339 : * locked. If flag has WANTPARENT or'ed into it, the parent directory is
340 : * returned unlocked. Otherwise the parent directory is not returned. If
341 : * the target of the pathname exists and LOCKLEAF is or'ed into the flag
342 : * the target is returned locked, otherwise it is returned unlocked.
343 : * When creating or renaming and LOCKPARENT is specified, the target may not
344 : * be ".". When deleting and LOCKPARENT is specified, the target may be ".".
345 : *
346 : * Overall outline of lookup:
347 : *
348 : * dirloop:
349 : * identify next component of name at ndp->ni_ptr
350 : * handle degenerate case where name is null string
351 : * if .. and crossing mount points and on mounted filesys, find parent
352 : * call VOP_LOOKUP routine for next component name
353 : * directory vnode returned in ni_dvp, unlocked unless LOCKPARENT set
354 : * component vnode returned in ni_vp (if it exists), locked.
355 : * if result vnode is mounted on and crossing mount points,
356 : * find mounted on vnode
357 : * if more components of name, do next level at dirloop
358 : * return the answer in ni_vp, locked if LOCKLEAF set
359 : * if LOCKPARENT set, return locked parent in ni_dvp
360 : * if WANTPARENT set, return unlocked parent in ni_dvp
361 : */
362 : int
363 0 : vfs_lookup(struct nameidata *ndp)
364 : {
365 : char *cp; /* pointer into pathname argument */
366 : struct vnode *dp = 0; /* the directory we are searching */
367 0 : struct vnode *tdp; /* saved dp */
368 : struct mount *mp; /* mount table entry */
369 : int docache; /* == 0 do not cache last component */
370 : int wantparent; /* 1 => wantparent or lockparent flag */
371 : int rdonly; /* lookup read-only flag bit */
372 : int error = 0;
373 : int dpunlocked = 0; /* dp has already been unlocked */
374 : int slashes;
375 0 : struct componentname *cnp = &ndp->ni_cnd;
376 : /*
377 : * Setup: break out flag bits into variables.
378 : */
379 0 : wantparent = cnp->cn_flags & (LOCKPARENT | WANTPARENT);
380 0 : docache = (cnp->cn_flags & NOCACHE) ^ NOCACHE;
381 0 : if (cnp->cn_nameiop == DELETE ||
382 0 : (wantparent && cnp->cn_nameiop != CREATE))
383 0 : docache = 0;
384 0 : rdonly = cnp->cn_flags & RDONLY;
385 0 : ndp->ni_dvp = NULL;
386 0 : cnp->cn_flags &= ~ISSYMLINK;
387 0 : dp = ndp->ni_startdir;
388 0 : ndp->ni_startdir = NULLVP;
389 0 : vn_lock(dp, LK_EXCLUSIVE | LK_RETRY);
390 :
391 : /*
392 : * If we have a leading string of slashes, remove them, and just make
393 : * sure the current node is a directory.
394 : */
395 0 : cp = cnp->cn_nameptr;
396 0 : if (*cp == '/') {
397 0 : do {
398 0 : cp++;
399 0 : } while (*cp == '/');
400 0 : ndp->ni_pathlen -= cp - cnp->cn_nameptr;
401 0 : cnp->cn_nameptr = cp;
402 :
403 0 : if (dp->v_type != VDIR) {
404 : error = ENOTDIR;
405 0 : goto bad;
406 : }
407 :
408 : /*
409 : * If we've exhausted the path name, then just return the
410 : * current node. If the caller requested the parent node (i.e.
411 : * it's a CREATE, DELETE, or RENAME), and we don't have one
412 : * (because this is the root directory), then we must fail.
413 : */
414 0 : if (cnp->cn_nameptr[0] == '\0') {
415 0 : if (ndp->ni_dvp == NULL && wantparent) {
416 : error = EISDIR;
417 0 : goto bad;
418 : }
419 0 : ndp->ni_vp = dp;
420 0 : cnp->cn_flags |= ISLASTCN;
421 0 : goto terminal;
422 : }
423 : }
424 :
425 : dirloop:
426 : /*
427 : * Search a new directory.
428 : *
429 : * The last component of the filename is left accessible via
430 : * cnp->cn_nameptr for callers that need the name. Callers needing
431 : * the name set the SAVENAME flag. When done, they assume
432 : * responsibility for freeing the pathname buffer.
433 : */
434 0 : cnp->cn_consume = 0;
435 :
436 : /* XXX: Figure out the length of the last component. */
437 0 : cp = cnp->cn_nameptr;
438 0 : while (*cp && (*cp != '/'))
439 0 : cp++;
440 0 : cnp->cn_namelen = cp - cnp->cn_nameptr;
441 0 : if (cnp->cn_namelen > NAME_MAX) {
442 : error = ENAMETOOLONG;
443 0 : goto bad;
444 : }
445 :
446 : #ifdef NAMEI_DIAGNOSTIC
447 : { char c = *cp;
448 : *cp = '\0';
449 : printf("{%s}: ", cnp->cn_nameptr);
450 : *cp = c; }
451 : #endif
452 0 : ndp->ni_pathlen -= cnp->cn_namelen;
453 0 : ndp->ni_next = cp;
454 : /*
455 : * If this component is followed by a slash, then move the pointer to
456 : * the next component forward, and remember that this component must be
457 : * a directory.
458 : */
459 0 : if (*cp == '/') {
460 0 : do {
461 0 : cp++;
462 0 : } while (*cp == '/');
463 0 : slashes = cp - ndp->ni_next;
464 0 : ndp->ni_pathlen -= slashes;
465 0 : ndp->ni_next = cp;
466 0 : cnp->cn_flags |= REQUIREDIR;
467 0 : } else {
468 : slashes = 0;
469 0 : cnp->cn_flags &= ~REQUIREDIR;
470 : }
471 : /*
472 : * We do special processing on the last component, whether or not it's
473 : * a directory. Cache all intervening lookups, but not the final one.
474 : */
475 0 : if (*cp == '\0') {
476 0 : if (docache)
477 0 : cnp->cn_flags |= MAKEENTRY;
478 : else
479 0 : cnp->cn_flags &= ~MAKEENTRY;
480 0 : cnp->cn_flags |= ISLASTCN;
481 0 : } else {
482 0 : cnp->cn_flags |= MAKEENTRY;
483 0 : cnp->cn_flags &= ~ISLASTCN;
484 : }
485 0 : if (cnp->cn_namelen == 2 &&
486 0 : cnp->cn_nameptr[1] == '.' && cnp->cn_nameptr[0] == '.')
487 0 : cnp->cn_flags |= ISDOTDOT;
488 : else
489 0 : cnp->cn_flags &= ~ISDOTDOT;
490 :
491 : /*
492 : * Handle "..": two special cases.
493 : * 1. If at root directory (e.g. after chroot)
494 : * or at absolute root directory
495 : * or we are under unveil restrictions
496 : * then ignore it so can't get out.
497 : * 2. If this vnode is the root of a mounted
498 : * filesystem, then replace it with the
499 : * vnode which was mounted on so we take the
500 : * .. in the other file system.
501 : */
502 0 : if (cnp->cn_flags & ISDOTDOT) {
503 0 : for (;;) {
504 0 : if (curproc->p_p->ps_uvvcount > 0) {
505 : #if 0
506 : error = ENOENT;
507 : goto bad;
508 : #else
509 0 : ndp->ni_unveil_match = NULL;
510 : #endif
511 0 : }
512 0 : if (dp == ndp->ni_rootdir || dp == rootvnode) {
513 0 : ndp->ni_dvp = dp;
514 0 : ndp->ni_vp = dp;
515 0 : vref(dp);
516 0 : curproc->p_p->ps_uvpcwd = NULL;
517 0 : curproc->p_p->ps_uvpcwdgone = 0;
518 0 : goto nextname;
519 : }
520 0 : if ((dp->v_flag & VROOT) == 0 ||
521 0 : (cnp->cn_flags & NOCROSSMOUNT))
522 : break;
523 0 : tdp = dp;
524 0 : dp = dp->v_mount->mnt_vnodecovered;
525 0 : vput(tdp);
526 0 : vref(dp);
527 0 : unveil_check_component(curproc, ndp, dp);
528 0 : vn_lock(dp, LK_EXCLUSIVE | LK_RETRY);
529 : }
530 : }
531 :
532 : /*
533 : * We now have a segment name to search for, and a directory to search.
534 : */
535 0 : ndp->ni_dvp = dp;
536 0 : ndp->ni_vp = NULL;
537 0 : cnp->cn_flags &= ~PDIRUNLOCK;
538 0 : unveil_check_component(curproc, ndp, dp);
539 :
540 0 : if ((error = VOP_LOOKUP(dp, &ndp->ni_vp, cnp)) != 0) {
541 : #ifdef DIAGNOSTIC
542 0 : if (ndp->ni_vp != NULL)
543 0 : panic("leaf should be empty");
544 : #endif
545 : #ifdef NAMEI_DIAGNOSTIC
546 : printf("not found\n");
547 : #endif
548 : /*
549 : * Allow for unveiling of a file in a directory
550 : * where we don't have access to create it ourselves
551 : */
552 0 : if (ndp->ni_pledge == PLEDGE_UNVEIL && error == EACCES)
553 0 : error = EJUSTRETURN;
554 :
555 0 : if (error != EJUSTRETURN)
556 : goto bad;
557 : /*
558 : * If this was not the last component, or there were trailing
559 : * slashes, then the name must exist.
560 : */
561 0 : if (cnp->cn_flags & REQUIREDIR) {
562 : error = ENOENT;
563 0 : goto bad;
564 : }
565 : /*
566 : * If creating and at end of pathname, then can consider
567 : * allowing file to be created. Check for a read only
568 : * filesystem and disallow this unless we are unveil'ing
569 : */
570 0 : if (ndp->ni_pledge != PLEDGE_UNVEIL && (rdonly ||
571 0 : (ndp->ni_dvp->v_mount->mnt_flag & MNT_RDONLY))) {
572 : error = EROFS;
573 0 : goto bad;
574 : }
575 : /*
576 : * We return with ni_vp NULL to indicate that the entry
577 : * doesn't currently exist, leaving a pointer to the
578 : * (possibly locked) directory inode in ndp->ni_dvp.
579 : */
580 0 : if (cnp->cn_flags & SAVESTART) {
581 0 : ndp->ni_startdir = ndp->ni_dvp;
582 0 : vref(ndp->ni_startdir);
583 0 : }
584 0 : return (0);
585 : }
586 : #ifdef NAMEI_DIAGNOSTIC
587 : printf("found\n");
588 : #endif
589 :
590 : /*
591 : * Take into account any additional components consumed by the
592 : * underlying filesystem. This will include any trailing slashes after
593 : * the last component consumed.
594 : */
595 0 : if (cnp->cn_consume > 0) {
596 0 : if (cnp->cn_consume >= slashes) {
597 0 : cnp->cn_flags &= ~REQUIREDIR;
598 0 : }
599 :
600 0 : ndp->ni_pathlen -= cnp->cn_consume - slashes;
601 0 : ndp->ni_next += cnp->cn_consume - slashes;
602 0 : cnp->cn_consume = 0;
603 0 : if (ndp->ni_next[0] == '\0')
604 0 : cnp->cn_flags |= ISLASTCN;
605 : }
606 :
607 0 : dp = ndp->ni_vp;
608 : /*
609 : * Check to see if the vnode has been mounted on;
610 : * if so find the root of the mounted file system.
611 : */
612 0 : while (dp->v_type == VDIR && (mp = dp->v_mountedhere) &&
613 0 : (cnp->cn_flags & NOCROSSMOUNT) == 0) {
614 0 : if (vfs_busy(mp, VB_READ|VB_WAIT))
615 0 : continue;
616 0 : VOP_UNLOCK(dp);
617 0 : error = VFS_ROOT(mp, &tdp);
618 0 : vfs_unbusy(mp);
619 0 : if (error) {
620 : dpunlocked = 1;
621 0 : goto bad2;
622 : }
623 0 : vrele(dp);
624 0 : ndp->ni_vp = dp = tdp;
625 : }
626 :
627 : /*
628 : * Check for symbolic link. Back up over any slashes that we skipped,
629 : * as we will need them again.
630 : */
631 0 : if ((dp->v_type == VLNK) && (cnp->cn_flags & (FOLLOW|REQUIREDIR))) {
632 0 : ndp->ni_pathlen += slashes;
633 0 : ndp->ni_next -= slashes;
634 0 : cnp->cn_flags |= ISSYMLINK;
635 0 : return (0);
636 : }
637 :
638 : /*
639 : * Check for directory, if the component was followed by a series of
640 : * slashes.
641 : */
642 0 : if ((dp->v_type != VDIR) && (cnp->cn_flags & REQUIREDIR)) {
643 : error = ENOTDIR;
644 0 : goto bad2;
645 : }
646 :
647 : nextname:
648 : /*
649 : * Not a symbolic link. If this was not the last component, then
650 : * continue at the next component, else return.
651 : */
652 0 : if (!(cnp->cn_flags & ISLASTCN)) {
653 0 : cnp->cn_nameptr = ndp->ni_next;
654 0 : vrele(ndp->ni_dvp);
655 0 : goto dirloop;
656 : }
657 :
658 : terminal:
659 : /*
660 : * Check for read-only file systems.
661 : */
662 0 : if (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME) {
663 : /*
664 : * Disallow directory write attempts on read-only
665 : * file systems.
666 : */
667 0 : if (rdonly || (dp->v_mount->mnt_flag & MNT_RDONLY) ||
668 0 : (wantparent &&
669 0 : (ndp->ni_dvp->v_mount->mnt_flag & MNT_RDONLY))) {
670 : error = EROFS;
671 0 : goto bad2;
672 : }
673 : }
674 0 : if (ndp->ni_dvp != NULL) {
675 0 : if (cnp->cn_flags & SAVESTART) {
676 0 : ndp->ni_startdir = ndp->ni_dvp;
677 0 : vref(ndp->ni_startdir);
678 0 : }
679 0 : if (!wantparent)
680 0 : vrele(ndp->ni_dvp);
681 : }
682 0 : if ((cnp->cn_flags & LOCKLEAF) == 0)
683 0 : VOP_UNLOCK(dp);
684 0 : return (0);
685 :
686 : bad2:
687 0 : if ((cnp->cn_flags & LOCKPARENT) && (cnp->cn_flags & ISLASTCN) &&
688 0 : ((cnp->cn_flags & PDIRUNLOCK) == 0))
689 0 : VOP_UNLOCK(ndp->ni_dvp);
690 0 : vrele(ndp->ni_dvp);
691 : bad:
692 0 : if (dpunlocked)
693 0 : vrele(dp);
694 : else
695 0 : vput(dp);
696 0 : ndp->ni_vp = NULL;
697 0 : return (error);
698 0 : }
699 :
700 : /*
701 : * Reacquire a path name component.
702 : */
703 : int
704 0 : vfs_relookup(struct vnode *dvp, struct vnode **vpp, struct componentname *cnp)
705 : {
706 : struct vnode *dp = 0; /* the directory we are searching */
707 : int wantparent; /* 1 => wantparent or lockparent flag */
708 : int rdonly; /* lookup read-only flag bit */
709 : int error = 0;
710 : #ifdef NAMEI_DIAGNOSTIC
711 : char *cp; /* DEBUG: check name ptr/len */
712 : #endif
713 :
714 : /*
715 : * Setup: break out flag bits into variables.
716 : */
717 0 : wantparent = cnp->cn_flags & (LOCKPARENT|WANTPARENT);
718 0 : rdonly = cnp->cn_flags & RDONLY;
719 0 : cnp->cn_flags &= ~ISSYMLINK;
720 : dp = dvp;
721 0 : vn_lock(dp, LK_EXCLUSIVE | LK_RETRY);
722 :
723 : /* dirloop: */
724 : /*
725 : * Search a new directory.
726 : *
727 : * The last component of the filename is left accessible via
728 : * cnp->cn_nameptr for callers that need the name. Callers needing
729 : * the name set the SAVENAME flag. When done, they assume
730 : * responsibility for freeing the pathname buffer.
731 : */
732 :
733 : #ifdef NAMEI_DIAGNOSTIC
734 : /* XXX: Figure out the length of the last component. */
735 : cp = cnp->cn_nameptr;
736 : while (*cp && (*cp != '/')) {
737 : cp++;
738 : }
739 : if (cnp->cn_namelen != cp - cnp->cn_nameptr)
740 : panic("relookup: bad len");
741 : if (*cp != 0)
742 : panic("relookup: not last component");
743 : printf("{%s}: ", cnp->cn_nameptr);
744 : #endif
745 :
746 : /*
747 : * Check for degenerate name (e.g. / or "")
748 : * which is a way of talking about a directory,
749 : * e.g. like "/." or ".".
750 : */
751 0 : if (cnp->cn_nameptr[0] == '\0')
752 0 : panic("relookup: null name");
753 :
754 0 : if (cnp->cn_flags & ISDOTDOT)
755 0 : panic ("relookup: lookup on dot-dot");
756 :
757 : /*
758 : * We now have a segment name to search for, and a directory to search.
759 : */
760 0 : if ((error = VOP_LOOKUP(dp, vpp, cnp)) != 0) {
761 : #ifdef DIAGNOSTIC
762 0 : if (*vpp != NULL)
763 0 : panic("leaf should be empty");
764 : #endif
765 0 : if (error != EJUSTRETURN)
766 : goto bad;
767 : /*
768 : * If creating and at end of pathname, then can consider
769 : * allowing file to be created.
770 : */
771 0 : if (rdonly || (dvp->v_mount->mnt_flag & MNT_RDONLY)) {
772 : error = EROFS;
773 0 : goto bad;
774 : }
775 : /* ASSERT(dvp == ndp->ni_startdir) */
776 0 : if (cnp->cn_flags & SAVESTART)
777 0 : vref(dvp);
778 : /*
779 : * We return with ni_vp NULL to indicate that the entry
780 : * doesn't currently exist, leaving a pointer to the
781 : * (possibly locked) directory inode in ndp->ni_dvp.
782 : */
783 0 : return (0);
784 : }
785 : dp = *vpp;
786 :
787 : #ifdef DIAGNOSTIC
788 : /*
789 : * Check for symbolic link
790 : */
791 0 : if (dp->v_type == VLNK && (cnp->cn_flags & FOLLOW))
792 0 : panic ("relookup: symlink found.");
793 : #endif
794 :
795 : /*
796 : * Check for read-only file systems.
797 : */
798 0 : if (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME) {
799 : /*
800 : * Disallow directory write attempts on read-only
801 : * file systems.
802 : */
803 0 : if (rdonly || (dp->v_mount->mnt_flag & MNT_RDONLY) ||
804 0 : (wantparent &&
805 0 : (dvp->v_mount->mnt_flag & MNT_RDONLY))) {
806 : error = EROFS;
807 : goto bad2;
808 : }
809 : }
810 : /* ASSERT(dvp == ndp->ni_startdir) */
811 0 : if (cnp->cn_flags & SAVESTART)
812 0 : vref(dvp);
813 0 : if (!wantparent)
814 0 : vrele(dvp);
815 0 : if ((cnp->cn_flags & LOCKLEAF) == 0)
816 0 : VOP_UNLOCK(dp);
817 0 : return (0);
818 :
819 : bad2:
820 0 : if ((cnp->cn_flags & LOCKPARENT) && (cnp->cn_flags & ISLASTCN))
821 0 : VOP_UNLOCK(dvp);
822 0 : vrele(dvp);
823 : bad:
824 0 : vput(dp);
825 0 : *vpp = NULL;
826 0 : return (error);
827 0 : }
|