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