#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "fuse_private.h"
#include "debug.h"
struct fuse_vnode *
alloc_vn(struct fuse *f, const char *path, ino_t ino, ino_t pino)
{
struct fuse_vnode *vn;
if ((vn = malloc(sizeof(*vn))) == NULL) {
DPERROR(__func__);
return (NULL);
}
vn->ino = ino;
vn->ref = 1;
if (strlcpy(vn->path, path, sizeof(vn->path)) >= sizeof(vn->path)) {
DPRINTF("%s: strlcpy name too long\n", __func__);
free(vn);
return (NULL);
}
if (pino == (ino_t)0)
vn->parent = NULL;
else {
if ((vn->parent = tree_get(&f->vnode_tree, pino)) == NULL) {
DPRINTF("%s: parent vnode %llu not in the vnode tree\n",
__func__, pino);
free(vn);
errno = ENOENT;
return (NULL);
}
ref_vn(vn->parent);
}
if (ino == (ino_t)-1) {
f->max_ino++;
vn->ino = f->max_ino;
}
return (vn);
}
void
ref_vn(struct fuse_vnode *vn)
{
vn->ref++;
}
void
unref_vn(struct fuse *f, struct fuse_vnode *vn)
{
if (--vn->ref == 0) {
tree_pop(&f->vnode_tree, vn->ino);
remove_vnode_from_name_tree(f, vn);
if (vn->parent != NULL)
unref_vn(f, vn->parent);
free(vn);
}
}
int
set_vn(struct fuse *f, struct fuse_vnode *v)
{
struct fuse_vn_head *vn_head;
struct fuse_vnode *vn;
if (tree_set(&f->vnode_tree, v->ino, v) == NULL)
return (0);
if (!dict_check(&f->name_tree, v->path)) {
vn_head = malloc(sizeof(*vn_head));
if (vn_head == NULL)
return (0);
SIMPLEQ_INIT(vn_head);
} else {
vn_head = dict_get(&f->name_tree, v->path);
if (vn_head == NULL)
return (0);
}
SIMPLEQ_FOREACH(vn, vn_head, node) {
if (v->parent == vn->parent && v->ino == vn->ino)
return (1);
}
SIMPLEQ_INSERT_TAIL(vn_head, v, node);
dict_set(&f->name_tree, v->path, vn_head);
return (1);
}
void
remove_vnode_from_name_tree(struct fuse *f, struct fuse_vnode *vn)
{
struct fuse_vn_head *vn_head;
struct fuse_vnode *v;
struct fuse_vnode *lastv;
vn_head = dict_get(&f->name_tree, vn->path);
if (vn_head == NULL)
return;
lastv = NULL;
SIMPLEQ_FOREACH(v, vn_head, node) {
if (v->parent == vn->parent)
break;
lastv = v;
}
if (v == NULL)
return;
if (v == SIMPLEQ_FIRST(vn_head))
SIMPLEQ_REMOVE_HEAD(vn_head, node);
else
SIMPLEQ_REMOVE_AFTER(vn_head, lastv, node);
if (SIMPLEQ_EMPTY(vn_head)) {
vn_head = dict_pop(&f->name_tree, vn->path);
free(vn_head);
}
}
struct fuse_vnode *
get_vn_by_name_and_parent(struct fuse *f, const char *path, ino_t pino)
{
struct fuse_vn_head *vn_head;
struct fuse_vnode *v = NULL;
vn_head = dict_get(&f->name_tree, path);
if (vn_head == NULL)
goto fail;
SIMPLEQ_FOREACH(v, vn_head, node)
if (v->parent && v->parent->ino == pino)
return (v);
fail:
errno = ENOENT;
return (NULL);
}
char *
build_realname(struct fuse *f, ino_t ino)
{
struct fuse_vnode *vn;
char *name;
char *tmp = NULL;
int firstshot = 0, ret;
name = strdup("/");
if (name == NULL)
return (NULL);
vn = tree_get(&f->vnode_tree, ino);
if (!vn || !name) {
free(name);
return (NULL);
}
while (vn->parent != NULL) {
if (firstshot++)
ret = asprintf(&tmp, "/%s%s", vn->path, name);
else
ret = asprintf(&tmp, "/%s", vn->path);
if (ret == -1) {
free(name);
return (NULL);
}
free(name);
name = tmp;
tmp = NULL;
vn = vn->parent;
}
if (ino == (ino_t)0)
DPRINTF("%s: NULL ino\t", __func__);
return (name);
}