* Copyright (c) 1982, 1986, 1989 Regents of the University of California.
* %sccs.include.redist.c%
* @(#)vfs_lookup.c 7.41 (Berkeley) %G%
* 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 */
struct componentname
*cnp
= &ndp
->ni_cnd
;
ndp
->ni_cnd
.cn_cred
= ndp
->ni_cnd
.cn_proc
->p_ucred
;
if (!cnp
->cn_cred
|| !cnp
->cn_proc
)
panic ("namei: bad cred/proc");
if (cnp
->cn_nameiop
& (~OPMASK
))
panic ("namei: nameiop contaminated with flags");
if (cnp
->cn_flags
& OPMASK
)
panic ("namei: flags contaminated with nameiops");
fdp
= cnp
->cn_proc
->p_fd
;
* Get a buffer for the name to be translated, and copy the
if ((cnp
->cn_flags
& HASBUF
) == 0)
MALLOC(cnp
->cn_pnbuf
, caddr_t
, MAXPATHLEN
, M_NAMEI
, M_WAITOK
);
if (ndp
->ni_segflg
== UIO_SYSSPACE
)
error
= copystr(ndp
->ni_dirp
, cnp
->cn_pnbuf
,
MAXPATHLEN
, &ndp
->ni_pathlen
);
error
= copyinstr(ndp
->ni_dirp
, cnp
->cn_pnbuf
,
MAXPATHLEN
, &ndp
->ni_pathlen
);
free(cnp
->cn_pnbuf
, M_NAMEI
);
if (KTRPOINT(cnp
->cn_proc
, KTR_NAMEI
))
ktrnamei(cnp
->cn_proc
->p_tracep
, cnp
->cn_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.
cnp
->cn_nameptr
= cnp
->cn_pnbuf
;
if (*(cnp
->cn_nameptr
) == '/') {
while (*(cnp
->cn_nameptr
) == '/') {
if (error
= lookup(ndp
)) {
FREE(cnp
->cn_pnbuf
, M_NAMEI
);
* Check for symbolic link
if ((cnp
->cn_flags
& ISSYMLINK
) == 0) {
if ((cnp
->cn_flags
& (SAVENAME
| SAVESTART
)) == 0)
FREE(cnp
->cn_pnbuf
, M_NAMEI
);
if ((cnp
->cn_flags
& 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
, cnp
->cn_cred
)) {
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(cnp
->cn_pnbuf
, M_NAMEI
);
cnp
->cn_pnbuf
[linklen
] = '\0';
ndp
->ni_pathlen
+= linklen
;
FREE(cnp
->cn_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 wantparent
; /* 1 => wantparent or lockparent flag */
int rdonly
; /* lookup read-only flag bit */
struct componentname
*cnp
= &ndp
->ni_cnd
;
* Setup: break out flag bits into variables.
wantparent
= cnp
->cn_flags
& (LOCKPARENT
| WANTPARENT
);
docache
= (cnp
->cn_flags
& NOCACHE
) ^ NOCACHE
;
if (cnp
->cn_nameiop
== DELETE
||
(wantparent
&& cnp
->cn_nameiop
!= CREATE
))
rdonly
= cnp
->cn_flags
& RDONLY
;
cnp
->cn_flags
&= ~ISSYMLINK
;
ndp
->ni_startdir
= NULLVP
;
* Search a new directory.
* The cn_hash value is for use by vfs_cache.
* The last component of the filename is left accessible via
* cnp->cn_nameptr 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
= cnp
->cn_nameptr
; *cp
!= 0 && *cp
!= '/'; cp
++)
cnp
->cn_hash
+= (unsigned char)*cp
;
cnp
->cn_namelen
= cp
- cnp
->cn_nameptr
;
if (cnp
->cn_namelen
>= NAME_MAX
) {
printf("{%s}: ", cnp
->cn_nameptr
);
ndp
->ni_pathlen
-= cnp
->cn_namelen
;
cnp
->cn_flags
|= MAKEENTRY
;
if (*cp
== '\0' && docache
== 0)
cnp
->cn_flags
&= ~MAKEENTRY
;
if (cnp
->cn_namelen
== 2 &&
cnp
->cn_nameptr
[1] == '.' && cnp
->cn_nameptr
[0] == '.')
cnp
->cn_flags
|= ISDOTDOT
;
else cnp
->cn_flags
&= ~ISDOTDOT
;
cnp
->cn_flags
|= ISLASTCN
;
else cnp
->cn_flags
&= ~ISLASTCN
;
* Check for degenerate name (e.g. / or "")
* which is a way of talking about a directory,
if (cnp
->cn_nameptr
[0] == '\0') {
if (cnp
->cn_nameiop
!= LOOKUP
|| wantparent
) {
if (dp
->v_type
!= VDIR
) {
if (!(cnp
->cn_flags
& LOCKLEAF
))
if (cnp
->cn_flags
& 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.
if (cnp
->cn_flags
& ISDOTDOT
) {
if (dp
== ndp
->ni_rootdir
) {
if ((dp
->v_flag
& VROOT
) == 0 ||
(cnp
->cn_flags
& 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
->ni_vp
, cnp
)) {
panic("leaf should be empty");
if (error
!= EJUSTRETURN
)
* If creating and at end of pathname, then can consider
* allowing file to be created.
if (rdonly
|| (ndp
->ni_dvp
->v_mount
->mnt_flag
& MNT_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 (cnp
->cn_flags
& SAVESTART
) {
ndp
->ni_startdir
= ndp
->ni_dvp
;
* Take into account any additional components consumed by
* the underlying filesystem.
if (cnp
->cn_consume
> 0) {
cnp
->cn_nameptr
+= cnp
->cn_consume
;
ndp
->ni_next
+= cnp
->cn_consume
;
ndp
->ni_pathlen
-= cnp
->cn_consume
;
* Check for symbolic link
if ((dp
->v_type
== VLNK
) &&
((cnp
->cn_flags
& FOLLOW
) || *ndp
->ni_next
== '/')) {
cnp
->cn_flags
|= ISSYMLINK
;
* 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
) &&
(cnp
->cn_flags
& NOCROSSMOUNT
) == 0) {
if (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
== '/') {
cnp
->cn_nameptr
= ndp
->ni_next
;
while (*cnp
->cn_nameptr
== '/') {
* Check for read-only file systems.
if (cnp
->cn_nameiop
== DELETE
|| cnp
->cn_nameiop
== RENAME
) {
* Disallow directory write attempts on read-only
if (rdonly
|| (dp
->v_mount
->mnt_flag
& MNT_RDONLY
) ||
(ndp
->ni_dvp
->v_mount
->mnt_flag
& MNT_RDONLY
))) {
if (cnp
->cn_flags
& SAVESTART
) {
ndp
->ni_startdir
= ndp
->ni_dvp
;
if ((cnp
->cn_flags
& LOCKLEAF
) == 0)
if ((cnp
->cn_flags
& LOCKPARENT
) && *ndp
->ni_next
== '\0')