xref: /haiku/src/add-ons/kernel/file_systems/websearchfs/websearchfs.c (revision 96e28a400a120f6f1289929c8c92f68bb2dbaf93)
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 
44 int32 refcount = 0;
45 
46 
47 extern struct attr_entry root_folder_attrs[];
48 extern struct attr_entry folders_attrs[];
49 extern struct attr_entry bookmark_attrs[];
50 extern struct attr_entry fake_bookmark_attrs[]; /* for debugging */
51 extern struct attr_entry template_1_attrs[];
52 extern struct attr_entry text_attrs[];
53 extern struct attr_entry mailto_me_bookmark_attrs[];
54 
55 extern char *readmestr;
56 
57 static fs_volume_ops sWebSearchFSVolumeOps;
58 static fs_vnode_ops sWebSearchFSVnodeOps;
59 
60 
61 static int websearchfs_create_gen(fs_volume *_volume, fs_node *dir, const char *name, int omode, int perms, ino_t *vnid, fs_node **node, struct attr_entry *iattrs, bool mkdir, bool uniq);
62 static int websearchfs_free_vnode(fs_volume *_volume, fs_node *node);
63 
fill_default_stat(struct stat * st,nspace_id nsid,ino_t vnid,mode_t mode)64 static 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  */
websearchfs_publish_static_entries(fs_volume * _volume)83 static 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 /*
116 err:
117 	TRACE("push_result_to_query: error 0x%08"B_PRIx32"\n", err);
118 	return err;
119 */
120 }
121 
websearchfs_mount(fs_volume * _vol,const char * devname,uint32 flags,const char * parms,ino_t * vnid)122 static 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 
websearchfs_unmount(fs_volume * _volume)187 static 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 
compare_fs_node_by_vnid(fs_node * node,ino_t * id)214 static int compare_fs_node_by_vnid(fs_node *node, ino_t *id)
215 {
216 	return !(node->vnid == *id);
217 }
218 
websearchfs_free_vnode(fs_volume * _volume,fs_node * node)219 static 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 
websearchfs_remove_vnode(fs_volume * _volume,fs_vnode * _node,bool reenter)233 static 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 	}
246 TRACE("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 */
250 TRACE("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);
254 TRACE("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 
websearchfs_read_vnode(fs_volume * _volume,ino_t vnid,fs_vnode * _node,int * _type,uint32 * _flags,bool reenter)264 static 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 
websearchfs_release_vnode(fs_volume * _volume,fs_vnode * _node,bool reenter)288 static 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 
compare_fs_node_by_name(fs_node * node,char * name)295 static 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 
websearchfs_get_vnode_name(fs_volume * _volume,fs_vnode * _node,char * buffer,size_t len)302 static 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 
websearchfs_walk(fs_volume * _volume,fs_vnode * _base,const char * file,ino_t * vnid)313 static 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 
websearchfs_opendir(fs_volume * _volume,fs_vnode * _node,void ** cookie)356 static 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 
websearchfs_closedir(fs_volume * _volume,fs_vnode * _node,void * _cookie)387 static 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 
websearchfs_rewinddir(fs_volume * _volume,fs_vnode * _node,void * _cookie)405 static 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 
websearchfs_readdir(fs_volume * _volume,fs_vnode * _node,void * _cookie,struct dirent * buf,size_t bufsize,uint32 * num)415 static 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 
websearchfs_free_dircookie(fs_volume * _volume,fs_vnode * _node,void * _cookie)473 static 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 
websearchfs_rstat(fs_volume * _volume,fs_vnode * _node,struct stat * st)490 static 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 
websearchfs_rfsstat(fs_volume * _volume,struct fs_info * info)510 static 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 
websearchfs_open(fs_volume * _volume,fs_vnode * _node,int omode,void ** cookie)528 static 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);
573 err_linsert:
574 	free(fc);
575 err_malloc:
576 all_ok:
577 	UNLOCK(&node->l);
578 err_n_l:
579 //	UNLOCK(&ns->l);
580 	return err;
581 }
582 
websearchfs_close(fs_volume * _volume,fs_vnode * _node,void * _cookie)583 static 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 
websearchfs_free_cookie(fs_volume * _volume,fs_vnode * _node,void * _cookie)603 static 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 
websearchfs_read(fs_volume * _volume,fs_vnode * _node,void * _cookie,off_t pos,void * buf,size_t * len)631 static 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 
websearchfs_write(fs_volume * _volume,fs_vnode * _node,void * _cookie,off_t pos,const void * buf,size_t * len)648 static 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 
websearchfs_wstat(fs_volume * _volume,fs_vnode * _node,const struct stat * st,uint32 mask)656 static 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 
websearchfs_wfsstat(fs_volume * _volume,const struct fs_info * info,uint32 mask)663 static 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  */
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)679 static int websearchfs_create_gen(fs_volume *_volume, fs_node *dir, const char *name, int omode, int perms, ino_t *vnid, fs_node **node, struct attr_entry *iattrs, bool mkdir, bool uniq)
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 
768 err_insnl:
769 	SLL_REMOVE(ns->nodes, nlnext, n);
770 err_lns:
771 	UNLOCK(&ns->l);
772 err_nl:
773 	free_lock(&n->l);
774 	atomic_add(&ns->nodecount, -1);
775 err_m:
776 	free(n);
777 err_l:
778 done:
779 	UNLOCK(&dir->l);
780 	return err;
781 }
782 
websearchfs_create(fs_volume * _volume,fs_vnode * _dir,const char * name,int omode,int perms,void ** cookie,ino_t * vnid)783 static 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 
websearchfs_unlink_gen(fs_volume * _volume,fs_node * dir,const char * name)803 static 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 
websearchfs_unlink(fs_volume * _volume,fs_vnode * _dir,const char * name)838 static 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 
websearchfs_rmdir(fs_volume * _volume,fs_vnode * _dir,const char * name)845 static 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 
websearchfs_unlink_node_rec(fs_volume * _volume,fs_node * node)853 static 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 
websearchfs_access(fs_volume * _volume,fs_vnode * _node,int mode)876 static 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 
websearchfs_mkdir(fs_volume * _volume,fs_vnode * _dir,const char * name,int perms)885 static 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 
websearchfs_open_attrdir(fs_volume * _volume,fs_vnode * _node,void ** cookie)895 static 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 
websearchfs_close_attrdir(fs_volume * _volume,fs_vnode * _node,void * _cookie)924 static 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 
websearchfs_free_attrdircookie(fs_volume * _volume,fs_vnode * _node,void * _cookie)939 static 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 
websearchfs_rewind_attrdir(fs_volume * _volume,fs_vnode * _node,void * _cookie)955 static 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 
websearchfs_read_attrdir(fs_volume * _volume,fs_vnode * _node,void * _cookie,struct dirent * buf,size_t bufsize,uint32 * num)965 static 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 */
websearchfs_open_attr_h(fs_volume * _volume,fs_vnode * _node,const char * name,int omode,void ** cookie)1017 static 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);
1068 err_linsert:
1069 	free(fc);
1070 err_malloc:
1071 all_ok:
1072 	UNLOCK(&node->l);
1073 err_n_l:
1074 //	UNLOCK(&ns->l);
1075 	return err;
1076 }
1077 
websearchfs_close_attr_h(fs_volume * _volume,fs_vnode * _node,void * _cookie)1078 static 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 
websearchfs_free_attr_cookie_h(fs_volume * _volume,fs_vnode * _node,void * _cookie)1099 static 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 
websearchfs_read_attr_stat(fs_volume * _volume,fs_vnode * _node,void * _cookie,struct stat * st)1120 static 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 
websearchfs_read_attr(fs_volume * _volume,fs_vnode * _node,void * _cookie,off_t pos,void * buf,size_t * len)1138 static 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 
compare_fs_node_by_recent_query_string(fs_node * node,char * query)1168 static 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 
websearchfs_open_query(fs_volume * _volume,const char * query,uint32 flags,port_id port,uint32 token,void ** cookie)1182 static 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);
1314 reuse:
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:
1326 err_gro:
1327 	if (qn->request)
1328 		duckduckgo_request_close(qn->request);
1329 err_gn:
1330 	put_vnode(_volume, qn->vnid);
1331 err_mkdir:
1332 	if (!reused)
1333 		websearchfs_unlink_gen(_volume, ns->root, qn->name);
1334 err_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 
websearchfs_close_query(fs_volume * _volume,void * _cookie)1342 static 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... */
websearchfs_free_query_cookie(fs_volume * _volume,void * _cookie)1370 static 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);
1389 no_node:
1390 	free(cookie);
1391 	return B_OK;
1392 }
1393 #endif
1394 
websearchfs_read_query(fs_volume * _volume,void * _cookie,struct dirent * buf,size_t bufsize,uint32 * num)1395 static 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 
websearchfs_push_result_to_query(struct duckduckgo_request * request,struct duckduckgo_result * result)1433 int 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 
1524 static status_t
websearchfs_std_ops(int32 op,...)1525 websearchfs_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 
1540 static 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 
1567 static 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 
1640 file_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 
1660 module_info *modules[] = {
1661 	(module_info *)&sWebSearchFSModule,
1662 	NULL,
1663 };
1664 
1665 
1666