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 <util/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 BFS_REPLACEMENT 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 return B_OK; 1353 } 1354 1355 1356 static int 1357 bfs_free_cookie(void *_ns, void *_node, void *_cookie) 1358 { 1359 FUNCTION(); 1360 1361 if (_ns == NULL || _node == NULL || _cookie == NULL) 1362 return B_BAD_VALUE; 1363 1364 file_cookie *cookie = (file_cookie *)_cookie; 1365 1366 Volume *volume = (Volume *)_ns; 1367 Inode *inode = (Inode *)_node; 1368 1369 if (cookie->open_mode & O_RWMASK) { 1370 #ifdef UNSAFE_GET_VNODE 1371 RecursiveLocker locker(volume->Lock()); 1372 #endif 1373 ReadLocked locked(inode->Lock()); 1374 1375 // trim the preallocated blocks and update the size, 1376 // and last_modified indices if needed 1377 1378 Transaction transaction(volume, inode->BlockNumber()); 1379 status_t status = B_OK; 1380 bool changed = false; 1381 Index index(volume); 1382 1383 if (inode->OldSize() != inode->Size()) { 1384 status = inode->Trim(&transaction); 1385 if (status < B_OK) 1386 FATAL(("Could not trim preallocated blocks!")); 1387 1388 index.UpdateSize(&transaction, inode); 1389 changed = true; 1390 } 1391 if (inode->OldLastModified() != inode->LastModified()) { 1392 index.UpdateLastModified(&transaction, inode, inode->LastModified()); 1393 changed = true; 1394 1395 // updating the index doesn't write back the inode 1396 inode->WriteBack(&transaction); 1397 } 1398 1399 if (status == B_OK) 1400 transaction.Done(); 1401 1402 if (changed) 1403 notify_listener(B_STAT_CHANGED, volume->ID(), 0, 0, inode->ID(), NULL); 1404 } 1405 1406 if (inode->Flags() & INODE_NO_CACHE) { 1407 volume->Pool().ReleaseBuffers(); 1408 inode->Node()->flags &= HOST_ENDIAN_TO_BFS_INT32(~INODE_NO_CACHE); 1409 // We don't need to save the inode, because INODE_NO_CACHE is a 1410 // non-permanent flag which will be removed when the inode is loaded 1411 // into memory. 1412 } 1413 1414 if (inode->Flags() & INODE_CHKBFS_RUNNING) { 1415 // "chkbfs" exited abnormally, so we have to stop it here... 1416 FATAL(("check process was aborted!\n")); 1417 volume->Allocator().StopChecking(NULL); 1418 } 1419 1420 return B_OK; 1421 } 1422 1423 1424 /** Checks access permissions, return B_NOT_ALLOWED if the action 1425 * is not allowed. 1426 */ 1427 1428 static int 1429 bfs_access(void *_ns, void *_node, int accessMode) 1430 { 1431 FUNCTION(); 1432 1433 if (_ns == NULL || _node == NULL) 1434 return B_BAD_VALUE; 1435 1436 Inode *inode = (Inode *)_node; 1437 status_t status = inode->CheckPermissions(accessMode); 1438 if (status < B_OK) 1439 RETURN_ERROR(status); 1440 1441 return B_OK; 1442 } 1443 1444 1445 static int 1446 bfs_read_link(void *_ns, void *_node, char *buffer, size_t *bufferSize) 1447 { 1448 FUNCTION(); 1449 1450 Inode *inode = (Inode *)_node; 1451 1452 if (!inode->IsSymLink()) 1453 RETURN_ERROR(B_BAD_VALUE); 1454 1455 if (inode->Flags() & INODE_LONG_SYMLINK) { 1456 status_t status = inode->ReadAt(0, (uint8 *)buffer, bufferSize); 1457 if (status < B_OK) 1458 RETURN_ERROR(status); 1459 1460 *bufferSize = inode->Size(); 1461 return B_OK; 1462 } 1463 1464 size_t numBytes = strlen((char *)&inode->Node()->short_symlink); 1465 uint32 bytes = numBytes; 1466 if (bytes > *bufferSize) 1467 bytes = *bufferSize; 1468 1469 memcpy(buffer, inode->Node()->short_symlink, bytes); 1470 *bufferSize = numBytes; 1471 1472 return B_OK; 1473 } 1474 1475 1476 // #pragma mark - 1477 // Directory functions 1478 1479 1480 static int 1481 bfs_mkdir(void *_ns, void *_directory, const char *name, int mode) 1482 { 1483 FUNCTION_START(("name = \"%s\", perms = %d\n", name, mode)); 1484 1485 if (_ns == NULL || _directory == NULL 1486 || name == NULL || *name == '\0') 1487 RETURN_ERROR(B_BAD_VALUE); 1488 1489 Volume *volume = (Volume *)_ns; 1490 Inode *directory = (Inode *)_directory; 1491 1492 if (!directory->IsDirectory()) 1493 RETURN_ERROR(B_BAD_TYPE); 1494 1495 status_t status = directory->CheckPermissions(W_OK); 1496 if (status < B_OK) 1497 RETURN_ERROR(status); 1498 1499 #ifdef UNSAFE_GET_VNODE 1500 RecursiveLocker locker(volume->Lock()); 1501 #endif 1502 Transaction transaction(volume, directory->BlockNumber()); 1503 1504 // Inode::Create() locks the inode if we pass the "id" parameter, but we 1505 // need it anyway 1506 off_t id; 1507 status = Inode::Create(&transaction, directory, name, S_DIRECTORY | (mode & S_IUMSK), 0, 0, &id); 1508 if (status == B_OK) { 1509 put_vnode(volume->ID(), id); 1510 transaction.Done(); 1511 1512 notify_listener(B_ENTRY_CREATED, volume->ID(), directory->ID(), 0, id, name); 1513 } 1514 1515 return status; 1516 } 1517 1518 1519 static int 1520 bfs_rmdir(void *_ns, void *_directory, const char *name) 1521 { 1522 FUNCTION_START(("name = \"%s\"\n", name)); 1523 1524 if (_ns == NULL || _directory == NULL || name == NULL || *name == '\0') 1525 return B_BAD_VALUE; 1526 1527 Volume *volume = (Volume *)_ns; 1528 Inode *directory = (Inode *)_directory; 1529 1530 #ifdef UNSAFE_GET_VNODE 1531 RecursiveLocker locker(volume->Lock()); 1532 #endif 1533 Transaction transaction(volume, directory->BlockNumber()); 1534 1535 off_t id; 1536 status_t status = directory->Remove(&transaction, name, &id, true); 1537 if (status == B_OK) { 1538 transaction.Done(); 1539 1540 notify_listener(B_ENTRY_REMOVED, volume->ID(), directory->ID(), 0, id, NULL); 1541 } 1542 1543 return status; 1544 } 1545 1546 1547 /** creates fs-specific "cookie" struct that keeps track of where 1548 * you are at in reading through directory entries in bfs_readdir. 1549 */ 1550 1551 static int 1552 bfs_open_dir(void *_ns, void *_node, void **_cookie) 1553 { 1554 FUNCTION(); 1555 1556 if (_ns == NULL || _node == NULL || _cookie == NULL) 1557 RETURN_ERROR(B_BAD_VALUE); 1558 1559 Inode *inode = (Inode *)_node; 1560 1561 // we don't ask here for directories only, because the bfs_open_index_dir() 1562 // function utilizes us (so we must be able to open indices as well) 1563 if (!inode->IsContainer()) 1564 RETURN_ERROR(B_BAD_VALUE); 1565 1566 BPlusTree *tree; 1567 if (inode->GetTree(&tree) != B_OK) 1568 RETURN_ERROR(B_BAD_VALUE); 1569 1570 TreeIterator *iterator = new TreeIterator(tree); 1571 if (iterator == NULL) 1572 RETURN_ERROR(B_NO_MEMORY); 1573 1574 *_cookie = iterator; 1575 return B_OK; 1576 } 1577 1578 1579 static int 1580 bfs_read_dir(void *_ns, void *_node, void *_cookie, long *num, 1581 struct dirent *dirent, size_t bufferSize) 1582 { 1583 FUNCTION(); 1584 1585 TreeIterator *iterator = (TreeIterator *)_cookie; 1586 if (iterator == NULL) 1587 RETURN_ERROR(B_BAD_VALUE); 1588 1589 uint16 length; 1590 vnode_id id; 1591 status_t status = iterator->GetNextEntry(dirent->d_name, &length, bufferSize, &id); 1592 if (status == B_ENTRY_NOT_FOUND) { 1593 *num = 0; 1594 return B_OK; 1595 } else if (status != B_OK) 1596 RETURN_ERROR(status); 1597 1598 Volume *volume = (Volume *)_ns; 1599 1600 dirent->d_dev = volume->ID(); 1601 dirent->d_ino = id; 1602 1603 #ifdef KEEP_WRONG_DIRENT_RECLEN 1604 dirent->d_reclen = length; 1605 #else 1606 dirent->d_reclen = sizeof(struct dirent) + length; 1607 #endif 1608 1609 *num = 1; 1610 return B_OK; 1611 } 1612 1613 1614 /** Sets the TreeIterator back to the beginning of the directory 1615 */ 1616 1617 static int 1618 bfs_rewind_dir(void * /*ns*/, void * /*node*/, void *_cookie) 1619 { 1620 FUNCTION(); 1621 TreeIterator *iterator = (TreeIterator *)_cookie; 1622 1623 if (iterator == NULL) 1624 RETURN_ERROR(B_BAD_VALUE); 1625 1626 return iterator->Rewind(); 1627 } 1628 1629 1630 static int 1631 bfs_close_dir(void * /*ns*/, void * /*node*/, void * /*_cookie*/) 1632 { 1633 FUNCTION(); 1634 // Do whatever you need to to close a directory, but DON'T free the cookie! 1635 return B_OK; 1636 } 1637 1638 1639 static int 1640 bfs_free_dir_cookie(void *ns, void *node, void *_cookie) 1641 { 1642 TreeIterator *iterator = (TreeIterator *)_cookie; 1643 1644 if (iterator == NULL) 1645 RETURN_ERROR(B_BAD_VALUE); 1646 1647 delete iterator; 1648 return B_OK; 1649 } 1650 1651 1652 // #pragma mark - 1653 // Attribute functions 1654 1655 1656 static int 1657 bfs_open_attrdir(void *_ns, void *_node, void **cookie) 1658 { 1659 FUNCTION(); 1660 1661 Inode *inode = (Inode *)_node; 1662 if (inode == NULL || inode->Node() == NULL) 1663 RETURN_ERROR(B_ERROR); 1664 1665 AttributeIterator *iterator = new AttributeIterator(inode); 1666 if (iterator == NULL) 1667 RETURN_ERROR(B_NO_MEMORY); 1668 1669 *cookie = iterator; 1670 return B_OK; 1671 } 1672 1673 1674 static int 1675 bfs_close_attrdir(void *ns, void *node, void *cookie) 1676 { 1677 FUNCTION(); 1678 return B_OK; 1679 } 1680 1681 1682 static int 1683 bfs_free_attrdir_cookie(void *ns, void *node, void *_cookie) 1684 { 1685 FUNCTION(); 1686 AttributeIterator *iterator = (AttributeIterator *)_cookie; 1687 1688 if (iterator == NULL) 1689 RETURN_ERROR(B_BAD_VALUE); 1690 1691 delete iterator; 1692 return B_OK; 1693 } 1694 1695 1696 static int 1697 bfs_rewind_attrdir(void *_ns, void *_node, void *_cookie) 1698 { 1699 FUNCTION(); 1700 1701 AttributeIterator *iterator = (AttributeIterator *)_cookie; 1702 if (iterator == NULL) 1703 RETURN_ERROR(B_BAD_VALUE); 1704 1705 RETURN_ERROR(iterator->Rewind()); 1706 } 1707 1708 1709 static int 1710 bfs_read_attrdir(void *_ns, void *node, void *_cookie, long *num, struct dirent *dirent, size_t bufsize) 1711 { 1712 FUNCTION(); 1713 AttributeIterator *iterator = (AttributeIterator *)_cookie; 1714 1715 if (iterator == NULL) 1716 RETURN_ERROR(B_BAD_VALUE); 1717 1718 uint32 type; 1719 size_t length; 1720 status_t status = iterator->GetNext(dirent->d_name, &length, &type, &dirent->d_ino); 1721 if (status == B_ENTRY_NOT_FOUND) { 1722 *num = 0; 1723 return B_OK; 1724 } else if (status != B_OK) 1725 RETURN_ERROR(status); 1726 1727 Volume *volume = (Volume *)_ns; 1728 1729 dirent->d_dev = volume->ID(); 1730 #ifdef KEEP_WRONG_DIRENT_RECLEN 1731 dirent->d_reclen = length; 1732 #else 1733 dirent->d_reclen = sizeof(struct dirent) + length; 1734 #endif 1735 1736 *num = 1; 1737 return B_OK; 1738 } 1739 1740 1741 static int 1742 bfs_remove_attr(void *_ns, void *_node, const char *name) 1743 { 1744 FUNCTION_START(("name = \"%s\"\n", name)); 1745 1746 if (_ns == NULL || _node == NULL || name == NULL) 1747 return B_BAD_VALUE; 1748 1749 Volume *volume = (Volume *)_ns; 1750 Inode *inode = (Inode *)_node; 1751 1752 status_t status = inode->CheckPermissions(W_OK); 1753 if (status < B_OK) 1754 return status; 1755 1756 #ifdef UNSAFE_GET_VNODE 1757 RecursiveLocker locker(volume->Lock()); 1758 #endif 1759 Transaction transaction(volume, inode->BlockNumber()); 1760 1761 status = inode->RemoveAttribute(&transaction, name); 1762 if (status == B_OK) { 1763 transaction.Done(); 1764 1765 notify_listener(B_ATTR_CHANGED, volume->ID(), 0, 0, inode->ID(), name); 1766 } 1767 1768 RETURN_ERROR(status); 1769 } 1770 1771 1772 static int 1773 bfs_rename_attr(void *ns, void *node, const char *oldname, const char *newname) 1774 { 1775 FUNCTION_START(("name = \"%s\", to = \"%s\"\n", oldname, newname)); 1776 1777 // ToDo: implement bfs_rename_attr()! 1778 // I'll skip the implementation here, and will do it for OpenBeOS - at least 1779 // there will be an API to move one attribute to another file, making that 1780 // function much more complicated - oh joy ;-) 1781 1782 RETURN_ERROR(B_ENTRY_NOT_FOUND); 1783 } 1784 1785 1786 static int 1787 bfs_stat_attr(void *ns, void *_node, const char *name, struct attr_info *attrInfo) 1788 { 1789 FUNCTION_START(("name = \"%s\"\n", name)); 1790 1791 Inode *inode = (Inode *)_node; 1792 if (inode == NULL || inode->Node() == NULL) 1793 RETURN_ERROR(B_ERROR); 1794 1795 // first, try to find it in the small data region 1796 small_data *smallData = NULL; 1797 if (inode->SmallDataLock().Lock() == B_OK) { 1798 if ((smallData = inode->FindSmallData((const char *)name)) != NULL) { 1799 attrInfo->type = smallData->Type(); 1800 attrInfo->size = smallData->DataSize(); 1801 } 1802 inode->SmallDataLock().Unlock(); 1803 } 1804 if (smallData != NULL) 1805 return B_OK; 1806 1807 // then, search in the attribute directory 1808 Inode *attribute; 1809 status_t status = inode->GetAttribute(name, &attribute); 1810 if (status == B_OK) { 1811 attrInfo->type = attribute->Type(); 1812 attrInfo->size = attribute->Size(); 1813 1814 inode->ReleaseAttribute(attribute); 1815 return B_OK; 1816 } 1817 1818 RETURN_ERROR(status); 1819 } 1820 1821 1822 static int 1823 bfs_write_attr(void *_ns, void *_node, const char *name, int type, const void *buffer, 1824 size_t *_length, off_t pos) 1825 { 1826 FUNCTION_START(("name = \"%s\"\n", name)); 1827 if (_ns == NULL || _node == NULL || name == NULL || *name == '\0') 1828 RETURN_ERROR(B_BAD_VALUE); 1829 1830 // Writing the name attribute using this function is not allowed, 1831 // also using the reserved indices name, last_modified, and size 1832 // shouldn't be allowed. 1833 // ToDo: we might think about allowing to update those values, but 1834 // really change their corresponding values in the bfs_inode structure 1835 if (name[0] == FILE_NAME_NAME && name[1] == '\0' 1836 || !strcmp(name, "name") 1837 || !strcmp(name, "last_modified") 1838 || !strcmp(name, "size")) 1839 RETURN_ERROR(B_NOT_ALLOWED); 1840 1841 Volume *volume = (Volume *)_ns; 1842 Inode *inode = (Inode *)_node; 1843 1844 status_t status = inode->CheckPermissions(W_OK); 1845 if (status < B_OK) 1846 return status; 1847 1848 #ifdef UNSAFE_GET_VNODE 1849 RecursiveLocker locker(volume->Lock()); 1850 #endif 1851 Transaction transaction(volume, inode->BlockNumber()); 1852 1853 status = inode->WriteAttribute(&transaction, name, type, pos, (const uint8 *)buffer, _length); 1854 if (status == B_OK) { 1855 transaction.Done(); 1856 1857 notify_listener(B_ATTR_CHANGED, volume->ID(), 0, 0, inode->ID(), name); 1858 } 1859 1860 return status; 1861 } 1862 1863 1864 static int 1865 bfs_read_attr(void *_ns, void *_node, const char *name, int type, void *buffer, 1866 size_t *_length, off_t pos) 1867 { 1868 FUNCTION(); 1869 Inode *inode = (Inode *)_node; 1870 1871 if (inode == NULL || name == NULL || *name == '\0' || buffer == NULL) 1872 RETURN_ERROR(B_BAD_VALUE); 1873 1874 status_t status = inode->CheckPermissions(R_OK); 1875 if (status < B_OK) 1876 return status; 1877 1878 return inode->ReadAttribute(name, type, pos, (uint8 *)buffer, _length); 1879 } 1880 1881 1882 // #pragma mark - 1883 // Index functions 1884 1885 1886 static int 1887 bfs_open_indexdir(void *_ns, void **_cookie) 1888 { 1889 FUNCTION(); 1890 if (_ns == NULL || _cookie == NULL) 1891 RETURN_ERROR(B_BAD_VALUE); 1892 1893 Volume *volume = (Volume *)_ns; 1894 1895 if (volume->IndicesNode() == NULL) 1896 RETURN_ERROR(B_ENTRY_NOT_FOUND); 1897 1898 // Since the indices root node is just a directory, and we are storing 1899 // a pointer to it in our Volume object, we can just use the directory 1900 // traversal functions. 1901 // In fact we're storing it in the Volume object for that reason. 1902 1903 RETURN_ERROR(bfs_open_dir(_ns, volume->IndicesNode(), _cookie)); 1904 } 1905 1906 1907 static int 1908 bfs_close_indexdir(void *_ns, void *_cookie) 1909 { 1910 FUNCTION(); 1911 if (_ns == NULL || _cookie == NULL) 1912 RETURN_ERROR(B_BAD_VALUE); 1913 1914 Volume *volume = (Volume *)_ns; 1915 RETURN_ERROR(bfs_close_dir(_ns, volume->IndicesNode(), _cookie)); 1916 } 1917 1918 1919 static int 1920 bfs_free_indexdir_cookie(void *_ns, void *_node, void *_cookie) 1921 { 1922 FUNCTION(); 1923 if (_ns == NULL || _cookie == NULL) 1924 RETURN_ERROR(B_BAD_VALUE); 1925 1926 Volume *volume = (Volume *)_ns; 1927 RETURN_ERROR(bfs_free_dir_cookie(_ns, volume->IndicesNode(), _cookie)); 1928 } 1929 1930 1931 static int 1932 bfs_rewind_indexdir(void *_ns, void *_cookie) 1933 { 1934 FUNCTION(); 1935 if (_ns == NULL || _cookie == NULL) 1936 RETURN_ERROR(B_BAD_VALUE); 1937 1938 Volume *volume = (Volume *)_ns; 1939 RETURN_ERROR(bfs_rewind_dir(_ns, volume->IndicesNode(), _cookie)); 1940 } 1941 1942 1943 static int 1944 bfs_read_indexdir(void *_ns, void *_cookie, long *num, struct dirent *dirent, size_t bufferSize) 1945 { 1946 FUNCTION(); 1947 if (_ns == NULL || _cookie == NULL) 1948 RETURN_ERROR(B_BAD_VALUE); 1949 1950 Volume *volume = (Volume *)_ns; 1951 RETURN_ERROR(bfs_read_dir(_ns, volume->IndicesNode(), _cookie, num, dirent, bufferSize)); 1952 } 1953 1954 1955 static int 1956 bfs_create_index(void *_ns, const char *name, int type, int flags) 1957 { 1958 FUNCTION_START(("name = \"%s\", type = %d, flags = %d\n", name, type, flags)); 1959 if (_ns == NULL || name == NULL || *name == '\0') 1960 return B_BAD_VALUE; 1961 1962 Volume *volume = (Volume *)_ns; 1963 1964 if (volume->IsReadOnly()) 1965 return B_READ_ONLY_DEVICE; 1966 1967 // only root users are allowed to create indices 1968 if (geteuid() != 0) 1969 return B_NOT_ALLOWED; 1970 1971 #ifdef UNSAFE_GET_VNODE 1972 RecursiveLocker locker(volume->Lock()); 1973 #endif 1974 Transaction transaction(volume, volume->Indices()); 1975 1976 Index index(volume); 1977 status_t status = index.Create(&transaction, name, type); 1978 1979 if (status == B_OK) 1980 transaction.Done(); 1981 1982 RETURN_ERROR(status); 1983 } 1984 1985 1986 static int 1987 bfs_remove_index(void *_ns, const char *name) 1988 { 1989 FUNCTION(); 1990 if (_ns == NULL || name == NULL || *name == '\0') 1991 return B_BAD_VALUE; 1992 1993 Volume *volume = (Volume *)_ns; 1994 1995 if (volume->IsReadOnly()) 1996 return B_READ_ONLY_DEVICE; 1997 1998 // only root users are allowed to remove indices 1999 if (geteuid() != 0) 2000 return B_NOT_ALLOWED; 2001 2002 Inode *indices; 2003 if ((indices = volume->IndicesNode()) == NULL) 2004 return B_ENTRY_NOT_FOUND; 2005 2006 #ifdef UNSAFE_GET_VNODE 2007 RecursiveLocker locker(volume->Lock()); 2008 #endif 2009 Transaction transaction(volume, volume->Indices()); 2010 2011 status_t status = indices->Remove(&transaction, name); 2012 if (status == B_OK) 2013 transaction.Done(); 2014 2015 RETURN_ERROR(status); 2016 } 2017 2018 2019 static int 2020 bfs_rename_index(void *ns, const char *oldname, const char *newname) 2021 { 2022 FUNCTION_START(("from = %s, to = %s\n", oldname, newname)); 2023 2024 // Well, renaming an index doesn't make that much sense, as you 2025 // would also need to remove every file in it (or the index 2026 // would contain wrong data) 2027 // But in that case, you can better remove the old one, and 2028 // create a new one... 2029 // There is also no way to call this function from a userland 2030 // application. 2031 2032 RETURN_ERROR(B_ENTRY_NOT_FOUND); 2033 } 2034 2035 2036 static int 2037 bfs_stat_index(void *_ns, const char *name, struct index_info *indexInfo) 2038 { 2039 FUNCTION_START(("name = %s\n", name)); 2040 if (_ns == NULL || name == NULL || indexInfo == NULL) 2041 RETURN_ERROR(B_BAD_VALUE); 2042 2043 Volume *volume = (Volume *)_ns; 2044 Index index(volume); 2045 status_t status = index.SetTo(name); 2046 if (status < B_OK) 2047 RETURN_ERROR(status); 2048 2049 bfs_inode *node = index.Node()->Node(); 2050 2051 indexInfo->type = index.Type(); 2052 indexInfo->size = node->data.Size(); 2053 indexInfo->modification_time = (time_t)(node->LastModifiedTime() >> INODE_TIME_SHIFT); 2054 indexInfo->creation_time = (time_t)(node->CreateTime() >> INODE_TIME_SHIFT); 2055 indexInfo->uid = node->UserID(); 2056 indexInfo->gid = node->GroupID(); 2057 2058 return B_OK; 2059 } 2060 2061 2062 // #pragma mark - 2063 // Query functions 2064 2065 2066 static int 2067 bfs_open_query(void *_ns, const char *queryString, ulong flags, port_id port, 2068 long token, void **cookie) 2069 { 2070 FUNCTION(); 2071 if (_ns == NULL || queryString == NULL || cookie == NULL) 2072 RETURN_ERROR(B_BAD_VALUE); 2073 2074 PRINT(("query = \"%s\", flags = %lu, port_id = %ld, token = %ld\n", queryString, flags, port, token)); 2075 2076 Volume *volume = (Volume *)_ns; 2077 2078 Expression *expression = new Expression((char *)queryString); 2079 if (expression == NULL) 2080 RETURN_ERROR(B_NO_MEMORY); 2081 2082 if (expression->InitCheck() < B_OK) { 2083 FATAL(("Could not parse query, stopped at: \"%s\"\n", expression->Position())); 2084 delete expression; 2085 RETURN_ERROR(B_BAD_VALUE); 2086 } 2087 2088 Query *query = new Query(volume, expression, flags); 2089 if (query == NULL) { 2090 delete expression; 2091 RETURN_ERROR(B_NO_MEMORY); 2092 } 2093 2094 if (flags & B_LIVE_QUERY) 2095 query->SetLiveMode(port, token); 2096 2097 *cookie = (void *)query; 2098 2099 return B_OK; 2100 } 2101 2102 2103 static int 2104 bfs_close_query(void *ns, void *cookie) 2105 { 2106 FUNCTION(); 2107 return B_OK; 2108 } 2109 2110 2111 static int 2112 bfs_free_query_cookie(void *ns, void *node, void *cookie) 2113 { 2114 FUNCTION(); 2115 if (cookie == NULL) 2116 RETURN_ERROR(B_BAD_VALUE); 2117 2118 Query *query = (Query *)cookie; 2119 Expression *expression = query->GetExpression(); 2120 delete query; 2121 delete expression; 2122 2123 return B_OK; 2124 } 2125 2126 2127 static int 2128 bfs_read_query(void */*ns*/, void *cookie, long *num, struct dirent *dirent, size_t bufferSize) 2129 { 2130 FUNCTION(); 2131 Query *query = (Query *)cookie; 2132 if (query == NULL) 2133 RETURN_ERROR(B_BAD_VALUE); 2134 2135 status_t status = query->GetNextEntry(dirent, bufferSize); 2136 if (status == B_OK) 2137 *num = 1; 2138 else if (status == B_ENTRY_NOT_FOUND) 2139 *num = 0; 2140 else 2141 return status; 2142 2143 return B_OK; 2144 } 2145 2146