Changeset View
Changeset View
Standalone View
Standalone View
sys/kern/vfs_lookup.c
Show First 20 Lines • Show All 65 Lines • ▼ Show 20 Lines | |||||
#include <security/mac/mac_framework.h> | #include <security/mac/mac_framework.h> | ||||
#include <vm/uma.h> | #include <vm/uma.h> | ||||
#define NAMEI_DIAGNOSTIC 1 | #define NAMEI_DIAGNOSTIC 1 | ||||
#undef NAMEI_DIAGNOSTIC | #undef NAMEI_DIAGNOSTIC | ||||
SDT_PROVIDER_DECLARE(vfs); | SDT_PROVIDER_DECLARE(vfs); | ||||
SDT_PROBE_DEFINE3(vfs, namei, lookup, entry, "struct vnode *", "char *", | SDT_PROBE_DEFINE4(vfs, namei, lookup, entry, "struct vnode *", "char *", | ||||
"unsigned long"); | "unsigned long", "bool"); | ||||
SDT_PROBE_DEFINE2(vfs, namei, lookup, return, "int", "struct vnode *"); | SDT_PROBE_DEFINE3(vfs, namei, lookup, return, "int", "struct vnode *", "bool"); | ||||
/* Allocation zone for namei. */ | /* Allocation zone for namei. */ | ||||
uma_zone_t namei_zone; | uma_zone_t namei_zone; | ||||
/* Placeholder vnode for mp traversal. */ | /* Placeholder vnode for mp traversal. */ | ||||
static struct vnode *vp_crossmp; | static struct vnode *vp_crossmp; | ||||
static int | static int | ||||
▲ Show 20 Lines • Show All 190 Lines • ▼ Show 20 Lines | while (*(cnp->cn_nameptr) == '/') { | ||||
cnp->cn_nameptr++; | cnp->cn_nameptr++; | ||||
ndp->ni_pathlen--; | ndp->ni_pathlen--; | ||||
} | } | ||||
*dpp = ndp->ni_rootdir; | *dpp = ndp->ni_rootdir; | ||||
vrefact(*dpp); | vrefact(*dpp); | ||||
return (0); | return (0); | ||||
} | } | ||||
/* | static int | ||||
* Convert a pathname into a pointer to a locked vnode. | namei_setup(struct nameidata *ndp, struct vnode **dpp, struct pwd **pwdp) | ||||
* | |||||
* 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 | |||||
* space or kernel space. | |||||
* | |||||
* Overall outline of namei: | |||||
* | |||||
* copy in name | |||||
* get starting directory | |||||
* while (!done && !error) { | |||||
* call lookup to search path. | |||||
* if symbolic link, massage name in buffer and continue | |||||
* } | |||||
*/ | |||||
int | |||||
namei(struct nameidata *ndp) | |||||
{ | { | ||||
char *cp; /* pointer into pathname argument */ | |||||
struct vnode *dp; /* the directory we are searching */ | |||||
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 pwd *pwd; | struct pwd *pwd; | ||||
cap_rights_t rights; | cap_rights_t rights; | ||||
struct filecaps dirfd_caps; | struct filecaps dirfd_caps; | ||||
struct uio auio; | int error, 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; | |||||
ndp->ni_cnd.cn_cred = ndp->ni_cnd.cn_thread->td_ucred; | |||||
KASSERT(cnp->cn_cred && p, ("namei: bad cred/proc")); | |||||
KASSERT((cnp->cn_nameiop & (~OPMASK)) == 0, | |||||
("namei: nameiop contaminated with flags")); | |||||
KASSERT((cnp->cn_flags & OPMASK) == 0, | |||||
("namei: flags contaminated with nameiops")); | |||||
MPASS(ndp->ni_startdir == NULL || ndp->ni_startdir->v_type == VDIR || | |||||
ndp->ni_startdir->v_type == VBAD); | |||||
TAILQ_INIT(&ndp->ni_cap_tracker); | |||||
ndp->ni_lcf = 0; | |||||
/* We will set this ourselves if we need it. */ | *pwdp = NULL; | ||||
cnp->cn_flags &= ~TRAILINGSLASH; | |||||
/* | |||||
* Get a buffer for the name to be translated, and copy the | |||||
* name into the buffer. | |||||
*/ | |||||
if ((cnp->cn_flags & HASBUF) == 0) | |||||
cnp->cn_pnbuf = uma_zalloc(namei_zone, M_WAITOK); | |||||
if (ndp->ni_segflg == UIO_SYSSPACE) | |||||
error = copystr(ndp->ni_dirp, cnp->cn_pnbuf, MAXPATHLEN, | |||||
&ndp->ni_pathlen); | |||||
else | |||||
error = copyinstr(ndp->ni_dirp, cnp->cn_pnbuf, MAXPATHLEN, | |||||
&ndp->ni_pathlen); | |||||
/* | |||||
* Don't allow empty pathnames. | |||||
*/ | |||||
if (error == 0 && *cnp->cn_pnbuf == '\0') | |||||
error = ENOENT; | |||||
#ifdef CAPABILITY_MODE | #ifdef CAPABILITY_MODE | ||||
/* | /* | ||||
* In capability mode, lookups must be restricted to happen in | * In capability mode, lookups must be restricted to happen in | ||||
* the subtree with the root specified by the file descriptor: | * the subtree with the root specified by the file descriptor: | ||||
* - The root must be real file descriptor, not the pseudo-descriptor | * - The root must be real file descriptor, not the pseudo-descriptor | ||||
* AT_FDCWD. | * AT_FDCWD. | ||||
* - The passed path must be relative and not absolute. | * - The passed path must be relative and not absolute. | ||||
* - If lookup_cap_dotdot is disabled, path must not contain the | * - If lookup_cap_dotdot is disabled, path must not contain the | ||||
* '..' components. | * '..' components. | ||||
* - If lookup_cap_dotdot is enabled, we verify that all '..' | * - If lookup_cap_dotdot is enabled, we verify that all '..' | ||||
* components lookups result in the directories which were | * components lookups result in the directories which were | ||||
* previously walked by us, which prevents an escape from | * previously walked by us, which prevents an escape from | ||||
* the relative root. | * the relative root. | ||||
*/ | */ | ||||
if (error == 0 && IN_CAPABILITY_MODE(td) && | if (IN_CAPABILITY_MODE(td) && (cnp->cn_flags & NOCAPCHECK) == 0) { | ||||
(cnp->cn_flags & NOCAPCHECK) == 0) { | |||||
ndp->ni_lcf |= NI_LCF_STRICTRELATIVE; | ndp->ni_lcf |= NI_LCF_STRICTRELATIVE; | ||||
if (ndp->ni_dirfd == AT_FDCWD) { | if (ndp->ni_dirfd == AT_FDCWD) { | ||||
#ifdef KTRACE | #ifdef KTRACE | ||||
if (KTRPOINT(td, KTR_CAPFAIL)) | if (KTRPOINT(td, KTR_CAPFAIL)) | ||||
ktrcapfail(CAPFAIL_LOOKUP, NULL, NULL); | ktrcapfail(CAPFAIL_LOOKUP, NULL, NULL); | ||||
#endif | #endif | ||||
error = ECAPMODE; | return (ECAPMODE); | ||||
} | } | ||||
} | } | ||||
#endif | #endif | ||||
if (error != 0) { | error = 0; | ||||
namei_cleanup_cnp(cnp); | |||||
ndp->ni_vp = NULL; | |||||
return (error); | |||||
} | |||||
ndp->ni_loopcnt = 0; | |||||
#ifdef KTRACE | |||||
if (KTRPOINT(td, KTR_NAMEI)) { | |||||
KASSERT(cnp->cn_thread == curthread, | |||||
("namei not using curthread")); | |||||
ktrnamei(cnp->cn_pnbuf); | |||||
} | |||||
#endif | |||||
/* | /* | ||||
* Get starting point for the translation. | * Get starting point for the translation. | ||||
*/ | */ | ||||
pwd = pwd_hold(td); | 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 = pwd->pwd_rdir; | ndp->ni_rootdir = pwd->pwd_rdir; | ||||
ndp->ni_topdir = pwd->pwd_jdir; | ndp->ni_topdir = pwd->pwd_jdir; | ||||
startdir_used = 0; | |||||
dp = NULL; | |||||
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); | error = namei_handle_root(ndp, dpp); | ||||
} else { | } else { | ||||
if (ndp->ni_startdir != NULL) { | if (ndp->ni_startdir != NULL) { | ||||
dp = ndp->ni_startdir; | *dpp = ndp->ni_startdir; | ||||
startdir_used = 1; | startdir_used = 1; | ||||
} else if (ndp->ni_dirfd == AT_FDCWD) { | } else if (ndp->ni_dirfd == AT_FDCWD) { | ||||
dp = pwd->pwd_cdir; | *dpp = pwd->pwd_cdir; | ||||
vrefact(dp); | vrefact(*dpp); | ||||
} else { | } else { | ||||
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); | ||||
Show All 10 Lines | if (ndp->ni_startdir != NULL) { | ||||
* safely returned to the caller. | * safely returned to the caller. | ||||
*/ | */ | ||||
} else { | } else { | ||||
if (dfp->f_ops == &badfileops) { | 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; | *dpp = dfp->f_vnode; | ||||
vrefact(dp); | vrefact(*dpp); | ||||
if ((dfp->f_flag & FSEARCH) != 0) | if ((dfp->f_flag & FSEARCH) != 0) | ||||
cnp->cn_flags |= NOEXECCHECK; | cnp->cn_flags |= NOEXECCHECK; | ||||
} | } | ||||
fdrop(dfp, td); | 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 && (*dpp)->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 = pwd->pwd_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 && (*dpp)->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; | ||||
} | } | ||||
/* | /* | ||||
* 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, *dpp, 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, *dpp, cnp->cn_pnbuf); | ||||
if (ndp->ni_startdir != NULL && !startdir_used) | if (ndp->ni_startdir != NULL && !startdir_used) | ||||
vrele(ndp->ni_startdir); | vrele(ndp->ni_startdir); | ||||
if (error != 0) { | if (error != 0) { | ||||
if (dp != NULL) | if (*dpp != NULL) | ||||
vrele(dp); | vrele(*dpp); | ||||
goto out; | return (error); | ||||
} | } | ||||
MPASS((ndp->ni_lcf & (NI_LCF_BENEATH_ABS | NI_LCF_LATCH)) != | MPASS((ndp->ni_lcf & (NI_LCF_BENEATH_ABS | NI_LCF_LATCH)) != | ||||
NI_LCF_BENEATH_ABS); | NI_LCF_BENEATH_ABS); | ||||
if (((ndp->ni_lcf & NI_LCF_STRICTRELATIVE) != 0 && | if (((ndp->ni_lcf & NI_LCF_STRICTRELATIVE) != 0 && | ||||
lookup_cap_dotdot != 0) || | lookup_cap_dotdot != 0) || | ||||
((ndp->ni_lcf & NI_LCF_STRICTRELATIVE) == 0 && | ((ndp->ni_lcf & NI_LCF_STRICTRELATIVE) == 0 && | ||||
(cnp->cn_flags & BENEATH) != 0)) | (cnp->cn_flags & BENEATH) != 0)) | ||||
ndp->ni_lcf |= NI_LCF_CAP_DOTDOT; | ndp->ni_lcf |= NI_LCF_CAP_DOTDOT; | ||||
SDT_PROBE3(vfs, namei, lookup, entry, dp, cnp->cn_pnbuf, | SDT_PROBE4(vfs, namei, lookup, entry, *dpp, cnp->cn_pnbuf, | ||||
cnp->cn_flags); | cnp->cn_flags, false); | ||||
*pwdp = pwd; | |||||
return (0); | |||||
} | |||||
/* | |||||
* Convert a pathname into a pointer to a locked vnode. | |||||
* | |||||
* 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 | |||||
* space or kernel space. | |||||
* | |||||
* Overall outline of namei: | |||||
* | |||||
* copy in name | |||||
* get starting directory | |||||
* while (!done && !error) { | |||||
* call lookup to search path. | |||||
* if symbolic link, massage name in buffer and continue | |||||
* } | |||||
*/ | |||||
int | |||||
namei(struct nameidata *ndp) | |||||
{ | |||||
char *cp; /* pointer into pathname argument */ | |||||
struct vnode *dp; /* the directory we are searching */ | |||||
struct iovec aiov; /* uio for reading symbolic links */ | |||||
struct componentname *cnp; | |||||
struct thread *td; | |||||
struct proc *p; | |||||
struct pwd *pwd; | |||||
struct uio auio; | |||||
int error, linklen; | |||||
enum cache_fpl_status status; | |||||
cnp = &ndp->ni_cnd; | |||||
td = cnp->cn_thread; | |||||
p = td->td_proc; | |||||
ndp->ni_cnd.cn_cred = ndp->ni_cnd.cn_thread->td_ucred; | |||||
KASSERT(cnp->cn_cred && p, ("namei: bad cred/proc")); | |||||
KASSERT((cnp->cn_nameiop & (~OPMASK)) == 0, | |||||
("namei: nameiop contaminated with flags")); | |||||
KASSERT((cnp->cn_flags & OPMASK) == 0, | |||||
("namei: flags contaminated with nameiops")); | |||||
MPASS(ndp->ni_startdir == NULL || ndp->ni_startdir->v_type == VDIR || | |||||
ndp->ni_startdir->v_type == VBAD); | |||||
TAILQ_INIT(&ndp->ni_cap_tracker); | |||||
ndp->ni_lcf = 0; | |||||
ndp->ni_loopcnt = 0; | |||||
dp = NULL; | |||||
/* We will set this ourselves if we need it. */ | |||||
cnp->cn_flags &= ~TRAILINGSLASH; | |||||
ndp->ni_vp = NULL; | |||||
/* | |||||
* Get a buffer for the name to be translated, and copy the | |||||
* name into the buffer. | |||||
*/ | |||||
if ((cnp->cn_flags & HASBUF) == 0) | |||||
cnp->cn_pnbuf = uma_zalloc(namei_zone, M_WAITOK); | |||||
if (ndp->ni_segflg == UIO_SYSSPACE) | |||||
error = copystr(ndp->ni_dirp, cnp->cn_pnbuf, MAXPATHLEN, | |||||
&ndp->ni_pathlen); | |||||
else | |||||
error = copyinstr(ndp->ni_dirp, cnp->cn_pnbuf, MAXPATHLEN, | |||||
&ndp->ni_pathlen); | |||||
if (error != 0) { | |||||
namei_cleanup_cnp(cnp); | |||||
return (error); | |||||
} | |||||
cnp->cn_nameptr = cnp->cn_pnbuf; | |||||
/* | |||||
* Don't allow empty pathnames. | |||||
*/ | |||||
if (*cnp->cn_pnbuf == '\0') { | |||||
namei_cleanup_cnp(cnp); | |||||
return (ENOENT); | |||||
} | |||||
#ifdef KTRACE | |||||
if (KTRPOINT(td, KTR_NAMEI)) { | |||||
KASSERT(cnp->cn_thread == curthread, | |||||
("namei not using curthread")); | |||||
ktrnamei(cnp->cn_pnbuf); | |||||
} | |||||
#endif | |||||
/* | |||||
* First try looking up the target without locking any vnodes. | |||||
kib: May be explain that lockless refers to the vnode locking. | |||||
* | |||||
* We may need to start from scratch or pick up where it left off. | |||||
*/ | |||||
error = cache_fplookup(ndp, &status, &pwd); | |||||
switch (status) { | |||||
case CACHE_FPL_STATUS_UNSET: | |||||
__assert_unreachable(); | |||||
break; | |||||
case CACHE_FPL_STATUS_HANDLED: | |||||
return (error); | |||||
case CACHE_FPL_STATUS_PARTIAL: | |||||
dp = ndp->ni_startdir; | |||||
break; | |||||
case CACHE_FPL_STATUS_ABORTED: | |||||
error = namei_setup(ndp, &dp, &pwd); | |||||
if (error != 0) { | |||||
namei_cleanup_cnp(cnp); | |||||
return (error); | |||||
} | |||||
break; | |||||
} | |||||
/* | |||||
* Locked lookup. | |||||
*/ | |||||
for (;;) { | for (;;) { | ||||
ndp->ni_startdir = dp; | ndp->ni_startdir = dp; | ||||
error = lookup(ndp); | error = lookup(ndp); | ||||
if (error != 0) | if (error != 0) | ||||
goto out; | goto out; | ||||
/* | /* | ||||
* If not a symbolic link, we're done. | * If not a symbolic link, we're done. | ||||
*/ | */ | ||||
if ((cnp->cn_flags & ISSYMLINK) == 0) { | if ((cnp->cn_flags & ISSYMLINK) == 0) { | ||||
if ((cnp->cn_flags & (SAVENAME | SAVESTART)) == 0) { | if ((cnp->cn_flags & (SAVENAME | SAVESTART)) == 0) { | ||||
namei_cleanup_cnp(cnp); | namei_cleanup_cnp(cnp); | ||||
} else | } else | ||||
cnp->cn_flags |= HASBUF; | cnp->cn_flags |= HASBUF; | ||||
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_PROBE3(vfs, namei, lookup, return, error, | ||||
(error == 0 ? ndp->ni_vp : NULL)); | (error == 0 ? ndp->ni_vp : NULL), false); | ||||
pwd_drop(pwd); | 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 | ||||
▲ Show 20 Lines • Show All 58 Lines • ▼ Show 20 Lines | #endif | ||||
} | } | ||||
vput(ndp->ni_vp); | vput(ndp->ni_vp); | ||||
ndp->ni_vp = NULL; | ndp->ni_vp = NULL; | ||||
vrele(ndp->ni_dvp); | vrele(ndp->ni_dvp); | ||||
out: | out: | ||||
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_PROBE3(vfs, namei, lookup, return, error, NULL, false); | ||||
pwd_drop(pwd); | 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) | ||||
{ | { | ||||
▲ Show 20 Lines • Show All 888 Lines • Show Last 20 Lines |
May be explain that lockless refers to the vnode locking.