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 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 */ 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 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 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 214 static int compare_fs_node_by_vnid(fs_node *node, ino_t *id) 215 { 216 return !(node->vnid == *id); 217 } 218 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 */ 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 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 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 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 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 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 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 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 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 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 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 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 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 */ 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 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 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 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 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 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 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 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... */ 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 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 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 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