/* * Copyright 2004-2010, François Revol, . * Distributed under the terms of the MIT License. */ /* * websearchfs - a bookmark-populated virtual filesystem using DuckDuckGo results. */ #define _BUILDING_fs 1 #include #include #include #include //#include #include #include #include #include #include #include "query.h" #include "websearchfs.h" #include "vnidpool.h" #include "duckduckgo_request.h" #include "settings.h" /* just publish fake entries; for debugging */ //#define NO_SEARCH #define PFS "websearchfs: " #define TRACE_WEBSEARCHFS #ifdef TRACE_WEVSEARCHFS # define TRACE(x...) fprintf(stderr, PFS x) #else # define TRACE(x...) #endif /* needed to get /bin/df tell the mountpoint... */ #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[]; /* for debugging */ 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; } /** Publishes some entries in the root vnode: a query template, the readme file, and a People file of the author. */ 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;// *dummy; //char ename[WEBSEARCHFS_NAME_LEN]; //char *p; //int i; 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);// + 1; 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; /* err: TRACE("push_result_to_query: error 0x%08"B_PRIx32"\n", err); return err; */ } 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); /* only allow a single mount */ 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; /* create root dir */ 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; // sll_insert 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; /* anything in still in use ? */ for (node = ns->nodes; node; node = ns->nodes) { ns->nodes = node->nlnext; /* better cache that before we free node */ websearchfs_free_vnode(_volume, node); } // Unlike in BeOS, we need to put the reference to our root node ourselves 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); //LOCK(&node->l); err = SLL_REMOVE(ns->nodes, nlnext, node); /* query dirs must be removed from the query list too */ 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; /*XXX: S_IFMT ?*/ *_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 memcmp(node->name, name, WEBSEARCHFS__NAME_LEN); //TRACE("find_by_name: '%s' <> '%s'\n", node->name, 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; // XXX: LOCK(&base->l) ? //*type = S_IFDIR; } else err = EINVAL; } else if (!strcmp(file, ".")) { /* root dir */ if (base) { // XXX: LOCK(&base->l) ? *vnid = base->vnid; //*type = S_IFDIR; } else err = EINVAL; } else if (base) { /* child of dir */ n = (fs_node *)SLL_FIND(base->children, next, (sll_compare_func)compare_fs_node_by_name, (void *)file); if (n) { *vnid = n->vnid; //*type = n->st.st_type & ~S_IUMSK; /*XXX: S_IFMT ?*/ } else err = ENOENT; } else err = ENOENT; if (err == B_OK) { if (get_vnode(_volume, *vnid, (void **)&dummy) != B_OK) /* inc ref count */ 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; // node = cookie->node; // work around VFS bug 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"); /* the VFS will correct that anyway */ 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"); /* the VFS will correct that anyway */ 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--); //XXX: care about n->hidden || n->deleted 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; // node = cookie->node; // work around VFS bug 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); /* just to make sure */ 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; //st->st_size = 0LL; 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; // websearchfs_BUFF_SIZE; info->io_size = 1024; // WEBSEARCHFS_BUFF_SIZE; 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_SHARED|*/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_node *dummy; 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(&ns->l); // if (err) // return err; err = LOCK(&node->l); if (err) goto err_n_l; err = EEXIST; #ifndef ALLOW_DIR_OPEN err = EINVAL;//EISDIR; 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; /* err = get_vnode(ns->nsid, node->vnid, &dummy); if (err) goto err_getvn;*/ //*vnid = node->vnid; *cookie = (void *)fc; err = B_OK; goto all_ok; //err_: // put_vnode(ns->nsid, node->nsid); //err_getvn: // SLL_REMOVE(node->opened, next, fc); err_linsert: free(fc); err_malloc: all_ok: UNLOCK(&node->l); err_n_l: // UNLOCK(&ns->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); //all_ok: //err_n_l: 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); /* just to make sure */ // if (err) // goto err_n_l; if (/*!node->is_perm &&*/ false) { /* not yet */ 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); // err = B_OK; //err_n_l: 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; } /* this one returns the created fs_node to caller (for use by query engine) */ /** * @param dir the dir's fs_node we mkdir in * @param name name to mkdir (basename is uniq is set) * @param perms create with those permissions * @param node make this point to the fs_node if !NULL * @param iattr indirect attributes to set if desired (must be statically allocated) * @param mkdir create a directory instead of a file * @param uniq choose an unique name, appending a number if required */ 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; //fs_node *dir = (fs_node *)_dir->private_node; 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) /* already existing entry in there! */ goto err_l; strncpy(newname, name, WEBSEARCHFS_NAME_LEN); newname[WEBSEARCHFS_NAME_LEN-1] = '\0'; for (i = 1; uniq && n && i < 5000; i++) { /* uniquify the name */ //sprintf("%"#(WEBSEARCHFS_NAME_LEN-8)"s %05d", name, 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)) /* still there! */ goto err_l; name = newname; if (n) { /* already exists, so return it */ 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); //n->is_perm = 1; 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; /* _TAIL so they are in order */ err = SLL_INSERT(dir->children, next, n); if (err) goto err_insnl; // err = new_vnode(ns->nsid, n->vnid, n); // if (err) // goto err_ins; n->parent = dir; dir->st.st_nlink++; UNLOCK(&ns->l); n->attrs_indirect = iattrs; notify_entry_created(ns->nsid, dir->vnid, name, n->vnid); /* dosfs doesn't do that one but I believe it should */ notify_stat_changed(B_STAT_CHANGED, -1, ns->nsid, -1); /* give node to caller if it wants it */ 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); /* don't let ppl mess our fs up */ 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); //dprintf(PFS"unlink(%" B_PRId32 ", %" B_PRId64 ", %s)\n", ns->nsid, dir->vnid, name); err = LOCK(&dir->l); if (err) return err; err = ENOENT; /* no need to check for S_ISDIR */ 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 (S_ISDIR(n->st.st_mode)) // err = EISDIR; 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); //notify_listener(B_STAT_CHANGED, ns->nsid, 0LL, 0LL, dir->vnid, NULL); 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) { //fs_nspace *ns = (fs_nspace *)_volume->private_volume; //fs_node *dir = (fs_node *)_dir->private_node; 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_nspace *ns = (fs_nspace *)_volume->private_volume; 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; // kill_request(); 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); } /* attr stuff */ 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); /* just to make sure */ 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; //fs_node *n = NULL; //fs_node *parent = node->parent; 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; } /* Haiku and BeOs differ in the way the handle attributes at the vfs layer. BeOS uses atomic calls on the vnode, Haiku retains the open/close/read/write semantics for attributes (loosing atomicity). Here we don't care much though, open is used for both to factorize attribute lookup. <- TODO _h suffixed funcs are for Haiku API, _b are for BeOS. */ /* for Haiku, but also used by BeOS calls to factorize code */ 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_node *dummy; 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; /* lookup attribute */ 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]; /* should check omode */ 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_: // put_vnode(ns->nsid, node->nsid); //err_getvn: // SLL_REMOVE(node->opened, next, fc); err_linsert: free(fc); err_malloc: all_ok: UNLOCK(&node->l); err_n_l: // UNLOCK(&ns->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); //all_ok: //err_n_l: 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); /* just to make sure */ // if (err) // goto err_n_l; UNLOCK(&node->l); free(cookie); // err = B_OK; //err_n_l: 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; } /* query stuff */ static int compare_fs_node_by_recent_query_string(fs_node *node, char *query) { time_t tm = time(NULL); //return memcmp(node->name, name, WEBSEARCHFS_NAME_LEN); 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; /* reject if older than 5 min */ 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; //int i; TRACE("open_query(%" B_PRId32 ", '%s', 0x%08" B_PRIx32 ", %" B_PRId32 ", %" B_PRId32 ")\n", ns->nsid, query, flags, port, token); // if (flags & B_LIVE_QUERY) // return ENOSYS; /* no live query yet, they are live enough anyway */ //return ENOSYS; if (!query || !cookie) return EINVAL; // filter out queries that aren't for us, we don't want to trigger DuckDuckGo searches when // apps check for mails, ... :) 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 (qstring[0] == '*') // strcpy(qstring+1, qstring); //if (qstring[strlen(qstring)-1] == '*') // qstring[strlen(qstring)-1] = '\0'; if (!query_strip_bracketed_Cc(qstring)) goto err_qs; } } if (!accepted) { free(qstring); /* return an empty cookie */ *cookie = (void *)c; return B_OK; } TRACE("open_query: QUERY: '%s'\n", qstring); /* reuse query if it's not too old */ 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); /* inc ref count */ if (err) goto err_mkdir; /* wait for the query to complete */ while (!qn->qcompleted) snooze(10000); goto reuse; } /* stripped name for folder */ strncpy(qname, qstring, WEBSEARCHFS_NAME_LEN); qname[WEBSEARCHFS_NAME_LEN-1] = '\0'; /* strip out slashes */ q = qname; while ((q = strchr(q, '/'))) strcpy(q, q + 1); /* should get/put_vnode(ns->root); around that I think... */ 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); /* inc ref count */ if (err) goto err_mkdir; //#ifndef NO_SEARCH /* let's ask DuckDuckGo */ 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 /* fake entries */ for (i = 0; i < 10; i++) { err = websearchfs_create_gen(_volume, qn, "B", 0, 0644, NULL, &n, fake_bookmark_attrs, false, true); /* fake that to test sorting */ *(int32 *)&n->attrs[1].value = i + 1; // hack 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 /*NO_SEARCH*/ // //err = duckduckgo_request_close(q->request); LOCK(&ns->l); SLL_INSERT(ns->queries, qnext, qn); UNLOCK(&ns->l); reuse: /* put the chocolate on the cookie */ c->node = qn; LOCK(&qn->l); SLL_INSERT(qn->opened, next, c); UNLOCK(&qn->l); qn->qcompleted = 1; /* tell other cookies we're done */ *cookie = (void *)c; free(qstring); return B_OK; //err_grp: 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); //err_m: 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); //return ENOSYS; q = cookie->node; if (!q) return B_OK; // kill_request(); LOCK(&q->l); SLL_REMOVE(q->opened, next, cookie); if (q->request /*&& !q->opened*/) { err = duckduckgo_request_close(q->request); } UNLOCK(&q->l); /* if last cookie on the query and sync_unlink, trash all */ if (sync_unlink_queries && !q->opened) err = websearchfs_unlink_node_rec(_volume, q); err = put_vnode(_volume, q->vnid); return err; } #ifdef __HAIKU__ /* protos are different... */ 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); /* just to make sure */ if (q->request /*&& !q->opened*/) { err = duckduckgo_request_close(q->request); } // if (err) // goto err_n_l; 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) { /* a query we don't care about, just return no entries to please apps */ *num = 0; return B_OK; } //return ENOSYS; 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); //dprintf(PFS"push_result_to_query(%" B_PRId32 ", %" B_PRId64 ", %ld:'%s')\n", ns->nsid, qn->vnid, result->id, result->name); //return ENOSYS; if (!ns || !qn) return EINVAL; // filter out queries that aren't for us, we don't want to trigger DuckDuckGo searches when // apps check for mails, ... :) /* stripped name for folder */ strncpy(ename, result->name, WEBSEARCHFS_NAME_LEN); ename[WEBSEARCHFS_NAME_LEN-1] = '\0'; /* strip out slashes */ 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; } // #pragma mark - 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, // no sync! &websearchfs_read_vnode, /* index directory & index operations */ NULL, // &websearchfs_open_index_dir NULL, // &websearchfs_close_index_dir NULL, // &websearchfs_free_index_dir_cookie NULL, // &websearchfs_read_index_dir NULL, // &websearchfs_rewind_index_dir NULL, // &websearchfs_create_index NULL, // &websearchfs_remove_index NULL, // &websearchfs_stat_index /* query operations */ &websearchfs_open_query, &websearchfs_close_query, &websearchfs_free_query_cookie, &websearchfs_read_query, NULL, // &websearchfs_rewind_query, }; static fs_vnode_ops sWebSearchFSVnodeOps = { /* vnode operations */ &websearchfs_walk, &websearchfs_get_vnode_name, //NULL, // fs_get_vnode_name &websearchfs_release_vnode, &websearchfs_remove_vnode, /* VM file access */ NULL, // &websearchfs_can_page NULL, // &websearchfs_read_pages NULL, // &websearchfs_write_pages NULL, // io() NULL, // cancel_io() NULL, // &websearchfs_get_file_map, NULL, // &websearchfs_ioctl NULL, // &websearchfs_setflags, NULL, // &websearchfs_select NULL, // &websearchfs_deselect NULL, // &websearchfs_fsync NULL, // &websearchfs_readlink, NULL, // &websearchfs_symlink, NULL, // &websearchfs_link, &websearchfs_unlink, NULL, // &websearchfs_rename, &websearchfs_access, &websearchfs_rstat, &websearchfs_wstat, NULL, // fs_preallocate /* file operations */ &websearchfs_create, &websearchfs_open, &websearchfs_close, &websearchfs_free_cookie, &websearchfs_read, &websearchfs_write, /* directory operations */ &websearchfs_mkdir, &websearchfs_rmdir, &websearchfs_opendir, &websearchfs_closedir, &websearchfs_free_dircookie, &websearchfs_readdir, &websearchfs_rewinddir, /* attribute directory operations */ &websearchfs_open_attrdir, &websearchfs_close_attrdir, &websearchfs_free_attrdircookie, &websearchfs_read_attrdir, &websearchfs_rewind_attrdir, /* attribute operations */ NULL, // &websearchfs_create_attr &websearchfs_open_attr_h, &websearchfs_close_attr_h, &websearchfs_free_attr_cookie_h, &websearchfs_read_attr, NULL, // &websearchfs_write_attr_h, &websearchfs_read_attr_stat, NULL, // &websearchfs_write_attr_stat NULL, // &websearchfs_rename_attr NULL, // &websearchfs_remove_attr }; file_system_module_info sWebSearchFSModule = { { "file_systems/websearchfs" B_CURRENT_FS_API_VERSION, 0, websearchfs_std_ops, }, "websearchfs", // short_name WEBSEARCHFS_PRETTY_NAME, // pretty_name 0,//B_DISK_SYSTEM_SUPPORTS_WRITING, // DDM flags // scanning NULL, // fs_identify_partition, NULL, // fs_scan_partition, NULL, // fs_free_identify_partition_cookie, NULL, // free_partition_content_cookie() &websearchfs_mount, }; module_info *modules[] = { (module_info *)&sWebSearchFSModule, NULL, };