* Copyright (c) 1982, 1986, 1989 Regents of the University of California.
* Redistribution and use in source and binary forms are permitted
* provided that the above copyright notice and this paragraph are
* duplicated in all such forms and that any documentation,
* advertising materials, and other materials related to such
* distribution and use acknowledge that the software was developed
* by the University of California, Berkeley. The name of the
* University may not be used to endorse or promote products derived
* from this software without specific prior written permission.
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
* @(#)vfs_lookup.c 7.16 (Berkeley) %G%
* Convert a pathname into a pointer to a locked inode.
* This is a very central and rather complicated routine.
* 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 and the target of the pathname
* exists, namei returns both the target and its parent directory locked.
* When creating or renaming and LOCKPARENT is specified, the target may not
* be ".". When deleting and LOCKPARENT is specified, the target may be ".".
* 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:
* copy next component of name to ndp->ni_dent
* handle degenerate case where name is null string
* if .. and on mounted filesys, find parent
* call 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 symbolic link, massage name in buffer and continue at dirloop
* if result inode is mounted on, find mounted on vnode
* if more components of name, do next level at dirloop
* return the answer in ni_vp as locked vnode;
* if LOCKPARENT set, return locked parent in ni_dvp
* NOTE: (LOOKUP | LOCKPARENT) currently returns the parent vnode unlocked.
register struct nameidata
*ndp
;
register char *cp
; /* pointer into pathname argument */
register struct vnode
*dp
= 0; /* the directory we are searching */
register int i
; /* Temp counter */
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 lockparent
; /* 1 => lockparent flag */
flag
= ndp
->ni_nameiop
& OPFLAG
;
wantparent
= ndp
->ni_nameiop
& (LOCKPARENT
|WANTPARENT
);
lockparent
= ndp
->ni_nameiop
& LOCKPARENT
;
docache
= (ndp
->ni_nameiop
& NOCACHE
) ^ NOCACHE
;
if (flag
== DELETE
|| wantparent
)
* Get a buffer for the name to be translated, and copy the
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
,
error
= copyinstr(ndp
->ni_dirp
, ndp
->ni_pnbuf
, MAXPATHLEN
,
free(ndp
->ni_pnbuf
, M_NAMEI
);
ndp
->ni_ptr
= ndp
->ni_pnbuf
;
if (KTRPOINT(u
.u_procp
, KTR_NAMEI
))
ktrnamei(u
.u_procp
->p_tracep
, ndp
->ni_pnbuf
);
* Get starting directory.
* Done at start of translation and after symbolic link.
if (*ndp
->ni_ptr
== '/') {
while (*ndp
->ni_ptr
== '/') {
if ((dp
= ndp
->ni_rdir
) == NULL
)
* We come to dirloop to search a new directory.
* Copy next component of name to ndp->ni_dent.
* XXX kern_exec looks at d_name
* ??? The ni_hash value may be useful for vfs_cache
* XXX There must be the last component of the filename left
* somewhere accessible via. ndp for NFS (and any other stateless file
* systems) in case they are doing a CREATE. The "Towards a..." noted
* that ni_ptr would be left pointing to the last component, but since
* the ni_pnbuf gets free'd, that is not a good idea.
for (cp
= ndp
->ni_ptr
; *cp
!= 0 && *cp
!= '/'; cp
++) {
if ((*cp
&0377) == ('/'|0200) || flag
!= DELETE
) {
ndp
->ni_namelen
= cp
- ndp
->ni_ptr
;
if (ndp
->ni_namelen
>= MAXNAMLEN
) {
ndp
->ni_pathlen
-= ndp
->ni_namelen
;
printf("{%s}: ", ndp
->ni_ptr
);
for (cp
= ndp
->ni_ptr
, i
= 0; *cp
!= 0 && *cp
!= '/'; cp
++) {
if ((*cp
&0377) == ('/'|0200) || flag
!= DELETE
) {
ndp
->ni_dent
.d_name
[i
++] = *cp
;
ndp
->ni_hash
+= (unsigned char)*cp
* i
;
ndp
->ni_dent
.d_namlen
= i
;
ndp
->ni_dent
.d_name
[i
] = '\0';
printf("{%s}: ", ndp
->ni_dent
.d_name
);
if (*cp
== '\0' && docache
== 0)
ndp
->ni_isdotdot
= (ndp
->ni_namelen
== 2 &&
ndp
->ni_dent
.d_name
[1] == '.' && ndp
->ni_dent
.d_name
[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
) {
free(ndp
->ni_pnbuf
, M_NAMEI
);
if (!(ndp
->ni_nameiop
& LOCKLEAF
))
* 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
* file system, then replace it with the
* vnode which was mounted on so we take the
* .. in the other file system.
if (dp
== ndp
->ni_rdir
|| dp
== rootdir
) {
if ((dp
->v_flag
& VROOT
) == 0)
dp
= dp
->v_mount
->m_vnodecovered
;
* We now have a segment name to search for, and a directory to search.
if (error
= VOP_LOOKUP(dp
, ndp
)) {
panic("leaf should be empty");
* If creating and at end of pathname, then can consider
* allowing file to be created.
if (ndp
->ni_dvp
->v_mount
->m_flag
& M_RDONLY
)
if (flag
== LOOKUP
|| flag
== DELETE
||
error
!= ENOENT
|| *cp
!= 0)
* 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.
FREE(ndp
->ni_pnbuf
, M_NAMEI
);
return (0); /* should this be ENOENT? */
* Check for symbolic link
if ((dp
->v_type
== VLNK
) &&
((ndp
->ni_nameiop
& FOLLOW
) || *ndp
->ni_next
== '/')) {
if (++ndp
->ni_loopcnt
> MAXSYMLINKS
) {
MALLOC(cp
, char *, MAXPATHLEN
, M_NAMEI
, M_WAITOK
);
aiov
.iov_len
= MAXPATHLEN
;
auio
.uio_segflg
= UIO_SYSSPACE
;
auio
.uio_resid
= MAXPATHLEN
;
if (error
= VOP_READLINK(dp
, &auio
, ndp
->ni_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(ndp
->ni_pnbuf
, M_NAMEI
);
ndp
->ni_pnbuf
[linklen
] = '\0';
if (lockparent
&& ndp
->ni_pathlen
== 1)
ndp
->ni_pathlen
+= linklen
;
* 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
)) {
while(mp
->m_flag
& M_MLOCK
) {
sleep((caddr_t
)mp
, PVFS
);
error
= VFS_ROOT(dp
->v_mountedhere
, &tdp
);
* Not a symbolic link. If more pathname,
* continue at next component, else return.
ndp
->ni_ptr
= ndp
->ni_next
;
if (*ndp
->ni_ptr
== '/') {
while (*ndp
->ni_ptr
== '/') {
* Check for read-only file systems and executing texts
* Disallow write attempts on read-only file systems;
* unless the file is a socket or a block or character
* device resident on the file system.
if ((dp
->v_mount
->m_flag
& M_RDONLY
) &&
* If there's shared text associated with
* the inode, try to free it up once. If
* we fail, we can't allow writing.
if (dp
->v_flag
& VTEXT
) {
if (wantparent
&& flag
!= CREATE
&&
(ndp
->ni_dvp
->v_mount
->m_flag
& M_RDONLY
)) {
if ((ndp
->ni_nameiop
& LOCKLEAF
) == 0)
FREE(ndp
->ni_pnbuf
, M_NAMEI
);
if (lockparent
&& *ndp
->ni_next
== '\0')
FREE(ndp
->ni_pnbuf
, M_NAMEI
);