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