1 /* kernel_interface - file system interface to BeOS' vnode layer 2 ** 3 ** Initial version by Axel Dörfler, axeld@pinc-software.de 4 ** This file may be used under the terms of the OpenBeOS License. 5 */ 6 7 8 #include "Debug.h" 9 #include "Volume.h" 10 #include "Inode.h" 11 #include "Index.h" 12 #include "BPlusTree.h" 13 #include "Query.h" 14 #include "bfs_control.h" 15 16 #include <kernel_cpp.h> 17 18 #include <string.h> 19 #include <stdio.h> 20 21 // BeOS vnode layer stuff 22 #include <KernelExport.h> 23 #ifndef _IMPEXP_KERNEL 24 # define _IMPEXP_KERNEL 25 #endif 26 27 extern "C" { 28 #include <fsproto.h> 29 #include <lock.h> 30 #include <cache.h> 31 } 32 #include <fs_index.h> 33 #include <fs_query.h> 34 35 36 #ifdef USER 37 # define dprintf printf 38 #endif 39 40 // file system API 41 static int bfs_mount(nspace_id nsid, const char *device, ulong flags, 42 void *parms, size_t len, void **data, vnode_id *vnid); 43 static int bfs_unmount(void *_ns); 44 static int bfs_read_fs_stat(void *_ns, struct fs_info *); 45 static int bfs_write_fs_stat(void *ns, struct fs_info *, long mode); 46 static int bfs_initialize(const char *devname, void *parms, size_t len); 47 48 static int bfs_sync(void *ns); 49 50 static int bfs_read_vnode(void *_ns, vnode_id vnid, char r, void **node); 51 static int bfs_release_vnode(void *_ns, void *_node, char r); 52 static int bfs_remove_vnode(void *ns, void *node, char r); 53 54 static int bfs_walk(void *_ns, void *_base, const char *file, 55 char **newpath, vnode_id *vnid); 56 57 static int bfs_ioctl(void *ns, void *node, void *cookie, int cmd, void *buf, size_t len); 58 static int bfs_setflags(void *ns, void *node, void *cookie, int flags); 59 60 static int bfs_select(void *ns, void *node, void *cookie, uint8 event, 61 uint32 ref, selectsync *sync); 62 static int bfs_deselect(void *ns, void *node, void *cookie, uint8 event, 63 selectsync *sync); 64 static int bfs_fsync(void *ns, void *node); 65 66 static int bfs_create(void *ns, void *dir, const char *name, 67 int perms, int omode, vnode_id *vnid, void **cookie); 68 static int bfs_symlink(void *ns, void *dir, const char *name, 69 const char *path); 70 static int bfs_link(void *ns, void *dir, const char *name, void *node); 71 static int bfs_unlink(void *ns, void *dir, const char *name); 72 static int bfs_rename(void *ns, void *oldDir, const char *oldName, void *newDir, const char *newName); 73 74 static int bfs_read_stat(void *_ns, void *_node, struct stat *st); 75 static int bfs_write_stat(void *ns, void *node, struct stat *st, long mask); 76 77 static int bfs_open(void *_ns, void *_node, int omode, void **cookie); 78 static int bfs_read(void *_ns, void *_node, void *cookie, off_t pos, 79 void *buf, size_t *len); 80 static int bfs_write(void *ns, void *node, void *cookie, off_t pos, 81 const void *buf, size_t *len); 82 static int bfs_free_cookie(void *ns, void *node, void *cookie); 83 static int bfs_close(void *ns, void *node, void *cookie); 84 85 static int bfs_access(void *_ns, void *_node, int mode); 86 static int bfs_read_link(void *_ns, void *_node, char *buffer, size_t *bufferSize); 87 88 // directory functions 89 static int bfs_mkdir(void *ns, void *dir, const char *name, int perms); 90 static int bfs_rmdir(void *ns, void *dir, const char *name); 91 static int bfs_open_dir(void *_ns, void *_node, void **cookie); 92 static int bfs_read_dir(void *_ns, void *_node, void *cookie, 93 long *num, struct dirent *dirent, size_t bufferSize); 94 static int bfs_rewind_dir(void *_ns, void *_node, void *cookie); 95 static int bfs_close_dir(void *_ns, void *_node, void *cookie); 96 static int bfs_free_dir_cookie(void *_ns, void *_node, void *cookie); 97 98 // attribute support 99 static int bfs_open_attrdir(void *ns, void *node, void **cookie); 100 static int bfs_close_attrdir(void *ns, void *node, void *cookie); 101 static int bfs_free_attrdir_cookie(void *ns, void *node, void *cookie); 102 static int bfs_rewind_attrdir(void *ns, void *node, void *cookie); 103 static int bfs_read_attrdir(void *ns, void *node, void *cookie, long *num, 104 struct dirent *buf, size_t bufferSize); 105 static int bfs_remove_attr(void *ns, void *node, const char *name); 106 static int bfs_rename_attr(void *ns, void *node, const char *oldname, 107 const char *newname); 108 static int bfs_stat_attr(void *ns, void *node, const char *name, 109 struct attr_info *buf); 110 static int bfs_write_attr(void *ns, void *node, const char *name, int type, 111 const void *buf, size_t *len, off_t pos); 112 static int bfs_read_attr(void *ns, void *node, const char *name, int type, 113 void *buf, size_t *len, off_t pos); 114 115 // index support 116 static int bfs_open_indexdir(void *ns, void **cookie); 117 static int bfs_close_indexdir(void *ns, void *cookie); 118 static int bfs_free_indexdir_cookie(void *ns, void *node, void *cookie); 119 static int bfs_rewind_indexdir(void *ns, void *cookie); 120 static int bfs_read_indexdir(void *ns, void *cookie, long *num, struct dirent *dirent, 121 size_t bufferSize); 122 static int bfs_create_index(void *ns, const char *name, int type, int flags); 123 static int bfs_remove_index(void *ns, const char *name); 124 static int bfs_rename_index(void *ns, const char *oldname, const char *newname); 125 static int bfs_stat_index(void *ns, const char *name, struct index_info *indexInfo); 126 127 // query support 128 static int bfs_open_query(void *ns, const char *query, ulong flags, 129 port_id port, long token, void **cookie); 130 static int bfs_close_query(void *ns, void *cookie); 131 static int bfs_free_query_cookie(void *ns, void *node, void *cookie); 132 static int bfs_read_query(void *ns, void *cookie, long *num, 133 struct dirent *buf, size_t bufsize); 134 135 // Dano compatibility (required for booting) 136 static int bfs_wake_vnode(void *ns, void *node); 137 static int bfs_suspend_vnode(void *ns, void *node); 138 139 140 /* vnode_ops struct. Fill this in to tell the kernel how to call 141 functions in your driver. 142 */ 143 144 vnode_ops fs_entry = { 145 &bfs_read_vnode, // read_vnode 146 &bfs_release_vnode, // write_vnode 147 &bfs_remove_vnode, // remove_vnode 148 NULL, // secure_vnode (not needed) 149 &bfs_walk, // walk 150 &bfs_access, // access 151 &bfs_create, // create 152 &bfs_mkdir, // mkdir 153 &bfs_symlink, // symlink 154 &bfs_link, // link 155 &bfs_rename, // rename 156 &bfs_unlink, // unlink 157 &bfs_rmdir, // rmdir 158 &bfs_read_link, // readlink 159 &bfs_open_dir, // opendir 160 &bfs_close_dir, // closedir 161 &bfs_free_dir_cookie, // free_dircookie 162 &bfs_rewind_dir, // rewinddir 163 &bfs_read_dir, // readdir 164 &bfs_open, // open file 165 &bfs_close, // close file 166 &bfs_free_cookie, // free cookie 167 &bfs_read, // read file 168 &bfs_write, // write file 169 NULL, // readv 170 NULL, // writev 171 &bfs_ioctl, // ioctl 172 &bfs_setflags, // setflags file 173 &bfs_read_stat, // read stat 174 &bfs_write_stat, // write stat 175 &bfs_fsync, // fsync 176 &bfs_initialize, // initialize 177 &bfs_mount, // mount 178 &bfs_unmount, // unmount 179 &bfs_sync, // sync 180 &bfs_read_fs_stat, // read fs stat 181 &bfs_write_fs_stat, // write fs stat 182 &bfs_select, // select 183 &bfs_deselect, // deselect 184 185 &bfs_open_indexdir, // open index dir 186 &bfs_close_indexdir, // close index dir 187 &bfs_free_indexdir_cookie, // free index dir cookie 188 &bfs_rewind_indexdir, // rewind index dir 189 &bfs_read_indexdir, // read index dir 190 &bfs_create_index, // create index 191 &bfs_remove_index, // remove index 192 &bfs_rename_index, // rename index 193 &bfs_stat_index, // stat index 194 195 &bfs_open_attrdir, // open attr dir 196 &bfs_close_attrdir, // close attr dir 197 &bfs_free_attrdir_cookie, // free attr dir cookie 198 &bfs_rewind_attrdir, // rewind attr dir 199 &bfs_read_attrdir, // read attr dir 200 &bfs_write_attr, // write attr 201 &bfs_read_attr, // read attr 202 &bfs_remove_attr, // remove attr 203 &bfs_rename_attr, // rename attr 204 &bfs_stat_attr, // stat attr 205 206 &bfs_open_query, // open query 207 &bfs_close_query, // close query 208 &bfs_free_query_cookie, // free query cookie 209 &bfs_read_query, // read query 210 211 &bfs_wake_vnode, // these two are added for Dano compatibility; 212 &bfs_suspend_vnode // they do nothing. 213 }; 214 215 #define BFS_IO_SIZE 65536 216 217 // ToDo: has to change to "bfs" later 218 #ifdef COMPILE_FOR_R5 219 # define BFS_NAME "bfs" 220 #else 221 # define BFS_NAME "obfs" 222 #endif 223 224 int32 api_version = B_CUR_FS_API_VERSION; 225 226 227 static int 228 bfs_mount(nspace_id nsid, const char *device, ulong flags, void *parms, 229 size_t len, void **data, vnode_id *rootID) 230 { 231 FUNCTION(); 232 233 Volume *volume = new Volume(nsid); 234 if (volume == NULL) 235 return B_NO_MEMORY; 236 237 status_t status; 238 if ((status = volume->Mount(device, flags)) == B_OK) { 239 *data = volume; 240 *rootID = volume->ToVnode(volume->Root()); 241 INFORM(("mounted \"%s\" (root node at %Ld, device = %s)\n", 242 volume->Name(), *rootID, device)); 243 244 #ifdef DEBUG 245 add_debugger_commands(); 246 #endif 247 } 248 else 249 delete volume; 250 251 RETURN_ERROR(status); 252 } 253 254 255 static int 256 bfs_unmount(void *ns) 257 { 258 FUNCTION(); 259 Volume* volume = (Volume *)ns; 260 261 status_t status = volume->Unmount(); 262 delete volume; 263 264 #ifdef DEBUG 265 remove_debugger_commands(); 266 #endif 267 RETURN_ERROR(status); 268 } 269 270 271 /** Fill in bfs_info struct for device. 272 */ 273 274 static int 275 bfs_read_fs_stat(void *_ns, struct fs_info *info) 276 { 277 FUNCTION(); 278 if (_ns == NULL || info == NULL) 279 return B_BAD_VALUE; 280 281 Volume *volume = (Volume *)_ns; 282 283 RecursiveLocker locker(volume->Lock()); 284 285 // File system flags. 286 info->flags = B_FS_IS_PERSISTENT | B_FS_HAS_ATTR | B_FS_HAS_MIME | B_FS_HAS_QUERY | 287 (volume->IsReadOnly() ? B_FS_IS_READONLY : 0); 288 289 info->io_size = BFS_IO_SIZE; 290 // whatever is appropriate here? Just use the same value as BFS (and iso9660) for now 291 292 info->block_size = volume->BlockSize(); 293 info->total_blocks = volume->NumBlocks(); 294 info->free_blocks = volume->FreeBlocks(); 295 296 // Volume name 297 strlcpy(info->volume_name, volume->Name(), sizeof(info->volume_name)); 298 299 // File system name 300 strlcpy(info->fsh_name, BFS_NAME, sizeof(info->fsh_name)); 301 302 return B_OK; 303 } 304 305 306 static int 307 bfs_write_fs_stat(void *_ns, struct fs_info *info, long mask) 308 { 309 FUNCTION_START(("mask = %ld\n", mask)); 310 311 Volume *volume = (Volume *)_ns; 312 disk_super_block &superBlock = volume->SuperBlock(); 313 314 RecursiveLocker locker(volume->Lock()); 315 316 status_t status = B_BAD_VALUE; 317 318 if (mask & WFSSTAT_NAME) { 319 strncpy(superBlock.name, info->volume_name, sizeof(superBlock.name) - 1); 320 superBlock.name[sizeof(superBlock.name) - 1] = '\0'; 321 322 status = volume->WriteSuperBlock(); 323 } 324 return status; 325 } 326 327 328 static int 329 bfs_initialize(const char *deviceName, void *parms, size_t len) 330 { 331 FUNCTION_START(("deviceName = %s, parameter len = %ld\n", deviceName, len)); 332 333 // This function is not available from the outside in BeOS 334 // It will be similarly implemented in OpenBeOS, though - the 335 // backend (to create the file system) is already done; just 336 // call Volume::Initialize(). 337 338 return B_ERROR; 339 } 340 341 342 static int 343 bfs_sync(void *_ns) 344 { 345 FUNCTION(); 346 if (_ns == NULL) 347 return B_BAD_VALUE; 348 349 Volume *volume = (Volume *)_ns; 350 351 return volume->Sync(); 352 } 353 354 355 // #pragma mark - 356 357 358 /** Reads in the node from disk and creates an inode object from it. 359 * Has to be cautious if the node in question is currently under 360 * construction, in which case it waits for that action to be completed, 361 * and uses the inode object from the construction instead of creating 362 * a new one. 363 * ToDo: Must not be called without the volume lock being held. Actually, 364 * this might even happen with the BDirectory(node_ref *) constructor 365 * (at least I think so, I haven't tested it yet), so we should better 366 * test this. Fortunately, we can easily solve the issue with our kernel. 367 */ 368 369 static int 370 bfs_read_vnode(void *_ns, vnode_id id, char reenter, void **_node) 371 { 372 FUNCTION_START(("vnode_id = %Ld\n", id)); 373 Volume *volume = (Volume *)_ns; 374 375 if (id < 0 || id > volume->NumBlocks()) { 376 FATAL(("inode at %Ld requested!\n", id)); 377 return B_ERROR; 378 } 379 380 CachedBlock cached(volume, id); 381 382 bfs_inode *node = (bfs_inode *)cached.Block(); 383 Inode *inode = NULL; 384 int32 tries = 0; 385 386 restartIfBusy: 387 status_t status = node->InitCheck(volume); 388 389 if (status == B_BUSY) { 390 inode = (Inode *)node->etc; 391 // We have to use the "etc" field to get the inode object 392 // (the inode is currently being constructed) 393 // We would need to call something like get_vnode() again here 394 // to get rid of the "etc" field - which would be nice, especially 395 // for other file systems which don't have this messy field. 396 397 // let us wait a bit and try again later 398 if (tries++ < 200) { 399 // wait for one second at maximum 400 snooze(5000); 401 goto restartIfBusy; 402 } 403 FATAL(("inode is not becoming unbusy (id = %Ld)\n", id)); 404 return status; 405 } else if (status < B_OK) { 406 FATAL(("inode at %Ld is corrupt!\n", id)); 407 return status; 408 } 409 410 // If the inode is currently being constructed, we already have an inode 411 // pointer (taken from the node's etc field). 412 // If not, we create a new one here 413 414 if (inode == NULL) { 415 inode = new Inode(&cached); 416 if (inode == NULL) 417 return B_NO_MEMORY; 418 419 status = inode->InitCheck(false); 420 if (status < B_OK) 421 delete inode; 422 } else 423 status = inode->InitCheck(false); 424 425 if (status == B_OK) 426 *_node = inode; 427 428 return status; 429 } 430 431 432 static int 433 bfs_release_vnode(void *ns, void *_node, char reenter) 434 { 435 //FUNCTION_START(("node = %p\n", _node)); 436 Inode *inode = (Inode *)_node; 437 438 delete inode; 439 440 return B_NO_ERROR; 441 } 442 443 444 static int 445 bfs_remove_vnode(void *_ns, void *_node, char reenter) 446 { 447 FUNCTION(); 448 449 if (_ns == NULL || _node == NULL) 450 return B_BAD_VALUE; 451 452 Volume *volume = (Volume *)_ns; 453 Inode *inode = (Inode *)_node; 454 455 // The "chkbfs" functionality uses this flag to prevent the space used 456 // up by the inode from being freed - this flag is set only in situations 457 // where this is a good idea... (the block bitmap will get fixed anyway 458 // in this case). 459 if (inode->Flags() & INODE_DONT_FREE_SPACE) { 460 delete inode; 461 return B_OK; 462 } 463 464 // If the inode isn't in use anymore, we were called before 465 // bfs_unlink() returns - in this case, we can just use the 466 // transaction which has already deleted the inode. 467 Transaction localTransaction, *transaction = NULL; 468 469 Journal *journal = volume->GetJournal(volume->ToBlock(inode->Parent())); 470 if (journal != NULL) 471 transaction = journal->CurrentTransaction(); 472 473 if (transaction == NULL) { 474 transaction = &localTransaction; 475 localTransaction.Start(volume, inode->BlockNumber()); 476 } 477 478 status_t status = inode->Free(transaction); 479 if (status == B_OK) { 480 if (transaction == &localTransaction) 481 localTransaction.Done(); 482 483 delete inode; 484 } 485 486 return status; 487 } 488 489 490 static int 491 bfs_wake_vnode(void *_ns, void *_node) 492 { 493 return B_OK; 494 } 495 496 497 static int 498 bfs_suspend_vnode(void *_ns, void *_node) 499 { 500 return B_OK; 501 } 502 503 504 // #pragma mark - 505 506 507 /** the walk function just "walks" through a directory looking for the 508 * specified file. It calls get_vnode() on its vnode-id to init it 509 * for the kernel. 510 */ 511 512 static int 513 bfs_walk(void *_ns, void *_directory, const char *file, char **_resolvedPath, vnode_id *_vnodeID) 514 { 515 //FUNCTION_START(("file = %s\n", file)); 516 if (_ns == NULL || _directory == NULL || file == NULL || _vnodeID == NULL) 517 return B_BAD_VALUE; 518 519 Volume *volume = (Volume *)_ns; 520 Inode *directory = (Inode *)_directory; 521 522 // check access permissions 523 status_t status = directory->CheckPermissions(R_OK); 524 if (status < B_OK) 525 RETURN_ERROR(status); 526 527 BPlusTree *tree; 528 if (directory->GetTree(&tree) != B_OK) 529 RETURN_ERROR(B_BAD_VALUE); 530 531 if ((status = tree->Find((uint8 *)file, (uint16)strlen(file), _vnodeID)) < B_OK) { 532 PRINT(("bfs_walk() could not find %Ld:\"%s\": %s\n", directory->BlockNumber(), file, strerror(status))); 533 return status; 534 } 535 536 RecursiveLocker locker(volume->Lock()); 537 // we have to hold the volume lock in order to not 538 // interfere with new_vnode() here 539 540 Inode *inode; 541 if ((status = get_vnode(volume->ID(), *_vnodeID, (void **)&inode)) != B_OK) { 542 REPORT_ERROR(status); 543 return B_ENTRY_NOT_FOUND; 544 } 545 546 // Is inode a symlink? Then resolve it, if we should 547 548 if (inode->IsSymLink() && _resolvedPath != NULL) { 549 status_t status = B_OK; 550 char *newPath = NULL; 551 552 // Symbolic links can store their target in the data stream (for links 553 // that take more than 144 bytes of storage [the size of the data_stream 554 // structure]), or directly instead of the data_stream class 555 // So we have to deal with both cases here. 556 557 // Note: we would naturally call bfs_read_link() here, but the API of the 558 // vnode layer would require us to always reserve a large chunk of memory 559 // for the path, so we're not going to do that 560 561 if (inode->Flags() & INODE_LONG_SYMLINK) { 562 size_t readBytes = inode->Size(); 563 char *data = (char *)malloc(readBytes); 564 if (data != NULL) { 565 status = inode->ReadAt(0, (uint8 *)data, &readBytes); 566 if (status == B_OK && readBytes == inode->Size()) 567 status = new_path(data, &newPath); 568 569 free(data); 570 } else 571 status = B_NO_MEMORY; 572 } else 573 status = new_path((char *)&inode->Node()->short_symlink, &newPath); 574 575 put_vnode(volume->ID(), inode->ID()); 576 if (status == B_OK) 577 *_resolvedPath = newPath; 578 579 RETURN_ERROR(status); 580 } 581 582 return B_OK; 583 } 584 585 586 static int 587 bfs_ioctl(void *_ns, void *_node, void *_cookie, int cmd, void *buffer, size_t bufferLength) 588 { 589 FUNCTION_START(("node = %p, cmd = %d, buf = %p, len = %ld\n", _node, cmd, buffer, bufferLength)); 590 591 if (_ns == NULL) 592 return B_BAD_VALUE; 593 594 Volume *volume = (Volume *)_ns; 595 Inode *inode = (Inode *)_node; 596 597 switch (cmd) { 598 case IOCTL_FILE_UNCACHED_IO: 599 { 600 if (inode == NULL) 601 return B_BAD_VALUE; 602 603 // if the inode is already set up for uncached access, bail out 604 if (inode->Flags() & INODE_NO_CACHE) { 605 FATAL(("File %Ld is already uncached\n", inode->ID())); 606 return B_ERROR; 607 } 608 609 PRINT(("uncached access to inode %Ld\n", inode->ID())); 610 611 // ToDo: sync the cache for this file! 612 // Unfortunately, we can't remove any blocks from the cache in BeOS, 613 // that means we can't guarantee consistency for the file contents 614 // spanning over both access modes. 615 616 // request buffers for being able to access the file without 617 // using the cache or allocating memory 618 status_t status = volume->Pool().RequestBuffers(volume->BlockSize()); 619 if (status == B_OK) 620 inode->Node()->flags |= HOST_ENDIAN_TO_BFS_INT32(INODE_NO_CACHE); 621 return status; 622 } 623 case IOCTL_CREATE_TIME: 624 { 625 if (inode == NULL || buffer == NULL) 626 return B_BAD_VALUE; 627 628 off_t *creationTime = (off_t *)buffer; 629 *creationTime = inode->Node()->CreateTime(); 630 return B_OK; 631 } 632 case IOCTL_MODIFIED_TIME: 633 { 634 if (inode == NULL || buffer == NULL) 635 return B_BAD_VALUE; 636 637 off_t *modifiedTime = (off_t *)buffer; 638 *modifiedTime = inode->LastModified(); 639 return B_OK; 640 } 641 case BFS_IOCTL_VERSION: 642 { 643 uint32 *version = (uint32 *)buffer; 644 645 *version = 0x10000; 646 return B_OK; 647 } 648 case BFS_IOCTL_START_CHECKING: 649 { 650 // start checking 651 BlockAllocator &allocator = volume->Allocator(); 652 check_control *control = (check_control *)buffer; 653 654 status_t status = allocator.StartChecking(control); 655 if (status == B_OK && inode != NULL) 656 inode->Node()->flags |= HOST_ENDIAN_TO_BFS_INT32(INODE_CHKBFS_RUNNING); 657 658 return status; 659 } 660 case BFS_IOCTL_STOP_CHECKING: 661 { 662 // stop checking 663 BlockAllocator &allocator = volume->Allocator(); 664 check_control *control = (check_control *)buffer; 665 666 status_t status = allocator.StopChecking(control); 667 if (status == B_OK && inode != NULL) 668 inode->Node()->flags &= HOST_ENDIAN_TO_BFS_INT32(~INODE_CHKBFS_RUNNING); 669 670 return status; 671 } 672 case BFS_IOCTL_CHECK_NEXT_NODE: 673 { 674 // check next 675 BlockAllocator &allocator = volume->Allocator(); 676 check_control *control = (check_control *)buffer; 677 678 return allocator.CheckNextNode(control); 679 } 680 #ifdef DEBUG 681 case 56742: 682 { 683 // allocate all free blocks and zero them out (a test for the BlockAllocator)! 684 BlockAllocator &allocator = volume->Allocator(); 685 Transaction transaction(volume, 0); 686 CachedBlock cached(volume); 687 block_run run; 688 while (allocator.AllocateBlocks(&transaction, 8, 0, 64, 1, run) == B_OK) { 689 PRINT(("write block_run(%ld, %d, %d)\n", run.allocation_group, run.start, run.length)); 690 for (int32 i = 0;i < run.length;i++) { 691 uint8 *block = cached.SetTo(run); 692 if (block != NULL) { 693 memset(block, 0, volume->BlockSize()); 694 cached.WriteBack(&transaction); 695 } 696 } 697 } 698 return B_OK; 699 } 700 case 56743: 701 dump_super_block(&volume->SuperBlock()); 702 return B_OK; 703 case 56744: 704 if (inode != NULL) 705 dump_inode(inode->Node()); 706 return B_OK; 707 case 56745: 708 if (inode != NULL) 709 dump_block((const char *)inode->Node(), volume->BlockSize()); 710 return B_OK; 711 #endif 712 } 713 return B_BAD_VALUE; 714 } 715 716 717 /** Sets the open-mode flags for the open file cookie - only 718 * supports O_APPEND currently, but that should be sufficient 719 * for a file system. 720 */ 721 722 static int 723 bfs_setflags(void *_ns, void *_node, void *_cookie, int flags) 724 { 725 FUNCTION_START(("node = %p, flags = %d", _node, flags)); 726 727 file_cookie *cookie = (file_cookie *)_cookie; 728 cookie->open_mode = (cookie->open_mode & ~O_APPEND) | (flags & O_APPEND); 729 730 return B_OK; 731 } 732 733 734 int 735 bfs_select(void *ns, void *node, void *cookie, uint8 event, uint32 ref, selectsync *sync) 736 { 737 FUNCTION_START(("event = %d, ref = %lu, sync = %p\n", event, ref, sync)); 738 notify_select_event(sync, ref); 739 740 return B_OK; 741 } 742 743 744 static int 745 bfs_deselect(void *ns, void *node, void *cookie, uint8 event, selectsync *sync) 746 { 747 FUNCTION(); 748 return B_OK; 749 } 750 751 752 static int 753 bfs_fsync(void *_ns, void *_node) 754 { 755 FUNCTION(); 756 if (_node == NULL) 757 return B_BAD_VALUE; 758 759 Inode *inode = (Inode *)_node; 760 return inode->Sync(); 761 } 762 763 764 /** Fills in the stat struct for a node 765 */ 766 767 static int 768 bfs_read_stat(void *_ns, void *_node, struct stat *st) 769 { 770 FUNCTION(); 771 772 Volume *volume = (Volume *)_ns; 773 Inode *inode = (Inode *)_node; 774 bfs_inode *node = inode->Node(); 775 776 st->st_dev = volume->ID(); 777 st->st_ino = inode->ID(); 778 st->st_nlink = 1; 779 st->st_blksize = BFS_IO_SIZE; 780 781 st->st_uid = node->UserID(); 782 st->st_gid = node->GroupID(); 783 st->st_mode = node->Mode(); 784 st->st_size = node->data.Size(); 785 786 st->st_atime = time(NULL); 787 st->st_mtime = st->st_ctime = (time_t)(node->LastModifiedTime() >> INODE_TIME_SHIFT); 788 st->st_crtime = (time_t)(node->CreateTime() >> INODE_TIME_SHIFT); 789 790 return B_NO_ERROR; 791 } 792 793 794 static int 795 bfs_write_stat(void *_ns, void *_node, struct stat *stat, long mask) 796 { 797 FUNCTION(); 798 799 if (_ns == NULL || _node == NULL || stat == NULL) 800 RETURN_ERROR(B_BAD_VALUE); 801 802 Volume *volume = (Volume *)_ns; 803 Inode *inode = (Inode *)_node; 804 805 // that may be incorrect here - I don't think we need write access to 806 // change most of the stat... 807 // we should definitely check a bit more if the new stats are correct and valid... 808 809 status_t status = inode->CheckPermissions(W_OK); 810 if (status < B_OK) 811 RETURN_ERROR(status); 812 813 #ifdef UNSAFE_GET_VNODE 814 RecursiveLocker locker(volume->Lock()); 815 #endif 816 817 WriteLocked locked(inode->Lock()); 818 if (locked.IsLocked() < B_OK) 819 RETURN_ERROR(B_ERROR); 820 821 Transaction transaction(volume, inode->BlockNumber()); 822 823 bfs_inode *node = inode->Node(); 824 825 if (mask & WSTAT_SIZE) { 826 // Since WSTAT_SIZE is the only thing that can fail directly, we 827 // do it first, so that the inode state will still be consistent 828 // with the on-disk version 829 if (inode->IsDirectory()) 830 return B_IS_A_DIRECTORY; 831 832 if (inode->Size() != stat->st_size) { 833 status = inode->SetFileSize(&transaction, stat->st_size); 834 if (status < B_OK) 835 return status; 836 837 // fill the new blocks (if any) with zeros 838 inode->FillGapWithZeros(inode->OldSize(), inode->Size()); 839 840 Index index(volume); 841 index.UpdateSize(&transaction, inode); 842 843 if ((mask & WSTAT_MTIME) == 0) 844 index.UpdateLastModified(&transaction, inode); 845 } 846 } 847 848 if (mask & WSTAT_MODE) { 849 PRINT(("original mode = %ld, stat->st_mode = %d\n", node->mode, stat->st_mode)); 850 node->mode = node->mode & ~S_IUMSK | stat->st_mode & S_IUMSK; 851 } 852 853 if (mask & WSTAT_UID) 854 node->uid = stat->st_uid; 855 if (mask & WSTAT_GID) 856 node->gid = stat->st_gid; 857 858 if (mask & WSTAT_MTIME) { 859 // Index::UpdateLastModified() will set the new time in the inode 860 Index index(volume); 861 index.UpdateLastModified(&transaction, inode, 862 (bigtime_t)stat->st_mtime << INODE_TIME_SHIFT); 863 } 864 if (mask & WSTAT_CRTIME) { 865 node->create_time = (bigtime_t)stat->st_crtime << INODE_TIME_SHIFT; 866 } 867 868 if ((status = inode->WriteBack(&transaction)) == B_OK) 869 transaction.Done(); 870 871 notify_listener(B_STAT_CHANGED, volume->ID(), 0, 0, inode->ID(), NULL); 872 873 return status; 874 } 875 876 877 int 878 bfs_create(void *_ns, void *_directory, const char *name, int omode, int mode, 879 vnode_id *vnodeID, void **_cookie) 880 { 881 FUNCTION_START(("name = \"%s\", perms = %d, omode = %d\n", name, mode, omode)); 882 883 if (_ns == NULL || _directory == NULL || _cookie == NULL 884 || name == NULL || *name == '\0') 885 RETURN_ERROR(B_BAD_VALUE); 886 887 Volume *volume = (Volume *)_ns; 888 Inode *directory = (Inode *)_directory; 889 890 if (!directory->IsDirectory()) 891 RETURN_ERROR(B_BAD_TYPE); 892 893 status_t status = directory->CheckPermissions(W_OK); 894 if (status < B_OK) 895 RETURN_ERROR(status); 896 897 // We are creating the cookie at this point, so that we don't have 898 // to remove the inode if we don't have enough free memory later... 899 file_cookie *cookie = (file_cookie *)malloc(sizeof(file_cookie)); 900 if (cookie == NULL) 901 RETURN_ERROR(B_NO_MEMORY); 902 903 // initialize the cookie 904 cookie->open_mode = omode; 905 cookie->last_size = 0; 906 cookie->last_notification = system_time(); 907 908 #ifdef UNSAFE_GET_VNODE 909 RecursiveLocker locker(volume->Lock()); 910 #endif 911 Transaction transaction(volume, directory->BlockNumber()); 912 913 status = Inode::Create(&transaction, directory, name, S_FILE | (mode & S_IUMSK), 914 omode, 0, vnodeID); 915 916 if (status >= B_OK) { 917 transaction.Done(); 918 919 // register the cookie 920 *_cookie = cookie; 921 922 notify_listener(B_ENTRY_CREATED, volume->ID(), directory->ID(), 0, *vnodeID, name); 923 } else 924 free(cookie); 925 926 return status; 927 } 928 929 930 int 931 bfs_symlink(void *_ns, void *_directory, const char *name, const char *path) 932 { 933 FUNCTION(); 934 935 if (_ns == NULL || _directory == NULL || path == NULL 936 || name == NULL || *name == '\0') 937 RETURN_ERROR(B_BAD_VALUE); 938 939 Volume *volume = (Volume *)_ns; 940 Inode *directory = (Inode *)_directory; 941 942 if (!directory->IsDirectory()) 943 RETURN_ERROR(B_BAD_TYPE); 944 945 status_t status = directory->CheckPermissions(W_OK); 946 if (status < B_OK) 947 RETURN_ERROR(status); 948 949 #ifdef UNSAFE_GET_VNODE 950 RecursiveLocker locker(volume->Lock()); 951 #endif 952 Transaction transaction(volume, directory->BlockNumber()); 953 954 Inode *link; 955 off_t id; 956 status = Inode::Create(&transaction, directory, name, S_SYMLINK | 0777, 0, 0, &id, &link); 957 if (status < B_OK) 958 RETURN_ERROR(status); 959 960 size_t length = strlen(path); 961 if (length < SHORT_SYMLINK_NAME_LENGTH) { 962 strcpy(link->Node()->short_symlink, path); 963 status = link->WriteBack(&transaction); 964 } else { 965 link->Node()->flags |= HOST_ENDIAN_TO_BFS_INT32(INODE_LONG_SYMLINK | INODE_LOGGED); 966 // The following call will have to write the inode back, so 967 // we don't have to do that here... 968 status = link->WriteAt(&transaction, 0, (const uint8 *)path, &length); 969 } 970 // ToDo: would be nice if Inode::Create() would let the INODE_NOT_READY 971 // flag set until here, so that it can be accessed directly 972 973 // Inode::Create() left the inode locked in memory 974 put_vnode(volume->ID(), id); 975 976 if (status == B_OK) { 977 transaction.Done(); 978 979 notify_listener(B_ENTRY_CREATED, volume->ID(), directory->ID(), 0, id, name); 980 } 981 982 return status; 983 } 984 985 986 int 987 bfs_link(void *ns, void *dir, const char *name, void *node) 988 { 989 FUNCTION_START(("name = \"%s\"\n", name)); 990 991 // This one won't be implemented in a binary compatible BFS 992 993 return B_ERROR; 994 } 995 996 997 int 998 bfs_unlink(void *_ns, void *_directory, const char *name) 999 { 1000 FUNCTION_START(("name = \"%s\"\n", name)); 1001 1002 if (_ns == NULL || _directory == NULL || name == NULL || *name == '\0') 1003 return B_BAD_VALUE; 1004 if (!strcmp(name, "..") || !strcmp(name, ".")) 1005 return B_NOT_ALLOWED; 1006 1007 Volume *volume = (Volume *)_ns; 1008 Inode *directory = (Inode *)_directory; 1009 1010 status_t status = directory->CheckPermissions(W_OK); 1011 if (status < B_OK) 1012 return status; 1013 1014 #ifdef UNSAFE_GET_VNODE 1015 RecursiveLocker locker(volume->Lock()); 1016 #endif 1017 Transaction transaction(volume, directory->BlockNumber()); 1018 1019 off_t id; 1020 if ((status = directory->Remove(&transaction, name, &id)) == B_OK) { 1021 transaction.Done(); 1022 1023 notify_listener(B_ENTRY_REMOVED, volume->ID(), directory->ID(), 0, id, NULL); 1024 } 1025 return status; 1026 } 1027 1028 1029 int 1030 bfs_rename(void *_ns, void *_oldDir, const char *oldName, void *_newDir, const char *newName) 1031 { 1032 FUNCTION_START(("oldDir = %p, oldName = \"%s\", newDir = %p, newName = \"%s\"\n", _oldDir, oldName, _newDir, newName)); 1033 1034 // there might be some more tests needed?! 1035 if (_ns == NULL || _oldDir == NULL || _newDir == NULL 1036 || oldName == NULL || *oldName == '\0' 1037 || newName == NULL || *newName == '\0' 1038 || !strcmp(oldName, ".") || !strcmp(oldName, "..") 1039 || !strcmp(newName, ".") || !strcmp(newName, "..") 1040 || strchr(newName, '/') != NULL) 1041 RETURN_ERROR(B_BAD_VALUE); 1042 1043 Volume *volume = (Volume *)_ns; 1044 Inode *oldDirectory = (Inode *)_oldDir; 1045 Inode *newDirectory = (Inode *)_newDir; 1046 1047 // are we already done? 1048 if (oldDirectory == newDirectory && !strcmp(oldName, newName)) 1049 return B_OK; 1050 1051 RecursiveLocker locker(volume->Lock()); 1052 1053 // get the directory's tree, and a pointer to the inode which should be changed 1054 BPlusTree *tree; 1055 status_t status = oldDirectory->GetTree(&tree); 1056 if (status < B_OK) 1057 RETURN_ERROR(status); 1058 1059 off_t id; 1060 status = tree->Find((const uint8 *)oldName, strlen(oldName), &id); 1061 if (status < B_OK) 1062 RETURN_ERROR(status); 1063 1064 Vnode vnode(volume, id); 1065 Inode *inode; 1066 if (vnode.Get(&inode) < B_OK) 1067 return B_IO_ERROR; 1068 1069 // Don't move a directory into one of its children - we soar up 1070 // from the newDirectory to either the root node or the old 1071 // directory, whichever comes first. 1072 // If we meet our inode on that way, we have to bail out. 1073 1074 if (oldDirectory != newDirectory) { 1075 vnode_id parent = volume->ToVnode(newDirectory->Parent()); 1076 vnode_id root = volume->RootNode()->ID(); 1077 1078 while (true) { 1079 if (parent == id) 1080 return B_BAD_VALUE; 1081 else if (parent == root || parent == oldDirectory->ID()) 1082 break; 1083 1084 Vnode vnode(volume, parent); 1085 Inode *parentNode; 1086 if (vnode.Get(&parentNode) < B_OK) 1087 return B_ERROR; 1088 1089 parent = volume->ToVnode(parentNode->Parent()); 1090 } 1091 } 1092 1093 // Everything okay? Then lets get to work... 1094 1095 Transaction transaction(volume, oldDirectory->BlockNumber()); 1096 1097 // First, try to make sure there is nothing that will stop us in 1098 // the target directory - since this is the only non-critical 1099 // failure, we will test this case first 1100 BPlusTree *newTree = tree; 1101 if (newDirectory != oldDirectory) { 1102 status = newDirectory->GetTree(&newTree); 1103 if (status < B_OK) 1104 RETURN_ERROR(status); 1105 } 1106 1107 status = newTree->Insert(&transaction, (const uint8 *)newName, strlen(newName), id); 1108 if (status == B_NAME_IN_USE) { 1109 // If there is already a file with that name, we have to remove 1110 // it, as long it's not a directory with files in it 1111 off_t clobber; 1112 if (newTree->Find((const uint8 *)newName, strlen(newName), &clobber) < B_OK) 1113 return B_NAME_IN_USE; 1114 if (clobber == id) 1115 return B_BAD_VALUE; 1116 1117 Vnode vnode(volume, clobber); 1118 Inode *other; 1119 if (vnode.Get(&other) < B_OK) 1120 return B_NAME_IN_USE; 1121 1122 status = newDirectory->Remove(&transaction, newName, NULL, other->IsDirectory()); 1123 if (status < B_OK) 1124 return status; 1125 1126 notify_listener(B_ENTRY_REMOVED, volume->ID(), newDirectory->ID(), 0, clobber, NULL); 1127 1128 status = newTree->Insert(&transaction, (const uint8 *)newName, strlen(newName), id); 1129 } 1130 if (status < B_OK) 1131 return status; 1132 1133 // If anything fails now, we have to remove the inode from the 1134 // new directory in any case to restore the previous state 1135 status_t bailStatus = B_OK; 1136 1137 // update the name only when they differ 1138 bool nameUpdated = false; 1139 if (strcmp(oldName, newName)) { 1140 status = inode->SetName(&transaction, newName); 1141 if (status == B_OK) { 1142 Index index(volume); 1143 index.UpdateName(&transaction, oldName, newName, inode); 1144 nameUpdated = true; 1145 } 1146 } 1147 1148 if (status == B_OK) { 1149 status = tree->Remove(&transaction, (const uint8 *)oldName, strlen(oldName), id); 1150 if (status == B_OK) { 1151 inode->Node()->parent = newDirectory->BlockRun(); 1152 1153 // if it's a directory, update the parent directory pointer 1154 // in its tree if necessary 1155 BPlusTree *movedTree = NULL; 1156 if (oldDirectory != newDirectory 1157 && inode->IsDirectory() 1158 && (status = inode->GetTree(&movedTree)) == B_OK) 1159 status = movedTree->Replace(&transaction, (const uint8 *)"..", 2, newDirectory->ID()); 1160 1161 if (status == B_OK) { 1162 status = inode->WriteBack(&transaction); 1163 if (status == B_OK) { 1164 transaction.Done(); 1165 1166 notify_listener(B_ENTRY_MOVED, volume->ID(), oldDirectory->ID(), 1167 newDirectory->ID(), id, newName); 1168 return B_OK; 1169 } 1170 } 1171 // Those better don't fail, or we switch to a read-only 1172 // device for safety reasons (Volume::Panic() does this 1173 // for us) 1174 // Anyway, if we overwrote a file in the target directory 1175 // this is lost now (only in-memory, not on-disk)... 1176 bailStatus = tree->Insert(&transaction, (const uint8 *)oldName, strlen(oldName), id); 1177 if (movedTree != NULL) 1178 movedTree->Replace(&transaction, (const uint8 *)"..", 2, oldDirectory->ID()); 1179 } 1180 } 1181 if (bailStatus == B_OK && nameUpdated) 1182 bailStatus = inode->SetName(&transaction, oldName); 1183 1184 if (bailStatus == B_OK) 1185 bailStatus = newTree->Remove(&transaction, (const uint8 *)newName, strlen(newName), id); 1186 1187 if (bailStatus < B_OK) 1188 volume->Panic(); 1189 1190 return status; 1191 } 1192 1193 1194 /** Opens the file with the specified mode. 1195 */ 1196 1197 static int 1198 bfs_open(void *_ns, void *_node, int omode, void **_cookie) 1199 { 1200 FUNCTION(); 1201 if (_ns == NULL || _node == NULL || _cookie == NULL) 1202 RETURN_ERROR(B_BAD_VALUE); 1203 1204 Volume *volume = (Volume *)_ns; 1205 Inode *inode = (Inode *)_node; 1206 1207 // we can't open a file which uses uncached access twice 1208 if (inode->Flags() & INODE_NO_CACHE) 1209 return B_BUSY; 1210 1211 // opening a directory read-only is allowed, although you can't read 1212 // any data from it. 1213 if (inode->IsDirectory() && omode & O_RWMASK) { 1214 omode = omode & ~O_RWMASK; 1215 // ToDo: for compatibility reasons, we don't return an error here... 1216 // e.g. "copyattr" tries to do that 1217 //return B_IS_A_DIRECTORY; 1218 } 1219 1220 status_t status = inode->CheckPermissions(oModeToAccess(omode)); 1221 if (status < B_OK) 1222 RETURN_ERROR(status); 1223 1224 // we could actually use the cookie to keep track of: 1225 // - the last block_run 1226 // - the location in the data_stream (indirect, double indirect, 1227 // position in block_run array) 1228 // 1229 // This could greatly speed up continuous reads of big files, especially 1230 // in the indirect block section. 1231 1232 file_cookie *cookie = (file_cookie *)malloc(sizeof(file_cookie)); 1233 if (cookie == NULL) 1234 RETURN_ERROR(B_NO_MEMORY); 1235 1236 // initialize the cookie 1237 cookie->open_mode = omode; 1238 // needed by e.g. bfs_write() for O_APPEND 1239 cookie->last_size = inode->Size(); 1240 cookie->last_notification = system_time(); 1241 1242 // Should we truncate the file? 1243 if (omode & O_TRUNC) { 1244 WriteLocked locked(inode->Lock()); 1245 Transaction transaction(volume, inode->BlockNumber()); 1246 1247 status_t status = inode->SetFileSize(&transaction, 0); 1248 if (status < B_OK) { 1249 // bfs_free_cookie() is only called if this function is successful 1250 free(cookie); 1251 return status; 1252 } 1253 1254 transaction.Done(); 1255 } 1256 1257 *_cookie = cookie; 1258 return B_OK; 1259 } 1260 1261 1262 /** Read a file specified by node, using information in cookie 1263 * and at offset specified by pos. read len bytes into buffer buf. 1264 */ 1265 1266 static int 1267 bfs_read(void *_ns, void *_node, void *_cookie, off_t pos, void *buffer, size_t *_length) 1268 { 1269 //FUNCTION(); 1270 Inode *inode = (Inode *)_node; 1271 1272 if (!inode->HasUserAccessableStream()) { 1273 *_length = 0; 1274 RETURN_ERROR(B_BAD_VALUE); 1275 } 1276 1277 ReadLocked locked(inode->Lock()); 1278 return inode->ReadAt(pos, (uint8 *)buffer, _length); 1279 } 1280 1281 1282 int 1283 bfs_write(void *_ns, void *_node, void *_cookie, off_t pos, const void *buffer, size_t *_length) 1284 { 1285 //FUNCTION(); 1286 // uncomment to be more robust against a buggy vnode layer ;-) 1287 //if (_ns == NULL || _node == NULL || _cookie == NULL) 1288 // return B_BAD_VALUE; 1289 1290 Volume *volume = (Volume *)_ns; 1291 Inode *inode = (Inode *)_node; 1292 1293 if (!inode->HasUserAccessableStream()) { 1294 *_length = 0; 1295 RETURN_ERROR(B_BAD_VALUE); 1296 } 1297 1298 file_cookie *cookie = (file_cookie *)_cookie; 1299 1300 if (cookie->open_mode & O_APPEND) 1301 pos = inode->Size(); 1302 1303 WriteLocked locked(inode->Lock()); 1304 if (locked.IsLocked() < B_OK) 1305 RETURN_ERROR(B_ERROR); 1306 1307 Transaction transaction; 1308 // We are not starting the transaction here, since 1309 // it might not be needed at all (the contents of 1310 // regular files aren't logged) 1311 1312 status_t status = inode->WriteAt(&transaction, pos, (const uint8 *)buffer, _length); 1313 1314 if (status == B_OK) 1315 transaction.Done(); 1316 1317 if (status == B_OK && (inode->Flags() & INODE_NO_CACHE) == 0) { 1318 // uncached files don't cause notifications during access, and 1319 // never want to write back any cached blocks 1320 1321 // periodically notify if the file size has changed 1322 // ToDo: should we better test for a change in the last_modified time only? 1323 if (cookie->last_size != inode->Size() 1324 && system_time() > cookie->last_notification + INODE_NOTIFICATION_INTERVAL) { 1325 notify_listener(B_STAT_CHANGED, volume->ID(), 0, 0, inode->ID(), NULL); 1326 cookie->last_size = inode->Size(); 1327 cookie->last_notification = system_time(); 1328 } 1329 1330 // This will flush the dirty blocks to disk from time to time. 1331 // It's done here and not in Inode::WriteAt() so that it won't 1332 // add to the duration of a transaction - it might even be a 1333 // good idea to offload those calls to another thread 1334 volume->WriteCachedBlocksIfNecessary(); 1335 } 1336 1337 return status; 1338 } 1339 1340 1341 /** Do whatever is necessary to close a file, EXCEPT for freeing 1342 * the cookie! 1343 */ 1344 1345 static int 1346 bfs_close(void *_ns, void *_node, void *_cookie) 1347 { 1348 FUNCTION(); 1349 if (_ns == NULL || _node == NULL || _cookie == NULL) 1350 return B_BAD_VALUE; 1351 1352 file_cookie *cookie = (file_cookie *)_cookie; 1353 1354 Volume *volume = (Volume *)_ns; 1355 Inode *inode = (Inode *)_node; 1356 1357 if (cookie->open_mode & O_RWMASK) { 1358 #ifdef UNSAFE_GET_VNODE 1359 RecursiveLocker locker(volume->Lock()); 1360 #endif 1361 ReadLocked locked(inode->Lock()); 1362 1363 // trim the preallocated blocks and update the size, 1364 // and last_modified indices if needed 1365 1366 Transaction transaction(volume, inode->BlockNumber()); 1367 status_t status = B_OK; 1368 bool changed = false; 1369 Index index(volume); 1370 1371 if (inode->OldSize() != inode->Size()) { 1372 status = inode->Trim(&transaction); 1373 if (status < B_OK) 1374 FATAL(("Could not trim preallocated blocks!")); 1375 1376 index.UpdateSize(&transaction, inode); 1377 changed = true; 1378 } 1379 if (inode->OldLastModified() != inode->LastModified()) { 1380 index.UpdateLastModified(&transaction, inode, inode->LastModified()); 1381 changed = true; 1382 1383 // updating the index doesn't write back the inode 1384 inode->WriteBack(&transaction); 1385 } 1386 1387 if (status == B_OK) 1388 transaction.Done(); 1389 1390 if (changed) 1391 notify_listener(B_STAT_CHANGED, volume->ID(), 0, 0, inode->ID(), NULL); 1392 } 1393 1394 return B_OK; 1395 } 1396 1397 1398 static int 1399 bfs_free_cookie(void *_ns, void *_node, void *_cookie) 1400 { 1401 FUNCTION(); 1402 1403 if (_ns == NULL || _node == NULL || _cookie == NULL) 1404 return B_BAD_VALUE; 1405 1406 file_cookie *cookie = (file_cookie *)_cookie; 1407 1408 Volume *volume = (Volume *)_ns; 1409 Inode *inode = (Inode *)_node; 1410 1411 if (cookie != NULL) 1412 free(cookie); 1413 1414 if (inode->Flags() & INODE_NO_CACHE) { 1415 volume->Pool().ReleaseBuffers(); 1416 inode->Node()->flags &= HOST_ENDIAN_TO_BFS_INT32(~INODE_NO_CACHE); 1417 // We don't need to save the inode, because INODE_NO_CACHE is a 1418 // non-permanent flag which will be removed when the inode is loaded 1419 // into memory. 1420 } 1421 if (inode->Flags() & INODE_CHKBFS_RUNNING) { 1422 // "chkbfs" exited abnormally, so we have to stop it here... 1423 FATAL(("check process was aborted!\n")); 1424 volume->Allocator().StopChecking(NULL); 1425 } 1426 1427 return B_OK; 1428 } 1429 1430 1431 /** Checks access permissions, return B_NOT_ALLOWED if the action 1432 * is not allowed. 1433 */ 1434 1435 static int 1436 bfs_access(void *_ns, void *_node, int accessMode) 1437 { 1438 FUNCTION(); 1439 1440 if (_ns == NULL || _node == NULL) 1441 return B_BAD_VALUE; 1442 1443 Inode *inode = (Inode *)_node; 1444 status_t status = inode->CheckPermissions(accessMode); 1445 if (status < B_OK) 1446 RETURN_ERROR(status); 1447 1448 return B_OK; 1449 } 1450 1451 1452 static int 1453 bfs_read_link(void *_ns, void *_node, char *buffer, size_t *bufferSize) 1454 { 1455 FUNCTION(); 1456 1457 Inode *inode = (Inode *)_node; 1458 1459 if (!inode->IsSymLink()) 1460 RETURN_ERROR(B_BAD_VALUE); 1461 1462 if (inode->Flags() & INODE_LONG_SYMLINK) { 1463 status_t status = inode->ReadAt(0, (uint8 *)buffer, bufferSize); 1464 if (status < B_OK) 1465 RETURN_ERROR(status); 1466 1467 *bufferSize = inode->Size(); 1468 return B_OK; 1469 } 1470 1471 size_t numBytes = strlen((char *)&inode->Node()->short_symlink); 1472 uint32 bytes = numBytes; 1473 if (bytes > *bufferSize) 1474 bytes = *bufferSize; 1475 1476 memcpy(buffer, inode->Node()->short_symlink, bytes); 1477 *bufferSize = numBytes; 1478 1479 return B_OK; 1480 } 1481 1482 1483 // #pragma mark - 1484 // Directory functions 1485 1486 1487 static int 1488 bfs_mkdir(void *_ns, void *_directory, const char *name, int mode) 1489 { 1490 FUNCTION_START(("name = \"%s\", perms = %d\n", name, mode)); 1491 1492 if (_ns == NULL || _directory == NULL 1493 || name == NULL || *name == '\0') 1494 RETURN_ERROR(B_BAD_VALUE); 1495 1496 Volume *volume = (Volume *)_ns; 1497 Inode *directory = (Inode *)_directory; 1498 1499 if (!directory->IsDirectory()) 1500 RETURN_ERROR(B_BAD_TYPE); 1501 1502 status_t status = directory->CheckPermissions(W_OK); 1503 if (status < B_OK) 1504 RETURN_ERROR(status); 1505 1506 #ifdef UNSAFE_GET_VNODE 1507 RecursiveLocker locker(volume->Lock()); 1508 #endif 1509 Transaction transaction(volume, directory->BlockNumber()); 1510 1511 // Inode::Create() locks the inode if we pass the "id" parameter, but we 1512 // need it anyway 1513 off_t id; 1514 status = Inode::Create(&transaction, directory, name, S_DIRECTORY | (mode & S_IUMSK), 0, 0, &id); 1515 if (status == B_OK) { 1516 put_vnode(volume->ID(), id); 1517 transaction.Done(); 1518 1519 notify_listener(B_ENTRY_CREATED, volume->ID(), directory->ID(), 0, id, name); 1520 } 1521 1522 return status; 1523 } 1524 1525 1526 static int 1527 bfs_rmdir(void *_ns, void *_directory, const char *name) 1528 { 1529 FUNCTION_START(("name = \"%s\"\n", name)); 1530 1531 if (_ns == NULL || _directory == NULL || name == NULL || *name == '\0') 1532 return B_BAD_VALUE; 1533 1534 Volume *volume = (Volume *)_ns; 1535 Inode *directory = (Inode *)_directory; 1536 1537 #ifdef UNSAFE_GET_VNODE 1538 RecursiveLocker locker(volume->Lock()); 1539 #endif 1540 Transaction transaction(volume, directory->BlockNumber()); 1541 1542 off_t id; 1543 status_t status = directory->Remove(&transaction, name, &id, true); 1544 if (status == B_OK) { 1545 transaction.Done(); 1546 1547 notify_listener(B_ENTRY_REMOVED, volume->ID(), directory->ID(), 0, id, NULL); 1548 } 1549 1550 return status; 1551 } 1552 1553 1554 /** creates fs-specific "cookie" struct that keeps track of where 1555 * you are at in reading through directory entries in bfs_readdir. 1556 */ 1557 1558 static int 1559 bfs_open_dir(void *_ns, void *_node, void **_cookie) 1560 { 1561 FUNCTION(); 1562 1563 if (_ns == NULL || _node == NULL || _cookie == NULL) 1564 RETURN_ERROR(B_BAD_VALUE); 1565 1566 Inode *inode = (Inode *)_node; 1567 1568 // we don't ask here for directories only, because the bfs_open_index_dir() 1569 // function utilizes us (so we must be able to open indices as well) 1570 if (!inode->IsContainer()) 1571 RETURN_ERROR(B_BAD_VALUE); 1572 1573 BPlusTree *tree; 1574 if (inode->GetTree(&tree) != B_OK) 1575 RETURN_ERROR(B_BAD_VALUE); 1576 1577 TreeIterator *iterator = new TreeIterator(tree); 1578 if (iterator == NULL) 1579 RETURN_ERROR(B_NO_MEMORY); 1580 1581 *_cookie = iterator; 1582 return B_OK; 1583 } 1584 1585 1586 static int 1587 bfs_read_dir(void *_ns, void *_node, void *_cookie, long *num, 1588 struct dirent *dirent, size_t bufferSize) 1589 { 1590 FUNCTION(); 1591 1592 TreeIterator *iterator = (TreeIterator *)_cookie; 1593 if (iterator == NULL) 1594 RETURN_ERROR(B_BAD_VALUE); 1595 1596 uint16 length; 1597 vnode_id id; 1598 status_t status = iterator->GetNextEntry(dirent->d_name, &length, bufferSize, &id); 1599 if (status == B_ENTRY_NOT_FOUND) { 1600 *num = 0; 1601 return B_OK; 1602 } else if (status != B_OK) 1603 RETURN_ERROR(status); 1604 1605 Volume *volume = (Volume *)_ns; 1606 1607 dirent->d_dev = volume->ID(); 1608 dirent->d_ino = id; 1609 1610 #ifdef KEEP_WRONG_DIRENT_RECLEN 1611 dirent->d_reclen = length; 1612 #else 1613 dirent->d_reclen = sizeof(struct dirent) + length; 1614 #endif 1615 1616 *num = 1; 1617 return B_OK; 1618 } 1619 1620 1621 /** Sets the TreeIterator back to the beginning of the directory 1622 */ 1623 1624 static int 1625 bfs_rewind_dir(void * /*ns*/, void * /*node*/, void *_cookie) 1626 { 1627 FUNCTION(); 1628 TreeIterator *iterator = (TreeIterator *)_cookie; 1629 1630 if (iterator == NULL) 1631 RETURN_ERROR(B_BAD_VALUE); 1632 1633 return iterator->Rewind(); 1634 } 1635 1636 1637 static int 1638 bfs_close_dir(void * /*ns*/, void * /*node*/, void * /*_cookie*/) 1639 { 1640 FUNCTION(); 1641 // Do whatever you need to to close a directory, but DON'T free the cookie! 1642 return B_OK; 1643 } 1644 1645 1646 static int 1647 bfs_free_dir_cookie(void *ns, void *node, void *_cookie) 1648 { 1649 TreeIterator *iterator = (TreeIterator *)_cookie; 1650 1651 if (iterator == NULL) 1652 RETURN_ERROR(B_BAD_VALUE); 1653 1654 delete iterator; 1655 return B_OK; 1656 } 1657 1658 1659 // #pragma mark - 1660 // Attribute functions 1661 1662 1663 static int 1664 bfs_open_attrdir(void *_ns, void *_node, void **cookie) 1665 { 1666 FUNCTION(); 1667 1668 Inode *inode = (Inode *)_node; 1669 if (inode == NULL || inode->Node() == NULL) 1670 RETURN_ERROR(B_ERROR); 1671 1672 AttributeIterator *iterator = new AttributeIterator(inode); 1673 if (iterator == NULL) 1674 RETURN_ERROR(B_NO_MEMORY); 1675 1676 *cookie = iterator; 1677 return B_OK; 1678 } 1679 1680 1681 static int 1682 bfs_close_attrdir(void *ns, void *node, void *cookie) 1683 { 1684 FUNCTION(); 1685 return B_OK; 1686 } 1687 1688 1689 static int 1690 bfs_free_attrdir_cookie(void *ns, void *node, void *_cookie) 1691 { 1692 FUNCTION(); 1693 AttributeIterator *iterator = (AttributeIterator *)_cookie; 1694 1695 if (iterator == NULL) 1696 RETURN_ERROR(B_BAD_VALUE); 1697 1698 delete iterator; 1699 return B_OK; 1700 } 1701 1702 1703 static int 1704 bfs_rewind_attrdir(void *_ns, void *_node, void *_cookie) 1705 { 1706 FUNCTION(); 1707 1708 AttributeIterator *iterator = (AttributeIterator *)_cookie; 1709 if (iterator == NULL) 1710 RETURN_ERROR(B_BAD_VALUE); 1711 1712 RETURN_ERROR(iterator->Rewind()); 1713 } 1714 1715 1716 static int 1717 bfs_read_attrdir(void *_ns, void *node, void *_cookie, long *num, struct dirent *dirent, size_t bufsize) 1718 { 1719 FUNCTION(); 1720 AttributeIterator *iterator = (AttributeIterator *)_cookie; 1721 1722 if (iterator == NULL) 1723 RETURN_ERROR(B_BAD_VALUE); 1724 1725 uint32 type; 1726 size_t length; 1727 status_t status = iterator->GetNext(dirent->d_name, &length, &type, &dirent->d_ino); 1728 if (status == B_ENTRY_NOT_FOUND) { 1729 *num = 0; 1730 return B_OK; 1731 } else if (status != B_OK) 1732 RETURN_ERROR(status); 1733 1734 Volume *volume = (Volume *)_ns; 1735 1736 dirent->d_dev = volume->ID(); 1737 #ifdef KEEP_WRONG_DIRENT_RECLEN 1738 dirent->d_reclen = length; 1739 #else 1740 dirent->d_reclen = sizeof(struct dirent) + length; 1741 #endif 1742 1743 *num = 1; 1744 return B_OK; 1745 } 1746 1747 1748 static int 1749 bfs_remove_attr(void *_ns, void *_node, const char *name) 1750 { 1751 FUNCTION_START(("name = \"%s\"\n", name)); 1752 1753 if (_ns == NULL || _node == NULL || name == NULL) 1754 return B_BAD_VALUE; 1755 1756 Volume *volume = (Volume *)_ns; 1757 Inode *inode = (Inode *)_node; 1758 1759 status_t status = inode->CheckPermissions(W_OK); 1760 if (status < B_OK) 1761 return status; 1762 1763 #ifdef UNSAFE_GET_VNODE 1764 RecursiveLocker locker(volume->Lock()); 1765 #endif 1766 Transaction transaction(volume, inode->BlockNumber()); 1767 1768 status = inode->RemoveAttribute(&transaction, name); 1769 if (status == B_OK) { 1770 transaction.Done(); 1771 1772 notify_listener(B_ATTR_CHANGED, volume->ID(), 0, 0, inode->ID(), name); 1773 } 1774 1775 RETURN_ERROR(status); 1776 } 1777 1778 1779 static int 1780 bfs_rename_attr(void *ns, void *node, const char *oldname, const char *newname) 1781 { 1782 FUNCTION_START(("name = \"%s\", to = \"%s\"\n", oldname, newname)); 1783 1784 // ToDo: implement bfs_rename_attr()! 1785 // I'll skip the implementation here, and will do it for OpenBeOS - at least 1786 // there will be an API to move one attribute to another file, making that 1787 // function much more complicated - oh joy ;-) 1788 1789 RETURN_ERROR(B_ENTRY_NOT_FOUND); 1790 } 1791 1792 1793 static int 1794 bfs_stat_attr(void *ns, void *_node, const char *name, struct attr_info *attrInfo) 1795 { 1796 FUNCTION_START(("name = \"%s\"\n", name)); 1797 1798 Inode *inode = (Inode *)_node; 1799 if (inode == NULL || inode->Node() == NULL) 1800 RETURN_ERROR(B_ERROR); 1801 1802 // first, try to find it in the small data region 1803 small_data *smallData = NULL; 1804 if (inode->SmallDataLock().Lock() == B_OK) { 1805 if ((smallData = inode->FindSmallData((const char *)name)) != NULL) { 1806 attrInfo->type = smallData->Type(); 1807 attrInfo->size = smallData->DataSize(); 1808 } 1809 inode->SmallDataLock().Unlock(); 1810 } 1811 if (smallData != NULL) 1812 return B_OK; 1813 1814 // then, search in the attribute directory 1815 Inode *attribute; 1816 status_t status = inode->GetAttribute(name, &attribute); 1817 if (status == B_OK) { 1818 attrInfo->type = attribute->Type(); 1819 attrInfo->size = attribute->Size(); 1820 1821 inode->ReleaseAttribute(attribute); 1822 return B_OK; 1823 } 1824 1825 RETURN_ERROR(status); 1826 } 1827 1828 1829 static int 1830 bfs_write_attr(void *_ns, void *_node, const char *name, int type, const void *buffer, 1831 size_t *_length, off_t pos) 1832 { 1833 FUNCTION_START(("name = \"%s\"\n", name)); 1834 if (_ns == NULL || _node == NULL || name == NULL || *name == '\0') 1835 RETURN_ERROR(B_BAD_VALUE); 1836 1837 // Writing the name attribute using this function is not allowed, 1838 // also using the reserved indices name, last_modified, and size 1839 // shouldn't be allowed. 1840 // ToDo: we might think about allowing to update those values, but 1841 // really change their corresponding values in the bfs_inode structure 1842 if (name[0] == FILE_NAME_NAME && name[1] == '\0' 1843 || !strcmp(name, "name") 1844 || !strcmp(name, "last_modified") 1845 || !strcmp(name, "size")) 1846 RETURN_ERROR(B_NOT_ALLOWED); 1847 1848 Volume *volume = (Volume *)_ns; 1849 Inode *inode = (Inode *)_node; 1850 1851 status_t status = inode->CheckPermissions(W_OK); 1852 if (status < B_OK) 1853 return status; 1854 1855 #ifdef UNSAFE_GET_VNODE 1856 RecursiveLocker locker(volume->Lock()); 1857 #endif 1858 Transaction transaction(volume, inode->BlockNumber()); 1859 1860 status = inode->WriteAttribute(&transaction, name, type, pos, (const uint8 *)buffer, _length); 1861 if (status == B_OK) { 1862 transaction.Done(); 1863 1864 notify_listener(B_ATTR_CHANGED, volume->ID(), 0, 0, inode->ID(), name); 1865 } 1866 1867 return status; 1868 } 1869 1870 1871 static int 1872 bfs_read_attr(void *_ns, void *_node, const char *name, int type, void *buffer, 1873 size_t *_length, off_t pos) 1874 { 1875 FUNCTION(); 1876 Inode *inode = (Inode *)_node; 1877 1878 if (inode == NULL || name == NULL || *name == '\0' || buffer == NULL) 1879 RETURN_ERROR(B_BAD_VALUE); 1880 1881 status_t status = inode->CheckPermissions(R_OK); 1882 if (status < B_OK) 1883 return status; 1884 1885 return inode->ReadAttribute(name, type, pos, (uint8 *)buffer, _length); 1886 } 1887 1888 1889 // #pragma mark - 1890 // Index functions 1891 1892 1893 static int 1894 bfs_open_indexdir(void *_ns, void **_cookie) 1895 { 1896 FUNCTION(); 1897 if (_ns == NULL || _cookie == NULL) 1898 RETURN_ERROR(B_BAD_VALUE); 1899 1900 Volume *volume = (Volume *)_ns; 1901 1902 if (volume->IndicesNode() == NULL) 1903 RETURN_ERROR(B_ENTRY_NOT_FOUND); 1904 1905 // Since the indices root node is just a directory, and we are storing 1906 // a pointer to it in our Volume object, we can just use the directory 1907 // traversal functions. 1908 // In fact we're storing it in the Volume object for that reason. 1909 1910 RETURN_ERROR(bfs_open_dir(_ns, volume->IndicesNode(), _cookie)); 1911 } 1912 1913 1914 static int 1915 bfs_close_indexdir(void *_ns, void *_cookie) 1916 { 1917 FUNCTION(); 1918 if (_ns == NULL || _cookie == NULL) 1919 RETURN_ERROR(B_BAD_VALUE); 1920 1921 Volume *volume = (Volume *)_ns; 1922 RETURN_ERROR(bfs_close_dir(_ns, volume->IndicesNode(), _cookie)); 1923 } 1924 1925 1926 static int 1927 bfs_free_indexdir_cookie(void *_ns, void *_node, void *_cookie) 1928 { 1929 FUNCTION(); 1930 if (_ns == NULL || _cookie == NULL) 1931 RETURN_ERROR(B_BAD_VALUE); 1932 1933 Volume *volume = (Volume *)_ns; 1934 RETURN_ERROR(bfs_free_dir_cookie(_ns, volume->IndicesNode(), _cookie)); 1935 } 1936 1937 1938 static int 1939 bfs_rewind_indexdir(void *_ns, void *_cookie) 1940 { 1941 FUNCTION(); 1942 if (_ns == NULL || _cookie == NULL) 1943 RETURN_ERROR(B_BAD_VALUE); 1944 1945 Volume *volume = (Volume *)_ns; 1946 RETURN_ERROR(bfs_rewind_dir(_ns, volume->IndicesNode(), _cookie)); 1947 } 1948 1949 1950 static int 1951 bfs_read_indexdir(void *_ns, void *_cookie, long *num, struct dirent *dirent, size_t bufferSize) 1952 { 1953 FUNCTION(); 1954 if (_ns == NULL || _cookie == NULL) 1955 RETURN_ERROR(B_BAD_VALUE); 1956 1957 Volume *volume = (Volume *)_ns; 1958 RETURN_ERROR(bfs_read_dir(_ns, volume->IndicesNode(), _cookie, num, dirent, bufferSize)); 1959 } 1960 1961 1962 static int 1963 bfs_create_index(void *_ns, const char *name, int type, int flags) 1964 { 1965 FUNCTION_START(("name = \"%s\", type = %d, flags = %d\n", name, type, flags)); 1966 if (_ns == NULL || name == NULL || *name == '\0') 1967 return B_BAD_VALUE; 1968 1969 Volume *volume = (Volume *)_ns; 1970 1971 if (volume->IsReadOnly()) 1972 return B_READ_ONLY_DEVICE; 1973 1974 // only root users are allowed to create indices 1975 if (geteuid() != 0) 1976 return B_NOT_ALLOWED; 1977 1978 #ifdef UNSAFE_GET_VNODE 1979 RecursiveLocker locker(volume->Lock()); 1980 #endif 1981 Transaction transaction(volume, volume->Indices()); 1982 1983 Index index(volume); 1984 status_t status = index.Create(&transaction, name, type); 1985 1986 if (status == B_OK) 1987 transaction.Done(); 1988 1989 RETURN_ERROR(status); 1990 } 1991 1992 1993 static int 1994 bfs_remove_index(void *_ns, const char *name) 1995 { 1996 FUNCTION(); 1997 if (_ns == NULL || name == NULL || *name == '\0') 1998 return B_BAD_VALUE; 1999 2000 Volume *volume = (Volume *)_ns; 2001 2002 if (volume->IsReadOnly()) 2003 return B_READ_ONLY_DEVICE; 2004 2005 // only root users are allowed to remove indices 2006 if (geteuid() != 0) 2007 return B_NOT_ALLOWED; 2008 2009 Inode *indices; 2010 if ((indices = volume->IndicesNode()) == NULL) 2011 return B_ENTRY_NOT_FOUND; 2012 2013 #ifdef UNSAFE_GET_VNODE 2014 RecursiveLocker locker(volume->Lock()); 2015 #endif 2016 Transaction transaction(volume, volume->Indices()); 2017 2018 status_t status = indices->Remove(&transaction, name); 2019 if (status == B_OK) 2020 transaction.Done(); 2021 2022 RETURN_ERROR(status); 2023 } 2024 2025 2026 static int 2027 bfs_rename_index(void *ns, const char *oldname, const char *newname) 2028 { 2029 FUNCTION_START(("from = %s, to = %s\n", oldname, newname)); 2030 2031 // Well, renaming an index doesn't make that much sense, as you 2032 // would also need to remove every file in it (or the index 2033 // would contain wrong data) 2034 // But in that case, you can better remove the old one, and 2035 // create a new one... 2036 // There is also no way to call this function from a userland 2037 // application. 2038 2039 RETURN_ERROR(B_ENTRY_NOT_FOUND); 2040 } 2041 2042 2043 static int 2044 bfs_stat_index(void *_ns, const char *name, struct index_info *indexInfo) 2045 { 2046 FUNCTION_START(("name = %s\n", name)); 2047 if (_ns == NULL || name == NULL || indexInfo == NULL) 2048 RETURN_ERROR(B_BAD_VALUE); 2049 2050 Volume *volume = (Volume *)_ns; 2051 Index index(volume); 2052 status_t status = index.SetTo(name); 2053 if (status < B_OK) 2054 RETURN_ERROR(status); 2055 2056 bfs_inode *node = index.Node()->Node(); 2057 2058 indexInfo->type = index.Type(); 2059 indexInfo->size = node->data.Size(); 2060 indexInfo->modification_time = (time_t)(node->LastModifiedTime() >> INODE_TIME_SHIFT); 2061 indexInfo->creation_time = (time_t)(node->CreateTime() >> INODE_TIME_SHIFT); 2062 indexInfo->uid = node->UserID(); 2063 indexInfo->gid = node->GroupID(); 2064 2065 return B_OK; 2066 } 2067 2068 2069 // #pragma mark - 2070 // Query functions 2071 2072 2073 static int 2074 bfs_open_query(void *_ns, const char *queryString, ulong flags, port_id port, 2075 long token, void **cookie) 2076 { 2077 FUNCTION(); 2078 if (_ns == NULL || queryString == NULL || cookie == NULL) 2079 RETURN_ERROR(B_BAD_VALUE); 2080 2081 PRINT(("query = \"%s\", flags = %lu, port_id = %ld, token = %ld\n", queryString, flags, port, token)); 2082 2083 Volume *volume = (Volume *)_ns; 2084 2085 Expression *expression = new Expression((char *)queryString); 2086 if (expression == NULL) 2087 RETURN_ERROR(B_NO_MEMORY); 2088 2089 if (expression->InitCheck() < B_OK) { 2090 FATAL(("Could not parse query, stopped at: \"%s\"\n", expression->Position())); 2091 delete expression; 2092 RETURN_ERROR(B_BAD_VALUE); 2093 } 2094 2095 Query *query = new Query(volume, expression, flags); 2096 if (query == NULL) { 2097 delete expression; 2098 RETURN_ERROR(B_NO_MEMORY); 2099 } 2100 2101 if (flags & B_LIVE_QUERY) 2102 query->SetLiveMode(port, token); 2103 2104 *cookie = (void *)query; 2105 2106 return B_OK; 2107 } 2108 2109 2110 static int 2111 bfs_close_query(void *ns, void *cookie) 2112 { 2113 FUNCTION(); 2114 return B_OK; 2115 } 2116 2117 2118 static int 2119 bfs_free_query_cookie(void *ns, void *node, void *cookie) 2120 { 2121 FUNCTION(); 2122 if (cookie == NULL) 2123 RETURN_ERROR(B_BAD_VALUE); 2124 2125 Query *query = (Query *)cookie; 2126 Expression *expression = query->GetExpression(); 2127 delete query; 2128 delete expression; 2129 2130 return B_OK; 2131 } 2132 2133 2134 static int 2135 bfs_read_query(void */*ns*/, void *cookie, long *num, struct dirent *dirent, size_t bufferSize) 2136 { 2137 FUNCTION(); 2138 Query *query = (Query *)cookie; 2139 if (query == NULL) 2140 RETURN_ERROR(B_BAD_VALUE); 2141 2142 status_t status = query->GetNextEntry(dirent, bufferSize); 2143 if (status == B_OK) 2144 *num = 1; 2145 else if (status == B_ENTRY_NOT_FOUND) 2146 *num = 0; 2147 else 2148 return status; 2149 2150 return B_OK; 2151 } 2152 2153