1/*
2 * Copyright 2004-2010, Fran��ois Revol, <revol@free.fr>.
3 * Distributed under the terms of the MIT License.
4 */
5
6/*
7 * websearchfs - a bookmark-populated virtual filesystem using DuckDuckGo results.
8 */
9
10#define _BUILDING_fs 1
11
12#include <sys/param.h>
13#include <sys/stat.h>
14#include <malloc.h>
15#include <KernelExport.h>
16//#include <NodeMonitor.h>
17#include <stddef.h>
18#include <stdio.h>
19#include <signal.h>
20#include <string.h>
21#include <fs_query.h>
22#include "query.h"
23#include "websearchfs.h"
24#include "vnidpool.h"
25#include "duckduckgo_request.h"
26#include "settings.h"
27
28/* just publish fake entries; for debugging */
29//#define NO_SEARCH
30
31#define PFS "websearchfs: "
32
33#define TRACE_WEBSEARCHFS
34#ifdef TRACE_WEVSEARCHFS
35#	define TRACE(x...) fprintf(stderr, PFS x)
36#else
37#	define TRACE(x...)
38#endif
39
40
41/* needed to get /bin/df tell the mountpoint... */
42#define ALLOW_DIR_OPEN
43
44int32 refcount = 0;
45
46
47extern struct attr_entry root_folder_attrs[];
48extern struct attr_entry folders_attrs[];
49extern struct attr_entry bookmark_attrs[];
50extern struct attr_entry fake_bookmark_attrs[]; /* for debugging */
51extern struct attr_entry template_1_attrs[];
52extern struct attr_entry text_attrs[];
53extern struct attr_entry mailto_me_bookmark_attrs[];
54
55extern char *readmestr;
56
57static fs_volume_ops sWebSearchFSVolumeOps;
58static fs_vnode_ops sWebSearchFSVnodeOps;
59
60
61static 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);
62static int websearchfs_free_vnode(fs_volume *_volume, fs_node *node);
63
64static void fill_default_stat(struct stat *st, nspace_id nsid, ino_t vnid, mode_t mode)
65{
66	time_t tm = time(NULL);
67	st->st_dev = nsid;
68	st->st_ino = vnid;
69	st->st_mode = mode;
70	st->st_nlink = 1;
71	st->st_uid = 0;
72	st->st_gid = 0;
73	st->st_size = 0LL;
74	st->st_blksize = 1024;
75	st->st_atime = tm;
76	st->st_mtime = tm;
77	st->st_ctime = tm;
78	st->st_crtime = tm;
79}
80
81/**	Publishes some entries in the root vnode: a query template, the readme file, and a People file of the author.
82 */
83static int websearchfs_publish_static_entries(fs_volume *_volume)
84{
85	fs_nspace *ns = (fs_nspace *)_volume->private_volume;
86	status_t err = B_OK;
87	fs_node *dir = ns->root;
88	fs_node *n;// *dummy;
89	//char ename[WEBSEARCHFS_NAME_LEN];
90	//char *p;
91	//int i;
92	TRACE("websearchfs_publish_static_entries(%" B_PRId32 ")\n", ns->nsid);
93	if (!ns || !dir)
94		return EINVAL;
95
96	err = websearchfs_create_gen(_volume, dir, "Search the Web", 0, 0444, NULL, &n, template_1_attrs, false, true);
97	if (err)
98		return err;
99	n->is_perm = 1;
100
101	err = websearchfs_create_gen(_volume, dir, "README", 0, 0444, NULL, &n, text_attrs, false, true);
102	if (err)
103		return err;
104	n->is_perm = 1;
105	n->data = readmestr;
106	n->data_size = strlen(n->data);// + 1;
107
108	err = websearchfs_create_gen(_volume, dir, "Author", 0, 0444, NULL, &n, mailto_me_bookmark_attrs, false, true);
109	if (err)
110		return err;
111	n->is_perm = 1;
112
113	return B_OK;
114
115/*
116err:
117	TRACE("push_result_to_query: error 0x%08"B_PRIx32"\n", err);
118	return err;
119*/
120}
121
122static status_t websearchfs_mount(fs_volume *_vol, const char *devname, uint32 flags,
123		const char *parms, ino_t *vnid)
124{
125	fs_nspace *ns;
126	fs_node *root;
127	int err;
128	TRACE("mount(%p, %s, 0x%08" B_PRIx32 ", %s, , )\n", _vol, devname, flags, parms);
129
130	/* only allow a single mount */
131	if (atomic_add(&refcount, 1))
132		return EALREADY;
133
134	err = load_settings();
135
136	ns = malloc(sizeof(fs_nspace));
137	if (!ns)
138		return B_NO_MEMORY;
139	memset(ns, 0, sizeof(fs_nspace));
140	ns->nsid = _vol->id;
141
142	err = vnidpool_alloc(&ns->vnids, MAX_VNIDS);
143	if (err < 0)
144		return err;
145	err = vnidpool_get(ns->vnids, &ns->rootid);
146	if (err < 0)
147		return err;
148	atomic_add(&ns->nodecount, 1);
149
150	new_lock(&(ns->l), "websearchfs main lock");
151
152	ns->nodes = NULL;
153
154	/* create root dir */
155	err = B_NO_MEMORY;
156	root = malloc(sizeof(fs_node));
157	ns->root = root;
158	if (root) {
159		memset(root, 0, sizeof(fs_node));
160		strcpy(root->name, ".");
161		root->is_perm = 1;
162		root->vnid = ns->rootid;
163		fill_default_stat(&root->st, ns->nsid, ns->rootid, 0777 | S_IFDIR);
164		root->attrs_indirect = root_folder_attrs;
165		new_lock(&(root->l), "websearchfs root dir");
166		TRACE("mount: root->l @ %p\n", &root->l);
167
168		_vol->private_volume = ns;
169		_vol->ops = &sWebSearchFSVolumeOps;
170		*vnid = ns->rootid;
171		ns->nodes = root; // sll_insert
172		err = publish_vnode(_vol, *vnid, root, &sWebSearchFSVnodeOps, S_IFDIR, 0);
173		if (err == B_OK) {
174			websearchfs_publish_static_entries(_vol);
175			TRACE("mount() OK, nspace@ %p, id %" B_PRId32 ", root@ %p, id %" B_PRId64 "\n", ns, ns->nsid, root, ns->rootid);
176			return B_OK;
177		}
178		free_lock(&root->l);
179		free(root);
180	}
181	free_lock(&ns->l);
182	free(ns);
183	atomic_add(&refcount, -1);
184	return err;
185}
186
187static status_t websearchfs_unmount(fs_volume *_volume)
188{
189	fs_nspace *ns = (fs_nspace *)_volume->private_volume;
190	status_t err;
191	struct fs_node *node;
192	TRACE("unmount(%" B_PRId32 ")\n", ns->nsid);
193	err = LOCK(&ns->l);
194	if (err)
195		return err;
196	/* anything in still in use ? */
197	for (node = ns->nodes; node; node = ns->nodes) {
198		ns->nodes = node->nlnext; /* better cache that before we free node */
199		websearchfs_free_vnode(_volume, node);
200	}
201
202	// Unlike in BeOS, we need to put the reference to our root node ourselves
203	put_vnode(_volume, ns->rootid);
204
205	free_lock(&ns->l);
206	vnidpool_free(ns->vnids);
207	free(ns);
208
209	atomic_add(&refcount, -1);
210
211	return B_OK;
212}
213
214static int compare_fs_node_by_vnid(fs_node *node, ino_t *id)
215{
216	return !(node->vnid == *id);
217}
218
219static int websearchfs_free_vnode(fs_volume *_volume, fs_node *node)
220{
221	fs_nspace *ns = (fs_nspace *)_volume->private_volume;
222	TRACE("%s(%" B_PRId32 ", %" B_PRId64 ")\n", __FUNCTION__, ns->nsid, node->vnid);
223	free_lock(&node->l);
224	atomic_add(&ns->nodecount, -1);
225	vnidpool_put(ns->vnids, node->vnid);
226	if (node->request)
227		duckduckgo_request_free(node->request);
228	free(node->result);
229	free(node);
230	return 0;
231}
232
233static status_t websearchfs_remove_vnode(fs_volume *_volume, fs_vnode *_node, bool reenter)
234{
235	fs_nspace *ns = (fs_nspace *)_volume->private_volume;
236	fs_node *node = (fs_node *)_node->private_node;
237	status_t err = B_OK;
238	TRACE("%s(%" B_PRId32 ", %" B_PRId64 ", %s)\n", __FUNCTION__, ns->nsid, node->vnid, reenter?"r":"!r");
239	if (!reenter)
240		err = LOCK(&ns->l);
241	if (err)
242		return err;
243	if (node->vnid == ns->rootid) {
244		TRACE("asked to remove the root node!!\n");
245	}
246TRACE("SLL_REMOVE(ns->nodes %p, nlnext, %p)\n", ns->nodes, node);
247	//LOCK(&node->l);
248	err = SLL_REMOVE(ns->nodes, nlnext, node);
249	/* query dirs must be removed from the query list too */
250TRACE("SLL_REMOVE(ns->queries %p, qnext, %p)\n", ns->nodes, node);
251	err = SLL_REMOVE(ns->queries, qnext, node);
252	if (node->parent) {
253		LOCK(&node->parent->l);
254TRACE("SLL_REMOVE(node->parent->children %p, next, %p)\n", node->parent->children, node);
255		SLL_REMOVE(node->parent->children, next, node);
256		UNLOCK(&node->parent->l);
257	}
258	websearchfs_free_vnode(_volume, node);
259	if (!reenter)
260		UNLOCK(&ns->l);
261	return err;
262}
263
264static status_t websearchfs_read_vnode(fs_volume *_volume, ino_t vnid, fs_vnode *_node, int* _type, uint32* _flags, bool reenter)
265{
266	fs_nspace *ns = (fs_nspace *)_volume->private_volume;
267	fs_node *n;
268	status_t err = B_OK;
269	TRACE("%s(%" B_PRId32 ", %" B_PRId64 ", %s)\n", __FUNCTION__, _volume->id, vnid, reenter?"r":"!r");
270	if (!reenter)
271		err = LOCK(&ns->l);
272	if (err)
273		return err;
274	n = (fs_node *)SLL_FIND(ns->nodes, nlnext, (sll_compare_func)compare_fs_node_by_vnid, (void *)&vnid);
275	if (n) {
276		_node->private_node = n;
277		_node->ops = &sWebSearchFSVnodeOps;
278		*_type = n->st.st_mode & ~S_IUMSK; /*XXX: S_IFMT ?*/
279		*_flags = 0;
280
281	} else
282		err = ENOENT;
283	if (!reenter)
284		UNLOCK(&ns->l);
285	return err;
286}
287
288static status_t websearchfs_release_vnode(fs_volume *_volume, fs_vnode *_node, bool reenter)
289{
290	fs_node *node = (fs_node *)_node->private_node;
291	TRACE("%s(%" B_PRId32 ", %" B_PRId64 ", %s)\n", __FUNCTION__, _volume->id, node->vnid, reenter?"r":"!r");
292	return B_OK;
293}
294
295static int compare_fs_node_by_name(fs_node *node, char *name)
296{
297	//return memcmp(node->name, name, WEBSEARCHFS__NAME_LEN);
298	//TRACE("find_by_name: '%s' <> '%s'\n", node->name, name);
299	return strncmp(node->name, name, WEBSEARCHFS_NAME_LEN);
300}
301
302static status_t websearchfs_get_vnode_name(fs_volume *_volume, fs_vnode *_node, char *buffer, size_t len)
303{
304	fs_nspace *ns = (fs_nspace *)_volume->private_volume;
305	fs_node *node = (fs_node *)_node->private_node;
306
307	TRACE("get_vnode_name(%" B_PRId32 ", %" B_PRId64 ", )\n", ns->nsid, (int64)(node?node->vnid:-1));
308	strlcpy(buffer, node->name, MIN(WEBSEARCHFS_NAME_LEN, len));
309	return B_OK;
310}
311
312
313static status_t websearchfs_walk(fs_volume *_volume, fs_vnode *_base, const char *file, ino_t *vnid)
314{
315	fs_nspace *ns = (fs_nspace *)_volume->private_volume;
316	fs_node *base = _base->private_node;
317	fs_node *n, *dummy;
318	status_t err = B_OK;
319	TRACE("walk(%" B_PRId32 ", %" B_PRId64 ", %s)\n", ns->nsid, (int64)(base?base->vnid:-1), file);
320	err = LOCK(&base->l);
321	if (err)
322		return err;
323	if (!file) {
324		err = EINVAL;
325	} else if (!strcmp(file, "..")) {
326		if (base && base->parent) {
327			*vnid = base->parent->vnid; // XXX: LOCK(&base->l) ?
328			//*type = S_IFDIR;
329		} else
330			err = EINVAL;
331	} else if (!strcmp(file, ".")) { /* root dir */
332		if (base) { // XXX: LOCK(&base->l) ?
333			*vnid = base->vnid;
334			//*type = S_IFDIR;
335		} else
336			err = EINVAL;
337	} else if (base) { /* child of dir */
338		n = (fs_node *)SLL_FIND(base->children, next,
339								(sll_compare_func)compare_fs_node_by_name, (void *)file);
340		if (n) {
341			*vnid = n->vnid;
342			//*type = n->st.st_type & ~S_IUMSK; /*XXX: S_IFMT ?*/
343		} else
344			err = ENOENT;
345	} else
346		err = ENOENT;
347	if (err == B_OK) {
348		if (get_vnode(_volume, *vnid, (void **)&dummy) != B_OK) /* inc ref count */
349			err = EINVAL;
350	}
351	UNLOCK(&base->l);
352	TRACE("walk() -> error 0x%08" B_PRIx32 "\n", err);
353	return err;
354}
355
356static status_t websearchfs_opendir(fs_volume *_volume, fs_vnode *_node, void **cookie)
357{
358	fs_nspace *ns = (fs_nspace *)_volume->private_volume;
359	fs_node *node = (fs_node *)_node->private_node;
360	status_t err = B_OK;
361	fs_dir_cookie *c;
362	TRACE("opendir(%" B_PRId32 ", %" B_PRId64 ")\n", ns->nsid, node->vnid);
363	if (!node)
364		return EINVAL;
365	if (!S_ISDIR(node->st.st_mode))
366		return B_NOT_A_DIRECTORY;
367	err = LOCK(&node->l);
368	if (err)
369		return err;
370	c = malloc(sizeof(fs_dir_cookie));
371	if (c) {
372		memset(c, 0, sizeof(fs_dir_cookie));
373		c->omode = O_RDONLY;
374		c->type = S_IFDIR;
375		c->node = node;
376		c->dir_current = 0;
377		*cookie = (void *)c;
378		SLL_INSERT(node->opened, next, c);
379		UNLOCK(&node->l);
380		return B_OK;
381	} else
382		err = B_NO_MEMORY;
383	UNLOCK(&node->l);
384	return err;
385}
386
387static status_t websearchfs_closedir(fs_volume *_volume, fs_vnode *_node, void *_cookie)
388{
389	fs_nspace *ns = (fs_nspace *)_volume->private_volume;
390	fs_node *node = (fs_node *)_node->private_node;
391	fs_dir_cookie *cookie = (fs_dir_cookie *)_cookie;
392	status_t err = B_OK;
393//	node = cookie->node; // work around VFS bug
394	TRACE("closedir(%" B_PRId32 ", %" B_PRId64 ", %p)\n", ns->nsid, node->vnid, cookie);
395	err = LOCK(&node->l);
396	if (err)
397		return err;
398
399	SLL_REMOVE(node->opened, next, cookie);
400	UNLOCK(&node->l);
401
402	return err;
403}
404
405static status_t websearchfs_rewinddir(fs_volume *_volume, fs_vnode *_node, void *_cookie)
406{
407	fs_nspace *ns = (fs_nspace *)_volume->private_volume;
408	fs_node *node = (fs_node *)_node->private_node;
409	fs_dir_cookie *cookie = (fs_dir_cookie *)_cookie;
410	TRACE("rewinddir(%" B_PRId32 ", %" B_PRId64 ")\n", ns->nsid, node->vnid);
411	cookie->dir_current = 0;
412	return B_OK;
413}
414
415static status_t websearchfs_readdir(fs_volume *_volume, fs_vnode *_node, void *_cookie,
416	struct dirent *buf, size_t bufsize, uint32 *num)
417{
418	fs_nspace *ns = (fs_nspace *)_volume->private_volume;
419	fs_node *node = (fs_node *)_node->private_node;
420	fs_dir_cookie *cookie = (fs_dir_cookie *)_cookie;
421	fs_node *n = NULL;
422	fs_node *parent = node->parent;
423	int index;
424	TRACE("readdir(%" B_PRId32 ", %" B_PRId64 ") @ %d\n", ns->nsid, node->vnid,
425		cookie->dir_current);
426	if (!node || !cookie || !num || !*num || !buf
427		|| (bufsize < (sizeof(dirent_t) + WEBSEARCHFS_NAME_LEN)))
428		return EINVAL;
429	LOCK(&node->l);
430	if (cookie->dir_current == 0) { /* .. */
431		TRACE("readdir: giving ..\n");
432		/* the VFS will correct that anyway */
433		buf->d_dev = ns->nsid;
434		buf->d_pdev = ns->nsid;
435		buf->d_ino = parent?parent->vnid:ns->rootid;
436		buf->d_pino = (parent && parent->parent)?parent->parent->vnid:ns->rootid;
437		strcpy(buf->d_name, "..");
438		buf->d_reclen = offsetof(struct dirent, d_name)+strlen(buf->d_name)+1;
439		cookie->dir_current++;
440		*num = 1;
441	} else if (cookie->dir_current == 1) { /* . */
442		TRACE("readdir: giving .\n");
443		/* the VFS will correct that anyway */
444		buf->d_dev = ns->nsid;
445		buf->d_pdev = ns->nsid;
446		buf->d_ino = node->vnid;
447		buf->d_pino = parent?parent->vnid:ns->rootid;
448		strcpy(buf->d_name, ".");
449		buf->d_reclen = offsetof(struct dirent, d_name)+strlen(buf->d_name)+1;
450		cookie->dir_current++;
451		*num = 1;
452	} else {
453		index = cookie->dir_current-2;
454		for (n = node->children; n && index; n = n->next, index--); //XXX: care about n->hidden || n->deleted
455		if (n) {
456			TRACE("readdir: giving ino %" B_PRId64 ", %s\n", n->vnid, n->name);
457			buf->d_dev = ns->nsid;
458			buf->d_pdev = ns->nsid;
459			buf->d_ino = n->vnid;
460			buf->d_pino = node->vnid;
461			strcpy(buf->d_name, n->name);
462			buf->d_reclen = offsetof(struct dirent, d_name)+strlen(buf->d_name)+1;
463			cookie->dir_current++;
464			*num = 1;
465		} else {
466			*num = 0;
467		}
468	}
469	UNLOCK(&node->l);
470	return B_OK;
471}
472
473static status_t websearchfs_free_dircookie(fs_volume *_volume, fs_vnode *_node, void *_cookie)
474{
475	fs_nspace *ns = (fs_nspace *)_volume->private_volume;
476	fs_node *node = (fs_node *)_node->private_node;
477	fs_dir_cookie *cookie = (fs_dir_cookie *)_cookie;
478	status_t err = B_OK;
479//	node = cookie->node; // work around VFS bug
480	TRACE("freedircookie(%" B_PRId32 ", %" B_PRId64 ", %p)\n", ns->nsid, node?node->vnid:(int64)0, (void *)cookie);
481	err = LOCK(&node->l);
482	if (err)
483		return err;
484	err = SLL_REMOVE(node->opened, next, cookie); /* just to make sure */
485	UNLOCK(&node->l);
486	free(cookie);
487	return B_OK;
488}
489
490static status_t websearchfs_rstat(fs_volume *_volume, fs_vnode *_node, struct stat *st)
491{
492	fs_nspace *ns = (fs_nspace *)_volume->private_volume;
493	fs_node *node = (fs_node *)_node->private_node;
494	status_t err = B_OK;
495	if (!node || !st)
496		return EINVAL;
497	err = LOCK(&node->l);
498	if (err)
499		return err;
500	memcpy(st, &node->st, sizeof(struct stat));
501	st->st_dev = ns->nsid;
502	st->st_ino = node->vnid;
503	if (node->data_size)
504		st->st_size = node->data_size;
505	//st->st_size = 0LL;
506	UNLOCK(&node->l);
507	return err;
508}
509
510static status_t websearchfs_rfsstat(fs_volume *_volume, struct fs_info *info)
511{
512	fs_nspace *ns = (fs_nspace *)_volume->private_volume;
513	info->block_size = 1024; // websearchfs_BUFF_SIZE;
514	info->io_size = 1024; // WEBSEARCHFS_BUFF_SIZE;
515	info->total_blocks=0;
516	info->free_blocks=0;
517	info->total_nodes=MAX_VNIDS;
518	info->free_nodes=ns->nodecount;
519	info->dev=ns->nsid;
520	info->root=ns->rootid;
521	info->flags=/*B_FS_IS_SHARED|*/B_FS_IS_PERSISTENT|B_FS_HAS_MIME|B_FS_HAS_ATTR|B_FS_HAS_QUERY;
522	strcpy (info->device_name, "");
523	strcpy (info->volume_name, "Web Search");
524	strcpy (info->fsh_name, WEBSEARCHFS_NAME);
525	return B_OK;
526}
527
528static status_t websearchfs_open(fs_volume *_volume, fs_vnode *_node, int omode, void **cookie)
529{
530	fs_nspace *ns = (fs_nspace *)_volume->private_volume;
531	fs_node *node = (fs_node *)_node->private_node;
532	status_t err = B_OK;
533	//fs_node *dummy;
534	fs_file_cookie *fc;
535	TRACE("open(%" B_PRId32 ", %" B_PRId64 ", 0x%x)\n", ns->nsid, node->vnid, omode);
536	if (!node || !cookie)
537		return EINVAL;
538
539//	err = LOCK(&ns->l);
540//	if (err)
541//		return err;
542	err = LOCK(&node->l);
543	if (err)
544		goto err_n_l;
545	err = EEXIST;
546#ifndef ALLOW_DIR_OPEN
547	err = EINVAL;//EISDIR;
548	if (S_ISDIR(node->st.st_mode))
549		goto err_malloc;
550#endif
551	err = B_NO_MEMORY;
552	fc = malloc(sizeof(fs_file_cookie));
553	if (!fc)
554		goto err_malloc;
555	memset(fc, 0, sizeof(fs_file_cookie));
556	fc->node = node;
557	fc->omode = omode;
558	fc->type = S_IFREG;
559	err = SLL_INSERT(node->opened, next, fc);
560	if (err)
561		goto err_linsert;
562/*	err = get_vnode(ns->nsid, node->vnid, &dummy);
563	if (err)
564		goto err_getvn;*/
565	//*vnid = node->vnid;
566	*cookie = (void *)fc;
567	err = B_OK;
568	goto all_ok;
569//err_:
570//	put_vnode(ns->nsid, node->nsid);
571//err_getvn:
572//	SLL_REMOVE(node->opened, next, fc);
573err_linsert:
574	free(fc);
575err_malloc:
576all_ok:
577	UNLOCK(&node->l);
578err_n_l:
579//	UNLOCK(&ns->l);
580	return err;
581}
582
583static status_t websearchfs_close(fs_volume *_volume, fs_vnode *_node, void *_cookie)
584{
585	fs_nspace *ns = (fs_nspace *)_volume->private_volume;
586	fs_node *node = (fs_node *)_node->private_node;
587	fs_file_cookie *cookie = (fs_file_cookie *)_cookie;
588	status_t err;
589	TRACE("close(%" B_PRId32 ", %" B_PRId64 ")\n", ns->nsid, node->vnid);
590	if (!ns || !node || !cookie)
591		return EINVAL;
592	err = LOCK(&node->l);
593	if (err)
594		return err;
595	SLL_REMOVE(node->opened, next, cookie);
596
597//all_ok:
598//err_n_l:
599	UNLOCK(&node->l);
600	return err;
601}
602
603static status_t websearchfs_free_cookie(fs_volume *_volume, fs_vnode *_node, void *_cookie)
604{
605	fs_nspace *ns = (fs_nspace *)_volume->private_volume;
606	fs_node *node = (fs_node *)_node->private_node;
607	fs_file_cookie *cookie = (fs_file_cookie *)_cookie;
608	status_t err = B_OK;
609	TRACE("freecookie(%" B_PRId32 ", %" B_PRId64 ")\n", ns->nsid, node->vnid);
610	err = LOCK(&node->l);
611	if (err)
612		return err;
613	err = SLL_REMOVE(node->opened, next, cookie); /* just to make sure */
614//	if (err)
615//		goto err_n_l;
616	if (/*!node->is_perm &&*/ false) { /* not yet */
617		err = remove_vnode(_volume, node->vnid);
618		ns->root->st.st_mtime = time(NULL);
619#if 0
620		notify_listener(B_ENTRY_REMOVED, ns->nsid, ns->rootid, 0LL, node->vnid, NULL);
621		notify_listener(B_STAT_CHANGED, ns->nsid, 0LL, 0LL, ns->rootid, NULL);
622#endif
623	}
624	UNLOCK(&node->l);
625	free(cookie);
626//	err = B_OK;
627//err_n_l:
628	return err;
629}
630
631static status_t websearchfs_read(fs_volume *_volume, fs_vnode *_node, void *_cookie, off_t pos, void *buf, size_t *len)
632{
633	fs_nspace *ns = (fs_nspace *)_volume->private_volume;
634	fs_node *node = (fs_node *)_node->private_node;
635	status_t err = B_OK;
636	TRACE("read(%" B_PRId32 ", %" B_PRId64 ", %jd, %zu)\n", ns->nsid, node->vnid, pos, *len);
637	if (pos < 0 || (size_t)pos > node->data_size)
638		err = EFPOS;
639	if (err || node->data_size == 0 || !node->data) {
640		*len = 0;
641		return err;
642	}
643	*len = MIN(*len, node->data_size - (long)pos);
644	memcpy(buf, ((char *)node->data) + pos, *len);
645	return B_OK;
646}
647
648static status_t websearchfs_write(fs_volume *_volume, fs_vnode *_node, void *_cookie, off_t pos, const void *buf, size_t *len)
649{
650	fs_node *node = (fs_node *)_node->private_node;
651	TRACE("write(%" B_PRId32 ", %" B_PRId64 ", %jd, %zu)\n", _volume->id, node->vnid, pos, *len);
652	*len = 0;
653	return ENOSYS;
654}
655
656static status_t websearchfs_wstat(fs_volume *_volume, fs_vnode *_node, const struct stat *st, uint32 mask)
657{
658	fs_node *node = (fs_node *)_node->private_node;
659	TRACE("wstat(%" B_PRId32 ", %" B_PRId64 ", , 0x%08" B_PRIx32 ")\n", _volume->id, node->vnid, mask);
660	return ENOSYS;
661}
662
663static status_t websearchfs_wfsstat(fs_volume *_volume, const struct fs_info *info, uint32 mask)
664{
665	TRACE("wfsstat(%" B_PRId32 ", , 0x%08" B_PRIx32 ")\n", _volume->id, mask);
666	return ENOSYS;
667}
668
669/* this one returns the created fs_node to caller (for use by query engine) */
670/**
671 * @param dir the dir's fs_node we mkdir in
672 * @param name name to mkdir (basename is uniq is set)
673 * @param perms create with those permissions
674 * @param node make this point to the fs_node if !NULL
675 * @param iattr indirect attributes to set if desired (must be statically allocated)
676 * @param mkdir create a directory instead of a file
677 * @param uniq choose an unique name, appending a number if required
678 */
679static 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)
680{
681	fs_nspace *ns = (fs_nspace *)_volume->private_volume;
682	//fs_node *dir = (fs_node *)_dir->private_node;
683	char newname[WEBSEARCHFS_NAME_LEN];
684	status_t err;
685	fs_node *n;
686	int i;
687	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');
688
689	if (strlen(name) > WEBSEARCHFS_NAME_LEN-1)
690		return ENAMETOOLONG;
691	err = LOCK(&dir->l);
692	if (err < 0)
693		return err;
694	err = ENOTDIR;
695	if (!S_ISDIR(dir->st.st_mode))
696		goto err_l;
697	n = (fs_node *)SLL_FIND(dir->children, next,
698							(sll_compare_func)compare_fs_node_by_name, (void *)name);
699	err = EEXIST;
700	if (n && (omode & O_EXCL) && !uniq) /* already existing entry in there! */
701		goto err_l;
702
703	strncpy(newname, name, WEBSEARCHFS_NAME_LEN);
704	newname[WEBSEARCHFS_NAME_LEN-1] = '\0';
705
706	for (i = 1; uniq && n && i < 5000; i++) { /* uniquify the name */
707		//sprintf("%"#(WEBSEARCHFS_NAME_LEN-8)"s %05d", name, i);
708		strncpy(newname, name, 56);
709		newname[56] = '\0';
710		sprintf(newname+strlen(newname), " %05d", i);
711		n = (fs_node *)SLL_FIND(dir->children, next,
712								(sll_compare_func)compare_fs_node_by_name, (void *)newname);
713	}
714	if (n && (uniq || mkdir)) /* still there! */
715		goto err_l;
716	name = newname;
717
718	if (n) { /* already exists, so return it */
719		if (node)
720			*node = n;
721		if (vnid)
722			*vnid = n->vnid;
723		err = B_OK;
724		goto done;
725	}
726	err = ENOMEM;
727	n = malloc(sizeof(fs_node));
728	if (!n)
729		goto err_l;
730	memset(n, 0, sizeof(fs_node));
731	err = vnidpool_get(ns->vnids, &n->vnid);
732	if (err < B_OK)
733		goto err_m;
734	atomic_add(&ns->nodecount, 1);
735	strcpy(n->name, name);
736	//n->is_perm = 1;
737	fill_default_stat(&n->st, ns->nsid, n->vnid, (perms & ~S_IFMT) | (mkdir?S_IFDIR:S_IFREG));
738
739	new_lock(&(n->l), mkdir?"websearchfs dir":"websearchfs file");
740
741	err = LOCK(&ns->l);
742	if (err)
743		goto err_nl;
744	err = SLL_INSERT(ns->nodes, nlnext, n);
745	if (err)
746		goto err_lns;
747	/* _TAIL so they are in order */
748	err = SLL_INSERT(dir->children, next, n);
749	if (err)
750		goto err_insnl;
751//	err = new_vnode(ns->nsid, n->vnid, n);
752//	if (err)
753//		goto err_ins;
754	n->parent = dir;
755	dir->st.st_nlink++;
756	UNLOCK(&ns->l);
757	n->attrs_indirect = iattrs;
758	notify_entry_created(ns->nsid, dir->vnid, name, n->vnid);
759	/* dosfs doesn't do that one but I believe it should */
760	notify_stat_changed(B_STAT_CHANGED, -1, ns->nsid, -1);
761	/* give node to caller if it wants it */
762	if (node)
763		*node = n;
764	if (vnid)
765		*vnid = n->vnid;
766	goto done;
767
768err_insnl:
769	SLL_REMOVE(ns->nodes, nlnext, n);
770err_lns:
771	UNLOCK(&ns->l);
772err_nl:
773	free_lock(&n->l);
774	atomic_add(&ns->nodecount, -1);
775err_m:
776	free(n);
777err_l:
778done:
779	UNLOCK(&dir->l);
780	return err;
781}
782
783static status_t websearchfs_create(fs_volume *_volume, fs_vnode *_dir, const char *name, int omode, int perms, void **cookie, ino_t *vnid)
784{
785	fs_nspace *ns = (fs_nspace *)_volume->private_volume;
786	fs_node *dir = (fs_node *)_dir->private_node;
787	status_t err;
788	fs_node *n;
789	struct fs_vnode child = { NULL, &sWebSearchFSVnodeOps };
790	TRACE("create(%" B_PRId32 ", %" B_PRId64 ", '%s', 0x%08x)\n", ns->nsid, dir->vnid, name, omode);
791	/* don't let ppl mess our fs up */
792	return ENOSYS;
793
794	err = websearchfs_create_gen(_volume, dir, name, omode, perms, vnid, &n, NULL, false, false);
795	if (err)
796		return err;
797
798	child.private_node = (void *)n;
799	err = websearchfs_open(_volume, &child, omode, cookie);
800	return err;
801}
802
803static int websearchfs_unlink_gen(fs_volume *_volume, fs_node *dir, const char *name)
804{
805	fs_nspace *ns = (fs_nspace *)_volume->private_volume;
806	status_t err;
807	fs_node *n;
808	TRACE("unlink(%" B_PRId32 ", %" B_PRId64 ", %s)\n", ns->nsid, dir->vnid, name);
809	//dprintf(PFS"unlink(%" B_PRId32 ", %" B_PRId64 ", %s)\n", ns->nsid, dir->vnid, name);
810	err = LOCK(&dir->l);
811	if (err)
812		return err;
813	err = ENOENT;
814	/* no need to check for S_ISDIR */
815	n = (fs_node *)SLL_FIND(dir->children, next,
816							(sll_compare_func)compare_fs_node_by_name, (void *)name);
817	if (n) {
818		if (n->children)
819			err = ENOTEMPTY;
820		else if (n->is_perm)
821			err = EROFS;
822		//else if (S_ISDIR(n->st.st_mode))
823		//	err = EISDIR;
824		else if (n->vnid == ns->rootid)
825			err = EACCES;
826		else {
827			SLL_REMOVE(dir->children, next, n);
828			notify_entry_removed(ns->nsid, dir->vnid, name, n->vnid);
829			//notify_listener(B_STAT_CHANGED, ns->nsid, 0LL, 0LL, dir->vnid, NULL);
830			remove_vnode(_volume, n->vnid);
831			err = B_OK;
832		}
833	}
834	UNLOCK(&dir->l);
835	return err;
836}
837
838static status_t websearchfs_unlink(fs_volume *_volume, fs_vnode *_dir, const char *name)
839{
840	//fs_nspace *ns = (fs_nspace *)_volume->private_volume;
841	//fs_node *dir = (fs_node *)_dir->private_node;
842	return websearchfs_unlink_gen(_volume, (fs_node *)_dir->private_node, name);
843}
844
845static status_t websearchfs_rmdir(fs_volume *_volume, fs_vnode *_dir, const char *name)
846{
847	//fs_nspace *ns = (fs_nspace *)_volume->private_volume;
848	fs_node *dir = (fs_node *)_dir->private_node;
849	TRACE("rmdir(%" B_PRId32 ", %" B_PRId64 ", %s)\n", _volume->id, dir->vnid, name);
850	return websearchfs_unlink(_volume, _dir, name);
851}
852
853static int websearchfs_unlink_node_rec(fs_volume *_volume, fs_node *node)
854{
855	fs_nspace *ns = (fs_nspace *)_volume->private_volume;
856	status_t err;
857	fs_node *n;
858	TRACE("websearchfs_unlink_node_rec(%" B_PRId32 ", %" B_PRId64 ":%s)\n", ns->nsid, node->vnid, node->name);
859	if (!ns || !node)
860		return EINVAL;
861	// kill_request();
862	LOCK(&node->l);
863	while (1) {
864		n = node->children;
865		if (!n)
866			break;
867		UNLOCK(&node->l);
868		err = websearchfs_unlink_node_rec(_volume, n);
869		LOCK(&node->l);
870	}
871	UNLOCK(&node->l);
872	err = websearchfs_unlink_gen(_volume, node->parent, node->name);
873	return err;
874}
875
876static status_t websearchfs_access(fs_volume *_volume, fs_vnode *_node, int mode)
877{
878	fs_nspace *ns = (fs_nspace *)_volume->private_volume;
879	fs_node *node = (fs_node *)_node->private_node;
880	TRACE("access(%" B_PRId32 ", %" B_PRId64 ", 0x%x)\n", ns->nsid, node->vnid, mode);
881	return B_OK;
882}
883
884
885static status_t websearchfs_mkdir(fs_volume *_volume, fs_vnode *_dir, const char *name, int perms)
886{
887	fs_nspace *ns = (fs_nspace *)_volume->private_volume;
888	fs_node *dir = (fs_node *)_dir->private_node;
889	TRACE("mkdir(%" B_PRId32 ", %" B_PRId64 ", '%s', 0x%08x)\n", ns->nsid, dir->vnid, name, perms);
890	return websearchfs_create_gen(_volume, dir, name, O_EXCL, perms, NULL, NULL, folders_attrs, true, false);
891}
892
893/* attr stuff */
894
895static status_t websearchfs_open_attrdir(fs_volume *_volume, fs_vnode *_node, void **cookie)
896{
897	fs_nspace *ns = (fs_nspace *)_volume->private_volume;
898	fs_node *node = (fs_node *)_node->private_node;
899	status_t err = B_OK;
900	fs_attr_dir_cookie *c;
901	TRACE("open_attrdir(%" B_PRId32 ", %" B_PRId64 ")\n", ns->nsid, node->vnid);
902	if (!node)
903		return EINVAL;
904	err = LOCK(&node->l);
905	if (err)
906		return err;
907	c = malloc(sizeof(fs_attr_dir_cookie));
908	if (c) {
909		memset(c, 0, sizeof(fs_attr_dir_cookie));
910		c->omode = O_RDONLY;
911		c->type = S_ATTR_DIR;
912		c->node = node;
913		c->dir_current = 0;
914		*cookie = (void *)c;
915		SLL_INSERT(node->opened, next, c);
916		UNLOCK(&node->l);
917		return B_OK;
918	} else
919		err = B_NO_MEMORY;
920	UNLOCK(&node->l);
921	return err;
922}
923
924static status_t websearchfs_close_attrdir(fs_volume *_volume, fs_vnode *_node, void *_cookie)
925{
926	fs_nspace *ns = (fs_nspace *)_volume->private_volume;
927	fs_node *node = (fs_node *)_node->private_node;
928	fs_attr_dir_cookie *cookie = (fs_attr_dir_cookie *)_cookie;
929	status_t err = B_OK;
930	TRACE("close_attrdir(%" B_PRId32 ", %" B_PRId64 ")\n", ns->nsid, node->vnid);
931	err = LOCK(&node->l);
932	if (err)
933		return err;
934	SLL_REMOVE(node->opened, next, cookie);
935	UNLOCK(&node->l);
936	return err;
937}
938
939static status_t websearchfs_free_attrdircookie(fs_volume *_volume, fs_vnode *_node, void *_cookie)
940{
941	fs_nspace *ns = (fs_nspace *)_volume->private_volume;
942	fs_node *node = (fs_node *)_node->private_node;
943	fs_attr_dir_cookie *cookie = (fs_attr_dir_cookie *)_cookie;
944	status_t err = B_OK;
945	TRACE("free_attrdircookie(%" B_PRId32 ", %" B_PRId64 ")\n", ns->nsid, node->vnid);
946	err = LOCK(&node->l);
947	if (err)
948		return err;
949	SLL_REMOVE(node->opened, next, cookie); /* just to make sure */
950	UNLOCK(&node->l);
951	free(cookie);
952	return B_OK;
953}
954
955static status_t websearchfs_rewind_attrdir(fs_volume *_volume, fs_vnode *_node, void *_cookie)
956{
957	fs_nspace *ns = (fs_nspace *)_volume->private_volume;
958	fs_node *node = (fs_node *)_node->private_node;
959	fs_attr_dir_cookie *cookie = (fs_attr_dir_cookie *)_cookie;
960	TRACE("rewind_attrdir(%" B_PRId32 ", %" B_PRId64 ")\n", ns->nsid, node->vnid);
961	cookie->dir_current = 0;
962	return B_OK;
963}
964
965static status_t websearchfs_read_attrdir(fs_volume *_volume, fs_vnode *_node, void *_cookie,
966	struct dirent *buf, size_t bufsize, uint32 *num)
967{
968	fs_nspace *ns = (fs_nspace *)_volume->private_volume;
969	fs_node *node = (fs_node *)_node->private_node;
970	fs_file_cookie *cookie = (fs_file_cookie *)_cookie;
971	//fs_node *n = NULL;
972	//fs_node *parent = node->parent;
973	attr_entry *ae = NULL;
974	int i;
975	int count_indirect;
976	TRACE("read_attrdir(%" B_PRId32 ", %" B_PRId64 ") @ %d\n", ns->nsid, node->vnid,
977		cookie->dir_current);
978	if (!node || !cookie || !num || !*num || !buf
979		|| (bufsize < (sizeof(dirent_t) + WEBSEARCHFS_NAME_LEN)))
980		return EINVAL;
981	LOCK(&node->l);
982	for (i = 0, count_indirect = 0; node->attrs_indirect && !ae && node->attrs_indirect[i].name;
983		i++, count_indirect++) {
984		if (i == cookie->dir_current)
985			ae = &node->attrs_indirect[i];
986	}
987	for (i = 0; !ae && i < 10 && node->attrs[i].name; i++) {
988		if (i + count_indirect == cookie->dir_current)
989			ae = &node->attrs[i];
990	}
991
992	if (ae) {
993		TRACE("read_attrdir: giving %s\n", ae->name);
994		buf->d_dev = ns->nsid;
995		buf->d_pdev = ns->nsid;
996		buf->d_ino = node->vnid;
997		buf->d_pino = node->parent?node->parent->vnid:ns->rootid;
998		strcpy(buf->d_name, ae->name);
999		buf->d_reclen = offsetof(struct dirent, d_name)+strlen(buf->d_name)+1;
1000		cookie->dir_current++;
1001		*num = 1;
1002	} else
1003		*num = 0;
1004
1005	UNLOCK(&node->l);
1006	return B_OK;
1007}
1008
1009/* Haiku and BeOs differ in the way the handle attributes at the vfs layer.
1010   BeOS uses atomic calls on the vnode,
1011   Haiku retains the open/close/read/write semantics for attributes (loosing atomicity).
1012   Here we don't care much though, open is used for both to factorize attribute lookup. <- TODO
1013   _h suffixed funcs are for Haiku API, _b are for BeOS.
1014 */
1015
1016/* for Haiku, but also used by BeOS calls to factorize code */
1017static status_t websearchfs_open_attr_h(fs_volume *_volume, fs_vnode *_node, const char *name, int omode, void **cookie)
1018{
1019	fs_nspace *ns = (fs_nspace *)_volume->private_volume;
1020	fs_node *node = (fs_node *)_node->private_node;
1021	status_t err = B_OK;
1022	//fs_node *dummy;
1023	fs_file_cookie *fc;
1024	attr_entry *ae = NULL;
1025	int i;
1026	TRACE("open_attr(%" B_PRId32 ", %" B_PRId64 ", %s, 0x%x)\n", ns->nsid, node->vnid, name, omode);
1027	if (!node || !name || !cookie)
1028		return EINVAL;
1029
1030	err = LOCK(&node->l);
1031	if (err)
1032		goto err_n_l;
1033
1034	/* lookup attribute */
1035	for (i = 0; node->attrs_indirect && !ae && node->attrs_indirect[i].name; i++)
1036		if (!strcmp(name, node->attrs_indirect[i].name))
1037			ae = &node->attrs_indirect[i];
1038	for (i = 0; !ae && i < 10 && node->attrs[i].name; i++)
1039		if (!strcmp(name, node->attrs[i].name))
1040			ae = &node->attrs[i];
1041
1042	/* should check omode */
1043	err = ENOENT;
1044	if (!ae)
1045		goto err_malloc;
1046	err = EEXIST;
1047
1048	err = B_NO_MEMORY;
1049	fc = malloc(sizeof(fs_file_cookie));
1050	if (!fc)
1051		goto err_malloc;
1052	memset(fc, 0, sizeof(fs_file_cookie));
1053	fc->node = node;
1054	fc->omode = omode;
1055	fc->type = S_ATTR;
1056	fc->attr = ae;
1057	err = SLL_INSERT(node->opened, next, fc);
1058	if (err)
1059		goto err_linsert;
1060
1061	*cookie = (void *)fc;
1062	err = B_OK;
1063	goto all_ok;
1064//err_:
1065//	put_vnode(ns->nsid, node->nsid);
1066//err_getvn:
1067//	SLL_REMOVE(node->opened, next, fc);
1068err_linsert:
1069	free(fc);
1070err_malloc:
1071all_ok:
1072	UNLOCK(&node->l);
1073err_n_l:
1074//	UNLOCK(&ns->l);
1075	return err;
1076}
1077
1078static status_t websearchfs_close_attr_h(fs_volume *_volume, fs_vnode *_node, void *_cookie)
1079{
1080	fs_nspace *ns = (fs_nspace *)_volume->private_volume;
1081	fs_node *node = (fs_node *)_node->private_node;
1082	fs_file_cookie *cookie = (fs_file_cookie *)_cookie;
1083	status_t err;
1084	TRACE("close_attr(%" B_PRId32 ", %" B_PRId64 ":%s)\n", ns->nsid, node->vnid,
1085		cookie->attr ? cookie->attr->name : "?");
1086	if (!ns || !node || !cookie)
1087		return EINVAL;
1088	err = LOCK(&node->l);
1089	if (err)
1090		return err;
1091	SLL_REMOVE(node->opened, next, cookie);
1092
1093//all_ok:
1094//err_n_l:
1095	UNLOCK(&node->l);
1096	return err;
1097}
1098
1099static status_t websearchfs_free_attr_cookie_h(fs_volume *_volume, fs_vnode *_node, void *_cookie)
1100{
1101	fs_nspace *ns = (fs_nspace *)_volume->private_volume;
1102	fs_node *node = (fs_node *)_node->private_node;
1103	fs_file_cookie *cookie = (fs_file_cookie *)_cookie;
1104	status_t err = B_OK;
1105	TRACE("free_attrcookie(%" B_PRId32 ", %" B_PRId64 ":%s)\n", ns->nsid, node->vnid,
1106		cookie->attr ? cookie->attr->name : "?");
1107	err = LOCK(&node->l);
1108	if (err)
1109		return err;
1110	err = SLL_REMOVE(node->opened, next, cookie); /* just to make sure */
1111//	if (err)
1112//		goto err_n_l;
1113	UNLOCK(&node->l);
1114	free(cookie);
1115//	err = B_OK;
1116//err_n_l:
1117	return err;
1118}
1119
1120static status_t websearchfs_read_attr_stat(fs_volume *_volume, fs_vnode *_node, void *_cookie,
1121	struct stat *st)
1122{
1123	fs_nspace *ns = (fs_nspace *)_volume->private_volume;
1124	fs_node *node = (fs_node *)_node->private_node;
1125	fs_file_cookie *cookie = (fs_file_cookie *)_cookie;
1126	status_t err = B_OK;
1127	attr_entry *ae = cookie->attr;
1128	TRACE("stat_attr(%" B_PRId32 ", %" B_PRId64 ":%s)\n", ns->nsid, node->vnid, ae->name);
1129	if (!node || !st || !cookie || !cookie->attr)
1130		return EINVAL;
1131	memcpy(st, &node->st, sizeof(struct stat));
1132	st->st_type = ae->type;
1133	st->st_size = ae->size;
1134	err = B_OK;
1135	return err;
1136}
1137
1138static status_t websearchfs_read_attr(fs_volume *_volume, fs_vnode *_node, void *_cookie,
1139	off_t pos, void *buf, size_t *len)
1140{
1141	fs_nspace *ns = (fs_nspace *)_volume->private_volume;
1142	fs_node *node = (fs_node *)_node->private_node;
1143	fs_file_cookie *cookie = (fs_file_cookie *)_cookie;
1144	status_t err = B_OK;
1145	attr_entry *ae = cookie->attr;
1146	TRACE("read_attr(%" B_PRId32 ", %" B_PRId64 ":%s)\n", ns->nsid, node->vnid, ae->name);
1147	if (!node || !cookie || !len || !*len)
1148		return EINVAL;
1149
1150	err = LOCK(&node->l);
1151
1152	if (ae && (pos < 0 || (size_t)pos < ae->size)) {
1153		memcpy(buf, (char *)ae->value + pos, MIN(*len, ae->size-pos));
1154		*len = MIN(*len, ae->size-pos);
1155		err = B_OK;
1156	} else {
1157		*len = 0;
1158		err = ENOENT;
1159	}
1160
1161	UNLOCK(&node->l);
1162	return err;
1163}
1164
1165
1166/* query stuff */
1167
1168static int compare_fs_node_by_recent_query_string(fs_node *node, char *query)
1169{
1170	time_t tm = time(NULL);
1171	//return memcmp(node->name, name, WEBSEARCHFS_NAME_LEN);
1172	TRACE("find_by_recent_query_string: '%s' <> '%s'\n", \
1173			node->request?node->request->query_string:NULL, query);
1174	if (!node->request || !node->request->query_string)
1175		return -1;
1176	/* reject if older than 5 min */
1177	if (node->st.st_crtime + 60 * 5 < tm)
1178		return -1;
1179	return strcmp(node->request->query_string, query);
1180}
1181
1182static status_t websearchfs_open_query(fs_volume *_volume, const char *query, uint32 flags,
1183					port_id port, uint32 token, void **cookie)
1184{
1185	fs_nspace *ns = (fs_nspace *)_volume->private_volume;
1186	status_t err = B_OK;
1187	fs_query_cookie *c;
1188	fs_node *qn, *dummy;
1189	const char *p;
1190	char *q;
1191	char *qstring = NULL;
1192	char qname[WEBSEARCHFS_NAME_LEN];
1193	bool accepted = true;
1194	bool reused = false;
1195	//int i;
1196	TRACE("open_query(%" B_PRId32 ", '%s', 0x%08" B_PRIx32 ", %" B_PRId32 ", %" B_PRId32 ")\n",
1197		ns->nsid, query, flags, port, token);
1198//	if (flags & B_LIVE_QUERY)
1199//		return ENOSYS; /* no live query yet, they are live enough anyway */
1200	//return ENOSYS;
1201	if (!query || !cookie)
1202		return EINVAL;
1203
1204	// filter out queries that aren't for us, we don't want to trigger DuckDuckGo searches when
1205	// apps check for mails, ... :)
1206
1207	err = B_NO_MEMORY;
1208	c = malloc(sizeof(fs_query_cookie));
1209	if (!c)
1210		return err;
1211	memset(c, 0, sizeof(fs_query_cookie));
1212	c->omode = O_RDONLY;
1213	c->type = S_IFQUERY;
1214	c->dir_current = 0;
1215
1216	err = ENOSYS;
1217	if (strncmp(query, "((name==\"*", 10))
1218		accepted = false;
1219	else {
1220		qstring = query_unescape_string(query + 10, &p, '"');
1221		if (!qstring)
1222			accepted = false;
1223		else if (!p)
1224			accepted = false;
1225		else if (strcmp(p, "\")&&(BEOS:TYPE==\"application/x-vnd.Be-bookmark\"))"))
1226			accepted = false;
1227		else {
1228			//if (qstring[0] == '*')
1229			//	strcpy(qstring+1, qstring);
1230			//if (qstring[strlen(qstring)-1] == '*')
1231			//	qstring[strlen(qstring)-1] = '\0';
1232			if (!query_strip_bracketed_Cc(qstring))
1233				goto err_qs;
1234		}
1235	}
1236
1237	if (!accepted) {
1238		free(qstring);
1239		/* return an empty cookie */
1240		*cookie = (void *)c;
1241		return B_OK;
1242	}
1243	TRACE("open_query: QUERY: '%s'\n", qstring);
1244	/* reuse query if it's not too old */
1245	LOCK(&ns->l);
1246	qn = SLL_FIND(ns->queries, qnext,
1247				(sll_compare_func)compare_fs_node_by_recent_query_string, (void *)qstring);
1248	UNLOCK(&ns->l);
1249	reused = (qn != NULL);
1250	if (reused) {
1251		TRACE("open_query: reusing %" B_PRId32 ":%" B_PRId64 "\n", ns->nsid, qn->vnid);
1252		err = get_vnode(_volume, qn->vnid, (void **)&dummy); /* inc ref count */
1253		if (err)
1254			goto err_mkdir;
1255		/* wait for the query to complete */
1256		while (!qn->qcompleted)
1257			snooze(10000);
1258		goto reuse;
1259	}
1260
1261	/* stripped name for folder */
1262	strncpy(qname, qstring, WEBSEARCHFS_NAME_LEN);
1263	qname[WEBSEARCHFS_NAME_LEN-1] = '\0';
1264
1265	/* strip out slashes */
1266	q = qname;
1267	while ((q = strchr(q, '/')))
1268		strcpy(q, q + 1);
1269
1270	/* should get/put_vnode(ns->root); around that I think... */
1271	err = websearchfs_create_gen(_volume, ns->root, qname, 0, 0755, NULL, &qn, folders_attrs, true, true);
1272	if (err)
1273		goto err_qs;
1274
1275	err = get_vnode(_volume, qn->vnid, (void **)&dummy); /* inc ref count */
1276	if (err)
1277		goto err_mkdir;
1278
1279//#ifndef NO_SEARCH
1280
1281	/* let's ask DuckDuckGo */
1282	err = duckduckgo_request_open(qstring, _volume, qn, &qn->request);
1283	if (err)
1284		goto err_gn;
1285
1286	TRACE("open_query: request_open done\n");
1287#ifndef NO_SEARCH
1288	err = duckduckgo_request_process(qn->request);
1289	if (err)
1290		goto err_gro;
1291	TRACE("open_query: request_process done\n");
1292
1293#else
1294	/* fake entries */
1295	for (i = 0; i < 10; i++) {
1296		err = websearchfs_create_gen(_volume, qn, "B", 0, 0644, NULL, &n, fake_bookmark_attrs, false, true);
1297		/* fake that to test sorting */
1298		*(int32 *)&n->attrs[1].value = i + 1; // hack
1299		n->attrs[0].type = 'LONG';
1300		n->attrs[0].value = &n->attrs[1].value;
1301		n->attrs[0].size = sizeof(int32);
1302		n->attrs[0].name = "WEBSEARCH:order";
1303		notify_attribute_changed(ns->nsid, -1, n->vnid, n->attrs[0].name, B_ATTR_CHANGED);
1304		if (err)
1305			goto err_gn;
1306	}
1307#endif /*NO_SEARCH*/
1308	//
1309	//err = duckduckgo_request_close(q->request);
1310
1311	LOCK(&ns->l);
1312	SLL_INSERT(ns->queries, qnext, qn);
1313	UNLOCK(&ns->l);
1314reuse:
1315	/* put the chocolate on the cookie */
1316	c->node = qn;
1317	LOCK(&qn->l);
1318	SLL_INSERT(qn->opened, next, c);
1319	UNLOCK(&qn->l);
1320	qn->qcompleted = 1; /* tell other cookies we're done */
1321	*cookie = (void *)c;
1322	free(qstring);
1323	return B_OK;
1324
1325//err_grp:
1326err_gro:
1327	if (qn->request)
1328		duckduckgo_request_close(qn->request);
1329err_gn:
1330	put_vnode(_volume, qn->vnid);
1331err_mkdir:
1332	if (!reused)
1333		websearchfs_unlink_gen(_volume, ns->root, qn->name);
1334err_qs:
1335	free(qstring);
1336//err_m:
1337	free(c);
1338	TRACE("open_query: error 0x%08" B_PRIx32 "\n", err);
1339	return err;
1340}
1341
1342static status_t websearchfs_close_query(fs_volume *_volume, void *_cookie)
1343{
1344	fs_nspace *ns = (fs_nspace *)_volume->private_volume;
1345	fs_query_cookie *cookie = (fs_query_cookie *)_cookie;
1346	status_t err;
1347	fs_node *q;
1348	TRACE("close_query(%" B_PRId32 ", %" B_PRId64 ")\n", ns->nsid,
1349		cookie->node ? cookie->node->vnid : (int64)0);
1350	//return ENOSYS;
1351	q = cookie->node;
1352	if (!q)
1353		return B_OK;
1354	// kill_request();
1355	LOCK(&q->l);
1356	SLL_REMOVE(q->opened, next, cookie);
1357	if (q->request /*&& !q->opened*/) {
1358		err = duckduckgo_request_close(q->request);
1359	}
1360	UNLOCK(&q->l);
1361	/* if last cookie on the query and sync_unlink, trash all */
1362	if (sync_unlink_queries && !q->opened)
1363		err = websearchfs_unlink_node_rec(_volume, q);
1364	err = put_vnode(_volume, q->vnid);
1365	return err;
1366}
1367
1368#ifdef __HAIKU__
1369/* protos are different... */
1370static status_t websearchfs_free_query_cookie(fs_volume *_volume, void *_cookie)
1371{
1372	fs_query_cookie *cookie = (fs_query_cookie *)_cookie;
1373	status_t err = B_OK;
1374	fs_node *q;
1375	TRACE("free_query_cookie(%" B_PRId32 ")\n", _volume->id);
1376	q = cookie->node;
1377	if (!q)
1378		goto no_node;
1379	err = LOCK(&q->l);
1380	if (err)
1381		return err;
1382	err = SLL_REMOVE(q->opened, next, cookie); /* just to make sure */
1383	if (q->request /*&& !q->opened*/) {
1384		err = duckduckgo_request_close(q->request);
1385	}
1386//	if (err)
1387//		goto err_n_l;
1388	UNLOCK(&q->l);
1389no_node:
1390	free(cookie);
1391	return B_OK;
1392}
1393#endif
1394
1395static status_t websearchfs_read_query(fs_volume *_volume, void *_cookie, struct dirent *buf,
1396	size_t bufsize, uint32 *num)
1397{
1398	fs_nspace *ns = (fs_nspace *)_volume->private_volume;
1399	fs_query_cookie *cookie = (fs_query_cookie *)_cookie;
1400	fs_node *n = NULL;
1401	fs_node *node = cookie->node;
1402	int index;
1403	TRACE("read_query(%" B_PRId32 ", %" B_PRId64 ") @ %d\n", ns->nsid, node ? node->vnid : (int64)0,
1404		cookie->dir_current);
1405	if (!cookie || !num || !*num || !buf || (bufsize < (sizeof(dirent_t) + WEBSEARCHFS_NAME_LEN)))
1406		return EINVAL;
1407	if (!node) {
1408		/* a query we don't care about, just return no entries to please apps */
1409		*num = 0;
1410		return B_OK;
1411	}
1412	//return ENOSYS;
1413	LOCK(&node->l);
1414	index = cookie->dir_current;
1415	for (n = node->children; n && index; n = n->next, index--);
1416	if (n) {
1417		TRACE("read_query: giving ino %" PRId64 ", %s\n", n->vnid, n->name);
1418		buf->d_dev = ns->nsid;
1419		buf->d_pdev = ns->nsid;
1420		buf->d_ino = n->vnid;
1421		buf->d_pino = node->vnid;
1422		strcpy(buf->d_name, n->name);
1423		buf->d_reclen = offsetof(struct dirent, d_name)+strlen(buf->d_name)+1;
1424		cookie->dir_current++;
1425		*num = 1;
1426	} else {
1427		*num = 0;
1428	}
1429	UNLOCK(&node->l);
1430	return B_OK;
1431}
1432
1433int websearchfs_push_result_to_query(struct duckduckgo_request *request,
1434	struct duckduckgo_result *result)
1435{
1436	status_t err = B_OK;
1437	fs_volume *_volume = request->volume;
1438	fs_nspace *ns = (fs_nspace *)_volume->private_volume;
1439	fs_node *qn = request->query_node;
1440	fs_node *n;
1441	char ename[WEBSEARCHFS_NAME_LEN];
1442	char *p;
1443	int i;
1444	TRACE("push_result_to_query(%" B_PRId32 ", %" B_PRId64 ", %ld:'%s')\n", ns->nsid, qn->vnid, result->id, result->name);
1445	//dprintf(PFS"push_result_to_query(%" B_PRId32 ", %" B_PRId64 ", %ld:'%s')\n", ns->nsid, qn->vnid, result->id, result->name);
1446	//return ENOSYS;
1447	if (!ns || !qn)
1448		return EINVAL;
1449
1450	// filter out queries that aren't for us, we don't want to trigger DuckDuckGo searches when
1451	// apps check for mails, ... :)
1452
1453	/* stripped name for folder */
1454	strncpy(ename, result->name, WEBSEARCHFS_NAME_LEN);
1455	ename[WEBSEARCHFS_NAME_LEN-1] = '\0';
1456	/* strip out slashes */
1457	p = ename;
1458	while ((p = strchr(p, '/')))
1459		*p++ = '_';
1460
1461	err = websearchfs_create_gen(_volume, qn, ename, 0, 0644, NULL, &n, bookmark_attrs, false, true);
1462	if (err)
1463		return err;
1464	LOCK(&n->l);
1465	n->result = result;
1466	i = 0;
1467	n->attrs[i].type = 'CSTR';
1468	n->attrs[i].value = result->name;
1469	n->attrs[i].size = strlen(result->name)+1;
1470	n->attrs[i].name = "META:title";
1471	notify_attribute_changed(ns->nsid, -1, n->vnid, n->attrs[i].name, B_ATTR_CREATED);
1472	i++;
1473	n->attrs[i].type = 'CSTR';
1474	n->attrs[i].value = result->url;
1475	n->attrs[i].size = strlen(result->url)+1;
1476	n->attrs[i].name = "META:url";
1477	notify_attribute_changed(ns->nsid, -1, n->vnid, n->attrs[i].name, B_ATTR_CREATED);
1478	i++;
1479	n->attrs[i].type = 'CSTR';
1480	n->attrs[i].value = request->query_string;
1481	n->attrs[i].size = strlen(request->query_string)+1;
1482	n->attrs[i].name = "META:keyw";
1483	notify_attribute_changed(ns->nsid, -1, n->vnid, n->attrs[i].name, B_ATTR_CREATED);
1484	i++;
1485	n->attrs[i].type = 'LONG';
1486	n->attrs[i].value = &result->id;
1487	n->attrs[i].size = sizeof(int32);
1488	n->attrs[i].name = "WEBSEARCH:order";
1489	notify_attribute_changed(ns->nsid, -1, n->vnid, n->attrs[i].name, B_ATTR_CREATED);
1490	i++;
1491	if (result->snipset[0]) {
1492		n->attrs[i].type = 'CSTR';
1493		n->attrs[i].value = result->snipset;
1494		n->attrs[i].size = strlen(result->snipset)+1;
1495		n->attrs[i].name = "WEBSEARCH:excerpt";
1496		notify_attribute_changed(ns->nsid, -1, n->vnid, n->attrs[i].name, B_ATTR_CREATED);
1497		i++;
1498	}
1499	if (result->cache_url[0]) {
1500		n->attrs[i].type = 'CSTR';
1501		n->attrs[i].value = result->cache_url;
1502		n->attrs[i].size = strlen(result->cache_url)+1;
1503		n->attrs[i].name = "WEBSEARCH:cache_url";
1504		notify_attribute_changed(ns->nsid, -1, n->vnid, n->attrs[i].name, B_ATTR_CREATED);
1505		i++;
1506	}
1507	if (result->similar_url[0]) {
1508		n->attrs[i].type = 'CSTR';
1509		n->attrs[i].value = result->similar_url;
1510		n->attrs[i].size = strlen(result->similar_url)+1;
1511		n->attrs[i].name = "WEBSEARCH:similar_url";
1512		notify_attribute_changed(ns->nsid, -1, n->vnid, n->attrs[i].name, B_ATTR_CREATED);
1513		i++;
1514	}
1515	UNLOCK(&n->l);
1516	return B_OK;
1517
1518	TRACE("push_result_to_query: error 0x%08" B_PRIx32 "\n", err);
1519	return err;
1520}
1521
1522//	#pragma mark -
1523
1524static status_t
1525websearchfs_std_ops(int32 op, ...)
1526{
1527	switch (op) {
1528		case B_MODULE_INIT:
1529			TRACE("std_ops(INIT)\n");
1530			return B_OK;
1531		case B_MODULE_UNINIT:
1532			TRACE("std_ops(UNINIT)\n");
1533			return B_OK;
1534		default:
1535			return B_ERROR;
1536	}
1537}
1538
1539
1540static fs_volume_ops sWebSearchFSVolumeOps = {
1541	&websearchfs_unmount,
1542	&websearchfs_rfsstat,
1543	&websearchfs_wfsstat,
1544	NULL,			// no sync!
1545	&websearchfs_read_vnode,
1546
1547	/* index directory & index operations */
1548	NULL,	// &websearchfs_open_index_dir
1549	NULL,	// &websearchfs_close_index_dir
1550	NULL,	// &websearchfs_free_index_dir_cookie
1551	NULL,	// &websearchfs_read_index_dir
1552	NULL,	// &websearchfs_rewind_index_dir
1553
1554	NULL,	// &websearchfs_create_index
1555	NULL,	// &websearchfs_remove_index
1556	NULL,	// &websearchfs_stat_index
1557
1558	/* query operations */
1559	&websearchfs_open_query,
1560	&websearchfs_close_query,
1561	&websearchfs_free_query_cookie,
1562	&websearchfs_read_query,
1563	NULL,	// &websearchfs_rewind_query,
1564};
1565
1566
1567static fs_vnode_ops sWebSearchFSVnodeOps = {
1568	/* vnode operations */
1569	&websearchfs_walk,
1570	&websearchfs_get_vnode_name, //NULL, // fs_get_vnode_name
1571	&websearchfs_release_vnode,
1572	&websearchfs_remove_vnode,
1573
1574	/* VM file access */
1575	NULL, 	// &websearchfs_can_page
1576	NULL,	// &websearchfs_read_pages
1577	NULL, 	// &websearchfs_write_pages
1578
1579	NULL,	// io()
1580	NULL,	// cancel_io()
1581
1582	NULL,	// &websearchfs_get_file_map,
1583
1584	NULL, 	// &websearchfs_ioctl
1585	NULL,	// &websearchfs_setflags,
1586	NULL,	// &websearchfs_select
1587	NULL,	// &websearchfs_deselect
1588	NULL, 	// &websearchfs_fsync
1589
1590	NULL,	// &websearchfs_readlink,
1591	NULL,	// &websearchfs_symlink,
1592
1593	NULL,	// &websearchfs_link,
1594	&websearchfs_unlink,
1595	NULL,	// &websearchfs_rename,
1596
1597	&websearchfs_access,
1598	&websearchfs_rstat,
1599	&websearchfs_wstat,
1600	NULL,	// fs_preallocate
1601
1602	/* file operations */
1603	&websearchfs_create,
1604	&websearchfs_open,
1605	&websearchfs_close,
1606	&websearchfs_free_cookie,
1607	&websearchfs_read,
1608	&websearchfs_write,
1609
1610	/* directory operations */
1611	&websearchfs_mkdir,
1612	&websearchfs_rmdir,
1613	&websearchfs_opendir,
1614	&websearchfs_closedir,
1615	&websearchfs_free_dircookie,
1616	&websearchfs_readdir,
1617	&websearchfs_rewinddir,
1618
1619	/* attribute directory operations */
1620	&websearchfs_open_attrdir,
1621	&websearchfs_close_attrdir,
1622	&websearchfs_free_attrdircookie,
1623	&websearchfs_read_attrdir,
1624	&websearchfs_rewind_attrdir,
1625
1626	/* attribute operations */
1627	NULL,	// &websearchfs_create_attr
1628	&websearchfs_open_attr_h,
1629	&websearchfs_close_attr_h,
1630	&websearchfs_free_attr_cookie_h,
1631	&websearchfs_read_attr,
1632	NULL,	// &websearchfs_write_attr_h,
1633
1634	&websearchfs_read_attr_stat,
1635	NULL,	// &websearchfs_write_attr_stat
1636	NULL,	// &websearchfs_rename_attr
1637	NULL,	// &websearchfs_remove_attr
1638};
1639
1640file_system_module_info sWebSearchFSModule = {
1641	{
1642		"file_systems/websearchfs" B_CURRENT_FS_API_VERSION,
1643		0,
1644		websearchfs_std_ops,
1645	},
1646
1647	"websearchfs",					// short_name
1648	WEBSEARCHFS_PRETTY_NAME,		// pretty_name
1649	0,//B_DISK_SYSTEM_SUPPORTS_WRITING, // DDM flags
1650
1651	// scanning
1652	NULL,	// fs_identify_partition,
1653	NULL,	// fs_scan_partition,
1654	NULL,	// fs_free_identify_partition_cookie,
1655	NULL,	// free_partition_content_cookie()
1656
1657	&websearchfs_mount,
1658};
1659
1660module_info *modules[] = {
1661	(module_info *)&sWebSearchFSModule,
1662	NULL,
1663};
1664
1665
1666