Add a new VOP, VOP_VPTOCNP, which translates a vnode to its component name
on a best-effort basis. Teach vn_fullpath to use this new VOP if a regular VFS cache lookup fails. This VOP is designed to supplement the VFS cache to provide a better chance that a vnode-to-name lookup will succeed. Currently, an implementation for devfs is being committed. The default implementation is to return ENOENT. A big thanks to kib for the mentorship on this, and to pho for running it through his stress test suite. Reviewed by: arch Approved by: kib
This commit is contained in:
parent
dc41cd35e3
commit
b9022449b3
@ -169,6 +169,7 @@ SYSCTL_OPAQUE(_vfs_cache, OID_AUTO, nchstats, CTLFLAG_RD, &nchstats,
|
||||
|
||||
|
||||
static void cache_zap(struct namecache *ncp);
|
||||
static int vn_vptocnp(struct vnode **vp, char **bp, char *buf, u_int *buflen);
|
||||
static int vn_fullpath1(struct thread *td, struct vnode *vp, struct vnode *rdir,
|
||||
char *buf, char **retbuf, u_int buflen);
|
||||
|
||||
@ -840,6 +841,38 @@ vn_fullpath_global(struct thread *td, struct vnode *vn,
|
||||
return (error);
|
||||
}
|
||||
|
||||
static int
|
||||
vn_vptocnp(struct vnode **vp, char **bp, char *buf, u_int *buflen)
|
||||
{
|
||||
struct vnode *dvp;
|
||||
int error, vfslocked;
|
||||
|
||||
vhold(*vp);
|
||||
CACHE_UNLOCK();
|
||||
vfslocked = VFS_LOCK_GIANT((*vp)->v_mount);
|
||||
vn_lock(*vp, LK_SHARED | LK_RETRY);
|
||||
vdrop(*vp);
|
||||
error = VOP_VPTOCNP(*vp, &dvp, buf, buflen);
|
||||
VOP_UNLOCK(*vp, 0);
|
||||
VFS_UNLOCK_GIANT(vfslocked);
|
||||
if (error) {
|
||||
numfullpathfail2++;
|
||||
return (error);
|
||||
}
|
||||
*bp = buf + *buflen;
|
||||
*vp = dvp;
|
||||
CACHE_LOCK();
|
||||
if ((*vp)->v_iflag & VI_DOOMED) {
|
||||
/* forced unmount */
|
||||
CACHE_UNLOCK();
|
||||
vdrop(*vp);
|
||||
return (ENOENT);
|
||||
}
|
||||
vdrop(*vp);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* The magic behind kern___getcwd() and vn_fullpath().
|
||||
*/
|
||||
@ -851,7 +884,8 @@ vn_fullpath1(struct thread *td, struct vnode *vp, struct vnode *rdir,
|
||||
int error, i, slash_prefixed;
|
||||
struct namecache *ncp;
|
||||
|
||||
bp = buf + buflen - 1;
|
||||
buflen--;
|
||||
bp = buf + buflen;
|
||||
*bp = '\0';
|
||||
error = 0;
|
||||
slash_prefixed = 0;
|
||||
@ -860,58 +894,77 @@ vn_fullpath1(struct thread *td, struct vnode *vp, struct vnode *rdir,
|
||||
numfullpathcalls++;
|
||||
if (vp->v_type != VDIR) {
|
||||
ncp = TAILQ_FIRST(&vp->v_cache_dst);
|
||||
if (!ncp) {
|
||||
numfullpathfail2++;
|
||||
CACHE_UNLOCK();
|
||||
return (ENOENT);
|
||||
if (ncp != NULL) {
|
||||
for (i = ncp->nc_nlen - 1; i >= 0 && bp > buf; i--)
|
||||
*--bp = ncp->nc_name[i];
|
||||
if (bp == buf) {
|
||||
numfullpathfail4++;
|
||||
CACHE_UNLOCK();
|
||||
return (ENOMEM);
|
||||
}
|
||||
vp = ncp->nc_dvp;
|
||||
} else {
|
||||
error = vn_vptocnp(&vp, &bp, buf, &buflen);
|
||||
if (error) {
|
||||
return (error);
|
||||
}
|
||||
}
|
||||
for (i = ncp->nc_nlen - 1; i >= 0 && bp > buf; i--)
|
||||
*--bp = ncp->nc_name[i];
|
||||
if (bp == buf) {
|
||||
*--bp = '/';
|
||||
buflen--;
|
||||
if (buflen < 0) {
|
||||
numfullpathfail4++;
|
||||
CACHE_UNLOCK();
|
||||
return (ENOMEM);
|
||||
}
|
||||
*--bp = '/';
|
||||
slash_prefixed = 1;
|
||||
vp = ncp->nc_dvp;
|
||||
}
|
||||
while (vp != rdir && vp != rootvnode) {
|
||||
if (vp->v_vflag & VV_ROOT) {
|
||||
if (vp->v_iflag & VI_DOOMED) { /* forced unmount */
|
||||
CACHE_UNLOCK();
|
||||
error = EBADF;
|
||||
break;
|
||||
}
|
||||
vp = vp->v_mount->mnt_vnodecovered;
|
||||
continue;
|
||||
}
|
||||
if (vp->v_dd == NULL) {
|
||||
if (vp->v_type != VDIR) {
|
||||
numfullpathfail1++;
|
||||
CACHE_UNLOCK();
|
||||
error = ENOTDIR;
|
||||
break;
|
||||
}
|
||||
ncp = TAILQ_FIRST(&vp->v_cache_dst);
|
||||
if (!ncp) {
|
||||
numfullpathfail2++;
|
||||
error = ENOENT;
|
||||
break;
|
||||
if (ncp != NULL) {
|
||||
MPASS(ncp->nc_dvp == vp->v_dd);
|
||||
buflen -= ncp->nc_nlen - 1;
|
||||
for (i = ncp->nc_nlen - 1; i >= 0 && bp != buf; i--)
|
||||
*--bp = ncp->nc_name[i];
|
||||
if (bp == buf) {
|
||||
numfullpathfail4++;
|
||||
CACHE_UNLOCK();
|
||||
error = ENOMEM;
|
||||
break;
|
||||
}
|
||||
vp = ncp->nc_dvp;
|
||||
} else {
|
||||
error = vn_vptocnp(&vp, &bp, buf, &buflen);
|
||||
if (error) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
MPASS(ncp->nc_dvp == vp->v_dd);
|
||||
for (i = ncp->nc_nlen - 1; i >= 0 && bp != buf; i--)
|
||||
*--bp = ncp->nc_name[i];
|
||||
if (bp == buf) {
|
||||
*--bp = '/';
|
||||
buflen--;
|
||||
if (buflen < 0) {
|
||||
numfullpathfail4++;
|
||||
CACHE_UNLOCK();
|
||||
error = ENOMEM;
|
||||
break;
|
||||
}
|
||||
*--bp = '/';
|
||||
slash_prefixed = 1;
|
||||
vp = ncp->nc_dvp;
|
||||
}
|
||||
if (error) {
|
||||
CACHE_UNLOCK();
|
||||
if (error)
|
||||
return (error);
|
||||
}
|
||||
if (!slash_prefixed) {
|
||||
if (bp == buf) {
|
||||
numfullpathfail4++;
|
||||
|
@ -98,6 +98,7 @@ struct vop_vector default_vnodeops = {
|
||||
.vop_revoke = VOP_PANIC,
|
||||
.vop_strategy = vop_nostrategy,
|
||||
.vop_unlock = vop_stdunlock,
|
||||
.vop_vptocnp = VOP_ENOENT,
|
||||
.vop_vptofh = vop_stdvptofh,
|
||||
};
|
||||
|
||||
@ -137,6 +138,13 @@ vop_einval(struct vop_generic_args *ap)
|
||||
return (EINVAL);
|
||||
}
|
||||
|
||||
int
|
||||
vop_enoent(struct vop_generic_args *ap)
|
||||
{
|
||||
|
||||
return (ENOENT);
|
||||
}
|
||||
|
||||
int
|
||||
vop_null(struct vop_generic_args *ap)
|
||||
{
|
||||
|
@ -595,3 +595,13 @@ vop_vptofh {
|
||||
IN struct vnode *vp;
|
||||
IN struct fid *fhp;
|
||||
};
|
||||
|
||||
%% vptocnp vp L L L
|
||||
%% vptocnp vpp - U -
|
||||
|
||||
vop_vptocnp {
|
||||
IN struct vnode *vp;
|
||||
OUT struct vnode **vpp;
|
||||
INOUT char *buf;
|
||||
INOUT int *buflen;
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user