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