Implement VOP_VPTOCNP() for tmpfs.
For directories, node->tn_spec.tn_dir.tn_parent pointer to the parent is used. For non-directories, the implementation is naive, all directory nodes are scanned to find a dirent linking the specified node. This can be significantly improved by maintaining tn_parent for all nodes, later. Tested by: pho (as part of larger patch) Sponsored by: The FreeBSD Foundation MFC after: 2 weeks
This commit is contained in:
parent
b4ba3b6459
commit
08c053e71c
@ -1406,6 +1406,132 @@ tmpfs_whiteout(struct vop_whiteout_args *ap)
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
tmpfs_vptocnp_dir(struct tmpfs_node *tn, struct tmpfs_node *tnp,
|
||||
struct tmpfs_dirent **pde)
|
||||
{
|
||||
struct tmpfs_dir_cursor dc;
|
||||
struct tmpfs_dirent *de;
|
||||
|
||||
for (de = tmpfs_dir_first(tnp, &dc); de != NULL;
|
||||
de = tmpfs_dir_next(tnp, &dc)) {
|
||||
if (de->td_node == tn) {
|
||||
*pde = de;
|
||||
return (0);
|
||||
}
|
||||
}
|
||||
return (ENOENT);
|
||||
}
|
||||
|
||||
static int
|
||||
tmpfs_vptocnp_fill(struct vnode *vp, struct tmpfs_node *tn,
|
||||
struct tmpfs_node *tnp, char *buf, int *buflen, struct vnode **dvp)
|
||||
{
|
||||
struct tmpfs_dirent *de;
|
||||
int error, i;
|
||||
|
||||
error = vn_vget_ino_gen(vp, tmpfs_vn_get_ino_alloc, tnp, LK_SHARED,
|
||||
dvp);
|
||||
if (error != 0)
|
||||
return (error);
|
||||
error = tmpfs_vptocnp_dir(tn, tnp, &de);
|
||||
if (error == 0) {
|
||||
i = *buflen;
|
||||
i -= de->td_namelen;
|
||||
if (i < 0) {
|
||||
error = ENOMEM;
|
||||
} else {
|
||||
bcopy(de->ud.td_name, buf + i, de->td_namelen);
|
||||
*buflen = i;
|
||||
}
|
||||
}
|
||||
if (error == 0) {
|
||||
if (vp != *dvp)
|
||||
VOP_UNLOCK(*dvp, 0);
|
||||
} else {
|
||||
if (vp != *dvp)
|
||||
vput(*dvp);
|
||||
else
|
||||
vrele(vp);
|
||||
}
|
||||
return (error);
|
||||
}
|
||||
|
||||
static int
|
||||
tmpfs_vptocnp(struct vop_vptocnp_args *ap)
|
||||
{
|
||||
struct vnode *vp, **dvp;
|
||||
struct tmpfs_node *tn, *tnp, *tnp1;
|
||||
struct tmpfs_dirent *de;
|
||||
struct tmpfs_mount *tm;
|
||||
char *buf;
|
||||
int *buflen;
|
||||
int error;
|
||||
|
||||
vp = ap->a_vp;
|
||||
dvp = ap->a_vpp;
|
||||
buf = ap->a_buf;
|
||||
buflen = ap->a_buflen;
|
||||
|
||||
tm = VFS_TO_TMPFS(vp->v_mount);
|
||||
tn = VP_TO_TMPFS_NODE(vp);
|
||||
if (tn->tn_type == VDIR) {
|
||||
tnp = tn->tn_dir.tn_parent;
|
||||
if (tnp == NULL)
|
||||
return (ENOENT);
|
||||
tmpfs_ref_node(tnp);
|
||||
error = tmpfs_vptocnp_fill(vp, tn, tn->tn_dir.tn_parent, buf,
|
||||
buflen, dvp);
|
||||
tmpfs_free_node(tm, tnp);
|
||||
return (error);
|
||||
}
|
||||
restart:
|
||||
TMPFS_LOCK(tm);
|
||||
LIST_FOREACH_SAFE(tnp, &tm->tm_nodes_used, tn_entries, tnp1) {
|
||||
if (tnp->tn_type != VDIR)
|
||||
continue;
|
||||
TMPFS_NODE_LOCK(tnp);
|
||||
tmpfs_ref_node_locked(tnp);
|
||||
|
||||
/*
|
||||
* tn_vnode cannot be instantiated while we hold the
|
||||
* node lock, so the directory cannot be changed while
|
||||
* we iterate over it. Do this to avoid instantiating
|
||||
* vnode for directories which cannot point to our
|
||||
* node.
|
||||
*/
|
||||
error = tnp->tn_vnode == NULL ? tmpfs_vptocnp_dir(tn, tnp,
|
||||
&de) : 0;
|
||||
|
||||
if (error == 0) {
|
||||
TMPFS_NODE_UNLOCK(tnp);
|
||||
TMPFS_UNLOCK(tm);
|
||||
error = tmpfs_vptocnp_fill(vp, tn, tnp, buf, buflen,
|
||||
dvp);
|
||||
if (error == 0) {
|
||||
tmpfs_free_node(tm, tnp);
|
||||
return (0);
|
||||
}
|
||||
if ((vp->v_iflag & VI_DOOMED) != 0) {
|
||||
tmpfs_free_node(tm, tnp);
|
||||
return (ENOENT);
|
||||
}
|
||||
TMPFS_LOCK(tm);
|
||||
TMPFS_NODE_LOCK(tnp);
|
||||
}
|
||||
if (tmpfs_free_node_locked(tm, tnp, false)) {
|
||||
goto restart;
|
||||
} else {
|
||||
KASSERT(tnp->tn_refcount > 0,
|
||||
("node %p refcount zero", tnp));
|
||||
tnp1 = LIST_NEXT(tnp, tn_entries);
|
||||
TMPFS_NODE_UNLOCK(tnp);
|
||||
}
|
||||
}
|
||||
TMPFS_UNLOCK(tm);
|
||||
return (ENOENT);
|
||||
}
|
||||
|
||||
/*
|
||||
* Vnode operations vector used for files stored in a tmpfs file system.
|
||||
*/
|
||||
@ -1438,5 +1564,6 @@ struct vop_vector tmpfs_vnodeop_entries = {
|
||||
.vop_vptofh = tmpfs_vptofh,
|
||||
.vop_whiteout = tmpfs_whiteout,
|
||||
.vop_bmap = VOP_EOPNOTSUPP,
|
||||
.vop_vptocnp = tmpfs_vptocnp,
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user