#include "fuse.h"
static MALLOC_DEFINE(M_FUSE_NODE, "fuse_node", "FUSE node");
static struct objcache *fuse_node_objcache = NULL;
static struct objcache_malloc_args fuse_node_args = {
sizeof(struct fuse_node), M_FUSE_NODE,
};
static int
fuse_node_cmp(struct fuse_node *p1, struct fuse_node *p2)
{
if (p1->ino < p2->ino)
return -1;
if (p1->ino > p2->ino)
return 1;
return 0;
}
RB_PROTOTYPE2(fuse_node_tree, fuse_node, entry, fuse_node_cmp, uint64_t);
RB_GENERATE2(fuse_node_tree, fuse_node, node_entry, fuse_node_cmp,
uint64_t, ino);
void
fuse_node_new(struct fuse_mount *fmp, uint64_t ino, enum vtype vtyp,
struct fuse_node **fnpp)
{
struct fuse_node *fnp;
fnp = objcache_get(fuse_node_objcache, M_WAITOK);
bzero(fnp, sizeof(*fnp));
fnp->fmp = fmp;
mtx_init(&fnp->node_lock, "fuse_node_lock");
fnp->ino = ino;
fnp->type = vtyp;
fnp->size = 0;
fnp->nlookup = 0;
fnp->fh = 0;
fnp->closed = false;
if (RB_INSERT(fuse_node_tree, &fmp->node_head, fnp)) {
panic("fuse_node_new: cannot insert %p\n", fnp);
}
*fnpp = fnp;
}
void
fuse_node_free(struct fuse_mount *fmp, struct fuse_node *fnp)
{
fuse_dbg("free ino=%ju\n", fnp->ino);
mtx_lock(&fmp->ino_lock);
RB_REMOVE(fuse_node_tree, &fmp->node_head, fnp);
mtx_unlock(&fmp->ino_lock);
objcache_put(fuse_node_objcache, fnp);
}
int
fuse_alloc_node(struct fuse_mount *fmp, struct fuse_node *dfnp,
uint64_t ino, enum vtype vtyp, struct vnode **vpp)
{
struct fuse_node *fnp;
int error;
int allocated = 0;
KKASSERT(dfnp->type == VDIR);
if (vtyp == VBLK || vtyp == VCHR || vtyp == VFIFO)
return EINVAL;
mtx_lock(&fmp->ino_lock);
fnp = RB_LOOKUP(fuse_node_tree, &fmp->node_head, ino);
if (fnp == NULL) {
fuse_node_new(fmp, ino, vtyp, &fnp);
allocated = 1;
}
mtx_unlock(&fmp->ino_lock);
error = fuse_node_vn(fnp, vpp);
if (error) {
if (allocated)
fuse_node_free(fmp, fnp);
}
return error;
}
int
fuse_node_vn(struct fuse_node *fnp, struct vnode **vpp)
{
struct mount *mp = fnp->fmp->mp;
struct vnode *vp;
struct vnode *newvp;
int error;
newvp = NULL;
retry:
error = 0;
if (fnp->vp == NULL && newvp == NULL) {
error = getnewvnode(VT_FUSE, mp, &newvp,
VLKTIMEOUT, LK_CANRECURSE);
if (error)
return error;
}
mtx_lock(&fnp->node_lock);
vp = fnp->vp;
if (vp) {
vhold(vp);
mtx_unlock(&fnp->node_lock);
error = vget(vp, LK_EXCLUSIVE | LK_RETRY);
vdrop(vp);
if (error)
goto retry;
if (fnp->vp != vp) {
vput(vp);
goto retry;
}
*vpp = vp;
if (newvp) {
newvp->v_type = VBAD;
vx_put(newvp);
}
return 0;
}
if (newvp == NULL) {
mtx_unlock(&fnp->node_lock);
goto retry;
}
fnp->vp = newvp;
mtx_unlock(&fnp->node_lock);
vp = newvp;
vp->v_type = fnp->type;
vp->v_data = fnp;
switch (vp->v_type) {
case VREG:
vinitvmio(vp, fnp->size, FUSE_BLKSIZE, -1);
break;
case VDIR:
break;
case VBLK:
case VCHR:
KKASSERT(0);
vp->v_ops = &mp->mnt_vn_spec_ops;
addaliasu(vp, umajor(0), uminor(0));
break;
case VLNK:
break;
case VSOCK:
break;
case VFIFO:
KKASSERT(0);
case VDATABASE:
break;
default:
KKASSERT(0);
}
vx_downgrade(vp);
*vpp = vp;
return error;
}
int
fuse_node_truncate(struct fuse_node *fnp, size_t oldsize, size_t newsize)
{
struct vnode *vp = fnp->vp;
int error;
fuse_dbg("ino=%ju update size %ju -> %ju\n",
fnp->ino, oldsize, newsize);
fnp->attr.va_size = fnp->size = newsize;
if (newsize < oldsize)
error = nvtruncbuf(vp, newsize, FUSE_BLKSIZE, -1, 0);
else
error = nvextendbuf(vp, oldsize, newsize, FUSE_BLKSIZE,
FUSE_BLKSIZE, -1, -1, 0);
return error;
}
void
fuse_node_init(void)
{
fuse_node_objcache = objcache_create("fuse_node", 0, 0,
NULL, NULL, NULL,
objcache_malloc_alloc_zero, objcache_malloc_free, &fuse_node_args);
}
void
fuse_node_cleanup(void)
{
objcache_destroy(fuse_node_objcache);
}