Changeset View
Standalone View
sys/kern/vfs_lookup.c
Show First 20 Lines • Show All 67 Lines • ▼ Show 20 Lines | |||||
#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_DEFINE3(vfs, namei, lookup, entry, "struct vnode *", "char *", | ||||
"unsigned long"); | "unsigned long"); | ||||
SDT_PROBE_DEFINE2(vfs, namei, lookup, return, "int", "struct vnode *"); | SDT_PROBE_DEFINE2(vfs, namei, lookup, return, "int", "struct vnode *"); | ||||
/* | /* 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; | ||||
struct nameicap_tracker { | |||||
struct vnode *dp; | |||||
TAILQ_ENTRY(nameicap_tracker) nm_link; | |||||
}; | |||||
/* Zone for cap mode tracker elements used for dotdot capability checks. */ | |||||
static uma_zone_t nt_zone; | |||||
static void | static void | ||||
nameiinit(void *dummy __unused) | nameiinit(void *dummy __unused) | ||||
{ | { | ||||
namei_zone = uma_zcreate("NAMEI", MAXPATHLEN, NULL, NULL, NULL, NULL, | namei_zone = uma_zcreate("NAMEI", MAXPATHLEN, NULL, NULL, NULL, NULL, | ||||
UMA_ALIGN_PTR, 0); | UMA_ALIGN_PTR, 0); | ||||
nt_zone = uma_zcreate("rentr", sizeof(struct nameicap_tracker), | |||||
rwatson: I wonder if "nameicap_tracker" or similar might be a better string than "rentr"? | |||||
kibAuthorUnsubmitted Not Done Inline ActionsI highly dislike long (or worse, containing space-delimited words) names for zones. Both vmstat -z and ddb 'show uma' become hard to read and parse. Anybody who looks at the zone needs to grep the source anyway. kib: I highly dislike long (or worse, containing space-delimited words) names for zones. Both… | |||||
NULL, NULL, NULL, NULL, sizeof(void *), 0); | |||||
getnewvnode("crossmp", NULL, &dead_vnodeops, &vp_crossmp); | getnewvnode("crossmp", NULL, &dead_vnodeops, &vp_crossmp); | ||||
vn_lock(vp_crossmp, LK_EXCLUSIVE); | vn_lock(vp_crossmp, LK_EXCLUSIVE); | ||||
VN_LOCK_ASHARE(vp_crossmp); | VN_LOCK_ASHARE(vp_crossmp); | ||||
VOP_UNLOCK(vp_crossmp, 0); | VOP_UNLOCK(vp_crossmp, 0); | ||||
} | } | ||||
SYSINIT(vfs, SI_SUB_VFS, SI_ORDER_SECOND, nameiinit, NULL); | SYSINIT(vfs, SI_SUB_VFS, SI_ORDER_SECOND, nameiinit, NULL); | ||||
static int lookup_shared = 1; | static int lookup_shared = 1; | ||||
SYSCTL_INT(_vfs, OID_AUTO, lookup_shared, CTLFLAG_RWTUN, &lookup_shared, 0, | SYSCTL_INT(_vfs, OID_AUTO, lookup_shared, CTLFLAG_RWTUN, &lookup_shared, 0, | ||||
"Enables/Disables shared locks for path name translation"); | "Enables/Disables shared locks for path name translation"); | ||||
static void | static void | ||||
nameicap_tracker_add(struct nameidata *ndp, struct vnode *dp) | |||||
{ | |||||
Not Done Inline ActionsMaybe enable \"..\" components in path lookup in capability mode? I think just "enable" is canonical, and nice to be shorter. You may want to also add a comment before this that the intent is this becomes unconditionally enabled, but that a sysctl is provided until verification efforts are complete. emaste: Maybe `enable \"..\" components in path lookup in capability mode`? I think just "enable" is… | |||||
struct nameicap_tracker *nt; | |||||
if (ndp->ni_strictrelative == 0 || dp->v_type != VDIR) | |||||
return; | |||||
nt = uma_zalloc(nt_zone, M_WAITOK); | |||||
vhold(dp); | |||||
nt->dp = dp; | |||||
TAILQ_INSERT_TAIL(&ndp->ni_cap_tracker, nt, nm_link); | |||||
} | |||||
static void | |||||
nameicap_cleanup(struct nameidata *ndp) | |||||
{ | |||||
struct nameicap_tracker *nt, *nt1; | |||||
KASSERT(TAILQ_EMPTY(&ndp->ni_cap_tracker) || | |||||
ndp->ni_strictrelative != 0, ("not strictrelative")); | |||||
TAILQ_FOREACH_SAFE(nt, &ndp->ni_cap_tracker, nm_link, nt1) { | |||||
TAILQ_REMOVE(&ndp->ni_cap_tracker, nt, nm_link); | |||||
vdrop(nt->dp); | |||||
uma_zfree(nt_zone, nt); | |||||
} | |||||
} | |||||
/* | |||||
* For dotdot lookups in capability mode, only allow the component | |||||
* lookup to succeed if the resulting directory was already traversed | |||||
Not Done Inline Actionsperhaps s/resulted/resulting/? rwatson: perhaps s/resulted/resulting/? | |||||
* during the operation. Also fail dotdot lookups for non-local | |||||
* filesystems, where external agents might assist local lookups to | |||||
* escape the compartment. | |||||
Not Done Inline ActionsI'm still pondering the rationale for this choice but feel that being conservative is safer. rwatson: I'm still pondering the rationale for this choice but feel that being conservative is safer. | |||||
*/ | |||||
static int | |||||
nameicap_check_dotdot(struct nameidata *ndp, struct vnode *dp) | |||||
{ | |||||
struct nameicap_tracker *nt; | |||||
struct mount *mp; | |||||
if (ndp->ni_strictrelative == 0 || dp == NULL || dp->v_type != VDIR) | |||||
return (0); | |||||
mp = dp->v_mount; | |||||
if (mp != NULL && (mp->mnt_flag & MNT_LOCAL) == 0) | |||||
return (ENOTCAPABLE); | |||||
rwatsonUnsubmitted Not Done Inline ActionsMy initial thinking was that this new approach is safe even for !MNT_LOCAL -- do we have reason to think we need to retain this check? rwatson: My initial thinking was that this new approach is safe even for !MNT_LOCAL -- do we have reason… | |||||
kibAuthorUnsubmitted Not Done Inline ActionsIt depends on what you consider the adverse behavior on the server ? If you consider it fine to remove the check, I will just do it without arguing. kib: It depends on what you consider the adverse behavior on the server ? If you consider it fine… | |||||
TAILQ_FOREACH_REVERSE(nt, &ndp->ni_cap_tracker, nameicap_tracker_head, | |||||
nm_link) { | |||||
if (dp == nt->dp) | |||||
return (0); | |||||
} | |||||
return (ENOTCAPABLE); | |||||
} | |||||
static void | |||||
namei_cleanup_cnp(struct componentname *cnp) | namei_cleanup_cnp(struct componentname *cnp) | ||||
{ | { | ||||
uma_zfree(namei_zone, cnp->cn_pnbuf); | uma_zfree(namei_zone, cnp->cn_pnbuf); | ||||
#ifdef DIAGNOSTIC | #ifdef DIAGNOSTIC | ||||
cnp->cn_pnbuf = NULL; | cnp->cn_pnbuf = NULL; | ||||
cnp->cn_nameptr = NULL; | cnp->cn_nameptr = NULL; | ||||
#endif | #endif | ||||
} | } | ||||
static int | static int | ||||
Show All 40 Lines | |||||
*/ | */ | ||||
int | int | ||||
namei(struct nameidata *ndp) | namei(struct nameidata *ndp) | ||||
{ | { | ||||
struct filedesc *fdp; /* pointer to file descriptor state */ | 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 thread *td; | |||||
struct proc *p; | |||||
cap_rights_t rights; | |||||
struct uio auio; | struct uio auio; | ||||
int error, linklen, startdir_used; | int error, linklen, startdir_used; | ||||
struct componentname *cnp = &ndp->ni_cnd; | |||||
struct thread *td = cnp->cn_thread; | |||||
struct proc *p = td->td_proc; | |||||
cnp = &ndp->ni_cnd; | |||||
td = cnp->cn_thread; | |||||
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); | ||||
if (!lookup_shared) | if (!lookup_shared) | ||||
cnp->cn_flags &= ~LOCKSHARED; | cnp->cn_flags &= ~LOCKSHARED; | ||||
fdp = p->p_fd; | fdp = p->p_fd; | ||||
TAILQ_INIT(&ndp->ni_cap_tracker); | |||||
/* 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 | ||||
* name into the buffer. | * name into the buffer. | ||||
*/ | */ | ||||
if ((cnp->cn_flags & HASBUF) == 0) | if ((cnp->cn_flags & HASBUF) == 0) | ||||
cnp->cn_pnbuf = uma_zalloc(namei_zone, M_WAITOK); | cnp->cn_pnbuf = uma_zalloc(namei_zone, M_WAITOK); | ||||
if (ndp->ni_segflg == UIO_SYSSPACE) | if (ndp->ni_segflg == UIO_SYSSPACE) | ||||
error = copystr(ndp->ni_dirp, cnp->cn_pnbuf, | error = copystr(ndp->ni_dirp, cnp->cn_pnbuf, MAXPATHLEN, | ||||
MAXPATHLEN, (size_t *)&ndp->ni_pathlen); | &ndp->ni_pathlen); | ||||
else | else | ||||
error = copyinstr(ndp->ni_dirp, cnp->cn_pnbuf, | error = copyinstr(ndp->ni_dirp, cnp->cn_pnbuf, MAXPATHLEN, | ||||
MAXPATHLEN, (size_t *)&ndp->ni_pathlen); | &ndp->ni_pathlen); | ||||
/* | /* | ||||
* Don't allow empty pathnames. | * Don't allow empty pathnames. | ||||
*/ | */ | ||||
if (error == 0 && *cnp->cn_pnbuf == '\0') | if (error == 0 && *cnp->cn_pnbuf == '\0') | ||||
error = ENOENT; | error = ENOENT; | ||||
#ifdef CAPABILITY_MODE | #ifdef CAPABILITY_MODE | ||||
/* | /* | ||||
* In capability mode, lookups must be "strictly relative" (i.e. | * In capability mode, lookups must be restricted to happen in | ||||
* not an absolute path, and not containing '..' components) to | * the subtree with the root specified by the file descriptor: | ||||
* a real file descriptor, not the pseudo-descriptor AT_FDCWD. | * - The root must be real file descriptor, not the pseudo-descriptor | ||||
Not Done Inline ActionsWith the new approach, we might need to clarify that this is not quite what we implement, although it is the effect of what we implement. rwatson: With the new approach, we might need to clarify that this is not quite what we implement… | |||||
* AT_FDCWD. | |||||
* - The passed path must be relative and not absolute. | |||||
* - We verify that all '..' components lookups result in the | |||||
* directories which were previously walked by us, which | |||||
* prevents an escape from the relative root. | |||||
*/ | */ | ||||
if (error == 0 && IN_CAPABILITY_MODE(td) && | if (error == 0 && IN_CAPABILITY_MODE(td) && | ||||
(cnp->cn_flags & NOCAPCHECK) == 0) { | (cnp->cn_flags & NOCAPCHECK) == 0) { | ||||
ndp->ni_strictrelative = 1; | ndp->ni_strictrelative = 1; | ||||
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); | ||||
Show All 39 Lines | #endif | ||||
} else { | } else { | ||||
if (ndp->ni_startdir != NULL) { | if (ndp->ni_startdir != NULL) { | ||||
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 = fdp->fd_cdir; | ||||
VREF(dp); | VREF(dp); | ||||
} else { | } else { | ||||
cap_rights_t rights; | |||||
rights = ndp->ni_rightsneeded; | rights = ndp->ni_rightsneeded; | ||||
cap_rights_set(&rights, CAP_LOOKUP); | cap_rights_set(&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); | ||||
error = fgetvp_rights(td, ndp->ni_dirfd, | error = fgetvp_rights(td, ndp->ni_dirfd, | ||||
Show All 37 Lines | for (;;) { | ||||
* 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) { | ||||
vrele(ndp->ni_rootdir); | vrele(ndp->ni_rootdir); | ||||
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; | ||||
nameicap_cleanup(ndp); | |||||
SDT_PROBE2(vfs, namei, lookup, return, 0, ndp->ni_vp); | SDT_PROBE2(vfs, namei, lookup, return, 0, ndp->ni_vp); | ||||
return (0); | return (0); | ||||
} | } | ||||
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 57 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: | ||||
vrele(ndp->ni_rootdir); | vrele(ndp->ni_rootdir); | ||||
namei_cleanup_cnp(cnp); | namei_cleanup_cnp(cnp); | ||||
nameicap_cleanup(ndp); | |||||
SDT_PROBE2(vfs, namei, lookup, return, error, NULL); | SDT_PROBE2(vfs, namei, lookup, return, error, NULL); | ||||
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 180 Lines • ▼ Show 20 Lines | #endif | ||||
if ((cnp->cn_flags & ISLASTCN) != 0 && | if ((cnp->cn_flags & ISLASTCN) != 0 && | ||||
cnp->cn_namelen == 1 && cnp->cn_nameptr[0] == '.' && | cnp->cn_namelen == 1 && cnp->cn_nameptr[0] == '.' && | ||||
(cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME)) { | (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME)) { | ||||
error = EINVAL; | error = EINVAL; | ||||
goto bad; | goto bad; | ||||
} | } | ||||
nameicap_tracker_add(ndp, dp); | |||||
/* | /* | ||||
* Check for degenerate name (e.g. / or "") | * Check for degenerate name (e.g. / or "") | ||||
* which is a way of talking about a directory, | * which is a way of talking about a directory, | ||||
* e.g. like "/." or ".". | * e.g. like "/." or ".". | ||||
*/ | */ | ||||
if (cnp->cn_nameptr[0] == '\0') { | if (cnp->cn_nameptr[0] == '\0') { | ||||
if (dp->v_type != VDIR) { | if (dp->v_type != VDIR) { | ||||
error = ENOTDIR; | error = ENOTDIR; | ||||
Show All 17 Lines | if (cnp->cn_nameptr[0] == '\0') { | ||||
if (!(cnp->cn_flags & (LOCKPARENT | LOCKLEAF))) | if (!(cnp->cn_flags & (LOCKPARENT | LOCKLEAF))) | ||||
VOP_UNLOCK(dp, 0); | VOP_UNLOCK(dp, 0); | ||||
/* XXX This should probably move to the top of function. */ | /* XXX This should probably move to the top of function. */ | ||||
if (cnp->cn_flags & SAVESTART) | if (cnp->cn_flags & SAVESTART) | ||||
panic("lookup: SAVESTART"); | panic("lookup: SAVESTART"); | ||||
goto success; | goto success; | ||||
} | } | ||||
/* | /* | ||||
Done Inline ActionsIf this were earlier in dirloop, would we be able to remove the call at line 371? jonathan: If this were earlier in `dirloop`, would we be able to remove the call at line 371? | |||||
* Handle "..": five special cases. | * Handle "..": five special cases. | ||||
* 0. If doing a capability lookup, return ENOTCAPABLE (this is a | |||||
* fairly conservative design choice, but it's the only one that we | |||||
* are satisfied guarantees the property we're looking for). | |||||
* 1. Return an error if this is the last component of | * 1. Return an error if this is the last component of | ||||
* the name and the operation is DELETE or RENAME. | * the name and the operation is DELETE or RENAME. | ||||
* 2. If at root directory (e.g. after chroot) | * 2. If at root directory (e.g. after chroot) | ||||
* or at absolute root directory | * or at absolute root directory | ||||
* then ignore it so can't get out. | * then ignore it so can't get out. | ||||
* 3. If this vnode is the root of a mounted | * 3. If this vnode is the root of a mounted | ||||
* filesystem, then replace it with the | * filesystem, then replace it with the | ||||
* vnode which was mounted on so we take the | * vnode which was mounted on so we take the | ||||
* .. in the other filesystem. | * .. in the other filesystem. | ||||
* 4. If the vnode is the top directory of | * 4. If the vnode is the top directory of | ||||
* the jail or chroot, don't let them out. | * the jail or chroot, don't let them out. | ||||
* 5. If doing a capability lookup, return ENOTCAPABLE if the | |||||
* lookup would escape from the initial file descriptor | |||||
* directory. Checks are done by ensuring that namei() | |||||
* already traversed the result of dotdot lookup. | |||||
*/ | */ | ||||
if (cnp->cn_flags & ISDOTDOT) { | if (cnp->cn_flags & ISDOTDOT) { | ||||
if (ndp->ni_strictrelative != 0) { | |||||
#ifdef KTRACE | |||||
if (KTRPOINT(curthread, KTR_CAPFAIL)) | |||||
ktrcapfail(CAPFAIL_LOOKUP, NULL, NULL); | |||||
#endif | |||||
error = ENOTCAPABLE; | |||||
goto bad; | |||||
} | |||||
if ((cnp->cn_flags & ISLASTCN) != 0 && | if ((cnp->cn_flags & ISLASTCN) != 0 && | ||||
Not Done Inline ActionsThis check is not sufficient to provide security in the presence of colluding concurrent userspace threads (t1, t2, both in capability mode) operating on a shared directory subtree, "a/b/c" and directory descriptors for "a" (afd) and "b" (bfd). When t1 performs an openat(bfd, "c/.."), t2 might simultaneously perform a renameat(afd, "b/c", afd, "c"). If the attacker wins the race, t1's evaluation if "c/.." could reach "a" rather than being blocked at "b", at which point the further lookup of ".." would escape both afd's and bfd's subtrees. Assuming I've described the scenario correctly, anyway! rwatson: This check is not sufficient to provide security in the presence of colluding concurrent… | |||||
(cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME)) { | (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME)) { | ||||
error = EINVAL; | error = EINVAL; | ||||
goto bad; | goto bad; | ||||
} | } | ||||
for (;;) { | for (;;) { | ||||
for (pr = cnp->cn_cred->cr_prison; pr != NULL; | for (pr = cnp->cn_cred->cr_prison; pr != NULL; | ||||
pr = pr->pr_parent) | pr = pr->pr_parent) | ||||
if (dp == pr->pr_root) | if (dp == pr->pr_root) | ||||
Show All 17 Lines | for (;;) { | ||||
} | } | ||||
tdp = dp; | tdp = dp; | ||||
dp = dp->v_mount->mnt_vnodecovered; | dp = dp->v_mount->mnt_vnodecovered; | ||||
VREF(dp); | VREF(dp); | ||||
vput(tdp); | vput(tdp); | ||||
vn_lock(dp, | vn_lock(dp, | ||||
compute_cn_lkflags(dp->v_mount, cnp->cn_lkflags | | compute_cn_lkflags(dp->v_mount, cnp->cn_lkflags | | ||||
LK_RETRY, ISDOTDOT)); | LK_RETRY, ISDOTDOT)); | ||||
error = nameicap_check_dotdot(ndp, dp); | |||||
if (error != 0) { | |||||
#ifdef KTRACE | |||||
Not Done Inline ActionsWe always do the same KTrace stuff when nameicap_check_dotdot fails, so could we move the KTrace bits to nameicap_check_dotdot? jonathan: We always do the same KTrace stuff when `nameicap_check_dotdot` fails, so could we move the… | |||||
Not Done Inline ActionsI pondered this but I think I do not want to do the change now. Current structure of nameicap_check_dotdot() is not suitable for the move, since there are two error exits. There are reservations about MNT_LOCAL already stated, I do not want to change the control flow without the decision about local. Additionally, that function is a helper with the single purpose, KTR/probes stuff lives in lookup() itself. kib: I pondered this but I think I do not want to do the change now.
Current structure of… | |||||
if (KTRPOINT(curthread, KTR_CAPFAIL)) | |||||
ktrcapfail(CAPFAIL_LOOKUP, NULL, NULL); | |||||
#endif | |||||
goto bad; | |||||
} | } | ||||
} | } | ||||
} | |||||
/* | /* | ||||
* 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. | ||||
*/ | */ | ||||
unionlookup: | unionlookup: | ||||
#ifdef MAC | #ifdef MAC | ||||
if ((cnp->cn_flags & NOMACCHECK) == 0) { | if ((cnp->cn_flags & NOMACCHECK) == 0) { | ||||
error = mac_vnode_check_lookup(cnp->cn_thread->td_ucred, dp, | error = mac_vnode_check_lookup(cnp->cn_thread->td_ucred, dp, | ||||
▲ Show 20 Lines • Show All 41 Lines • ▼ Show 20 Lines | if ((error == ENOENT) && | ||||
(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; | ||||
VREF(dp); | VREF(dp); | ||||
vput(tdp); | vput(tdp); | ||||
vn_lock(dp, | vn_lock(dp, | ||||
compute_cn_lkflags(dp->v_mount, cnp->cn_lkflags | | compute_cn_lkflags(dp->v_mount, cnp->cn_lkflags | | ||||
LK_RETRY, cnp->cn_flags)); | LK_RETRY, cnp->cn_flags)); | ||||
nameicap_tracker_add(ndp, dp); | |||||
goto unionlookup; | goto unionlookup; | ||||
} | } | ||||
if (error == ERELOOKUP) { | if (error == ERELOOKUP) { | ||||
vref(dp); | vref(dp); | ||||
ndp->ni_vp = dp; | ndp->ni_vp = dp; | ||||
error = 0; | error = 0; | ||||
relookup = 1; | relookup = 1; | ||||
▲ Show 20 Lines • Show All 103 Lines • ▼ Show 20 Lines | KASSERT((cnp->cn_flags & ISLASTCN) || *ndp->ni_next == '/', | ||||
("lookup: invalid path state.")); | ("lookup: invalid path state.")); | ||||
if (relookup) { | if (relookup) { | ||||
relookup = 0; | relookup = 0; | ||||
if (ndp->ni_dvp != dp) | if (ndp->ni_dvp != dp) | ||||
vput(ndp->ni_dvp); | vput(ndp->ni_dvp); | ||||
else | else | ||||
vrele(ndp->ni_dvp); | vrele(ndp->ni_dvp); | ||||
goto dirloop; | goto dirloop; | ||||
} | |||||
if (cnp->cn_flags & ISDOTDOT) { | |||||
error = nameicap_check_dotdot(ndp, ndp->ni_vp); | |||||
if (error != 0) { | |||||
#ifdef KTRACE | |||||
if (KTRPOINT(curthread, KTR_CAPFAIL)) | |||||
ktrcapfail(CAPFAIL_LOOKUP, NULL, NULL); | |||||
#endif | |||||
goto bad2; | |||||
} | |||||
} | } | ||||
if (*ndp->ni_next == '/') { | if (*ndp->ni_next == '/') { | ||||
cnp->cn_nameptr = ndp->ni_next; | cnp->cn_nameptr = ndp->ni_next; | ||||
while (*cnp->cn_nameptr == '/') { | while (*cnp->cn_nameptr == '/') { | ||||
cnp->cn_nameptr++; | cnp->cn_nameptr++; | ||||
ndp->ni_pathlen--; | ndp->ni_pathlen--; | ||||
} | } | ||||
if (ndp->ni_dvp != dp) | if (ndp->ni_dvp != dp) | ||||
▲ Show 20 Lines • Show All 394 Lines • Show Last 20 Lines |
I wonder if "nameicap_tracker" or similar might be a better string than "rentr"?