* Copyright (c) 1982, 1986, 1989 Regents of the University of California.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* @(#)vfs_lookup.c 7.32 (Berkeley) 5/21/91
* PATCHES MAGIC LEVEL PATCH THAT GOT US HERE
* -------------------- ----- ----------------------
* CURRENT PATCH LEVEL: 1 00006
* -------------------- ----- ----------------------
* 17 Aug 92 Christoph Robitschko Fixed parent of chroot panic
* Convert a pathname into a pointer to a locked inode.
* The FOLLOW flag is set when symbolic links are to be followed
* when they occur at the end of the name translation process.
* Symbolic links are always followed for all other pathname
* components other than the last.
* The segflg defines whether the name is to be copied from user
* Overall outline of namei:
* while (!done && !error) {
* call lookup to search path.
* if symbolic link, massage name in buffer and continue
register struct nameidata
*ndp
;
register struct filedesc
*fdp
; /* pointer to file descriptor state */
register char *cp
; /* pointer into pathname argument */
register struct vnode
*dp
; /* the directory we are searching */
struct iovec aiov
; /* uio for reading symbolic links */
ndp
->ni_cred
= p
->p_ucred
;
* Get a buffer for the name to be translated, and copy the
if ((ndp
->ni_nameiop
& HASBUF
) == 0)
MALLOC(ndp
->ni_pnbuf
, caddr_t
, MAXPATHLEN
, M_NAMEI
, M_WAITOK
);
if (ndp
->ni_segflg
== UIO_SYSSPACE
)
error
= copystr(ndp
->ni_dirp
, ndp
->ni_pnbuf
,
MAXPATHLEN
, &ndp
->ni_pathlen
);
error
= copyinstr(ndp
->ni_dirp
, ndp
->ni_pnbuf
,
MAXPATHLEN
, &ndp
->ni_pathlen
);
free(ndp
->ni_pnbuf
, M_NAMEI
);
if (KTRPOINT(p
, KTR_NAMEI
))
ktrnamei(p
->p_tracep
, ndp
->ni_pnbuf
);
* Get starting point for the translation.
if ((ndp
->ni_rootdir
= fdp
->fd_rdir
) == NULL
)
ndp
->ni_rootdir
= rootdir
;
* Check if root directory should replace current directory.
* Done at start of translation and after symbolic link.
ndp
->ni_ptr
= ndp
->ni_pnbuf
;
if (*ndp
->ni_ptr
== '/') {
while (*ndp
->ni_ptr
== '/') {
if (error
= lookup(ndp
, p
)) {
FREE(ndp
->ni_pnbuf
, M_NAMEI
);
* Check for symbolic link
if ((ndp
->ni_nameiop
& (SAVENAME
| SAVESTART
)) == 0)
FREE(ndp
->ni_pnbuf
, M_NAMEI
);
ndp
->ni_nameiop
|= HASBUF
;
if ((ndp
->ni_nameiop
& LOCKPARENT
) && ndp
->ni_pathlen
== 1)
if (ndp
->ni_loopcnt
++ >= MAXSYMLINKS
) {
MALLOC(cp
, char *, MAXPATHLEN
, M_NAMEI
, M_WAITOK
);
aiov
.iov_len
= MAXPATHLEN
;
auio
.uio_segflg
= UIO_SYSSPACE
;
auio
.uio_procp
= (struct proc
*)0;
auio
.uio_resid
= MAXPATHLEN
;
if (error
= VOP_READLINK(ndp
->ni_vp
, &auio
, p
->p_ucred
)) {
linklen
= MAXPATHLEN
- auio
.uio_resid
;
if (linklen
+ ndp
->ni_pathlen
>= MAXPATHLEN
) {
if (ndp
->ni_pathlen
> 1) {
bcopy(ndp
->ni_next
, cp
+ linklen
, ndp
->ni_pathlen
);
FREE(ndp
->ni_pnbuf
, M_NAMEI
);
ndp
->ni_pnbuf
[linklen
] = '\0';
ndp
->ni_pathlen
+= linklen
;
FREE(ndp
->ni_pnbuf
, M_NAMEI
);
* This is a very central and rather complicated routine.
* The pathname is pointed to by ni_ptr and is of length ni_pathlen.
* The starting directory is taken from ni_startdir. The pathname is
* descended until done, or a symbolic link is encountered. The variable
* ni_more is clear if the path is completed; it is set to one if a
* symbolic link needing interpretation is encountered.
* The flag argument is LOOKUP, CREATE, RENAME, or DELETE depending on
* whether the name is to be looked up, created, renamed, or deleted.
* When CREATE, RENAME, or DELETE is specified, information usable in
* creating, renaming, or deleting a directory entry may be calculated.
* If flag has LOCKPARENT or'ed into it, the parent directory is returned
* locked. If flag has WANTPARENT or'ed into it, the parent directory is
* returned unlocked. Otherwise the parent directory is not returned. If
* the target of the pathname exists and LOCKLEAF is or'ed into the flag
* the target is returned locked, otherwise it is returned unlocked.
* When creating or renaming and LOCKPARENT is specified, the target may not
* be ".". When deleting and LOCKPARENT is specified, the target may be ".".
* NOTE: (LOOKUP | LOCKPARENT) currently returns the parent vnode unlocked.
* Overall outline of lookup:
* identify next component of name at ndp->ni_ptr
* handle degenerate case where name is null string
* if .. and crossing mount points and on mounted filesys, find parent
* call VOP_LOOKUP routine for next component name
* directory vnode returned in ni_dvp, unlocked unless LOCKPARENT set
* component vnode returned in ni_vp (if it exists), locked.
* if result vnode is mounted on and crossing mount points,
* if more components of name, do next level at dirloop
* return the answer in ni_vp, locked if LOCKLEAF set
* if LOCKPARENT set, return locked parent in ni_dvp
* if WANTPARENT set, return unlocked parent in ni_dvp
register struct nameidata
*ndp
;
register char *cp
; /* pointer into pathname argument */
register struct vnode
*dp
= 0; /* the directory we are searching */
struct vnode
*tdp
; /* saved dp */
struct mount
*mp
; /* mount table entry */
int docache
; /* == 0 do not cache last component */
int flag
; /* LOOKUP, CREATE, RENAME or DELETE */
int wantparent
; /* 1 => wantparent or lockparent flag */
int rdonly
; /* mounted read-only flag bit(s) */
* Setup: break out flag bits into variables.
flag
= ndp
->ni_nameiop
& OPMASK
;
wantparent
= ndp
->ni_nameiop
& (LOCKPARENT
|WANTPARENT
);
docache
= (ndp
->ni_nameiop
& NOCACHE
) ^ NOCACHE
;
if (flag
== DELETE
|| (wantparent
&& flag
!= CREATE
))
if (ndp
->ni_nameiop
& REMOTE
)
ndp
->ni_startdir
= NULLVP
;
* Search a new directory.
* The ni_hash value is for use by vfs_cache.
* The last component of the filename is left accessible via
* ndp->ptr for callers that need the name. Callers needing
* the name set the SAVENAME flag. When done, they assume
* responsibility for freeing the pathname buffer.
for (cp
= ndp
->ni_ptr
; *cp
!= 0 && *cp
!= '/'; cp
++)
ndp
->ni_hash
+= (unsigned char)*cp
;
ndp
->ni_namelen
= cp
- ndp
->ni_ptr
;
if (ndp
->ni_namelen
>= NAME_MAX
) {
printf("{%s}: ", ndp
->ni_ptr
);
ndp
->ni_pathlen
-= ndp
->ni_namelen
;
if (*cp
== '\0' && docache
== 0)
ndp
->ni_isdotdot
= (ndp
->ni_namelen
== 2 &&
ndp
->ni_ptr
[1] == '.' && ndp
->ni_ptr
[0] == '.');
* Check for degenerate name (e.g. / or "")
* which is a way of talking about a directory,
if (ndp
->ni_ptr
[0] == '\0') {
if (flag
!= LOOKUP
|| wantparent
) {
if (dp
->v_type
!= VDIR
) {
if (!(ndp
->ni_nameiop
& LOCKLEAF
))
if (ndp
->ni_nameiop
& SAVESTART
)
panic("lookup: SAVESTART");
* Handle "..": two special cases.
* 1. If at root directory (e.g. after chroot)
* then ignore it so can't get out.
* 2. If this vnode is the root of a mounted
* filesystem, then replace it with the
* vnode which was mounted on so we take the
* .. in the other file system.
/* 17 Aug 92*/ if ((dp
== ndp
->ni_rootdir
) || (dp
== rootdir
)) {
if ((dp
->v_flag
& VROOT
) == 0 ||
(ndp
->ni_nameiop
& NOCROSSMOUNT
))
dp
= dp
->v_mount
->mnt_vnodecovered
;
* We now have a segment name to search for, and a directory to search.
if (error
= VOP_LOOKUP(dp
, ndp
, p
)) {
panic("leaf should be empty");
if (flag
== LOOKUP
|| flag
== DELETE
||
error
!= ENOENT
|| *cp
!= 0)
* If creating and at end of pathname, then can consider
* allowing file to be created.
if (ndp
->ni_dvp
->v_mount
->mnt_flag
& rdonly
) {
* We return with ni_vp NULL to indicate that the entry
* doesn't currently exist, leaving a pointer to the
* (possibly locked) directory inode in ndp->ni_dvp.
if (ndp
->ni_nameiop
& SAVESTART
) {
ndp
->ni_startdir
= ndp
->ni_dvp
;
* Check for symbolic link
if ((dp
->v_type
== VLNK
) &&
((ndp
->ni_nameiop
& FOLLOW
) || *ndp
->ni_next
== '/')) {
* Check to see if the vnode has been mounted on;
* if so find the root of the mounted file system.
while (dp
->v_type
== VDIR
&& (mp
= dp
->v_mountedhere
) &&
(ndp
->ni_nameiop
& NOCROSSMOUNT
) == 0) {
while(mp
->mnt_flag
& MNT_MLOCK
) {
mp
->mnt_flag
|= MNT_MWAIT
;
sleep((caddr_t
)mp
, PVFS
);
if (error
= VFS_ROOT(dp
->v_mountedhere
, &tdp
))
* Not a symbolic link. If more pathname,
* continue at next component, else return.
if (*ndp
->ni_next
== '/') {
ndp
->ni_ptr
= ndp
->ni_next
;
while (*ndp
->ni_ptr
== '/') {
* Check for read-only file systems.
if (flag
== DELETE
|| flag
== RENAME
) {
* Disallow directory write attempts on read-only
if ((dp
->v_mount
->mnt_flag
& rdonly
) ||
(wantparent
&& (ndp
->ni_dvp
->v_mount
->mnt_flag
& rdonly
))) {
if (ndp
->ni_nameiop
& SAVESTART
) {
ndp
->ni_startdir
= ndp
->ni_dvp
;
if ((ndp
->ni_nameiop
& LOCKLEAF
) == 0)
if ((ndp
->ni_nameiop
& LOCKPARENT
) && *ndp
->ni_next
== '\0')