#define _BUILDING_fs 1
#include <sys/param.h>
#include <sys/stat.h>
#include <malloc.h>
#include <KernelExport.h>
#include <stddef.h>
#include <stdio.h>
#include <signal.h>
#include <string.h>
#include <fs_query.h>
#include "query.h"
#include "websearchfs.h"
#include "vnidpool.h"
#include "duckduckgo_request.h"
#include "settings.h"
#define PFS "websearchfs: "
#define TRACE_WEBSEARCHFS
#ifdef TRACE_WEVSEARCHFS
# define TRACE(x...) fprintf(stderr, PFS x)
#else
# define TRACE(x...)
#endif
#define ALLOW_DIR_OPEN
int32 refcount = 0;
extern struct attr_entry root_folder_attrs[];
extern struct attr_entry folders_attrs[];
extern struct attr_entry bookmark_attrs[];
extern struct attr_entry fake_bookmark_attrs[];
extern struct attr_entry template_1_attrs[];
extern struct attr_entry text_attrs[];
extern struct attr_entry mailto_me_bookmark_attrs[];
extern char *readmestr;
static fs_volume_ops sWebSearchFSVolumeOps;
static fs_vnode_ops sWebSearchFSVnodeOps;
static int websearchfs_create_gen(fs_volume *_volume, fs_node *dir, const char *name, int omode, int perms, ino_t *vnid, fs_node **node, struct attr_entry *iattrs, bool mkdir, bool uniq);
static int websearchfs_free_vnode(fs_volume *_volume, fs_node *node);
static void fill_default_stat(struct stat *st, nspace_id nsid, ino_t vnid, mode_t mode)
{
time_t tm = time(NULL);
st->st_dev = nsid;
st->st_ino = vnid;
st->st_mode = mode;
st->st_nlink = 1;
st->st_uid = 0;
st->st_gid = 0;
st->st_size = 0LL;
st->st_blksize = 1024;
st->st_atime = tm;
st->st_mtime = tm;
st->st_ctime = tm;
st->st_crtime = tm;
}
static int websearchfs_publish_static_entries(fs_volume *_volume)
{
fs_nspace *ns = (fs_nspace *)_volume->private_volume;
status_t err = B_OK;
fs_node *dir = ns->root;
fs_node *n;
TRACE("websearchfs_publish_static_entries(%" B_PRId32 ")\n", ns->nsid);
if (!ns || !dir)
return EINVAL;
err = websearchfs_create_gen(_volume, dir, "Search the Web", 0, 0444, NULL, &n, template_1_attrs, false, true);
if (err)
return err;
n->is_perm = 1;
err = websearchfs_create_gen(_volume, dir, "README", 0, 0444, NULL, &n, text_attrs, false, true);
if (err)
return err;
n->is_perm = 1;
n->data = readmestr;
n->data_size = strlen(n->data);
err = websearchfs_create_gen(_volume, dir, "Author", 0, 0444, NULL, &n, mailto_me_bookmark_attrs, false, true);
if (err)
return err;
n->is_perm = 1;
return B_OK;
}
static status_t websearchfs_mount(fs_volume *_vol, const char *devname, uint32 flags,
const char *parms, ino_t *vnid)
{
fs_nspace *ns;
fs_node *root;
int err;
TRACE("mount(%p, %s, 0x%08" B_PRIx32 ", %s, , )\n", _vol, devname, flags, parms);
if (atomic_add(&refcount, 1))
return EALREADY;
err = load_settings();
ns = malloc(sizeof(fs_nspace));
if (!ns)
return B_NO_MEMORY;
memset(ns, 0, sizeof(fs_nspace));
ns->nsid = _vol->id;
err = vnidpool_alloc(&ns->vnids, MAX_VNIDS);
if (err < 0)
return err;
err = vnidpool_get(ns->vnids, &ns->rootid);
if (err < 0)
return err;
atomic_add(&ns->nodecount, 1);
new_lock(&(ns->l), "websearchfs main lock");
ns->nodes = NULL;
err = B_NO_MEMORY;
root = malloc(sizeof(fs_node));
ns->root = root;
if (root) {
memset(root, 0, sizeof(fs_node));
strcpy(root->name, ".");
root->is_perm = 1;
root->vnid = ns->rootid;
fill_default_stat(&root->st, ns->nsid, ns->rootid, 0777 | S_IFDIR);
root->attrs_indirect = root_folder_attrs;
new_lock(&(root->l), "websearchfs root dir");
TRACE("mount: root->l @ %p\n", &root->l);
_vol->private_volume = ns;
_vol->ops = &sWebSearchFSVolumeOps;
*vnid = ns->rootid;
ns->nodes = root;
err = publish_vnode(_vol, *vnid, root, &sWebSearchFSVnodeOps, S_IFDIR, 0);
if (err == B_OK) {
websearchfs_publish_static_entries(_vol);
TRACE("mount() OK, nspace@ %p, id %" B_PRId32 ", root@ %p, id %" B_PRId64 "\n", ns, ns->nsid, root, ns->rootid);
return B_OK;
}
free_lock(&root->l);
free(root);
}
free_lock(&ns->l);
free(ns);
atomic_add(&refcount, -1);
return err;
}
static status_t websearchfs_unmount(fs_volume *_volume)
{
fs_nspace *ns = (fs_nspace *)_volume->private_volume;
status_t err;
struct fs_node *node;
TRACE("unmount(%" B_PRId32 ")\n", ns->nsid);
err = LOCK(&ns->l);
if (err)
return err;
for (node = ns->nodes; node; node = ns->nodes) {
ns->nodes = node->nlnext;
websearchfs_free_vnode(_volume, node);
}
put_vnode(_volume, ns->rootid);
free_lock(&ns->l);
vnidpool_free(ns->vnids);
free(ns);
atomic_add(&refcount, -1);
return B_OK;
}
static int compare_fs_node_by_vnid(fs_node *node, ino_t *id)
{
return !(node->vnid == *id);
}
static int websearchfs_free_vnode(fs_volume *_volume, fs_node *node)
{
fs_nspace *ns = (fs_nspace *)_volume->private_volume;
TRACE("%s(%" B_PRId32 ", %" B_PRId64 ")\n", __FUNCTION__, ns->nsid, node->vnid);
free_lock(&node->l);
atomic_add(&ns->nodecount, -1);
vnidpool_put(ns->vnids, node->vnid);
if (node->request)
duckduckgo_request_free(node->request);
free(node->result);
free(node);
return 0;
}
static status_t websearchfs_remove_vnode(fs_volume *_volume, fs_vnode *_node, bool reenter)
{
fs_nspace *ns = (fs_nspace *)_volume->private_volume;
fs_node *node = (fs_node *)_node->private_node;
status_t err = B_OK;
TRACE("%s(%" B_PRId32 ", %" B_PRId64 ", %s)\n", __FUNCTION__, ns->nsid, node->vnid, reenter?"r":"!r");
if (!reenter)
err = LOCK(&ns->l);
if (err)
return err;
if (node->vnid == ns->rootid) {
TRACE("asked to remove the root node!!\n");
}
TRACE("SLL_REMOVE(ns->nodes %p, nlnext, %p)\n", ns->nodes, node);
err = SLL_REMOVE(ns->nodes, nlnext, node);
TRACE("SLL_REMOVE(ns->queries %p, qnext, %p)\n", ns->nodes, node);
err = SLL_REMOVE(ns->queries, qnext, node);
if (node->parent) {
LOCK(&node->parent->l);
TRACE("SLL_REMOVE(node->parent->children %p, next, %p)\n", node->parent->children, node);
SLL_REMOVE(node->parent->children, next, node);
UNLOCK(&node->parent->l);
}
websearchfs_free_vnode(_volume, node);
if (!reenter)
UNLOCK(&ns->l);
return err;
}
static status_t websearchfs_read_vnode(fs_volume *_volume, ino_t vnid, fs_vnode *_node, int* _type, uint32* _flags, bool reenter)
{
fs_nspace *ns = (fs_nspace *)_volume->private_volume;
fs_node *n;
status_t err = B_OK;
TRACE("%s(%" B_PRId32 ", %" B_PRId64 ", %s)\n", __FUNCTION__, _volume->id, vnid, reenter?"r":"!r");
if (!reenter)
err = LOCK(&ns->l);
if (err)
return err;
n = (fs_node *)SLL_FIND(ns->nodes, nlnext, (sll_compare_func)compare_fs_node_by_vnid, (void *)&vnid);
if (n) {
_node->private_node = n;
_node->ops = &sWebSearchFSVnodeOps;
*_type = n->st.st_mode & ~S_IUMSK;
*_flags = 0;
} else
err = ENOENT;
if (!reenter)
UNLOCK(&ns->l);
return err;
}
static status_t websearchfs_release_vnode(fs_volume *_volume, fs_vnode *_node, bool reenter)
{
fs_node *node = (fs_node *)_node->private_node;
TRACE("%s(%" B_PRId32 ", %" B_PRId64 ", %s)\n", __FUNCTION__, _volume->id, node->vnid, reenter?"r":"!r");
return B_OK;
}
static int compare_fs_node_by_name(fs_node *node, char *name)
{
return strncmp(node->name, name, WEBSEARCHFS_NAME_LEN);
}
static status_t websearchfs_get_vnode_name(fs_volume *_volume, fs_vnode *_node, char *buffer, size_t len)
{
fs_nspace *ns = (fs_nspace *)_volume->private_volume;
fs_node *node = (fs_node *)_node->private_node;
TRACE("get_vnode_name(%" B_PRId32 ", %" B_PRId64 ", )\n", ns->nsid, (int64)(node?node->vnid:-1));
strlcpy(buffer, node->name, MIN(WEBSEARCHFS_NAME_LEN, len));
return B_OK;
}
static status_t websearchfs_walk(fs_volume *_volume, fs_vnode *_base, const char *file, ino_t *vnid)
{
fs_nspace *ns = (fs_nspace *)_volume->private_volume;
fs_node *base = _base->private_node;
fs_node *n, *dummy;
status_t err = B_OK;
TRACE("walk(%" B_PRId32 ", %" B_PRId64 ", %s)\n", ns->nsid, (int64)(base?base->vnid:-1), file);
err = LOCK(&base->l);
if (err)
return err;
if (!file) {
err = EINVAL;
} else if (!strcmp(file, "..")) {
if (base && base->parent) {
*vnid = base->parent->vnid;
} else
err = EINVAL;
} else if (!strcmp(file, ".")) {
if (base) {
*vnid = base->vnid;
} else
err = EINVAL;
} else if (base) {
n = (fs_node *)SLL_FIND(base->children, next,
(sll_compare_func)compare_fs_node_by_name, (void *)file);
if (n) {
*vnid = n->vnid;
} else
err = ENOENT;
} else
err = ENOENT;
if (err == B_OK) {
if (get_vnode(_volume, *vnid, (void **)&dummy) != B_OK)
err = EINVAL;
}
UNLOCK(&base->l);
TRACE("walk() -> error 0x%08" B_PRIx32 "\n", err);
return err;
}
static status_t websearchfs_opendir(fs_volume *_volume, fs_vnode *_node, void **cookie)
{
fs_nspace *ns = (fs_nspace *)_volume->private_volume;
fs_node *node = (fs_node *)_node->private_node;
status_t err = B_OK;
fs_dir_cookie *c;
TRACE("opendir(%" B_PRId32 ", %" B_PRId64 ")\n", ns->nsid, node->vnid);
if (!node)
return EINVAL;
if (!S_ISDIR(node->st.st_mode))
return B_NOT_A_DIRECTORY;
err = LOCK(&node->l);
if (err)
return err;
c = malloc(sizeof(fs_dir_cookie));
if (c) {
memset(c, 0, sizeof(fs_dir_cookie));
c->omode = O_RDONLY;
c->type = S_IFDIR;
c->node = node;
c->dir_current = 0;
*cookie = (void *)c;
SLL_INSERT(node->opened, next, c);
UNLOCK(&node->l);
return B_OK;
} else
err = B_NO_MEMORY;
UNLOCK(&node->l);
return err;
}
static status_t websearchfs_closedir(fs_volume *_volume, fs_vnode *_node, void *_cookie)
{
fs_nspace *ns = (fs_nspace *)_volume->private_volume;
fs_node *node = (fs_node *)_node->private_node;
fs_dir_cookie *cookie = (fs_dir_cookie *)_cookie;
status_t err = B_OK;
TRACE("closedir(%" B_PRId32 ", %" B_PRId64 ", %p)\n", ns->nsid, node->vnid, cookie);
err = LOCK(&node->l);
if (err)
return err;
SLL_REMOVE(node->opened, next, cookie);
UNLOCK(&node->l);
return err;
}
static status_t websearchfs_rewinddir(fs_volume *_volume, fs_vnode *_node, void *_cookie)
{
fs_nspace *ns = (fs_nspace *)_volume->private_volume;
fs_node *node = (fs_node *)_node->private_node;
fs_dir_cookie *cookie = (fs_dir_cookie *)_cookie;
TRACE("rewinddir(%" B_PRId32 ", %" B_PRId64 ")\n", ns->nsid, node->vnid);
cookie->dir_current = 0;
return B_OK;
}
static status_t websearchfs_readdir(fs_volume *_volume, fs_vnode *_node, void *_cookie,
struct dirent *buf, size_t bufsize, uint32 *num)
{
fs_nspace *ns = (fs_nspace *)_volume->private_volume;
fs_node *node = (fs_node *)_node->private_node;
fs_dir_cookie *cookie = (fs_dir_cookie *)_cookie;
fs_node *n = NULL;
fs_node *parent = node->parent;
int index;
TRACE("readdir(%" B_PRId32 ", %" B_PRId64 ") @ %d\n", ns->nsid, node->vnid,
cookie->dir_current);
if (!node || !cookie || !num || !*num || !buf
|| (bufsize < (sizeof(dirent_t) + WEBSEARCHFS_NAME_LEN)))
return EINVAL;
LOCK(&node->l);
if (cookie->dir_current == 0) {
TRACE("readdir: giving ..\n");
buf->d_dev = ns->nsid;
buf->d_pdev = ns->nsid;
buf->d_ino = parent?parent->vnid:ns->rootid;
buf->d_pino = (parent && parent->parent)?parent->parent->vnid:ns->rootid;
strcpy(buf->d_name, "..");
buf->d_reclen = offsetof(struct dirent, d_name)+strlen(buf->d_name)+1;
cookie->dir_current++;
*num = 1;
} else if (cookie->dir_current == 1) {
TRACE("readdir: giving .\n");
buf->d_dev = ns->nsid;
buf->d_pdev = ns->nsid;
buf->d_ino = node->vnid;
buf->d_pino = parent?parent->vnid:ns->rootid;
strcpy(buf->d_name, ".");
buf->d_reclen = offsetof(struct dirent, d_name)+strlen(buf->d_name)+1;
cookie->dir_current++;
*num = 1;
} else {
index = cookie->dir_current-2;
for (n = node->children; n && index; n = n->next, index--);
if (n) {
TRACE("readdir: giving ino %" B_PRId64 ", %s\n", n->vnid, n->name);
buf->d_dev = ns->nsid;
buf->d_pdev = ns->nsid;
buf->d_ino = n->vnid;
buf->d_pino = node->vnid;
strcpy(buf->d_name, n->name);
buf->d_reclen = offsetof(struct dirent, d_name)+strlen(buf->d_name)+1;
cookie->dir_current++;
*num = 1;
} else {
*num = 0;
}
}
UNLOCK(&node->l);
return B_OK;
}
static status_t websearchfs_free_dircookie(fs_volume *_volume, fs_vnode *_node, void *_cookie)
{
fs_nspace *ns = (fs_nspace *)_volume->private_volume;
fs_node *node = (fs_node *)_node->private_node;
fs_dir_cookie *cookie = (fs_dir_cookie *)_cookie;
status_t err = B_OK;
TRACE("freedircookie(%" B_PRId32 ", %" B_PRId64 ", %p)\n", ns->nsid, node?node->vnid:(int64)0, (void *)cookie);
err = LOCK(&node->l);
if (err)
return err;
err = SLL_REMOVE(node->opened, next, cookie);
UNLOCK(&node->l);
free(cookie);
return B_OK;
}
static status_t websearchfs_rstat(fs_volume *_volume, fs_vnode *_node, struct stat *st)
{
fs_nspace *ns = (fs_nspace *)_volume->private_volume;
fs_node *node = (fs_node *)_node->private_node;
status_t err = B_OK;
if (!node || !st)
return EINVAL;
err = LOCK(&node->l);
if (err)
return err;
memcpy(st, &node->st, sizeof(struct stat));
st->st_dev = ns->nsid;
st->st_ino = node->vnid;
if (node->data_size)
st->st_size = node->data_size;
UNLOCK(&node->l);
return err;
}
static status_t websearchfs_rfsstat(fs_volume *_volume, struct fs_info *info)
{
fs_nspace *ns = (fs_nspace *)_volume->private_volume;
info->block_size = 1024;
info->io_size = 1024;
info->total_blocks=0;
info->free_blocks=0;
info->total_nodes=MAX_VNIDS;
info->free_nodes=ns->nodecount;
info->dev=ns->nsid;
info->root=ns->rootid;
info->flags=B_FS_IS_PERSISTENT|B_FS_HAS_MIME|B_FS_HAS_ATTR|B_FS_HAS_QUERY;
strcpy (info->device_name, "");
strcpy (info->volume_name, "Web Search");
strcpy (info->fsh_name, WEBSEARCHFS_NAME);
return B_OK;
}
static status_t websearchfs_open(fs_volume *_volume, fs_vnode *_node, int omode, void **cookie)
{
fs_nspace *ns = (fs_nspace *)_volume->private_volume;
fs_node *node = (fs_node *)_node->private_node;
status_t err = B_OK;
fs_file_cookie *fc;
TRACE("open(%" B_PRId32 ", %" B_PRId64 ", 0x%x)\n", ns->nsid, node->vnid, omode);
if (!node || !cookie)
return EINVAL;
err = LOCK(&node->l);
if (err)
goto err_n_l;
err = EEXIST;
#ifndef ALLOW_DIR_OPEN
err = EINVAL;
if (S_ISDIR(node->st.st_mode))
goto err_malloc;
#endif
err = B_NO_MEMORY;
fc = malloc(sizeof(fs_file_cookie));
if (!fc)
goto err_malloc;
memset(fc, 0, sizeof(fs_file_cookie));
fc->node = node;
fc->omode = omode;
fc->type = S_IFREG;
err = SLL_INSERT(node->opened, next, fc);
if (err)
goto err_linsert;
*cookie = (void *)fc;
err = B_OK;
goto all_ok;
err_linsert:
free(fc);
err_malloc:
all_ok:
UNLOCK(&node->l);
err_n_l:
return err;
}
static status_t websearchfs_close(fs_volume *_volume, fs_vnode *_node, void *_cookie)
{
fs_nspace *ns = (fs_nspace *)_volume->private_volume;
fs_node *node = (fs_node *)_node->private_node;
fs_file_cookie *cookie = (fs_file_cookie *)_cookie;
status_t err;
TRACE("close(%" B_PRId32 ", %" B_PRId64 ")\n", ns->nsid, node->vnid);
if (!ns || !node || !cookie)
return EINVAL;
err = LOCK(&node->l);
if (err)
return err;
SLL_REMOVE(node->opened, next, cookie);
UNLOCK(&node->l);
return err;
}
static status_t websearchfs_free_cookie(fs_volume *_volume, fs_vnode *_node, void *_cookie)
{
fs_nspace *ns = (fs_nspace *)_volume->private_volume;
fs_node *node = (fs_node *)_node->private_node;
fs_file_cookie *cookie = (fs_file_cookie *)_cookie;
status_t err = B_OK;
TRACE("freecookie(%" B_PRId32 ", %" B_PRId64 ")\n", ns->nsid, node->vnid);
err = LOCK(&node->l);
if (err)
return err;
err = SLL_REMOVE(node->opened, next, cookie);
if ( false) {
err = remove_vnode(_volume, node->vnid);
ns->root->st.st_mtime = time(NULL);
#if 0
notify_listener(B_ENTRY_REMOVED, ns->nsid, ns->rootid, 0LL, node->vnid, NULL);
notify_listener(B_STAT_CHANGED, ns->nsid, 0LL, 0LL, ns->rootid, NULL);
#endif
}
UNLOCK(&node->l);
free(cookie);
return err;
}
static status_t websearchfs_read(fs_volume *_volume, fs_vnode *_node, void *_cookie, off_t pos, void *buf, size_t *len)
{
fs_nspace *ns = (fs_nspace *)_volume->private_volume;
fs_node *node = (fs_node *)_node->private_node;
status_t err = B_OK;
TRACE("read(%" B_PRId32 ", %" B_PRId64 ", %jd, %zu)\n", ns->nsid, node->vnid, pos, *len);
if (pos < 0 || (size_t)pos > node->data_size)
err = EFPOS;
if (err || node->data_size == 0 || !node->data) {
*len = 0;
return err;
}
*len = MIN(*len, node->data_size - (long)pos);
memcpy(buf, ((char *)node->data) + pos, *len);
return B_OK;
}
static status_t websearchfs_write(fs_volume *_volume, fs_vnode *_node, void *_cookie, off_t pos, const void *buf, size_t *len)
{
fs_node *node = (fs_node *)_node->private_node;
TRACE("write(%" B_PRId32 ", %" B_PRId64 ", %jd, %zu)\n", _volume->id, node->vnid, pos, *len);
*len = 0;
return ENOSYS;
}
static status_t websearchfs_wstat(fs_volume *_volume, fs_vnode *_node, const struct stat *st, uint32 mask)
{
fs_node *node = (fs_node *)_node->private_node;
TRACE("wstat(%" B_PRId32 ", %" B_PRId64 ", , 0x%08" B_PRIx32 ")\n", _volume->id, node->vnid, mask);
return ENOSYS;
}
static status_t websearchfs_wfsstat(fs_volume *_volume, const struct fs_info *info, uint32 mask)
{
TRACE("wfsstat(%" B_PRId32 ", , 0x%08" B_PRIx32 ")\n", _volume->id, mask);
return ENOSYS;
}
static int websearchfs_create_gen(fs_volume *_volume, fs_node *dir, const char *name, int omode, int perms, ino_t *vnid, fs_node **node, struct attr_entry *iattrs, bool mkdir, bool uniq)
{
fs_nspace *ns = (fs_nspace *)_volume->private_volume;
char newname[WEBSEARCHFS_NAME_LEN];
status_t err;
fs_node *n;
int i;
TRACE("create_gen(%" B_PRId32 ", %" B_PRId64 ", '%s', 0x%08x, %c, %c)\n", ns->nsid, dir->vnid, name, omode, mkdir?'t':'f', uniq?'t':'f');
if (strlen(name) > WEBSEARCHFS_NAME_LEN-1)
return ENAMETOOLONG;
err = LOCK(&dir->l);
if (err < 0)
return err;
err = ENOTDIR;
if (!S_ISDIR(dir->st.st_mode))
goto err_l;
n = (fs_node *)SLL_FIND(dir->children, next,
(sll_compare_func)compare_fs_node_by_name, (void *)name);
err = EEXIST;
if (n && (omode & O_EXCL) && !uniq)
goto err_l;
strncpy(newname, name, WEBSEARCHFS_NAME_LEN);
newname[WEBSEARCHFS_NAME_LEN-1] = '\0';
for (i = 1; uniq && n && i < 5000; i++) {
strncpy(newname, name, 56);
newname[56] = '\0';
sprintf(newname+strlen(newname), " %05d", i);
n = (fs_node *)SLL_FIND(dir->children, next,
(sll_compare_func)compare_fs_node_by_name, (void *)newname);
}
if (n && (uniq || mkdir))
goto err_l;
name = newname;
if (n) {
if (node)
*node = n;
if (vnid)
*vnid = n->vnid;
err = B_OK;
goto done;
}
err = ENOMEM;
n = malloc(sizeof(fs_node));
if (!n)
goto err_l;
memset(n, 0, sizeof(fs_node));
err = vnidpool_get(ns->vnids, &n->vnid);
if (err < B_OK)
goto err_m;
atomic_add(&ns->nodecount, 1);
strcpy(n->name, name);
fill_default_stat(&n->st, ns->nsid, n->vnid, (perms & ~S_IFMT) | (mkdir?S_IFDIR:S_IFREG));
new_lock(&(n->l), mkdir?"websearchfs dir":"websearchfs file");
err = LOCK(&ns->l);
if (err)
goto err_nl;
err = SLL_INSERT(ns->nodes, nlnext, n);
if (err)
goto err_lns;
err = SLL_INSERT(dir->children, next, n);
if (err)
goto err_insnl;
n->parent = dir;
dir->st.st_nlink++;
UNLOCK(&ns->l);
n->attrs_indirect = iattrs;
notify_entry_created(ns->nsid, dir->vnid, name, n->vnid);
notify_stat_changed(B_STAT_CHANGED, -1, ns->nsid, -1);
if (node)
*node = n;
if (vnid)
*vnid = n->vnid;
goto done;
err_insnl:
SLL_REMOVE(ns->nodes, nlnext, n);
err_lns:
UNLOCK(&ns->l);
err_nl:
free_lock(&n->l);
atomic_add(&ns->nodecount, -1);
err_m:
free(n);
err_l:
done:
UNLOCK(&dir->l);
return err;
}
static status_t websearchfs_create(fs_volume *_volume, fs_vnode *_dir, const char *name, int omode, int perms, void **cookie, ino_t *vnid)
{
fs_nspace *ns = (fs_nspace *)_volume->private_volume;
fs_node *dir = (fs_node *)_dir->private_node;
status_t err;
fs_node *n;
struct fs_vnode child = { NULL, &sWebSearchFSVnodeOps };
TRACE("create(%" B_PRId32 ", %" B_PRId64 ", '%s', 0x%08x)\n", ns->nsid, dir->vnid, name, omode);
return ENOSYS;
err = websearchfs_create_gen(_volume, dir, name, omode, perms, vnid, &n, NULL, false, false);
if (err)
return err;
child.private_node = (void *)n;
err = websearchfs_open(_volume, &child, omode, cookie);
return err;
}
static int websearchfs_unlink_gen(fs_volume *_volume, fs_node *dir, const char *name)
{
fs_nspace *ns = (fs_nspace *)_volume->private_volume;
status_t err;
fs_node *n;
TRACE("unlink(%" B_PRId32 ", %" B_PRId64 ", %s)\n", ns->nsid, dir->vnid, name);
err = LOCK(&dir->l);
if (err)
return err;
err = ENOENT;
n = (fs_node *)SLL_FIND(dir->children, next,
(sll_compare_func)compare_fs_node_by_name, (void *)name);
if (n) {
if (n->children)
err = ENOTEMPTY;
else if (n->is_perm)
err = EROFS;
else if (n->vnid == ns->rootid)
err = EACCES;
else {
SLL_REMOVE(dir->children, next, n);
notify_entry_removed(ns->nsid, dir->vnid, name, n->vnid);
remove_vnode(_volume, n->vnid);
err = B_OK;
}
}
UNLOCK(&dir->l);
return err;
}
static status_t websearchfs_unlink(fs_volume *_volume, fs_vnode *_dir, const char *name)
{
return websearchfs_unlink_gen(_volume, (fs_node *)_dir->private_node, name);
}
static status_t websearchfs_rmdir(fs_volume *_volume, fs_vnode *_dir, const char *name)
{
fs_node *dir = (fs_node *)_dir->private_node;
TRACE("rmdir(%" B_PRId32 ", %" B_PRId64 ", %s)\n", _volume->id, dir->vnid, name);
return websearchfs_unlink(_volume, _dir, name);
}
static int websearchfs_unlink_node_rec(fs_volume *_volume, fs_node *node)
{
fs_nspace *ns = (fs_nspace *)_volume->private_volume;
status_t err;
fs_node *n;
TRACE("websearchfs_unlink_node_rec(%" B_PRId32 ", %" B_PRId64 ":%s)\n", ns->nsid, node->vnid, node->name);
if (!ns || !node)
return EINVAL;
LOCK(&node->l);
while (1) {
n = node->children;
if (!n)
break;
UNLOCK(&node->l);
err = websearchfs_unlink_node_rec(_volume, n);
LOCK(&node->l);
}
UNLOCK(&node->l);
err = websearchfs_unlink_gen(_volume, node->parent, node->name);
return err;
}
static status_t websearchfs_access(fs_volume *_volume, fs_vnode *_node, int mode)
{
fs_nspace *ns = (fs_nspace *)_volume->private_volume;
fs_node *node = (fs_node *)_node->private_node;
TRACE("access(%" B_PRId32 ", %" B_PRId64 ", 0x%x)\n", ns->nsid, node->vnid, mode);
return B_OK;
}
static status_t websearchfs_mkdir(fs_volume *_volume, fs_vnode *_dir, const char *name, int perms)
{
fs_nspace *ns = (fs_nspace *)_volume->private_volume;
fs_node *dir = (fs_node *)_dir->private_node;
TRACE("mkdir(%" B_PRId32 ", %" B_PRId64 ", '%s', 0x%08x)\n", ns->nsid, dir->vnid, name, perms);
return websearchfs_create_gen(_volume, dir, name, O_EXCL, perms, NULL, NULL, folders_attrs, true, false);
}
static status_t websearchfs_open_attrdir(fs_volume *_volume, fs_vnode *_node, void **cookie)
{
fs_nspace *ns = (fs_nspace *)_volume->private_volume;
fs_node *node = (fs_node *)_node->private_node;
status_t err = B_OK;
fs_attr_dir_cookie *c;
TRACE("open_attrdir(%" B_PRId32 ", %" B_PRId64 ")\n", ns->nsid, node->vnid);
if (!node)
return EINVAL;
err = LOCK(&node->l);
if (err)
return err;
c = malloc(sizeof(fs_attr_dir_cookie));
if (c) {
memset(c, 0, sizeof(fs_attr_dir_cookie));
c->omode = O_RDONLY;
c->type = S_ATTR_DIR;
c->node = node;
c->dir_current = 0;
*cookie = (void *)c;
SLL_INSERT(node->opened, next, c);
UNLOCK(&node->l);
return B_OK;
} else
err = B_NO_MEMORY;
UNLOCK(&node->l);
return err;
}
static status_t websearchfs_close_attrdir(fs_volume *_volume, fs_vnode *_node, void *_cookie)
{
fs_nspace *ns = (fs_nspace *)_volume->private_volume;
fs_node *node = (fs_node *)_node->private_node;
fs_attr_dir_cookie *cookie = (fs_attr_dir_cookie *)_cookie;
status_t err = B_OK;
TRACE("close_attrdir(%" B_PRId32 ", %" B_PRId64 ")\n", ns->nsid, node->vnid);
err = LOCK(&node->l);
if (err)
return err;
SLL_REMOVE(node->opened, next, cookie);
UNLOCK(&node->l);
return err;
}
static status_t websearchfs_free_attrdircookie(fs_volume *_volume, fs_vnode *_node, void *_cookie)
{
fs_nspace *ns = (fs_nspace *)_volume->private_volume;
fs_node *node = (fs_node *)_node->private_node;
fs_attr_dir_cookie *cookie = (fs_attr_dir_cookie *)_cookie;
status_t err = B_OK;
TRACE("free_attrdircookie(%" B_PRId32 ", %" B_PRId64 ")\n", ns->nsid, node->vnid);
err = LOCK(&node->l);
if (err)
return err;
SLL_REMOVE(node->opened, next, cookie);
UNLOCK(&node->l);
free(cookie);
return B_OK;
}
static status_t websearchfs_rewind_attrdir(fs_volume *_volume, fs_vnode *_node, void *_cookie)
{
fs_nspace *ns = (fs_nspace *)_volume->private_volume;
fs_node *node = (fs_node *)_node->private_node;
fs_attr_dir_cookie *cookie = (fs_attr_dir_cookie *)_cookie;
TRACE("rewind_attrdir(%" B_PRId32 ", %" B_PRId64 ")\n", ns->nsid, node->vnid);
cookie->dir_current = 0;
return B_OK;
}
static status_t websearchfs_read_attrdir(fs_volume *_volume, fs_vnode *_node, void *_cookie,
struct dirent *buf, size_t bufsize, uint32 *num)
{
fs_nspace *ns = (fs_nspace *)_volume->private_volume;
fs_node *node = (fs_node *)_node->private_node;
fs_file_cookie *cookie = (fs_file_cookie *)_cookie;
attr_entry *ae = NULL;
int i;
int count_indirect;
TRACE("read_attrdir(%" B_PRId32 ", %" B_PRId64 ") @ %d\n", ns->nsid, node->vnid,
cookie->dir_current);
if (!node || !cookie || !num || !*num || !buf
|| (bufsize < (sizeof(dirent_t) + WEBSEARCHFS_NAME_LEN)))
return EINVAL;
LOCK(&node->l);
for (i = 0, count_indirect = 0; node->attrs_indirect && !ae && node->attrs_indirect[i].name;
i++, count_indirect++) {
if (i == cookie->dir_current)
ae = &node->attrs_indirect[i];
}
for (i = 0; !ae && i < 10 && node->attrs[i].name; i++) {
if (i + count_indirect == cookie->dir_current)
ae = &node->attrs[i];
}
if (ae) {
TRACE("read_attrdir: giving %s\n", ae->name);
buf->d_dev = ns->nsid;
buf->d_pdev = ns->nsid;
buf->d_ino = node->vnid;
buf->d_pino = node->parent?node->parent->vnid:ns->rootid;
strcpy(buf->d_name, ae->name);
buf->d_reclen = offsetof(struct dirent, d_name)+strlen(buf->d_name)+1;
cookie->dir_current++;
*num = 1;
} else
*num = 0;
UNLOCK(&node->l);
return B_OK;
}
static status_t websearchfs_open_attr_h(fs_volume *_volume, fs_vnode *_node, const char *name, int omode, void **cookie)
{
fs_nspace *ns = (fs_nspace *)_volume->private_volume;
fs_node *node = (fs_node *)_node->private_node;
status_t err = B_OK;
fs_file_cookie *fc;
attr_entry *ae = NULL;
int i;
TRACE("open_attr(%" B_PRId32 ", %" B_PRId64 ", %s, 0x%x)\n", ns->nsid, node->vnid, name, omode);
if (!node || !name || !cookie)
return EINVAL;
err = LOCK(&node->l);
if (err)
goto err_n_l;
for (i = 0; node->attrs_indirect && !ae && node->attrs_indirect[i].name; i++)
if (!strcmp(name, node->attrs_indirect[i].name))
ae = &node->attrs_indirect[i];
for (i = 0; !ae && i < 10 && node->attrs[i].name; i++)
if (!strcmp(name, node->attrs[i].name))
ae = &node->attrs[i];
err = ENOENT;
if (!ae)
goto err_malloc;
err = EEXIST;
err = B_NO_MEMORY;
fc = malloc(sizeof(fs_file_cookie));
if (!fc)
goto err_malloc;
memset(fc, 0, sizeof(fs_file_cookie));
fc->node = node;
fc->omode = omode;
fc->type = S_ATTR;
fc->attr = ae;
err = SLL_INSERT(node->opened, next, fc);
if (err)
goto err_linsert;
*cookie = (void *)fc;
err = B_OK;
goto all_ok;
err_linsert:
free(fc);
err_malloc:
all_ok:
UNLOCK(&node->l);
err_n_l:
return err;
}
static status_t websearchfs_close_attr_h(fs_volume *_volume, fs_vnode *_node, void *_cookie)
{
fs_nspace *ns = (fs_nspace *)_volume->private_volume;
fs_node *node = (fs_node *)_node->private_node;
fs_file_cookie *cookie = (fs_file_cookie *)_cookie;
status_t err;
TRACE("close_attr(%" B_PRId32 ", %" B_PRId64 ":%s)\n", ns->nsid, node->vnid,
cookie->attr ? cookie->attr->name : "?");
if (!ns || !node || !cookie)
return EINVAL;
err = LOCK(&node->l);
if (err)
return err;
SLL_REMOVE(node->opened, next, cookie);
UNLOCK(&node->l);
return err;
}
static status_t websearchfs_free_attr_cookie_h(fs_volume *_volume, fs_vnode *_node, void *_cookie)
{
fs_nspace *ns = (fs_nspace *)_volume->private_volume;
fs_node *node = (fs_node *)_node->private_node;
fs_file_cookie *cookie = (fs_file_cookie *)_cookie;
status_t err = B_OK;
TRACE("free_attrcookie(%" B_PRId32 ", %" B_PRId64 ":%s)\n", ns->nsid, node->vnid,
cookie->attr ? cookie->attr->name : "?");
err = LOCK(&node->l);
if (err)
return err;
err = SLL_REMOVE(node->opened, next, cookie);
UNLOCK(&node->l);
free(cookie);
return err;
}
static status_t websearchfs_read_attr_stat(fs_volume *_volume, fs_vnode *_node, void *_cookie,
struct stat *st)
{
fs_nspace *ns = (fs_nspace *)_volume->private_volume;
fs_node *node = (fs_node *)_node->private_node;
fs_file_cookie *cookie = (fs_file_cookie *)_cookie;
status_t err = B_OK;
attr_entry *ae = cookie->attr;
TRACE("stat_attr(%" B_PRId32 ", %" B_PRId64 ":%s)\n", ns->nsid, node->vnid, ae->name);
if (!node || !st || !cookie || !cookie->attr)
return EINVAL;
memcpy(st, &node->st, sizeof(struct stat));
st->st_type = ae->type;
st->st_size = ae->size;
err = B_OK;
return err;
}
static status_t websearchfs_read_attr(fs_volume *_volume, fs_vnode *_node, void *_cookie,
off_t pos, void *buf, size_t *len)
{
fs_nspace *ns = (fs_nspace *)_volume->private_volume;
fs_node *node = (fs_node *)_node->private_node;
fs_file_cookie *cookie = (fs_file_cookie *)_cookie;
status_t err = B_OK;
attr_entry *ae = cookie->attr;
TRACE("read_attr(%" B_PRId32 ", %" B_PRId64 ":%s)\n", ns->nsid, node->vnid, ae->name);
if (!node || !cookie || !len || !*len)
return EINVAL;
err = LOCK(&node->l);
if (ae && (pos < 0 || (size_t)pos < ae->size)) {
memcpy(buf, (char *)ae->value + pos, MIN(*len, ae->size-pos));
*len = MIN(*len, ae->size-pos);
err = B_OK;
} else {
*len = 0;
err = ENOENT;
}
UNLOCK(&node->l);
return err;
}
static int compare_fs_node_by_recent_query_string(fs_node *node, char *query)
{
time_t tm = time(NULL);
TRACE("find_by_recent_query_string: '%s' <> '%s'\n", \
node->request?node->request->query_string:NULL, query);
if (!node->request || !node->request->query_string)
return -1;
if (node->st.st_crtime + 60 * 5 < tm)
return -1;
return strcmp(node->request->query_string, query);
}
static status_t websearchfs_open_query(fs_volume *_volume, const char *query, uint32 flags,
port_id port, uint32 token, void **cookie)
{
fs_nspace *ns = (fs_nspace *)_volume->private_volume;
status_t err = B_OK;
fs_query_cookie *c;
fs_node *qn, *dummy;
const char *p;
char *q;
char *qstring = NULL;
char qname[WEBSEARCHFS_NAME_LEN];
bool accepted = true;
bool reused = false;
TRACE("open_query(%" B_PRId32 ", '%s', 0x%08" B_PRIx32 ", %" B_PRId32 ", %" B_PRId32 ")\n",
ns->nsid, query, flags, port, token);
if (!query || !cookie)
return EINVAL;
err = B_NO_MEMORY;
c = malloc(sizeof(fs_query_cookie));
if (!c)
return err;
memset(c, 0, sizeof(fs_query_cookie));
c->omode = O_RDONLY;
c->type = S_IFQUERY;
c->dir_current = 0;
err = ENOSYS;
if (strncmp(query, "((name==\"*", 10))
accepted = false;
else {
qstring = query_unescape_string(query + 10, &p, '"');
if (!qstring)
accepted = false;
else if (!p)
accepted = false;
else if (strcmp(p, "\")&&(BEOS:TYPE==\"application/x-vnd.Be-bookmark\"))"))
accepted = false;
else {
if (!query_strip_bracketed_Cc(qstring))
goto err_qs;
}
}
if (!accepted) {
free(qstring);
*cookie = (void *)c;
return B_OK;
}
TRACE("open_query: QUERY: '%s'\n", qstring);
LOCK(&ns->l);
qn = SLL_FIND(ns->queries, qnext,
(sll_compare_func)compare_fs_node_by_recent_query_string, (void *)qstring);
UNLOCK(&ns->l);
reused = (qn != NULL);
if (reused) {
TRACE("open_query: reusing %" B_PRId32 ":%" B_PRId64 "\n", ns->nsid, qn->vnid);
err = get_vnode(_volume, qn->vnid, (void **)&dummy);
if (err)
goto err_mkdir;
while (!qn->qcompleted)
snooze(10000);
goto reuse;
}
strncpy(qname, qstring, WEBSEARCHFS_NAME_LEN);
qname[WEBSEARCHFS_NAME_LEN-1] = '\0';
q = qname;
while ((q = strchr(q, '/')))
strcpy(q, q + 1);
err = websearchfs_create_gen(_volume, ns->root, qname, 0, 0755, NULL, &qn, folders_attrs, true, true);
if (err)
goto err_qs;
err = get_vnode(_volume, qn->vnid, (void **)&dummy);
if (err)
goto err_mkdir;
err = duckduckgo_request_open(qstring, _volume, qn, &qn->request);
if (err)
goto err_gn;
TRACE("open_query: request_open done\n");
#ifndef NO_SEARCH
err = duckduckgo_request_process(qn->request);
if (err)
goto err_gro;
TRACE("open_query: request_process done\n");
#else
for (i = 0; i < 10; i++) {
err = websearchfs_create_gen(_volume, qn, "B", 0, 0644, NULL, &n, fake_bookmark_attrs, false, true);
*(int32 *)&n->attrs[1].value = i + 1;
n->attrs[0].type = 'LONG';
n->attrs[0].value = &n->attrs[1].value;
n->attrs[0].size = sizeof(int32);
n->attrs[0].name = "WEBSEARCH:order";
notify_attribute_changed(ns->nsid, -1, n->vnid, n->attrs[0].name, B_ATTR_CHANGED);
if (err)
goto err_gn;
}
#endif
LOCK(&ns->l);
SLL_INSERT(ns->queries, qnext, qn);
UNLOCK(&ns->l);
reuse:
c->node = qn;
LOCK(&qn->l);
SLL_INSERT(qn->opened, next, c);
UNLOCK(&qn->l);
qn->qcompleted = 1;
*cookie = (void *)c;
free(qstring);
return B_OK;
err_gro:
if (qn->request)
duckduckgo_request_close(qn->request);
err_gn:
put_vnode(_volume, qn->vnid);
err_mkdir:
if (!reused)
websearchfs_unlink_gen(_volume, ns->root, qn->name);
err_qs:
free(qstring);
free(c);
TRACE("open_query: error 0x%08" B_PRIx32 "\n", err);
return err;
}
static status_t websearchfs_close_query(fs_volume *_volume, void *_cookie)
{
fs_nspace *ns = (fs_nspace *)_volume->private_volume;
fs_query_cookie *cookie = (fs_query_cookie *)_cookie;
status_t err;
fs_node *q;
TRACE("close_query(%" B_PRId32 ", %" B_PRId64 ")\n", ns->nsid,
cookie->node ? cookie->node->vnid : (int64)0);
q = cookie->node;
if (!q)
return B_OK;
LOCK(&q->l);
SLL_REMOVE(q->opened, next, cookie);
if (q->request ) {
err = duckduckgo_request_close(q->request);
}
UNLOCK(&q->l);
if (sync_unlink_queries && !q->opened)
err = websearchfs_unlink_node_rec(_volume, q);
err = put_vnode(_volume, q->vnid);
return err;
}
#ifdef __HAIKU__
static status_t websearchfs_free_query_cookie(fs_volume *_volume, void *_cookie)
{
fs_query_cookie *cookie = (fs_query_cookie *)_cookie;
status_t err = B_OK;
fs_node *q;
TRACE("free_query_cookie(%" B_PRId32 ")\n", _volume->id);
q = cookie->node;
if (!q)
goto no_node;
err = LOCK(&q->l);
if (err)
return err;
err = SLL_REMOVE(q->opened, next, cookie);
if (q->request ) {
err = duckduckgo_request_close(q->request);
}
UNLOCK(&q->l);
no_node:
free(cookie);
return B_OK;
}
#endif
static status_t websearchfs_read_query(fs_volume *_volume, void *_cookie, struct dirent *buf,
size_t bufsize, uint32 *num)
{
fs_nspace *ns = (fs_nspace *)_volume->private_volume;
fs_query_cookie *cookie = (fs_query_cookie *)_cookie;
fs_node *n = NULL;
fs_node *node = cookie->node;
int index;
TRACE("read_query(%" B_PRId32 ", %" B_PRId64 ") @ %d\n", ns->nsid, node ? node->vnid : (int64)0,
cookie->dir_current);
if (!cookie || !num || !*num || !buf || (bufsize < (sizeof(dirent_t) + WEBSEARCHFS_NAME_LEN)))
return EINVAL;
if (!node) {
*num = 0;
return B_OK;
}
LOCK(&node->l);
index = cookie->dir_current;
for (n = node->children; n && index; n = n->next, index--);
if (n) {
TRACE("read_query: giving ino %" PRId64 ", %s\n", n->vnid, n->name);
buf->d_dev = ns->nsid;
buf->d_pdev = ns->nsid;
buf->d_ino = n->vnid;
buf->d_pino = node->vnid;
strcpy(buf->d_name, n->name);
buf->d_reclen = offsetof(struct dirent, d_name)+strlen(buf->d_name)+1;
cookie->dir_current++;
*num = 1;
} else {
*num = 0;
}
UNLOCK(&node->l);
return B_OK;
}
int websearchfs_push_result_to_query(struct duckduckgo_request *request,
struct duckduckgo_result *result)
{
status_t err = B_OK;
fs_volume *_volume = request->volume;
fs_nspace *ns = (fs_nspace *)_volume->private_volume;
fs_node *qn = request->query_node;
fs_node *n;
char ename[WEBSEARCHFS_NAME_LEN];
char *p;
int i;
TRACE("push_result_to_query(%" B_PRId32 ", %" B_PRId64 ", %ld:'%s')\n", ns->nsid, qn->vnid, result->id, result->name);
if (!ns || !qn)
return EINVAL;
strncpy(ename, result->name, WEBSEARCHFS_NAME_LEN);
ename[WEBSEARCHFS_NAME_LEN-1] = '\0';
p = ename;
while ((p = strchr(p, '/')))
*p++ = '_';
err = websearchfs_create_gen(_volume, qn, ename, 0, 0644, NULL, &n, bookmark_attrs, false, true);
if (err)
return err;
LOCK(&n->l);
n->result = result;
i = 0;
n->attrs[i].type = 'CSTR';
n->attrs[i].value = result->name;
n->attrs[i].size = strlen(result->name)+1;
n->attrs[i].name = "META:title";
notify_attribute_changed(ns->nsid, -1, n->vnid, n->attrs[i].name, B_ATTR_CREATED);
i++;
n->attrs[i].type = 'CSTR';
n->attrs[i].value = result->url;
n->attrs[i].size = strlen(result->url)+1;
n->attrs[i].name = "META:url";
notify_attribute_changed(ns->nsid, -1, n->vnid, n->attrs[i].name, B_ATTR_CREATED);
i++;
n->attrs[i].type = 'CSTR';
n->attrs[i].value = request->query_string;
n->attrs[i].size = strlen(request->query_string)+1;
n->attrs[i].name = "META:keyw";
notify_attribute_changed(ns->nsid, -1, n->vnid, n->attrs[i].name, B_ATTR_CREATED);
i++;
n->attrs[i].type = 'LONG';
n->attrs[i].value = &result->id;
n->attrs[i].size = sizeof(int32);
n->attrs[i].name = "WEBSEARCH:order";
notify_attribute_changed(ns->nsid, -1, n->vnid, n->attrs[i].name, B_ATTR_CREATED);
i++;
if (result->snipset[0]) {
n->attrs[i].type = 'CSTR';
n->attrs[i].value = result->snipset;
n->attrs[i].size = strlen(result->snipset)+1;
n->attrs[i].name = "WEBSEARCH:excerpt";
notify_attribute_changed(ns->nsid, -1, n->vnid, n->attrs[i].name, B_ATTR_CREATED);
i++;
}
if (result->cache_url[0]) {
n->attrs[i].type = 'CSTR';
n->attrs[i].value = result->cache_url;
n->attrs[i].size = strlen(result->cache_url)+1;
n->attrs[i].name = "WEBSEARCH:cache_url";
notify_attribute_changed(ns->nsid, -1, n->vnid, n->attrs[i].name, B_ATTR_CREATED);
i++;
}
if (result->similar_url[0]) {
n->attrs[i].type = 'CSTR';
n->attrs[i].value = result->similar_url;
n->attrs[i].size = strlen(result->similar_url)+1;
n->attrs[i].name = "WEBSEARCH:similar_url";
notify_attribute_changed(ns->nsid, -1, n->vnid, n->attrs[i].name, B_ATTR_CREATED);
i++;
}
UNLOCK(&n->l);
return B_OK;
TRACE("push_result_to_query: error 0x%08" B_PRIx32 "\n", err);
return err;
}
static status_t
websearchfs_std_ops(int32 op, ...)
{
switch (op) {
case B_MODULE_INIT:
TRACE("std_ops(INIT)\n");
return B_OK;
case B_MODULE_UNINIT:
TRACE("std_ops(UNINIT)\n");
return B_OK;
default:
return B_ERROR;
}
}
static fs_volume_ops sWebSearchFSVolumeOps = {
&websearchfs_unmount,
&websearchfs_rfsstat,
&websearchfs_wfsstat,
NULL,
&websearchfs_read_vnode,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
&websearchfs_open_query,
&websearchfs_close_query,
&websearchfs_free_query_cookie,
&websearchfs_read_query,
NULL,
};
static fs_vnode_ops sWebSearchFSVnodeOps = {
&websearchfs_walk,
&websearchfs_get_vnode_name,
&websearchfs_release_vnode,
&websearchfs_remove_vnode,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
&websearchfs_unlink,
NULL,
&websearchfs_access,
&websearchfs_rstat,
&websearchfs_wstat,
NULL,
&websearchfs_create,
&websearchfs_open,
&websearchfs_close,
&websearchfs_free_cookie,
&websearchfs_read,
&websearchfs_write,
&websearchfs_mkdir,
&websearchfs_rmdir,
&websearchfs_opendir,
&websearchfs_closedir,
&websearchfs_free_dircookie,
&websearchfs_readdir,
&websearchfs_rewinddir,
&websearchfs_open_attrdir,
&websearchfs_close_attrdir,
&websearchfs_free_attrdircookie,
&websearchfs_read_attrdir,
&websearchfs_rewind_attrdir,
NULL,
&websearchfs_open_attr_h,
&websearchfs_close_attr_h,
&websearchfs_free_attr_cookie_h,
&websearchfs_read_attr,
NULL,
&websearchfs_read_attr_stat,
NULL,
NULL,
NULL,
};
file_system_module_info sWebSearchFSModule = {
{
"file_systems/websearchfs" B_CURRENT_FS_API_VERSION,
0,
websearchfs_std_ops,
},
"websearchfs",
WEBSEARCHFS_PRETTY_NAME,
0,
NULL,
NULL,
NULL,
NULL,
&websearchfs_mount,
};
module_info *modules[] = {
(module_info *)&sWebSearchFSModule,
NULL,
};