Changeset View
Changeset View
Standalone View
Standalone View
head/sys/kern/vfs_lookup.c
Show First 20 Lines • Show All 297 Lines • ▼ Show 20 Lines | |||||
* while (!done && !error) { | * while (!done && !error) { | ||||
* call lookup to search path. | * call lookup to search path. | ||||
* if symbolic link, massage name in buffer and continue | * if symbolic link, massage name in buffer and continue | ||||
* } | * } | ||||
*/ | */ | ||||
int | int | ||||
namei(struct nameidata *ndp) | namei(struct nameidata *ndp) | ||||
{ | { | ||||
struct filedesc *fdp; /* pointer to file descriptor state */ | |||||
char *cp; /* pointer into pathname argument */ | char *cp; /* pointer into pathname argument */ | ||||
struct vnode *dp; /* the directory we are searching */ | struct vnode *dp; /* the directory we are searching */ | ||||
struct iovec aiov; /* uio for reading symbolic links */ | struct iovec aiov; /* uio for reading symbolic links */ | ||||
struct componentname *cnp; | struct componentname *cnp; | ||||
struct file *dfp; | struct file *dfp; | ||||
struct thread *td; | struct thread *td; | ||||
struct proc *p; | struct proc *p; | ||||
struct pwd *pwd; | |||||
cap_rights_t rights; | cap_rights_t rights; | ||||
struct filecaps dirfd_caps; | struct filecaps dirfd_caps; | ||||
struct uio auio; | struct uio auio; | ||||
int error, linklen, startdir_used; | int error, linklen, startdir_used; | ||||
cnp = &ndp->ni_cnd; | cnp = &ndp->ni_cnd; | ||||
td = cnp->cn_thread; | td = cnp->cn_thread; | ||||
p = td->td_proc; | p = td->td_proc; | ||||
ndp->ni_cnd.cn_cred = ndp->ni_cnd.cn_thread->td_ucred; | ndp->ni_cnd.cn_cred = ndp->ni_cnd.cn_thread->td_ucred; | ||||
KASSERT(cnp->cn_cred && p, ("namei: bad cred/proc")); | KASSERT(cnp->cn_cred && p, ("namei: bad cred/proc")); | ||||
KASSERT((cnp->cn_nameiop & (~OPMASK)) == 0, | KASSERT((cnp->cn_nameiop & (~OPMASK)) == 0, | ||||
("namei: nameiop contaminated with flags")); | ("namei: nameiop contaminated with flags")); | ||||
KASSERT((cnp->cn_flags & OPMASK) == 0, | KASSERT((cnp->cn_flags & OPMASK) == 0, | ||||
("namei: flags contaminated with nameiops")); | ("namei: flags contaminated with nameiops")); | ||||
MPASS(ndp->ni_startdir == NULL || ndp->ni_startdir->v_type == VDIR || | MPASS(ndp->ni_startdir == NULL || ndp->ni_startdir->v_type == VDIR || | ||||
ndp->ni_startdir->v_type == VBAD); | ndp->ni_startdir->v_type == VBAD); | ||||
fdp = p->p_fd; | |||||
TAILQ_INIT(&ndp->ni_cap_tracker); | TAILQ_INIT(&ndp->ni_cap_tracker); | ||||
ndp->ni_lcf = 0; | ndp->ni_lcf = 0; | ||||
/* We will set this ourselves if we need it. */ | /* We will set this ourselves if we need it. */ | ||||
cnp->cn_flags &= ~TRAILINGSLASH; | cnp->cn_flags &= ~TRAILINGSLASH; | ||||
/* | /* | ||||
* Get a buffer for the name to be translated, and copy the | * Get a buffer for the name to be translated, and copy the | ||||
▲ Show 20 Lines • Show All 51 Lines • ▼ Show 20 Lines | if (KTRPOINT(td, KTR_NAMEI)) { | ||||
KASSERT(cnp->cn_thread == curthread, | KASSERT(cnp->cn_thread == curthread, | ||||
("namei not using curthread")); | ("namei not using curthread")); | ||||
ktrnamei(cnp->cn_pnbuf); | ktrnamei(cnp->cn_pnbuf); | ||||
} | } | ||||
#endif | #endif | ||||
/* | /* | ||||
* Get starting point for the translation. | * Get starting point for the translation. | ||||
*/ | */ | ||||
FILEDESC_SLOCK(fdp); | pwd = pwd_hold(td); | ||||
/* | /* | ||||
* The reference on ni_rootdir is acquired in the block below to avoid | * The reference on ni_rootdir is acquired in the block below to avoid | ||||
* back-to-back atomics for absolute lookups. | * back-to-back atomics for absolute lookups. | ||||
*/ | */ | ||||
ndp->ni_rootdir = fdp->fd_rdir; | ndp->ni_rootdir = pwd->pwd_rdir; | ||||
ndp->ni_topdir = fdp->fd_jdir; | ndp->ni_topdir = pwd->pwd_jdir; | ||||
startdir_used = 0; | startdir_used = 0; | ||||
dp = NULL; | dp = NULL; | ||||
cnp->cn_nameptr = cnp->cn_pnbuf; | cnp->cn_nameptr = cnp->cn_pnbuf; | ||||
if (cnp->cn_pnbuf[0] == '/') { | if (cnp->cn_pnbuf[0] == '/') { | ||||
ndp->ni_resflags |= NIRES_ABS; | ndp->ni_resflags |= NIRES_ABS; | ||||
error = namei_handle_root(ndp, &dp, 2); | error = namei_handle_root(ndp, &dp, 2); | ||||
if (error != 0) { | if (error != 0) { | ||||
/* | /* | ||||
* Simplify error handling, we should almost never be | * Simplify error handling, we should almost never be | ||||
* here. | * here. | ||||
*/ | */ | ||||
vrefact(ndp->ni_rootdir); | vrefact(ndp->ni_rootdir); | ||||
} | } | ||||
} else { | } else { | ||||
if (ndp->ni_startdir != NULL) { | if (ndp->ni_startdir != NULL) { | ||||
vrefact(ndp->ni_rootdir); | vrefact(ndp->ni_rootdir); | ||||
dp = ndp->ni_startdir; | dp = ndp->ni_startdir; | ||||
startdir_used = 1; | startdir_used = 1; | ||||
} else if (ndp->ni_dirfd == AT_FDCWD) { | } else if (ndp->ni_dirfd == AT_FDCWD) { | ||||
dp = fdp->fd_cdir; | dp = pwd->pwd_cdir; | ||||
if (dp == ndp->ni_rootdir) { | if (dp == ndp->ni_rootdir) { | ||||
vrefactn(dp, 2); | vrefactn(dp, 2); | ||||
} else { | } else { | ||||
vrefact(ndp->ni_rootdir); | vrefact(ndp->ni_rootdir); | ||||
vrefact(dp); | vrefact(dp); | ||||
} | } | ||||
} else { | } else { | ||||
vrefact(ndp->ni_rootdir); | vrefact(ndp->ni_rootdir); | ||||
rights = ndp->ni_rightsneeded; | rights = ndp->ni_rightsneeded; | ||||
cap_rights_set_one(&rights, CAP_LOOKUP); | cap_rights_set_one(&rights, CAP_LOOKUP); | ||||
if (cnp->cn_flags & AUDITVNODE1) | if (cnp->cn_flags & AUDITVNODE1) | ||||
AUDIT_ARG_ATFD1(ndp->ni_dirfd); | AUDIT_ARG_ATFD1(ndp->ni_dirfd); | ||||
if (cnp->cn_flags & AUDITVNODE2) | if (cnp->cn_flags & AUDITVNODE2) | ||||
AUDIT_ARG_ATFD2(ndp->ni_dirfd); | AUDIT_ARG_ATFD2(ndp->ni_dirfd); | ||||
/* | /* | ||||
* Effectively inlined fgetvp_rights, because we need to | * Effectively inlined fgetvp_rights, because we need to | ||||
* inspect the file as well as grabbing the vnode. | * inspect the file as well as grabbing the vnode. | ||||
*/ | */ | ||||
error = fget_cap_locked(fdp, ndp->ni_dirfd, &rights, | error = fget_cap(td, ndp->ni_dirfd, &rights, | ||||
&dfp, &ndp->ni_filecaps); | &dfp, &ndp->ni_filecaps); | ||||
if (error != 0) { | if (error != 0) { | ||||
/* | /* | ||||
* Preserve the error; it should either be EBADF | * Preserve the error; it should either be EBADF | ||||
* or capability-related, both of which can be | * or capability-related, both of which can be | ||||
* safely returned to the caller. | * safely returned to the caller. | ||||
*/ | */ | ||||
} else if (dfp->f_ops == &badfileops) { | } else { | ||||
if (dfp->f_ops == &badfileops) { | |||||
error = EBADF; | error = EBADF; | ||||
} else if (dfp->f_vnode == NULL) { | } else if (dfp->f_vnode == NULL) { | ||||
error = ENOTDIR; | error = ENOTDIR; | ||||
} else { | } else { | ||||
dp = dfp->f_vnode; | dp = dfp->f_vnode; | ||||
vrefact(dp); | vrefact(dp); | ||||
if ((dfp->f_flag & FSEARCH) != 0) | if ((dfp->f_flag & FSEARCH) != 0) | ||||
cnp->cn_flags |= NOEXECCHECK; | cnp->cn_flags |= NOEXECCHECK; | ||||
} | } | ||||
fdrop(dfp, td); | |||||
} | |||||
#ifdef CAPABILITIES | #ifdef CAPABILITIES | ||||
/* | /* | ||||
* If file descriptor doesn't have all rights, | * If file descriptor doesn't have all rights, | ||||
* all lookups relative to it must also be | * all lookups relative to it must also be | ||||
* strictly relative. | * strictly relative. | ||||
*/ | */ | ||||
CAP_ALL(&rights); | CAP_ALL(&rights); | ||||
if (!cap_rights_contains(&ndp->ni_filecaps.fc_rights, | if (!cap_rights_contains(&ndp->ni_filecaps.fc_rights, | ||||
&rights) || | &rights) || | ||||
ndp->ni_filecaps.fc_fcntls != CAP_FCNTL_ALL || | ndp->ni_filecaps.fc_fcntls != CAP_FCNTL_ALL || | ||||
ndp->ni_filecaps.fc_nioctls != -1) { | ndp->ni_filecaps.fc_nioctls != -1) { | ||||
ndp->ni_lcf |= NI_LCF_STRICTRELATIVE; | ndp->ni_lcf |= NI_LCF_STRICTRELATIVE; | ||||
} | } | ||||
#endif | #endif | ||||
} | } | ||||
if (error == 0 && dp->v_type != VDIR) | if (error == 0 && dp->v_type != VDIR) | ||||
error = ENOTDIR; | error = ENOTDIR; | ||||
} | } | ||||
if (error == 0 && (cnp->cn_flags & BENEATH) != 0) { | if (error == 0 && (cnp->cn_flags & BENEATH) != 0) { | ||||
if (ndp->ni_dirfd == AT_FDCWD) { | if (ndp->ni_dirfd == AT_FDCWD) { | ||||
ndp->ni_beneath_latch = fdp->fd_cdir; | ndp->ni_beneath_latch = pwd->pwd_cdir; | ||||
vrefact(ndp->ni_beneath_latch); | vrefact(ndp->ni_beneath_latch); | ||||
} else { | } else { | ||||
rights = ndp->ni_rightsneeded; | rights = ndp->ni_rightsneeded; | ||||
cap_rights_set_one(&rights, CAP_LOOKUP); | cap_rights_set_one(&rights, CAP_LOOKUP); | ||||
error = fgetvp_rights(td, ndp->ni_dirfd, &rights, | error = fgetvp_rights(td, ndp->ni_dirfd, &rights, | ||||
&dirfd_caps, &ndp->ni_beneath_latch); | &dirfd_caps, &ndp->ni_beneath_latch); | ||||
if (error == 0 && dp->v_type != VDIR) { | if (error == 0 && dp->v_type != VDIR) { | ||||
vrele(ndp->ni_beneath_latch); | vrele(ndp->ni_beneath_latch); | ||||
error = ENOTDIR; | error = ENOTDIR; | ||||
} | } | ||||
} | } | ||||
if (error == 0) | if (error == 0) | ||||
ndp->ni_lcf |= NI_LCF_LATCH; | ndp->ni_lcf |= NI_LCF_LATCH; | ||||
} | } | ||||
FILEDESC_SUNLOCK(fdp); | |||||
/* | /* | ||||
* If we are auditing the kernel pathname, save the user pathname. | * If we are auditing the kernel pathname, save the user pathname. | ||||
*/ | */ | ||||
if (cnp->cn_flags & AUDITVNODE1) | if (cnp->cn_flags & AUDITVNODE1) | ||||
AUDIT_ARG_UPATH1_VP(td, ndp->ni_rootdir, dp, cnp->cn_pnbuf); | AUDIT_ARG_UPATH1_VP(td, ndp->ni_rootdir, dp, cnp->cn_pnbuf); | ||||
if (cnp->cn_flags & AUDITVNODE2) | if (cnp->cn_flags & AUDITVNODE2) | ||||
AUDIT_ARG_UPATH2_VP(td, ndp->ni_rootdir, dp, cnp->cn_pnbuf); | AUDIT_ARG_UPATH2_VP(td, ndp->ni_rootdir, dp, cnp->cn_pnbuf); | ||||
if (ndp->ni_startdir != NULL && !startdir_used) | if (ndp->ni_startdir != NULL && !startdir_used) | ||||
Show All 29 Lines | if ((cnp->cn_flags & ISSYMLINK) == 0) { | ||||
if ((ndp->ni_lcf & (NI_LCF_BENEATH_ABS | | if ((ndp->ni_lcf & (NI_LCF_BENEATH_ABS | | ||||
NI_LCF_BENEATH_LATCHED)) == NI_LCF_BENEATH_ABS) { | NI_LCF_BENEATH_LATCHED)) == NI_LCF_BENEATH_ABS) { | ||||
NDFREE(ndp, 0); | NDFREE(ndp, 0); | ||||
error = ENOTCAPABLE; | error = ENOTCAPABLE; | ||||
} | } | ||||
nameicap_cleanup(ndp, true); | nameicap_cleanup(ndp, true); | ||||
SDT_PROBE2(vfs, namei, lookup, return, error, | SDT_PROBE2(vfs, namei, lookup, return, error, | ||||
(error == 0 ? ndp->ni_vp : NULL)); | (error == 0 ? ndp->ni_vp : NULL)); | ||||
pwd_drop(pwd); | |||||
return (error); | return (error); | ||||
} | } | ||||
if (ndp->ni_loopcnt++ >= MAXSYMLINKS) { | if (ndp->ni_loopcnt++ >= MAXSYMLINKS) { | ||||
error = ELOOP; | error = ELOOP; | ||||
break; | break; | ||||
} | } | ||||
#ifdef MAC | #ifdef MAC | ||||
if ((cnp->cn_flags & NOMACCHECK) == 0) { | if ((cnp->cn_flags & NOMACCHECK) == 0) { | ||||
▲ Show 20 Lines • Show All 59 Lines • ▼ Show 20 Lines | #endif | ||||
ndp->ni_vp = NULL; | ndp->ni_vp = NULL; | ||||
vrele(ndp->ni_dvp); | vrele(ndp->ni_dvp); | ||||
out: | out: | ||||
vrele(ndp->ni_rootdir); | vrele(ndp->ni_rootdir); | ||||
MPASS(error != 0); | MPASS(error != 0); | ||||
namei_cleanup_cnp(cnp); | namei_cleanup_cnp(cnp); | ||||
nameicap_cleanup(ndp, true); | nameicap_cleanup(ndp, true); | ||||
SDT_PROBE2(vfs, namei, lookup, return, error, NULL); | SDT_PROBE2(vfs, namei, lookup, return, error, NULL); | ||||
pwd_drop(pwd); | |||||
return (error); | return (error); | ||||
} | } | ||||
static int | static int | ||||
compute_cn_lkflags(struct mount *mp, int lkflags, int cnflags) | compute_cn_lkflags(struct mount *mp, int lkflags, int cnflags) | ||||
{ | { | ||||
if (mp == NULL || ((lkflags & LK_SHARED) && | if (mp == NULL || ((lkflags & LK_SHARED) && | ||||
▲ Show 20 Lines • Show All 887 Lines • Show Last 20 Lines |