version 1.71, 2006/07/23 22:06:12 |
version 1.71.4.4, 2007/02/09 21:03:53 |
Line 70 __KERNEL_RCSID(0, "$NetBSD$"); |
|
Line 70 __KERNEL_RCSID(0, "$NetBSD$"); |
|
#define MAGICLINKS 0 |
#define MAGICLINKS 0 |
#endif |
#endif |
|
|
|
struct pathname_internal { |
|
char *pathbuf; |
|
boolean_t needfree; |
|
}; |
|
|
int vfs_magiclinks = MAGICLINKS; |
int vfs_magiclinks = MAGICLINKS; |
|
|
struct pool pnbuf_pool; /* pathname buffer pool */ |
struct pool pnbuf_pool; /* pathname buffer pool */ |
Line 163 symlink_magic(struct proc *p, char *cp, |
|
Line 168 symlink_magic(struct proc *p, char *cp, |
|
} else if (MATCH("ostype")) { |
} else if (MATCH("ostype")) { |
SUBSTITUTE("ostype", ostype, |
SUBSTITUTE("ostype", ostype, |
strlen(ostype)); |
strlen(ostype)); |
|
} else if (MATCH("uid")) { |
|
char uidtmp[11]; /* XXX elad */ |
|
|
|
(void)snprintf(uidtmp, sizeof(uidtmp), "%u", |
|
kauth_cred_geteuid(kauth_cred_get())); |
|
SUBSTITUTE("uid", uidtmp, strlen(uidtmp)); |
} else { |
} else { |
tmp[newlen++] = '@'; |
tmp[newlen++] = '@'; |
if (termchar == VC) |
if (termchar == VC) |
Line 185 symlink_magic(struct proc *p, char *cp, |
|
Line 196 symlink_magic(struct proc *p, char *cp, |
|
#undef MATCH |
#undef MATCH |
#undef SUBSTITUTE |
#undef SUBSTITUTE |
|
|
|
int |
|
pathname_get(const char *dirp, enum uio_seg segflg, pathname_t *path) |
|
{ |
|
int error; |
|
|
|
if (dirp == NULL) |
|
return (EFAULT); |
|
|
|
*path = malloc(sizeof(struct pathname_internal), M_TEMP, |
|
M_ZERO|M_WAITOK); |
|
|
|
if (segflg == UIO_USERSPACE) { |
|
(*path)->pathbuf = PNBUF_GET(); |
|
error = copyinstr(dirp, (*path)->pathbuf, MAXPATHLEN, |
|
NULL); |
|
if (error) { |
|
PNBUF_PUT((*path)->pathbuf); |
|
free(*path, M_TEMP); |
|
*path = NULL; |
|
return (error); |
|
} |
|
(*path)->needfree = TRUE; |
|
} else { |
|
(*path)->pathbuf = __UNCONST(dirp); |
|
(*path)->needfree = FALSE; |
|
} |
|
|
|
return (0); |
|
} |
|
|
|
const char * |
|
pathname_path(pathname_t path) |
|
{ |
|
KASSERT(path != NULL); |
|
return (path->pathbuf); |
|
} |
|
|
|
void |
|
pathname_put(pathname_t path) |
|
{ |
|
if (path != NULL) { |
|
if (path->pathbuf != NULL && path->needfree) |
|
PNBUF_PUT(path->pathbuf); |
|
free(path, M_TEMP); |
|
} |
|
} |
|
|
/* |
/* |
* Convert a pathname into a pointer to a locked vnode. |
* Convert a pathname into a pointer to a locked vnode. |
* |
* |
Line 276 namei(struct nameidata *ndp) |
|
Line 334 namei(struct nameidata *ndp) |
|
dp = cwdi->cwdi_cdir; |
dp = cwdi->cwdi_cdir; |
VREF(dp); |
VREF(dp); |
} |
} |
|
vn_lock(dp, LK_EXCLUSIVE | LK_RETRY); |
for (;;) { |
for (;;) { |
if (!dp->v_mount) |
if (!dp->v_mount) { |
{ |
|
/* Give up if the directory is no longer mounted */ |
/* Give up if the directory is no longer mounted */ |
|
vput(dp); |
PNBUF_PUT(cnp->cn_pnbuf); |
PNBUF_PUT(cnp->cn_pnbuf); |
return (ENOENT); |
return (ENOENT); |
} |
} |
cnp->cn_nameptr = cnp->cn_pnbuf; |
cnp->cn_nameptr = cnp->cn_pnbuf; |
ndp->ni_startdir = dp; |
ndp->ni_startdir = dp; |
if ((error = lookup(ndp)) != 0) { |
error = lookup(ndp); |
|
if (error != 0) { |
|
if (ndp->ni_dvp) { |
|
vput(ndp->ni_dvp); |
|
} |
PNBUF_PUT(cnp->cn_pnbuf); |
PNBUF_PUT(cnp->cn_pnbuf); |
return (error); |
return (error); |
} |
} |
|
|
/* |
/* |
* Check for symbolic link |
* Check for symbolic link |
*/ |
*/ |
if ((cnp->cn_flags & ISSYMLINK) == 0) { |
if ((cnp->cn_flags & ISSYMLINK) == 0) { |
|
if ((cnp->cn_flags & LOCKPARENT) == 0 && ndp->ni_dvp) { |
|
if (ndp->ni_dvp == ndp->ni_vp) { |
|
vrele(ndp->ni_dvp); |
|
} else { |
|
vput(ndp->ni_dvp); |
|
} |
|
} |
if ((cnp->cn_flags & (SAVENAME | SAVESTART)) == 0) |
if ((cnp->cn_flags & (SAVENAME | SAVESTART)) == 0) |
PNBUF_PUT(cnp->cn_pnbuf); |
PNBUF_PUT(cnp->cn_pnbuf); |
else |
else |
cnp->cn_flags |= HASBUF; |
cnp->cn_flags |= HASBUF; |
return (0); |
return (0); |
} |
} |
if ((cnp->cn_flags & LOCKPARENT) && (cnp->cn_flags & ISLASTCN)) |
|
VOP_UNLOCK(ndp->ni_dvp, 0); |
|
if (ndp->ni_loopcnt++ >= MAXSYMLINKS) { |
if (ndp->ni_loopcnt++ >= MAXSYMLINKS) { |
error = ELOOP; |
error = ELOOP; |
break; |
break; |
Line 325 namei(struct nameidata *ndp) |
|
Line 395 namei(struct nameidata *ndp) |
|
UIO_SETUP_SYSSPACE(&auio); |
UIO_SETUP_SYSSPACE(&auio); |
error = VOP_READLINK(ndp->ni_vp, &auio, cnp->cn_cred); |
error = VOP_READLINK(ndp->ni_vp, &auio, cnp->cn_cred); |
if (error) { |
if (error) { |
badlink: |
badlink: |
if (ndp->ni_pathlen > 1) |
if (ndp->ni_pathlen > 1) |
PNBUF_PUT(cp); |
PNBUF_PUT(cp); |
break; |
break; |
Line 335 namei(struct nameidata *ndp) |
|
Line 405 namei(struct nameidata *ndp) |
|
error = ENOENT; |
error = ENOENT; |
goto badlink; |
goto badlink; |
} |
} |
|
|
/* |
/* |
* Do symlink substitution, if appropriate, and |
* Do symlink substitution, if appropriate, and |
* check length for potential overflow. |
* check length for potential overflow. |
Line 354 namei(struct nameidata *ndp) |
|
Line 425 namei(struct nameidata *ndp) |
|
ndp->ni_pathlen += linklen; |
ndp->ni_pathlen += linklen; |
vput(ndp->ni_vp); |
vput(ndp->ni_vp); |
dp = ndp->ni_dvp; |
dp = ndp->ni_dvp; |
|
|
/* |
/* |
* Check if root directory should replace current directory. |
* Check if root directory should replace current directory. |
*/ |
*/ |
if (cnp->cn_pnbuf[0] == '/') { |
if (cnp->cn_pnbuf[0] == '/') { |
vrele(dp); |
vput(dp); |
dp = ndp->ni_rootdir; |
dp = ndp->ni_rootdir; |
VREF(dp); |
VREF(dp); |
|
vn_lock(dp, LK_EXCLUSIVE | LK_RETRY); |
} |
} |
} |
} |
PNBUF_PUT(cnp->cn_pnbuf); |
KASSERT(ndp->ni_dvp != ndp->ni_vp); |
vrele(ndp->ni_dvp); |
vput(ndp->ni_dvp); |
vput(ndp->ni_vp); |
vput(ndp->ni_vp); |
ndp->ni_vp = NULL; |
ndp->ni_vp = NULL; |
|
PNBUF_PUT(cnp->cn_pnbuf); |
return (error); |
return (error); |
} |
} |
|
|
Line 413 namei_hash(const char *name, const char |
|
Line 487 namei_hash(const char *name, const char |
|
* When CREATE, RENAME, or DELETE is specified, information usable in |
* When CREATE, RENAME, or DELETE is specified, information usable in |
* creating, renaming, or deleting a directory entry may be calculated. |
* creating, renaming, or deleting a directory entry may be calculated. |
* If flag has LOCKPARENT or'ed into it, the parent directory is returned |
* 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 |
* locked. Otherwise the parent directory is not returned. If the target |
* returned unlocked. Otherwise the parent directory is not returned. If |
* of the pathname exists and LOCKLEAF is or'ed into the flag the target |
* the target of the pathname exists and LOCKLEAF is or'ed into the flag |
* is returned locked, otherwise it is returned unlocked. When creating |
* the target is returned locked, otherwise it is returned unlocked. |
* or renaming and LOCKPARENT is specified, the target may not be ".". |
* When creating or renaming and LOCKPARENT is specified, the target may not |
* When deleting and LOCKPARENT is specified, the target may be ".". |
* be ".". When deleting and LOCKPARENT is specified, the target may be ".". |
|
* |
* |
* Overall outline of lookup: |
* Overall outline of lookup: |
* |
* |
Line 427 namei_hash(const char *name, const char |
|
Line 500 namei_hash(const char *name, const char |
|
* handle degenerate case where name is null string |
* handle degenerate case where name is null string |
* if .. and crossing mount points and on mounted filesys, find parent |
* if .. and crossing mount points and on mounted filesys, find parent |
* call VOP_LOOKUP routine for next component name |
* call VOP_LOOKUP routine for next component name |
* directory vnode returned in ni_dvp, unlocked unless LOCKPARENT set |
* directory vnode returned in ni_dvp, locked. |
* component vnode returned in ni_vp (if it exists), locked. |
* component vnode returned in ni_vp (if it exists), locked. |
* if result vnode is mounted on and crossing mount points, |
* if result vnode is mounted on and crossing mount points, |
* find mounted on vnode |
* find mounted on vnode |
* if more components of name, do next level at dirloop |
* if more components of name, do next level at dirloop |
* return the answer in ni_vp, locked if LOCKLEAF set |
* return the answer in ni_vp, locked if LOCKLEAF set |
* if LOCKPARENT set, return locked parent in ni_dvp |
* if LOCKPARENT set, return locked parent in ni_dvp |
* if WANTPARENT set, return unlocked parent in ni_dvp |
|
*/ |
*/ |
int |
int |
lookup(struct nameidata *ndp) |
lookup(struct nameidata *ndp) |
Line 444 lookup(struct nameidata *ndp) |
|
Line 516 lookup(struct nameidata *ndp) |
|
struct vnode *tdp; /* saved dp */ |
struct vnode *tdp; /* saved dp */ |
struct mount *mp; /* mount table entry */ |
struct mount *mp; /* mount table entry */ |
int docache; /* == 0 do not cache last component */ |
int docache; /* == 0 do not cache last component */ |
int wantparent; /* 1 => wantparent or lockparent flag */ |
|
int rdonly; /* lookup read-only flag bit */ |
int rdonly; /* lookup read-only flag bit */ |
int error = 0; |
int error = 0; |
int slashes; |
int slashes; |
int dpunlocked = 0; /* dp has already been unlocked */ |
|
struct componentname *cnp = &ndp->ni_cnd; |
struct componentname *cnp = &ndp->ni_cnd; |
struct lwp *l = cnp->cn_lwp; |
struct lwp *l = cnp->cn_lwp; |
|
|
/* |
/* |
* Setup: break out flag bits into variables. |
* Setup: break out flag bits into variables. |
*/ |
*/ |
wantparent = cnp->cn_flags & (LOCKPARENT | WANTPARENT); |
|
docache = (cnp->cn_flags & NOCACHE) ^ NOCACHE; |
docache = (cnp->cn_flags & NOCACHE) ^ NOCACHE; |
if (cnp->cn_nameiop == DELETE || |
if (cnp->cn_nameiop == DELETE) |
(wantparent && cnp->cn_nameiop != CREATE)) |
|
docache = 0; |
docache = 0; |
rdonly = cnp->cn_flags & RDONLY; |
rdonly = cnp->cn_flags & RDONLY; |
ndp->ni_dvp = NULL; |
ndp->ni_dvp = NULL; |
cnp->cn_flags &= ~ISSYMLINK; |
cnp->cn_flags &= ~ISSYMLINK; |
dp = ndp->ni_startdir; |
dp = ndp->ni_startdir; |
ndp->ni_startdir = NULLVP; |
ndp->ni_startdir = NULLVP; |
vn_lock(dp, LK_EXCLUSIVE | LK_RETRY); |
|
|
|
/* |
/* |
* If we have a leading string of slashes, remove them, and just make |
* If we have a leading string of slashes, remove them, and just make |
Line 481 lookup(struct nameidata *ndp) |
|
Line 548 lookup(struct nameidata *ndp) |
|
|
|
if (dp->v_type != VDIR) { |
if (dp->v_type != VDIR) { |
error = ENOTDIR; |
error = ENOTDIR; |
|
vput(dp); |
goto bad; |
goto bad; |
} |
} |
|
|
Line 491 lookup(struct nameidata *ndp) |
|
Line 559 lookup(struct nameidata *ndp) |
|
* (because this is the root directory), then we must fail. |
* (because this is the root directory), then we must fail. |
*/ |
*/ |
if (cnp->cn_nameptr[0] == '\0') { |
if (cnp->cn_nameptr[0] == '\0') { |
if (ndp->ni_dvp == NULL && wantparent) { |
if (ndp->ni_dvp == NULL && cnp->cn_nameiop != LOOKUP) { |
switch (cnp->cn_nameiop) { |
switch (cnp->cn_nameiop) { |
case CREATE: |
case CREATE: |
error = EEXIST; |
error = EEXIST; |
Line 503 lookup(struct nameidata *ndp) |
|
Line 571 lookup(struct nameidata *ndp) |
|
default: |
default: |
KASSERT(0); |
KASSERT(0); |
} |
} |
|
vput(dp); |
goto bad; |
goto bad; |
} |
} |
ndp->ni_vp = dp; |
ndp->ni_vp = dp; |
|
|
* cnp->cn_nameptr for callers that need the name. Callers needing |
* cnp->cn_nameptr for callers that need the name. Callers needing |
* the name set the SAVENAME flag. When done, they assume |
* the name set the SAVENAME flag. When done, they assume |
* responsibility for freeing the pathname buffer. |
* responsibility for freeing the pathname buffer. |
|
* |
|
* At this point, our only vnode state is that "dp" is held and locked. |
*/ |
*/ |
cnp->cn_consume = 0; |
cnp->cn_consume = 0; |
cp = NULL; |
cp = NULL; |
cnp->cn_hash = namei_hash(cnp->cn_nameptr, &cp); |
cnp->cn_hash = namei_hash(cnp->cn_nameptr, &cp); |
cnp->cn_namelen = cp - cnp->cn_nameptr; |
cnp->cn_namelen = cp - cnp->cn_nameptr; |
if (cnp->cn_namelen > NAME_MAX) { |
if (cnp->cn_namelen > NAME_MAX) { |
|
vput(dp); |
error = ENAMETOOLONG; |
error = ENAMETOOLONG; |
|
ndp->ni_dvp = NULL; |
goto bad; |
goto bad; |
} |
} |
#ifdef NAMEI_DIAGNOSTIC |
#ifdef NAMEI_DIAGNOSTIC |
|
|
} |
} |
if (ndp->ni_rootdir != rootvnode) { |
if (ndp->ni_rootdir != rootvnode) { |
int retval; |
int retval; |
|
|
VOP_UNLOCK(dp, 0); |
VOP_UNLOCK(dp, 0); |
retval = vn_isunder(dp, ndp->ni_rootdir, l); |
retval = vn_isunder(dp, ndp->ni_rootdir, l); |
vn_lock(dp, LK_EXCLUSIVE | LK_RETRY); |
vn_lock(dp, LK_EXCLUSIVE | LK_RETRY); |
|
|
|
|
/* |
/* |
* We now have a segment name to search for, and a directory to search. |
* We now have a segment name to search for, and a directory to search. |
|
* Again, our only vnode state is that "dp" is held and locked. |
*/ |
*/ |
unionlookup: |
unionlookup: |
ndp->ni_dvp = dp; |
ndp->ni_dvp = dp; |
ndp->ni_vp = NULL; |
ndp->ni_vp = NULL; |
cnp->cn_flags &= ~PDIRUNLOCK; |
error = VOP_LOOKUP(dp, &ndp->ni_vp, cnp); |
if ((error = VOP_LOOKUP(dp, &ndp->ni_vp, cnp)) != 0) { |
if (error != 0) { |
#ifdef DIAGNOSTIC |
#ifdef DIAGNOSTIC |
if (ndp->ni_vp != NULL) |
if (ndp->ni_vp != NULL) |
panic("leaf `%s' should be empty", cnp->cn_nameptr); |
panic("leaf `%s' should be empty", cnp->cn_nameptr); |
|
|
(dp->v_mount->mnt_flag & MNT_UNION)) { |
(dp->v_mount->mnt_flag & MNT_UNION)) { |
tdp = dp; |
tdp = dp; |
dp = dp->v_mount->mnt_vnodecovered; |
dp = dp->v_mount->mnt_vnodecovered; |
if (cnp->cn_flags & PDIRUNLOCK) |
vput(tdp); |
vrele(tdp); |
|
else |
|
vput(tdp); |
|
VREF(dp); |
VREF(dp); |
vn_lock(dp, LK_EXCLUSIVE | LK_RETRY); |
vn_lock(dp, LK_EXCLUSIVE | LK_RETRY); |
goto unionlookup; |
goto unionlookup; |
} |
} |
|
|
if (cnp->cn_flags & PDIRUNLOCK) |
|
dpunlocked = 1; |
|
|
|
if (error != EJUSTRETURN) |
if (error != EJUSTRETURN) |
goto bad; |
goto bad; |
|
|
/* |
/* |
* If this was not the last component, or there were trailing |
* If this was not the last component, or there were trailing |
* slashes, and we are not going to create a directory, |
* slashes, and we are not going to create a directory, |
|
|
error = ENOENT; |
error = ENOENT; |
goto bad; |
goto bad; |
} |
} |
|
|
/* |
/* |
* If creating and at end of pathname, then can consider |
* If creating and at end of pathname, then can consider |
* allowing file to be created. |
* allowing file to be created. |
|
|
error = EROFS; |
error = EROFS; |
goto bad; |
goto bad; |
} |
} |
|
|
/* |
/* |
* We return with ni_vp NULL to indicate that the entry |
* We return with ni_vp NULL to indicate that the entry |
* doesn't currently exist, leaving a pointer to the |
* doesn't currently exist, leaving a pointer to the |
|
|
} |
} |
|
|
dp = ndp->ni_vp; |
dp = ndp->ni_vp; |
|
|
|
/* |
|
* "dp" and "ndp->ni_dvp" are both locked and held, |
|
* and may be the same vnode. |
|
*/ |
|
|
/* |
/* |
* Check to see if the vnode has been mounted on; |
* Check to see if the vnode has been mounted on; |
* if so find the root of the mounted file system. |
* if so find the root of the mounted file system. |
|
|
(cnp->cn_flags & NOCROSSMOUNT) == 0) { |
(cnp->cn_flags & NOCROSSMOUNT) == 0) { |
if (vfs_busy(mp, 0, 0)) |
if (vfs_busy(mp, 0, 0)) |
continue; |
continue; |
VOP_UNLOCK(dp, 0); |
|
|
KASSERT(ndp->ni_dvp != dp); |
|
VOP_UNLOCK(ndp->ni_dvp, 0); |
|
vput(dp); |
error = VFS_ROOT(mp, &tdp); |
error = VFS_ROOT(mp, &tdp); |
vfs_unbusy(mp); |
vfs_unbusy(mp); |
if (error) { |
if (error) { |
dpunlocked = 1; |
vn_lock(ndp->ni_dvp, LK_EXCLUSIVE | LK_RETRY); |
goto bad2; |
goto bad; |
} |
} |
vrele(dp); |
VOP_UNLOCK(tdp, 0); |
ndp->ni_vp = dp = tdp; |
ndp->ni_vp = dp = tdp; |
|
vn_lock(ndp->ni_dvp, LK_EXCLUSIVE | LK_RETRY); |
|
vn_lock(ndp->ni_vp, LK_EXCLUSIVE | LK_RETRY); |
} |
} |
|
|
/* |
/* |
|
|
*/ |
*/ |
if ((dp->v_type != VDIR) && (cnp->cn_flags & REQUIREDIR)) { |
if ((dp->v_type != VDIR) && (cnp->cn_flags & REQUIREDIR)) { |
error = ENOTDIR; |
error = ENOTDIR; |
goto bad2; |
KASSERT(dp != ndp->ni_dvp); |
|
vput(dp); |
|
goto bad; |
} |
} |
|
|
nextname: |
nextname: |
|
|
/* |
/* |
* Not a symbolic link. If this was not the last component, then |
* Not a symbolic link. If this was not the last component, then |
* continue at the next component, else return. |
* continue at the next component, else return. |
*/ |
*/ |
if (!(cnp->cn_flags & ISLASTCN)) { |
if (!(cnp->cn_flags & ISLASTCN)) { |
cnp->cn_nameptr = ndp->ni_next; |
cnp->cn_nameptr = ndp->ni_next; |
vrele(ndp->ni_dvp); |
if (ndp->ni_dvp == dp) { |
|
vrele(ndp->ni_dvp); |
|
} else { |
|
vput(ndp->ni_dvp); |
|
} |
goto dirloop; |
goto dirloop; |
} |
} |
|
|
terminal: |
terminal: |
|
|
/* |
/* |
* Disallow directory write attempts on read-only file systems. |
* Disallow directory write attempts on read-only file systems. |
*/ |
*/ |
if (rdonly && |
if (rdonly && |
(cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME)) { |
(cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME)) { |
|
|
/* |
/* |
* Disallow directory write attempts on read-only |
* Disallow directory write attempts on read-only |
* file systems. |
* file systems. |
*/ |
*/ |
error = EROFS; |
error = EROFS; |
goto bad2; |
if (dp != ndp->ni_dvp) { |
|
vput(dp); |
|
} |
|
goto bad; |
} |
} |
if (ndp->ni_dvp != NULL) { |
if (ndp->ni_dvp != NULL) { |
if (cnp->cn_flags & SAVESTART) { |
if (cnp->cn_flags & SAVESTART) { |
ndp->ni_startdir = ndp->ni_dvp; |
ndp->ni_startdir = ndp->ni_dvp; |
VREF(ndp->ni_startdir); |
VREF(ndp->ni_startdir); |
} |
} |
if (!wantparent) |
|
vrele(ndp->ni_dvp); |
|
} |
} |
if ((cnp->cn_flags & LOCKLEAF) == 0) |
if ((cnp->cn_flags & LOCKLEAF) == 0) { |
VOP_UNLOCK(dp, 0); |
VOP_UNLOCK(dp, 0); |
|
} |
return (0); |
return (0); |
|
|
bad2: |
|
if ((cnp->cn_flags & LOCKPARENT) && (cnp->cn_flags & ISLASTCN) && |
|
((cnp->cn_flags & PDIRUNLOCK) == 0)) |
|
VOP_UNLOCK(ndp->ni_dvp, 0); |
|
vrele(ndp->ni_dvp); |
|
bad: |
bad: |
if (dpunlocked) |
|
vrele(dp); |
|
else |
|
vput(dp); |
|
ndp->ni_vp = NULL; |
ndp->ni_vp = NULL; |
return (error); |
return (error); |
} |
} |
|
|
/* |
/* |
* Reacquire a path name component. |
* Reacquire a path name component. |
|
* dvp is locked on entry and exit. |
|
* *vpp is locked on exit unless it's NULL. |
*/ |
*/ |
int |
int |
relookup(struct vnode *dvp, struct vnode **vpp, struct componentname *cnp) |
relookup(struct vnode *dvp, struct vnode **vpp, struct componentname *cnp) |
{ |
{ |
struct vnode *dp = 0; /* the directory we are searching */ |
|
int wantparent; /* 1 => wantparent or lockparent flag */ |
|
int rdonly; /* lookup read-only flag bit */ |
int rdonly; /* lookup read-only flag bit */ |
int error = 0; |
int error = 0; |
#ifdef DEBUG |
#ifdef DEBUG |
u_long newhash; /* DEBUG: check name hash */ |
uint32_t newhash; /* DEBUG: check name hash */ |
const char *cp; /* DEBUG: check name ptr/len */ |
const char *cp; /* DEBUG: check name ptr/len */ |
#endif /* DEBUG */ |
#endif /* DEBUG */ |
|
|
/* |
/* |
* Setup: break out flag bits into variables. |
* Setup: break out flag bits into variables. |
*/ |
*/ |
wantparent = cnp->cn_flags & (LOCKPARENT|WANTPARENT); |
|
rdonly = cnp->cn_flags & RDONLY; |
rdonly = cnp->cn_flags & RDONLY; |
cnp->cn_flags &= ~ISSYMLINK; |
cnp->cn_flags &= ~ISSYMLINK; |
dp = dvp; |
|
vn_lock(dp, LK_EXCLUSIVE | LK_RETRY); |
|
|
|
/* dirloop: */ |
|
/* |
/* |
* Search a new directory. |
* Search a new directory. |
* |
* |
Line 836 relookup(struct vnode *dvp, struct vnode |
|
Line 917 relookup(struct vnode *dvp, struct vnode |
|
#ifdef DEBUG |
#ifdef DEBUG |
cp = NULL; |
cp = NULL; |
newhash = namei_hash(cnp->cn_nameptr, &cp); |
newhash = namei_hash(cnp->cn_nameptr, &cp); |
if (newhash != cnp->cn_hash) |
if ((uint32_t)newhash != (uint32_t)cnp->cn_hash) |
panic("relookup: bad hash"); |
panic("relookup: bad hash"); |
if (cnp->cn_namelen != cp - cnp->cn_nameptr) |
if (cnp->cn_namelen != cp - cnp->cn_nameptr) |
panic("relookup: bad len"); |
panic("relookup: bad len"); |
Line 845 relookup(struct vnode *dvp, struct vnode |
|
Line 926 relookup(struct vnode *dvp, struct vnode |
|
if (*cp != 0) |
if (*cp != 0) |
panic("relookup: not last component"); |
panic("relookup: not last component"); |
#endif /* DEBUG */ |
#endif /* DEBUG */ |
#ifdef NAMEI_DIAGNOSTIC |
|
printf("{%s}: ", cnp->cn_nameptr); |
|
#endif /* NAMEI_DIAGNOSTIC */ |
|
|
|
/* |
/* |
* Check for degenerate name (e.g. / or "") |
* Check for degenerate name (e.g. / or "") |
Line 863 relookup(struct vnode *dvp, struct vnode |
|
Line 941 relookup(struct vnode *dvp, struct vnode |
|
/* |
/* |
* We now have a segment name to search for, and a directory to search. |
* We now have a segment name to search for, and a directory to search. |
*/ |
*/ |
if ((error = VOP_LOOKUP(dp, vpp, cnp)) != 0) { |
if ((error = VOP_LOOKUP(dvp, vpp, cnp)) != 0) { |
#ifdef DIAGNOSTIC |
#ifdef DIAGNOSTIC |
if (*vpp != NULL) |
if (*vpp != NULL) |
panic("leaf `%s' should be empty", cnp->cn_nameptr); |
panic("leaf `%s' should be empty", cnp->cn_nameptr); |
#endif |
#endif |
if (error != EJUSTRETURN) |
if (error != EJUSTRETURN) |
goto bad; |
goto bad; |
/* |
|
* If creating and at end of pathname, then can consider |
|
* allowing file to be created. |
|
*/ |
|
if (rdonly) { |
|
error = EROFS; |
|
goto bad; |
|
} |
|
/* ASSERT(dvp == ndp->ni_startdir) */ |
|
if (cnp->cn_flags & SAVESTART) |
|
VREF(dvp); |
|
/* |
|
* We return with ni_vp NULL to indicate that the entry |
|
* doesn't currently exist, leaving a pointer to the |
|
* (possibly locked) directory vnode in ndp->ni_dvp. |
|
*/ |
|
return (0); |
|
} |
} |
dp = *vpp; |
|
|
|
#ifdef DIAGNOSTIC |
#ifdef DIAGNOSTIC |
/* |
/* |
* Check for symbolic link |
* Check for symbolic link |
*/ |
*/ |
if (dp->v_type == VLNK && (cnp->cn_flags & FOLLOW)) |
if (*vpp && (*vpp)->v_type == VLNK && (cnp->cn_flags & FOLLOW)) |
panic("relookup: symlink found"); |
panic("relookup: symlink found"); |
#endif |
#endif |
|
|
/* |
/* |
* Check for read-only file systems. |
* Check for read-only file systems. |
*/ |
*/ |
if (rdonly && |
if (rdonly && cnp->cn_nameiop != LOOKUP) { |
(cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME)) { |
|
error = EROFS; |
error = EROFS; |
goto bad2; |
if (*vpp) { |
|
vput(*vpp); |
|
} |
|
goto bad; |
} |
} |
/* ASSERT(dvp == ndp->ni_startdir) */ |
|
if (cnp->cn_flags & SAVESTART) |
if (cnp->cn_flags & SAVESTART) |
VREF(dvp); |
VREF(dvp); |
if (!wantparent) |
|
vrele(dvp); |
|
if ((cnp->cn_flags & LOCKLEAF) == 0) |
|
VOP_UNLOCK(dp, 0); |
|
return (0); |
return (0); |
|
|
bad2: |
|
if ((cnp->cn_flags & LOCKPARENT) && (cnp->cn_flags & ISLASTCN)) |
|
VOP_UNLOCK(dvp, 0); |
|
vrele(dvp); |
|
bad: |
bad: |
vput(dp); |
|
*vpp = NULL; |
*vpp = NULL; |
return (error); |
return (error); |
} |
} |