vfs_lookup.c revision 42408
1139825Simp/* 2139740Sphk * Copyright (c) 1982, 1986, 1989, 1993 350974Swpaul * The Regents of the University of California. All rights reserved. 450974Swpaul * (c) UNIX System Laboratories, Inc. 550974Swpaul * All or some portions of this file are derived from material licensed 650974Swpaul * to the University of California by American Telephone and Telegraph 750974Swpaul * Co. or Unix System Laboratories, Inc. and are reproduced herein with 850974Swpaul * the permission of UNIX System Laboratories, Inc. 950974Swpaul * 1050974Swpaul * Redistribution and use in source and binary forms, with or without 1150974Swpaul * modification, are permitted provided that the following conditions 1250974Swpaul * are met: 1350974Swpaul * 1. Redistributions of source code must retain the above copyright 1450974Swpaul * notice, this list of conditions and the following disclaimer. 1550974Swpaul * 2. Redistributions in binary form must reproduce the above copyright 1650974Swpaul * notice, this list of conditions and the following disclaimer in the 1750974Swpaul * documentation and/or other materials provided with the distribution. 1850974Swpaul * 3. All advertising materials mentioning features or use of this software 1950974Swpaul * must display the following acknowledgement: 2050974Swpaul * This product includes software developed by the University of 2150974Swpaul * California, Berkeley and its contributors. 2250974Swpaul * 4. Neither the name of the University nor the names of its contributors 2350974Swpaul * may be used to endorse or promote products derived from this software 2450974Swpaul * without specific prior written permission. 2550974Swpaul * 2650974Swpaul * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 2750974Swpaul * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2850974Swpaul * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2950974Swpaul * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 3050974Swpaul * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 3150974Swpaul * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 3250974Swpaul * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 3350974Swpaul * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 34122678Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 35122678Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 36122678Sobrien * SUCH DAMAGE. 3750974Swpaul * 3850974Swpaul * @(#)vfs_lookup.c 8.4 (Berkeley) 2/16/94 3950974Swpaul * $Id: vfs_lookup.c,v 1.29 1999/01/05 18:49:52 eivind Exp $ 4050974Swpaul */ 4164963Swpaul 4264963Swpaul#include "opt_ktrace.h" 4364963Swpaul 4450974Swpaul#include <sys/param.h> 4550974Swpaul#include <sys/systm.h> 4650974Swpaul#include <sys/namei.h> 4750974Swpaul#include <sys/vnode.h> 4850974Swpaul#include <sys/mount.h> 4950974Swpaul#include <sys/filedesc.h> 5050974Swpaul#include <sys/proc.h> 5150974Swpaul 5250974Swpaul#ifdef KTRACE 5350974Swpaul#include <sys/ktrace.h> 5450974Swpaul#endif 5550974Swpaul 5650974Swpaul#include <vm/vm_zone.h> 5750974Swpaul 5850974Swpaul/* 5950974Swpaul * Convert a pathname into a pointer to a locked inode. 6050974Swpaul * 6150974Swpaul * The FOLLOW flag is set when symbolic links are to be followed 6250974Swpaul * when they occur at the end of the name translation process. 6350974Swpaul * Symbolic links are always followed for all other pathname 6450974Swpaul * components other than the last. 6550974Swpaul * 6650974Swpaul * The segflg defines whether the name is to be copied from user 67129876Sphk * space or kernel space. 6850974Swpaul * 6987059Sluigi * Overall outline of namei: 7050974Swpaul * 7150974Swpaul * copy in name 7250974Swpaul * get starting directory 7350974Swpaul * while (!done && !error) { 7450974Swpaul * call lookup to search path. 7550974Swpaul * if symbolic link, massage name in buffer and continue 7687390Sjhay * } 7787390Sjhay */ 7850974Swpaulint 7950974Swpaulnamei(ndp) 8050974Swpaul register struct nameidata *ndp; 8150974Swpaul{ 8250974Swpaul register struct filedesc *fdp; /* pointer to file descriptor state */ 8350974Swpaul register char *cp; /* pointer into pathname argument */ 8450974Swpaul register struct vnode *dp; /* the directory we are searching */ 8550974Swpaul struct iovec aiov; /* uio for reading symbolic links */ 8650974Swpaul struct uio auio; 8750974Swpaul int error, linklen; 8850974Swpaul struct componentname *cnp = &ndp->ni_cnd; 89119288Simp struct proc *p = cnp->cn_proc; 90119288Simp 9150974Swpaul ndp->ni_cnd.cn_cred = ndp->ni_cnd.cn_proc->p_ucred; 9250974Swpaul KASSERT(cnp->cn_cred && cnp->cn_proc, ("namei: bad cred/proc")); 9350974Swpaul KASSERT((cnp->cn_nameiop & (~OPMASK)) == 0, 9450974Swpaul ("namei: nameiop contaminated with flags")); 9550974Swpaul KASSERT((cnp->cn_flags & OPMASK) == 0, 96113506Smdodd ("namei: flags contaminated with nameiops")); 97113506Smdodd fdp = cnp->cn_proc->p_fd; 9859758Speter 9959758Speter /* 10051089Speter * Get a buffer for the name to be translated, and copy the 10150974Swpaul * name into the buffer. 10250974Swpaul */ 10350974Swpaul if ((cnp->cn_flags & HASBUF) == 0) 10450974Swpaul cnp->cn_pnbuf = zalloc(namei_zone); 10550974Swpaul if (ndp->ni_segflg == UIO_SYSSPACE) 10650974Swpaul error = copystr(ndp->ni_dirp, cnp->cn_pnbuf, 10750974Swpaul MAXPATHLEN, (size_t *)&ndp->ni_pathlen); 10850974Swpaul else 109119712Sphk error = copyinstr(ndp->ni_dirp, cnp->cn_pnbuf, 11050974Swpaul MAXPATHLEN, (size_t *)&ndp->ni_pathlen); 11150974Swpaul 11250974Swpaul /* 113139801Sphk * Don't allow empty pathnames. 114139801Sphk */ 115139801Sphk if (!error && *cnp->cn_pnbuf == '\0') 116139801Sphk error = ENOENT; 117139801Sphk 118139801Sphk if (error) { 119139801Sphk zfree(namei_zone, cnp->cn_pnbuf); 120139801Sphk ndp->ni_vp = NULL; 121139801Sphk return (error); 122139801Sphk } 123139801Sphk ndp->ni_loopcnt = 0; 124139801Sphk#ifdef KTRACE 12550974Swpaul if (KTRPOINT(cnp->cn_proc, KTR_NAMEI)) 12650974Swpaul ktrnamei(cnp->cn_proc->p_tracep, cnp->cn_pnbuf); 12750974Swpaul#endif 12850974Swpaul 12950974Swpaul /* 13051030Swpaul * Get starting point for the translation. 13151030Swpaul */ 13250974Swpaul ndp->ni_rootdir = fdp->fd_rdir; 13350974Swpaul 13450974Swpaul dp = fdp->fd_cdir; 13550974Swpaul VREF(dp); 13650974Swpaul for (;;) { 13750974Swpaul /* 13850974Swpaul * Check if root directory should replace current directory. 13950974Swpaul * Done at start of translation and after symbolic link. 14050974Swpaul */ 14150974Swpaul cnp->cn_nameptr = cnp->cn_pnbuf; 14250974Swpaul if (*(cnp->cn_nameptr) == '/') { 14350974Swpaul vrele(dp); 14450974Swpaul while (*(cnp->cn_nameptr) == '/') { 14550974Swpaul cnp->cn_nameptr++; 14650974Swpaul ndp->ni_pathlen--; 14750974Swpaul } 14881713Swpaul dp = ndp->ni_rootdir; 149139740Sphk VREF(dp); 15081713Swpaul } 15181713Swpaul ndp->ni_startdir = dp; 15281713Swpaul error = lookup(ndp); 15381713Swpaul if (error) { 15481713Swpaul zfree(namei_zone, cnp->cn_pnbuf); 15581713Swpaul return (error); 15681713Swpaul } 15781713Swpaul /* 158139740Sphk * Check for symbolic link 15981713Swpaul */ 16081713Swpaul if ((cnp->cn_flags & ISSYMLINK) == 0) { 16181713Swpaul if ((cnp->cn_flags & (SAVENAME | SAVESTART)) == 0) 16281713Swpaul zfree(namei_zone, cnp->cn_pnbuf); 16381713Swpaul else 16481713Swpaul cnp->cn_flags |= HASBUF; 16581713Swpaul 16681713Swpaul if (ndp->ni_vp && ndp->ni_vp->v_type == VREG && 167139740Sphk (cnp->cn_nameiop != DELETE) && 16881713Swpaul ((cnp->cn_flags & (NOOBJ|LOCKLEAF)) == 16981713Swpaul LOCKLEAF)) 17081713Swpaul vfs_object_create(ndp->ni_vp, 17181713Swpaul ndp->ni_cnd.cn_proc, 17281713Swpaul ndp->ni_cnd.cn_cred); 17381713Swpaul 17481713Swpaul return (0); 17562672Swpaul } 17662672Swpaul if ((cnp->cn_flags & LOCKPARENT) && ndp->ni_pathlen == 1) 17762672Swpaul VOP_UNLOCK(ndp->ni_dvp, 0, p); 17862672Swpaul if (ndp->ni_loopcnt++ >= MAXSYMLINKS) { 179139740Sphk error = ELOOP; 180139740Sphk break; 18162672Swpaul } 18262672Swpaul if (ndp->ni_pathlen > 1) 18362672Swpaul cp = zalloc(namei_zone); 18462672Swpaul else 18562672Swpaul cp = cnp->cn_pnbuf; 18662672Swpaul aiov.iov_base = cp; 18762672Swpaul aiov.iov_len = MAXPATHLEN; 18862672Swpaul auio.uio_iov = &aiov; 18962672Swpaul auio.uio_iovcnt = 1; 190102334Salfred auio.uio_offset = 0; 191139740Sphk auio.uio_rw = UIO_READ; 19250974Swpaul auio.uio_segflg = UIO_SYSSPACE; 19350974Swpaul auio.uio_procp = (struct proc *)0; 19450974Swpaul auio.uio_resid = MAXPATHLEN; 19550974Swpaul error = VOP_READLINK(ndp->ni_vp, &auio, cnp->cn_cred); 19650974Swpaul if (error) { 19750974Swpaul if (ndp->ni_pathlen > 1) 19850974Swpaul zfree(namei_zone, cp); 199102334Salfred break; 200139740Sphk } 20150974Swpaul linklen = MAXPATHLEN - auio.uio_resid; 202139708Sphk if (linklen + ndp->ni_pathlen >= MAXPATHLEN) { 20350974Swpaul if (ndp->ni_pathlen > 1) 20450974Swpaul zfree(namei_zone, cp); 20550974Swpaul error = ENAMETOOLONG; 20650974Swpaul break; 20750974Swpaul } 20850974Swpaul if (ndp->ni_pathlen > 1) { 20950974Swpaul bcopy(ndp->ni_next, cp + linklen, ndp->ni_pathlen); 21050974Swpaul zfree(namei_zone, cnp->cn_pnbuf); 21150974Swpaul cnp->cn_pnbuf = cp; 21250974Swpaul } else 21350974Swpaul cnp->cn_pnbuf[linklen] = '\0'; 21450974Swpaul ndp->ni_pathlen += linklen; 21550974Swpaul vput(ndp->ni_vp); 21650974Swpaul dp = ndp->ni_dvp; 21750974Swpaul } 21850974Swpaul zfree(namei_zone, cnp->cn_pnbuf); 21950974Swpaul vrele(ndp->ni_dvp); 22050974Swpaul vput(ndp->ni_vp); 22150974Swpaul ndp->ni_vp = NULL; 22250974Swpaul return (error); 22350974Swpaul} 22450974Swpaul 22550974Swpaul/* 226102334Salfred * Search a pathname. 227139740Sphk * This is a very central and rather complicated routine. 22850974Swpaul * 229139708Sphk * The pathname is pointed to by ni_ptr and is of length ni_pathlen. 23050974Swpaul * The starting directory is taken from ni_startdir. The pathname is 23150974Swpaul * descended until done, or a symbolic link is encountered. The variable 23250974Swpaul * ni_more is clear if the path is completed; it is set to one if a 23350974Swpaul * symbolic link needing interpretation is encountered. 23450974Swpaul * 23550974Swpaul * The flag argument is LOOKUP, CREATE, RENAME, or DELETE depending on 23650974Swpaul * whether the name is to be looked up, created, renamed, or deleted. 23750974Swpaul * When CREATE, RENAME, or DELETE is specified, information usable in 23850974Swpaul * creating, renaming, or deleting a directory entry may be calculated. 23950974Swpaul * If flag has LOCKPARENT or'ed into it, the parent directory is returned 24050974Swpaul * locked. If flag has WANTPARENT or'ed into it, the parent directory is 24150974Swpaul * returned unlocked. Otherwise the parent directory is not returned. If 24250974Swpaul * the target of the pathname exists and LOCKLEAF is or'ed into the flag 24350974Swpaul * the target is returned locked, otherwise it is returned unlocked. 24450974Swpaul * When creating or renaming and LOCKPARENT is specified, the target may not 24550974Swpaul * be ".". When deleting and LOCKPARENT is specified, the target may be ".". 24650974Swpaul * 24750974Swpaul * Overall outline of lookup: 24850974Swpaul * 24950974Swpaul * dirloop: 25050974Swpaul * identify next component of name at ndp->ni_ptr 25150974Swpaul * handle degenerate case where name is null string 25250974Swpaul * if .. and crossing mount points and on mounted filesys, find parent 253102334Salfred * call VOP_LOOKUP routine for next component name 254139740Sphk * directory vnode returned in ni_dvp, unlocked unless LOCKPARENT set 25550974Swpaul * component vnode returned in ni_vp (if it exists), locked. 256139708Sphk * if result vnode is mounted on and crossing mount points, 25750974Swpaul * find mounted on vnode 25850974Swpaul * if more components of name, do next level at dirloop 25950974Swpaul * return the answer in ni_vp, locked if LOCKLEAF set 26050974Swpaul * if LOCKPARENT set, return locked parent in ni_dvp 26150974Swpaul * if WANTPARENT set, return unlocked parent in ni_dvp 26250974Swpaul */ 26350974Swpaulint 26462672Swpaullookup(ndp) 26562672Swpaul register struct nameidata *ndp; 26650974Swpaul{ 26750974Swpaul register char *cp; /* pointer into pathname argument */ 26850974Swpaul register struct vnode *dp = 0; /* the directory we are searching */ 26950974Swpaul struct vnode *tdp; /* saved dp */ 27050974Swpaul struct mount *mp; /* mount table entry */ 27150974Swpaul int docache; /* == 0 do not cache last component */ 27250974Swpaul int wantparent; /* 1 => wantparent or lockparent flag */ 27350974Swpaul int rdonly; /* lookup read-only flag bit */ 27450974Swpaul int trailing_slash; 27550974Swpaul int error = 0; 27650974Swpaul struct componentname *cnp = &ndp->ni_cnd; 27750974Swpaul struct proc *p = cnp->cn_proc; 27850974Swpaul 27950974Swpaul /* 28050974Swpaul * Setup: break out flag bits into variables. 28150974Swpaul */ 28250974Swpaul wantparent = cnp->cn_flags & (LOCKPARENT | WANTPARENT); 28350974Swpaul docache = (cnp->cn_flags & NOCACHE) ^ NOCACHE; 28450974Swpaul if (cnp->cn_nameiop == DELETE || 28550974Swpaul (wantparent && cnp->cn_nameiop != CREATE && 28650974Swpaul cnp->cn_nameiop != LOOKUP)) 28750974Swpaul docache = 0; 28850974Swpaul rdonly = cnp->cn_flags & RDONLY; 28950974Swpaul ndp->ni_dvp = NULL; 29050974Swpaul cnp->cn_flags &= ~ISSYMLINK; 29150974Swpaul dp = ndp->ni_startdir; 29250974Swpaul ndp->ni_startdir = NULLVP; 29350974Swpaul vn_lock(dp, LK_EXCLUSIVE | LK_RETRY, p); 29450974Swpaul 29550974Swpauldirloop: 296102334Salfred /* 297139740Sphk * Search a new directory. 29850974Swpaul * 29950974Swpaul * The cn_hash value is for use by vfs_cache. 30050974Swpaul * The last component of the filename is left accessible via 30150974Swpaul * cnp->cn_nameptr for callers that need the name. Callers needing 30250974Swpaul * the name set the SAVENAME flag. When done, they assume 30350974Swpaul * responsibility for freeing the pathname buffer. 30450974Swpaul */ 30550974Swpaul cnp->cn_consume = 0; 30650974Swpaul cnp->cn_hash = 0; 30750974Swpaul for (cp = cnp->cn_nameptr; *cp != 0 && *cp != '/'; cp++) 30850974Swpaul cnp->cn_hash += (unsigned char)*cp; 30950974Swpaul cnp->cn_namelen = cp - cnp->cn_nameptr; 31050974Swpaul if (cnp->cn_namelen > NAME_MAX) { 31150974Swpaul error = ENAMETOOLONG; 312144243Sobrien goto bad; 313102334Salfred } 314139740Sphk#ifdef NAMEI_DIAGNOSTIC 31572197Swpaul { char c = *cp; 31672197Swpaul *cp = '\0'; 31772197Swpaul printf("{%s}: ", cnp->cn_nameptr); 31872197Swpaul *cp = c; } 31972197Swpaul#endif 32072197Swpaul ndp->ni_pathlen -= cnp->cn_namelen; 32172197Swpaul ndp->ni_next = cp; 32287994Sarchie 32372197Swpaul /* 32472197Swpaul * Replace multiple slashes by a single slash and trailing slashes 32572197Swpaul * by a null. This must be done before VOP_LOOKUP() because some 32672197Swpaul * fs's don't know about trailing slashes. Remember if there were 32772197Swpaul * trailing slashes to handle symlinks, existing non-directories 32872197Swpaul * and non-existing files that won't be directories specially later. 32972197Swpaul */ 33072197Swpaul trailing_slash = 0; 33172197Swpaul while (*cp == '/' && (cp[1] == '/' || cp[1] == '\0')) { 33272197Swpaul cp++; 33372197Swpaul ndp->ni_pathlen--; 33472197Swpaul if (*cp == '\0') { 33572197Swpaul trailing_slash = 1; 33672197Swpaul *ndp->ni_next = '\0'; /* XXX for direnter() ... */ 33787994Sarchie } 33887994Sarchie } 33972197Swpaul ndp->ni_next = cp; 34072197Swpaul 34172197Swpaul cnp->cn_flags |= MAKEENTRY; 34272197Swpaul if (*cp == '\0' && docache == 0) 34387994Sarchie cnp->cn_flags &= ~MAKEENTRY; 34472197Swpaul if (cnp->cn_namelen == 2 && 34572197Swpaul cnp->cn_nameptr[1] == '.' && cnp->cn_nameptr[0] == '.') 34687994Sarchie cnp->cn_flags |= ISDOTDOT; 34772197Swpaul else 34872197Swpaul cnp->cn_flags &= ~ISDOTDOT; 349102334Salfred if (*ndp->ni_next == 0) 350139740Sphk cnp->cn_flags |= ISLASTCN; 35172197Swpaul else 35272197Swpaul cnp->cn_flags &= ~ISLASTCN; 35372197Swpaul 35472197Swpaul 35572197Swpaul /* 35672197Swpaul * Check for degenerate name (e.g. / or "") 35772197Swpaul * which is a way of talking about a directory, 35872197Swpaul * e.g. like "/." or ".". 35972197Swpaul */ 36072197Swpaul if (cnp->cn_nameptr[0] == '\0') { 36172197Swpaul if (dp->v_type != VDIR) { 36272197Swpaul error = ENOTDIR; 36372197Swpaul goto bad; 364144243Sobrien } 36572197Swpaul if (cnp->cn_nameiop != LOOKUP) { 366144243Sobrien error = EISDIR; 367144243Sobrien goto bad; 368144243Sobrien } 36972197Swpaul if (wantparent) { 37072197Swpaul ndp->ni_dvp = dp; 37172197Swpaul VREF(dp); 37272197Swpaul } 37372197Swpaul ndp->ni_vp = dp; 37472197Swpaul if (!(cnp->cn_flags & (LOCKPARENT | LOCKLEAF))) 37572197Swpaul VOP_UNLOCK(dp, 0, p); 37672197Swpaul if (cnp->cn_flags & SAVESTART) 37772197Swpaul panic("lookup: SAVESTART"); 37889296Swpaul return (0); 379102334Salfred } 380139740Sphk 38189296Swpaul /* 38289296Swpaul * Handle "..": two special cases. 38389296Swpaul * 1. If at root directory (e.g. after chroot) 38489296Swpaul * or at absolute root directory 38589296Swpaul * then ignore it so can't get out. 38689296Swpaul * 2. If this vnode is the root of a mounted 38789296Swpaul * filesystem, then replace it with the 38889296Swpaul * vnode which was mounted on so we take the 38989296Swpaul * .. in the other file system. 39089296Swpaul */ 39189296Swpaul if (cnp->cn_flags & ISDOTDOT) { 39289296Swpaul for (;;) { 39389296Swpaul if (dp == ndp->ni_rootdir || dp == rootvnode) { 39489296Swpaul ndp->ni_dvp = dp; 39589296Swpaul ndp->ni_vp = dp; 39689296Swpaul VREF(dp); 39789296Swpaul goto nextname; 39889296Swpaul } 39989296Swpaul if ((dp->v_flag & VROOT) == 0 || 40089296Swpaul (cnp->cn_flags & NOCROSSMOUNT)) 40189296Swpaul break; 40289296Swpaul tdp = dp; 40372197Swpaul dp = dp->v_mount->mnt_vnodecovered; 40472197Swpaul vput(tdp); 405109060Smbr VREF(dp); 406109060Smbr vn_lock(dp, LK_EXCLUSIVE | LK_RETRY, p); 407109060Smbr } 408139740Sphk } 409139740Sphk 410109060Smbr /* 411139708Sphk * We now have a segment name to search for, and a directory to search. 412109060Smbr */ 413109060Smbrunionlookup: 414109060Smbr ndp->ni_dvp = dp; 415109060Smbr ndp->ni_vp = NULL; 416109060Smbr ASSERT_VOP_LOCKED(dp, "lookup"); 417109060Smbr if (error = VOP_LOOKUP(dp, &ndp->ni_vp, cnp)) { 418109060Smbr KASSERT(ndp->ni_vp == NULL, ("leaf should be empty")); 419109060Smbr#ifdef NAMEI_DIAGNOSTIC 420109060Smbr printf("not found\n"); 421109060Smbr#endif 422109060Smbr if ((error == ENOENT) && 423109060Smbr (dp->v_flag & VROOT) && 424109060Smbr (dp->v_mount->mnt_flag & MNT_UNION)) { 425109060Smbr tdp = dp; 426139740Sphk dp = dp->v_mount->mnt_vnodecovered; 427139740Sphk vput(tdp); 428109060Smbr VREF(dp); 429109060Smbr vn_lock(dp, LK_EXCLUSIVE | LK_RETRY, p); 430109060Smbr goto unionlookup; 431109060Smbr } 432109060Smbr 433109060Smbr if (error != EJUSTRETURN) 434109060Smbr goto bad; 435109060Smbr /* 436109060Smbr * If creating and at end of pathname, then can consider 437109060Smbr * allowing file to be created. 438109060Smbr */ 439109060Smbr if (rdonly) { 440109060Smbr error = EROFS; 441109060Smbr goto bad; 442109060Smbr } 443109060Smbr if (*cp == '\0' && trailing_slash && 444109060Smbr !(cnp->cn_flags & WILLBEDIR)) { 445109060Smbr error = ENOENT; 446109060Smbr goto bad; 447109060Smbr } 448109060Smbr /* 449139740Sphk * We return with ni_vp NULL to indicate that the entry 450139740Sphk * doesn't currently exist, leaving a pointer to the 451109060Smbr * (possibly locked) directory inode in ndp->ni_dvp. 452109060Smbr */ 453109060Smbr if (cnp->cn_flags & SAVESTART) { 454109060Smbr ndp->ni_startdir = ndp->ni_dvp; 455109060Smbr VREF(ndp->ni_startdir); 456109060Smbr } 457109060Smbr return (0); 458109060Smbr } 459109060Smbr#ifdef NAMEI_DIAGNOSTIC 460109060Smbr printf("found\n"); 461109060Smbr#endif 462109060Smbr 463109060Smbr ASSERT_VOP_LOCKED(ndp->ni_vp, "lookup"); 464109060Smbr 465109060Smbr /* 466109060Smbr * Take into account any additional components consumed by 467109060Smbr * the underlying filesystem. 468109060Smbr */ 469109060Smbr if (cnp->cn_consume > 0) { 470109060Smbr cnp->cn_nameptr += cnp->cn_consume; 471109060Smbr ndp->ni_next += cnp->cn_consume; 472109060Smbr ndp->ni_pathlen -= cnp->cn_consume; 473109060Smbr cnp->cn_consume = 0; 474109060Smbr } 475109060Smbr 476109060Smbr dp = ndp->ni_vp; 477109060Smbr 478109060Smbr /* 479109060Smbr * Check to see if the vnode has been mounted on; 480109060Smbr * if so find the root of the mounted file system. 481109060Smbr */ 482109060Smbr while (dp->v_type == VDIR && (mp = dp->v_mountedhere) && 483109060Smbr (cnp->cn_flags & NOCROSSMOUNT) == 0) { 484109060Smbr if (vfs_busy(mp, 0, 0, p)) 485109060Smbr continue; 486109060Smbr error = VFS_ROOT(mp, &tdp); 487109060Smbr vfs_unbusy(mp, p); 488109060Smbr if (error) 489109060Smbr goto bad2; 490109060Smbr vput(dp); 491109060Smbr ndp->ni_vp = dp = tdp; 492109060Smbr } 493109060Smbr 494109060Smbr /* 495109060Smbr * Check for symbolic link 496109060Smbr */ 497109060Smbr if ((dp->v_type == VLNK) && 498109060Smbr ((cnp->cn_flags & FOLLOW) || trailing_slash || 499109060Smbr *ndp->ni_next == '/')) { 500109060Smbr cnp->cn_flags |= ISSYMLINK; 501109060Smbr if (dp->v_mount->mnt_flag & MNT_NOSYMFOLLOW) { 502109060Smbr error = EACCES; 503109060Smbr goto bad2; 504109060Smbr } 505109060Smbr return (0); 506109060Smbr } 507109060Smbr 508109060Smbr /* 509109060Smbr * Check for bogus trailing slashes. 510109060Smbr */ 511109060Smbr if (trailing_slash && dp->v_type != VDIR) { 512109060Smbr error = ENOTDIR; 513109060Smbr goto bad2; 514109060Smbr } 515109060Smbr 516109060Smbrnextname: 517109060Smbr /* 518109060Smbr * Not a symbolic link. If more pathname, 519109060Smbr * continue at next component, else return. 520109060Smbr */ 521109060Smbr if (*ndp->ni_next == '/') { 522109060Smbr cnp->cn_nameptr = ndp->ni_next; 523109060Smbr while (*cnp->cn_nameptr == '/') { 524109060Smbr cnp->cn_nameptr++; 525109060Smbr ndp->ni_pathlen--; 526109060Smbr } 527109060Smbr if (ndp->ni_dvp != ndp->ni_vp) { 528109060Smbr ASSERT_VOP_UNLOCKED(ndp->ni_dvp, "lookup"); 529109060Smbr } 530109060Smbr vrele(ndp->ni_dvp); 531109060Smbr goto dirloop; 532109060Smbr } 533109060Smbr /* 534109060Smbr * Disallow directory write attempts on read-only file systems. 535109060Smbr */ 536109060Smbr if (rdonly && 537109060Smbr (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME)) { 538139740Sphk error = EROFS; 539139740Sphk goto bad2; 540109060Smbr } 541109060Smbr if (cnp->cn_flags & SAVESTART) { 542109060Smbr ndp->ni_startdir = ndp->ni_dvp; 543109060Smbr VREF(ndp->ni_startdir); 544109060Smbr } 545109060Smbr if (!wantparent) 546109060Smbr vrele(ndp->ni_dvp); 547109060Smbr 548109060Smbr if ((cnp->cn_flags & LOCKLEAF) == 0) 549109060Smbr VOP_UNLOCK(dp, 0, p); 550109060Smbr return (0); 551109060Smbr 552109060Smbrbad2: 553109060Smbr if ((cnp->cn_flags & LOCKPARENT) && *ndp->ni_next == '\0') 554109060Smbr VOP_UNLOCK(ndp->ni_dvp, 0, p); 555109060Smbr vrele(ndp->ni_dvp); 556109060Smbrbad: 557109060Smbr vput(dp); 558109060Smbr ndp->ni_vp = NULL; 559109060Smbr return (error); 560109060Smbr} 561109060Smbr 562109060Smbr/* 563109060Smbr * relookup - lookup a path name component 564109060Smbr * Used by lookup to re-aquire things. 565109060Smbr */ 566109060Smbrint 567109060Smbrrelookup(dvp, vpp, cnp) 568109060Smbr struct vnode *dvp, **vpp; 569109060Smbr struct componentname *cnp; 570109060Smbr{ 571109060Smbr struct proc *p = cnp->cn_proc; 572109060Smbr struct vnode *dp = 0; /* the directory we are searching */ 573109060Smbr int docache; /* == 0 do not cache last component */ 574109060Smbr int wantparent; /* 1 => wantparent or lockparent flag */ 575109060Smbr int rdonly; /* lookup read-only flag bit */ 576109060Smbr int error = 0; 577109060Smbr#ifdef NAMEI_DIAGNOSTIC 578109060Smbr int newhash; /* DEBUG: check name hash */ 579109060Smbr char *cp; /* DEBUG: check name ptr/len */ 580109060Smbr#endif 581109060Smbr 582102334Salfred /* 583139740Sphk * Setup: break out flag bits into variables. 58450974Swpaul */ 58550974Swpaul wantparent = cnp->cn_flags & (LOCKPARENT|WANTPARENT); 586109060Smbr docache = (cnp->cn_flags & NOCACHE) ^ NOCACHE; 58750974Swpaul if (cnp->cn_nameiop == DELETE || 58850974Swpaul (wantparent && cnp->cn_nameiop != CREATE)) 58950974Swpaul docache = 0; 59062672Swpaul rdonly = cnp->cn_flags & RDONLY; 59162672Swpaul cnp->cn_flags &= ~ISSYMLINK; 59262672Swpaul dp = dvp; 59362672Swpaul vn_lock(dp, LK_EXCLUSIVE | LK_RETRY, p); 59462672Swpaul 59562672Swpaul/* dirloop: */ 59662672Swpaul /* 59762672Swpaul * Search a new directory. 59862672Swpaul * 59962672Swpaul * The cn_hash value is for use by vfs_cache. 60062672Swpaul * The last component of the filename is left accessible via 60162672Swpaul * cnp->cn_nameptr for callers that need the name. Callers needing 60262672Swpaul * the name set the SAVENAME flag. When done, they assume 60362672Swpaul * responsibility for freeing the pathname buffer. 60462672Swpaul */ 605109060Smbr#ifdef NAMEI_DIAGNOSTIC 60662672Swpaul for (newhash = 0, cp = cnp->cn_nameptr; *cp != 0 && *cp != '/'; cp++) 60762672Swpaul newhash += (unsigned char)*cp; 608109976Smbr if (newhash != cnp->cn_hash) 609109976Smbr panic("relookup: bad hash"); 610109976Smbr if (cnp->cn_namelen != cp - cnp->cn_nameptr) 611109976Smbr panic ("relookup: bad len"); 612109976Smbr if (*cp != 0) 61389296Swpaul panic("relookup: not last component"); 614109976Smbr printf("{%s}: ", cnp->cn_nameptr); 615109976Smbr#endif 61650974Swpaul 617109976Smbr /* 618109976Smbr * Check for degenerate name (e.g. / or "") 61950974Swpaul * which is a way of talking about a directory, 620109976Smbr * e.g. like "/." or ".". 621109976Smbr */ 622109976Smbr if (cnp->cn_nameptr[0] == '\0') { 62350974Swpaul if (cnp->cn_nameiop != LOOKUP || wantparent) { 624109976Smbr error = EISDIR; 625109976Smbr goto bad; 626109976Smbr } 627109976Smbr if (dp->v_type != VDIR) { 628109976Smbr error = ENOTDIR; 629109976Smbr goto bad; 630109976Smbr } 631109976Smbr if (!(cnp->cn_flags & LOCKLEAF)) 632109976Smbr VOP_UNLOCK(dp, 0, p); 633109976Smbr *vpp = dp; 634109976Smbr if (cnp->cn_flags & SAVESTART) 635109976Smbr panic("lookup: SAVESTART"); 636109976Smbr return (0); 637109976Smbr } 638109976Smbr 639109976Smbr if (cnp->cn_flags & ISDOTDOT) 640109976Smbr panic ("relookup: lookup on dot-dot"); 641109976Smbr 642109976Smbr /* 643109976Smbr * We now have a segment name to search for, and a directory to search. 644109976Smbr */ 645109976Smbr if (error = VOP_LOOKUP(dp, vpp, cnp)) { 646109976Smbr KASSERT(*vpp == NULL, ("leaf should be empty")); 647109976Smbr if (error != EJUSTRETURN) 648109976Smbr goto bad; 649109976Smbr /* 65050974Swpaul * If creating and at end of pathname, then can consider 65150974Swpaul * allowing file to be created. 652102334Salfred */ 653139740Sphk if (rdonly) { 65450974Swpaul error = EROFS; 65550974Swpaul goto bad; 656109060Smbr } 65750974Swpaul /* ASSERT(dvp == ndp->ni_startdir) */ 65850974Swpaul if (cnp->cn_flags & SAVESTART) 65950974Swpaul VREF(dvp); 66062672Swpaul /* 66162672Swpaul * We return with ni_vp NULL to indicate that the entry 66262672Swpaul * doesn't currently exist, leaving a pointer to the 66362672Swpaul * (possibly locked) directory inode in ndp->ni_dvp. 66462672Swpaul */ 66562672Swpaul return (0); 66662672Swpaul } 667109976Smbr dp = *vpp; 668109976Smbr 669109976Smbr /* 670109976Smbr * Check for symbolic link 671109976Smbr */ 672109976Smbr KASSERT(dp->v_type != VLNK || !(cnp->cn_flags & FOLLOW), 673109976Smbr ("relookup: symlink found.\n")); 674109976Smbr 67550974Swpaul /* 676109976Smbr * Disallow directory write attempts on read-only file systems. 677109976Smbr */ 67850974Swpaul if (rdonly && 679109976Smbr (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME)) { 680109976Smbr error = EROFS; 681109976Smbr goto bad2; 68250974Swpaul } 683109976Smbr /* ASSERT(dvp == ndp->ni_startdir) */ 684109976Smbr if (cnp->cn_flags & SAVESTART) 685109976Smbr VREF(dvp); 686109976Smbr 68750974Swpaul if (!wantparent) 688109976Smbr vrele(dvp); 689109976Smbr 690109976Smbr if (dp->v_type == VREG && 691109976Smbr ((cnp->cn_flags & (NOOBJ|LOCKLEAF)) == LOCKLEAF)) 692109976Smbr vfs_object_create(dp, cnp->cn_proc, cnp->cn_cred); 693109976Smbr 694109976Smbr if ((cnp->cn_flags & LOCKLEAF) == 0) 695109976Smbr VOP_UNLOCK(dp, 0, p); 696109976Smbr return (0); 697109976Smbr 698109976Smbrbad2: 69950974Swpaul if ((cnp->cn_flags & LOCKPARENT) && (cnp->cn_flags & ISLASTCN)) 70050974Swpaul VOP_UNLOCK(dvp, 0, p); 70150974Swpaul vrele(dvp); 702102334Salfredbad: 703139717Sphk vput(dp); 70450974Swpaul *vpp = NULL; 70550974Swpaul return (error); 70650974Swpaul} 70750974Swpaul