1 /* 2 * Copyright 2001-2007, Axel Dörfler, axeld@pinc-software.de. 3 * This file may be used under the terms of the MIT License. 4 */ 5 6 //! file system interface to Haiku's vnode layer 7 8 9 #include "Debug.h" 10 #include "Volume.h" 11 #include "Inode.h" 12 #include "Index.h" 13 #include "BPlusTree.h" 14 #include "Query.h" 15 #include "Attribute.h" 16 #include "bfs_control.h" 17 #include "bfs_disk_system.h" 18 19 20 #define BFS_IO_SIZE 65536 21 22 23 struct identify_cookie { 24 disk_super_block super_block; 25 }; 26 27 28 extern void fill_stat_buffer(Inode *inode, struct stat &stat); 29 30 31 void 32 fill_stat_buffer(Inode *inode, struct stat &stat) 33 { 34 const bfs_inode &node = inode->Node(); 35 36 stat.st_dev = inode->GetVolume()->ID(); 37 stat.st_ino = inode->ID(); 38 stat.st_nlink = 1; 39 stat.st_blksize = BFS_IO_SIZE; 40 41 stat.st_uid = node.UserID(); 42 stat.st_gid = node.GroupID(); 43 stat.st_mode = node.Mode(); 44 stat.st_type = node.Type(); 45 46 stat.st_atime = time(NULL); 47 stat.st_mtime = stat.st_ctime = (time_t)(node.LastModifiedTime() >> INODE_TIME_SHIFT); 48 stat.st_crtime = (time_t)(node.CreateTime() >> INODE_TIME_SHIFT); 49 50 if (inode->IsSymLink() && (node.Flags() & INODE_LONG_SYMLINK) == 0) { 51 // symlinks report the size of the link here 52 stat.st_size = strlen(node.short_symlink) + 1; 53 } else 54 stat.st_size = inode->Size(); 55 } 56 57 58 // #pragma mark - Scanning 59 60 61 static float 62 bfs_identify_partition(int fd, partition_data *partition, void **_cookie) 63 { 64 disk_super_block superBlock; 65 status_t status = Volume::Identify(fd, &superBlock); 66 if (status != B_OK) 67 return status; 68 69 identify_cookie *cookie = new identify_cookie; 70 memcpy(&cookie->super_block, &superBlock, sizeof(disk_super_block)); 71 72 *_cookie = cookie; 73 return 0.8f; 74 } 75 76 77 static status_t 78 bfs_scan_partition(int fd, partition_data *partition, void *_cookie) 79 { 80 identify_cookie *cookie = (identify_cookie *)_cookie; 81 82 partition->status = B_PARTITION_VALID; 83 partition->flags |= B_PARTITION_FILE_SYSTEM; 84 partition->content_size = cookie->super_block.NumBlocks() 85 * cookie->super_block.BlockSize(); 86 partition->block_size = cookie->super_block.BlockSize(); 87 partition->content_name = strdup(cookie->super_block.name); 88 if (partition->content_name == NULL) 89 return B_NO_MEMORY; 90 91 return B_OK; 92 } 93 94 95 static void 96 bfs_free_identify_partition_cookie(partition_data *partition, void *_cookie) 97 { 98 identify_cookie *cookie = (identify_cookie *)_cookie; 99 100 delete cookie; 101 } 102 103 104 // #pragma mark - 105 106 107 static status_t 108 bfs_mount(dev_t mountID, const char *device, uint32 flags, const char *args, 109 void **_data, ino_t *_rootID) 110 { 111 FUNCTION(); 112 113 Volume *volume = new Volume(mountID); 114 if (volume == NULL) 115 return B_NO_MEMORY; 116 117 status_t status; 118 if ((status = volume->Mount(device, flags)) == B_OK) { 119 *_data = volume; 120 *_rootID = volume->ToVnode(volume->Root()); 121 INFORM(("mounted \"%s\" (root node at %Ld, device = %s)\n", 122 volume->Name(), *_rootID, device)); 123 } 124 else 125 delete volume; 126 127 RETURN_ERROR(status); 128 } 129 130 131 static status_t 132 bfs_unmount(void *ns) 133 { 134 FUNCTION(); 135 Volume* volume = (Volume *)ns; 136 137 status_t status = volume->Unmount(); 138 delete volume; 139 140 RETURN_ERROR(status); 141 } 142 143 144 /** Fill in bfs_info struct for device. 145 */ 146 147 static status_t 148 bfs_read_fs_stat(void *_ns, struct fs_info *info) 149 { 150 FUNCTION(); 151 if (_ns == NULL || info == NULL) 152 return B_BAD_VALUE; 153 154 Volume *volume = (Volume *)_ns; 155 156 RecursiveLocker locker(volume->Lock()); 157 158 // File system flags. 159 info->flags = B_FS_IS_PERSISTENT | B_FS_HAS_ATTR | B_FS_HAS_MIME | B_FS_HAS_QUERY | 160 (volume->IsReadOnly() ? B_FS_IS_READONLY : 0); 161 162 info->io_size = BFS_IO_SIZE; 163 // whatever is appropriate here? Just use the same value as BFS (and iso9660) for now 164 165 info->block_size = volume->BlockSize(); 166 info->total_blocks = volume->NumBlocks(); 167 info->free_blocks = volume->FreeBlocks(); 168 169 // Volume name 170 strlcpy(info->volume_name, volume->Name(), sizeof(info->volume_name)); 171 172 // File system name 173 strlcpy(info->fsh_name, "bfs", sizeof(info->fsh_name)); 174 175 return B_OK; 176 } 177 178 179 static status_t 180 bfs_write_fs_stat(void *_ns, const struct fs_info *info, uint32 mask) 181 { 182 FUNCTION_START(("mask = %ld\n", mask)); 183 184 Volume *volume = (Volume *)_ns; 185 disk_super_block &superBlock = volume->SuperBlock(); 186 187 RecursiveLocker locker(volume->Lock()); 188 189 status_t status = B_BAD_VALUE; 190 191 if (mask & FS_WRITE_FSINFO_NAME) { 192 strncpy(superBlock.name, info->volume_name, sizeof(superBlock.name) - 1); 193 superBlock.name[sizeof(superBlock.name) - 1] = '\0'; 194 195 status = volume->WriteSuperBlock(); 196 } 197 return status; 198 } 199 200 201 static status_t 202 bfs_sync(void *_ns) 203 { 204 FUNCTION(); 205 if (_ns == NULL) 206 return B_BAD_VALUE; 207 208 Volume *volume = (Volume *)_ns; 209 210 return volume->Sync(); 211 } 212 213 214 // #pragma mark - 215 216 217 /*! Reads in the node from disk and creates an inode object from it. 218 */ 219 static status_t 220 bfs_get_vnode(void *_ns, ino_t id, void **_node, bool reenter) 221 { 222 //FUNCTION_START(("ino_t = %Ld\n", id)); 223 Volume *volume = (Volume *)_ns; 224 225 // first inode may be after the log area, we don't go through 226 // the hassle and try to load an earlier block from disk 227 if (id < volume->ToBlock(volume->Log()) + volume->Log().Length() 228 || id > volume->NumBlocks()) { 229 INFORM(("inode at %Ld requested!\n", id)); 230 return B_ERROR; 231 } 232 233 CachedBlock cached(volume, id); 234 bfs_inode *node = (bfs_inode *)cached.Block(); 235 if (node == NULL) { 236 FATAL(("could not read inode: %Ld\n", id)); 237 return B_IO_ERROR; 238 } 239 240 status_t status = node->InitCheck(volume); 241 if (status < B_OK) { 242 FATAL(("inode at %Ld is corrupt!\n", id)); 243 return status; 244 } 245 246 Inode *inode = new Inode(volume, id); 247 if (inode == NULL) 248 return B_NO_MEMORY; 249 250 status = inode->InitCheck(false); 251 if (status < B_OK) 252 delete inode; 253 254 if (status == B_OK) 255 *_node = inode; 256 257 return status; 258 } 259 260 261 static status_t 262 bfs_put_vnode(void *_volume, void *_node, bool reenter) 263 { 264 Volume *volume = (Volume *)_volume; 265 Inode *inode = (Inode *)_node; 266 267 // since a directory's size can be changed without having it opened, 268 // we need to take care about their preallocated blocks here 269 if (!volume->IsReadOnly() && inode->NeedsTrimming()) { 270 Transaction transaction(volume, inode->BlockNumber()); 271 272 if (inode->TrimPreallocation(transaction) == B_OK) 273 transaction.Done(); 274 else if (transaction.HasParent()) { 275 // ToDo: for now, we don't let sub-transactions fail 276 transaction.Done(); 277 } 278 } 279 280 delete inode; 281 return B_OK; 282 } 283 284 285 static status_t 286 bfs_remove_vnode(void *_ns, void *_node, bool reenter) 287 { 288 FUNCTION(); 289 290 if (_ns == NULL || _node == NULL) 291 return B_BAD_VALUE; 292 293 Volume *volume = (Volume *)_ns; 294 Inode *inode = (Inode *)_node; 295 296 // The "chkbfs" functionality uses this flag to prevent the space used 297 // up by the inode from being freed - this flag is set only in situations 298 // where this is a good idea... (the block bitmap will get fixed anyway 299 // in this case). 300 if (inode->Flags() & INODE_DONT_FREE_SPACE) { 301 delete inode; 302 return B_OK; 303 } 304 305 // If the inode isn't in use anymore, we were called before 306 // bfs_unlink() returns - in this case, we can just use the 307 // transaction which has already deleted the inode. 308 Transaction transaction(volume, volume->ToBlock(inode->Parent())); 309 310 status_t status = inode->Free(transaction); 311 if (status == B_OK) { 312 transaction.Done(); 313 314 delete inode; 315 } else if (transaction.HasParent()) { 316 // ToDo: for now, we don't let sub-transactions fail 317 transaction.Done(); 318 } 319 320 return status; 321 } 322 323 324 static bool 325 bfs_can_page(fs_volume _fs, fs_vnode _v, fs_cookie _cookie) 326 { 327 // ToDo: we're obviously not even asked... 328 return false; 329 } 330 331 332 static status_t 333 bfs_read_pages(fs_volume _fs, fs_vnode _node, fs_cookie _cookie, off_t pos, 334 const iovec *vecs, size_t count, size_t *_numBytes, bool reenter) 335 { 336 Volume *volume = (Volume *)_fs; 337 Inode *inode = (Inode *)_node; 338 339 if (inode->FileCache() == NULL) 340 RETURN_ERROR(B_BAD_VALUE); 341 342 if (!reenter) 343 inode->Lock().Lock(); 344 345 uint32 vecIndex = 0; 346 size_t vecOffset = 0; 347 size_t bytesLeft = *_numBytes; 348 status_t status; 349 350 while (true) { 351 file_io_vec fileVecs[8]; 352 uint32 fileVecCount = 8; 353 354 status = file_map_translate(inode->Map(), pos, bytesLeft, fileVecs, 355 &fileVecCount); 356 if (status != B_OK && status != B_BUFFER_OVERFLOW) 357 break; 358 359 bool bufferOverflow = status == B_BUFFER_OVERFLOW; 360 361 size_t bytes = bytesLeft; 362 status = read_file_io_vec_pages(volume->Device(), fileVecs, 363 fileVecCount, vecs, count, &vecIndex, &vecOffset, &bytes); 364 if (status != B_OK || !bufferOverflow) 365 break; 366 367 pos += bytes; 368 bytesLeft -= bytes; 369 } 370 371 if (!reenter) 372 inode->Lock().Unlock(); 373 374 return status; 375 } 376 377 378 static status_t 379 bfs_write_pages(fs_volume _fs, fs_vnode _node, fs_cookie _cookie, off_t pos, 380 const iovec *vecs, size_t count, size_t *_numBytes, bool reenter) 381 { 382 Volume *volume = (Volume *)_fs; 383 Inode *inode = (Inode *)_node; 384 385 if (inode->FileCache() == NULL) 386 RETURN_ERROR(B_BAD_VALUE); 387 388 if (!reenter) 389 inode->Lock().Lock(); 390 391 uint32 vecIndex = 0; 392 size_t vecOffset = 0; 393 size_t bytesLeft = *_numBytes; 394 status_t status; 395 396 while (true) { 397 file_io_vec fileVecs[8]; 398 uint32 fileVecCount = 8; 399 400 status = file_map_translate(inode->Map(), pos, bytesLeft, fileVecs, 401 &fileVecCount); 402 if (status != B_OK && status != B_BUFFER_OVERFLOW) 403 break; 404 405 bool bufferOverflow = status == B_BUFFER_OVERFLOW; 406 407 size_t bytes = bytesLeft; 408 status = write_file_io_vec_pages(volume->Device(), fileVecs, 409 fileVecCount, vecs, count, &vecIndex, &vecOffset, &bytes); 410 if (status != B_OK || !bufferOverflow) 411 break; 412 413 pos += bytes; 414 bytesLeft -= bytes; 415 } 416 417 if (!reenter) 418 inode->Lock().Unlock(); 419 420 return status; 421 } 422 423 424 static status_t 425 bfs_get_file_map(fs_volume _fs, fs_vnode _node, off_t offset, size_t size, 426 struct file_io_vec *vecs, size_t *_count) 427 { 428 Volume *volume = (Volume *)_fs; 429 Inode *inode = (Inode *)_node; 430 431 int32 blockShift = volume->BlockShift(); 432 size_t index = 0, max = *_count; 433 block_run run; 434 off_t fileOffset; 435 436 //FUNCTION_START(("offset = %Ld, size = %lu\n", offset, size)); 437 438 while (true) { 439 status_t status = inode->FindBlockRun(offset, run, fileOffset); 440 if (status != B_OK) 441 return status; 442 443 vecs[index].offset = volume->ToOffset(run) + offset - fileOffset; 444 vecs[index].length = (run.Length() << blockShift) - offset + fileOffset; 445 446 offset += vecs[index].length; 447 448 // are we already done? 449 if (size <= vecs[index].length 450 || offset >= inode->Size()) { 451 if (offset > inode->Size()) { 452 // make sure the extent ends with the last official file 453 // block (without taking any preallocations into account) 454 vecs[index].length = (inode->Size() - fileOffset + volume->BlockSize() - 1) 455 & ~(volume->BlockSize() - 1); 456 } 457 *_count = index + 1; 458 return B_OK; 459 } 460 461 size -= vecs[index].length; 462 index++; 463 464 if (index >= max) { 465 // we're out of file_io_vecs; let's bail out 466 *_count = index; 467 return B_BUFFER_OVERFLOW; 468 } 469 } 470 471 // can never get here 472 return B_ERROR; 473 } 474 475 476 // #pragma mark - 477 478 479 static status_t 480 bfs_lookup(void *_ns, void *_directory, const char *file, ino_t *_vnodeID, int *_type) 481 { 482 //FUNCTION_START(("file = %s\n", file)); 483 if (_ns == NULL || _directory == NULL || file == NULL || _vnodeID == NULL) 484 return B_BAD_VALUE; 485 486 Volume *volume = (Volume *)_ns; 487 Inode *directory = (Inode *)_directory; 488 489 // check access permissions 490 status_t status = directory->CheckPermissions(X_OK); 491 if (status < B_OK) 492 RETURN_ERROR(status); 493 494 BPlusTree *tree; 495 if (directory->GetTree(&tree) != B_OK) 496 RETURN_ERROR(B_BAD_VALUE); 497 498 if ((status = tree->Find((uint8 *)file, (uint16)strlen(file), _vnodeID)) < B_OK) { 499 //PRINT(("bfs_walk() could not find %Ld:\"%s\": %s\n", directory->BlockNumber(), file, strerror(status))); 500 return status; 501 } 502 503 RecursiveLocker locker(volume->Lock()); 504 // we have to hold the volume lock in order to not 505 // interfere with new_vnode() here 506 507 Inode *inode; 508 if ((status = get_vnode(volume->ID(), *_vnodeID, (void **)&inode)) != B_OK) { 509 REPORT_ERROR(status); 510 return B_ENTRY_NOT_FOUND; 511 } 512 513 *_type = inode->Mode(); 514 515 return B_OK; 516 } 517 518 519 static status_t 520 bfs_get_vnode_name(fs_volume _fs, fs_vnode _node, char *buffer, 521 size_t bufferSize) 522 { 523 Inode *inode = (Inode *)_node; 524 525 return inode->GetName(buffer, bufferSize); 526 } 527 528 529 static status_t 530 bfs_ioctl(void *_fs, void *_node, void *_cookie, ulong cmd, void *buffer, 531 size_t bufferLength) 532 { 533 FUNCTION_START(("node = %p, cmd = %lu, buf = %p, len = %ld\n", _node, cmd, 534 buffer, bufferLength)); 535 536 Volume *volume = (Volume *)_fs; 537 Inode *inode = (Inode *)_node; 538 539 switch (cmd) { 540 case BFS_IOCTL_VERSION: 541 { 542 uint32 *version = (uint32 *)buffer; 543 544 *version = 0x10000; 545 return B_OK; 546 } 547 case BFS_IOCTL_START_CHECKING: 548 { 549 // start checking 550 BlockAllocator &allocator = volume->Allocator(); 551 check_control *control = (check_control *)buffer; 552 553 status_t status = allocator.StartChecking(control); 554 if (status == B_OK && inode != NULL) 555 inode->Node().flags |= HOST_ENDIAN_TO_BFS_INT32(INODE_CHKBFS_RUNNING); 556 557 return status; 558 } 559 case BFS_IOCTL_STOP_CHECKING: 560 { 561 // stop checking 562 BlockAllocator &allocator = volume->Allocator(); 563 check_control *control = (check_control *)buffer; 564 565 status_t status = allocator.StopChecking(control); 566 if (status == B_OK && inode != NULL) 567 inode->Node().flags &= HOST_ENDIAN_TO_BFS_INT32(~INODE_CHKBFS_RUNNING); 568 569 return status; 570 } 571 case BFS_IOCTL_CHECK_NEXT_NODE: 572 { 573 // check next 574 BlockAllocator &allocator = volume->Allocator(); 575 check_control *control = (check_control *)buffer; 576 577 return allocator.CheckNextNode(control); 578 } 579 case BFS_IOCTL_UPDATE_BOOT_BLOCK: 580 { 581 // let's makebootable (or anyone else) update the boot block 582 // while BFS is mounted 583 if (user_memcpy(&volume->SuperBlock().pad_to_block, 584 (uint8 *)buffer + offsetof(disk_super_block, pad_to_block), 585 sizeof(volume->SuperBlock().pad_to_block)) < B_OK) 586 return B_BAD_ADDRESS; 587 588 return volume->WriteSuperBlock(); 589 } 590 #ifdef DEBUG 591 case 56742: 592 { 593 // allocate all free blocks and zero them out (a test for the BlockAllocator)! 594 BlockAllocator &allocator = volume->Allocator(); 595 Transaction transaction(volume, 0); 596 CachedBlock cached(volume); 597 block_run run; 598 while (allocator.AllocateBlocks(transaction, 8, 0, 64, 1, run) == B_OK) { 599 PRINT(("write block_run(%ld, %d, %d)\n", run.allocation_group, 600 run.start, run.length)); 601 for (int32 i = 0;i < run.length;i++) { 602 uint8 *block = cached.SetToWritable(transaction, run); 603 if (block != NULL) 604 memset(block, 0, volume->BlockSize()); 605 } 606 } 607 return B_OK; 608 } 609 case 56743: 610 dump_super_block(&volume->SuperBlock()); 611 return B_OK; 612 case 56744: 613 if (inode != NULL) 614 dump_inode(&inode->Node()); 615 return B_OK; 616 case 56745: 617 if (inode != NULL) { 618 NodeGetter node(volume, inode); 619 dump_block((const char *)node.Node(), volume->BlockSize()); 620 } 621 return B_OK; 622 #endif 623 } 624 return B_BAD_VALUE; 625 } 626 627 628 /** Sets the open-mode flags for the open file cookie - only 629 * supports O_APPEND currently, but that should be sufficient 630 * for a file system. 631 */ 632 633 static status_t 634 bfs_set_flags(void *_ns, void *_node, void *_cookie, int flags) 635 { 636 FUNCTION_START(("node = %p, flags = %d", _node, flags)); 637 638 file_cookie *cookie = (file_cookie *)_cookie; 639 cookie->open_mode = (cookie->open_mode & ~O_APPEND) | (flags & O_APPEND); 640 641 return B_OK; 642 } 643 644 645 #if 0 646 static status_t 647 bfs_select(void *ns, void *node, void *cookie, uint8 event, uint32 ref, selectsync *sync) 648 { 649 FUNCTION_START(("event = %d, ref = %lu, sync = %p\n", event, ref, sync)); 650 notify_select_event(sync, ref); 651 652 return B_OK; 653 } 654 655 656 static status_t 657 bfs_deselect(void *ns, void *node, void *cookie, uint8 event, selectsync *sync) 658 { 659 FUNCTION(); 660 return B_OK; 661 } 662 #endif 663 664 static status_t 665 bfs_fsync(void *_ns, void *_node) 666 { 667 FUNCTION(); 668 if (_node == NULL) 669 return B_BAD_VALUE; 670 671 Inode *inode = (Inode *)_node; 672 ReadLocked locked(inode->Lock()); 673 674 status_t status = locked.IsLocked(); 675 if (status < B_OK) 676 RETURN_ERROR(status); 677 678 return inode->Sync(); 679 } 680 681 682 /** Fills in the stat struct for a node 683 */ 684 685 static status_t 686 bfs_read_stat(void *_ns, void *_node, struct stat *stat) 687 { 688 FUNCTION(); 689 690 Inode *inode = (Inode *)_node; 691 692 fill_stat_buffer(inode, *stat); 693 return B_OK; 694 } 695 696 697 static status_t 698 bfs_write_stat(void *_ns, void *_node, const struct stat *stat, uint32 mask) 699 { 700 FUNCTION(); 701 702 if (_ns == NULL || _node == NULL || stat == NULL) 703 RETURN_ERROR(B_BAD_VALUE); 704 705 Volume *volume = (Volume *)_ns; 706 Inode *inode = (Inode *)_node; 707 708 // TODO: we should definitely check a bit more if the new stats are 709 // valid - or even better, the VFS should check this before calling us 710 711 status_t status = inode->CheckPermissions(W_OK); 712 if (status < B_OK) 713 RETURN_ERROR(status); 714 715 Transaction transaction(volume, inode->BlockNumber()); 716 717 WriteLocked locked(inode->Lock()); 718 if (locked.IsLocked() < B_OK) 719 RETURN_ERROR(B_ERROR); 720 721 bfs_inode &node = inode->Node(); 722 723 if (mask & B_STAT_SIZE) { 724 // Since WSTAT_SIZE is the only thing that can fail directly, we 725 // do it first, so that the inode state will still be consistent 726 // with the on-disk version 727 if (inode->IsDirectory()) 728 return B_IS_A_DIRECTORY; 729 730 if (inode->Size() != stat->st_size) { 731 status = inode->SetFileSize(transaction, stat->st_size); 732 if (status < B_OK) 733 return status; 734 735 // fill the new blocks (if any) with zeros 736 inode->FillGapWithZeros(inode->OldSize(), inode->Size()); 737 738 if (!inode->IsDeleted()) { 739 Index index(volume); 740 index.UpdateSize(transaction, inode); 741 742 if ((mask & B_STAT_MODIFICATION_TIME) == 0) 743 index.UpdateLastModified(transaction, inode); 744 } 745 } 746 } 747 748 if (mask & B_STAT_MODE) { 749 PRINT(("original mode = %ld, stat->st_mode = %d\n", node.Mode(), stat->st_mode)); 750 node.mode = HOST_ENDIAN_TO_BFS_INT32(node.Mode() & ~S_IUMSK 751 | stat->st_mode & S_IUMSK); 752 } 753 754 if (mask & B_STAT_UID) 755 node.uid = HOST_ENDIAN_TO_BFS_INT32(stat->st_uid); 756 if (mask & B_STAT_GID) 757 node.gid = HOST_ENDIAN_TO_BFS_INT32(stat->st_gid); 758 759 if (mask & B_STAT_MODIFICATION_TIME) { 760 if (inode->IsDirectory()) { 761 // directory modification times are not part of the index 762 node.last_modified_time = HOST_ENDIAN_TO_BFS_INT64( 763 (bigtime_t)stat->st_mtime << INODE_TIME_SHIFT); 764 } else if (!inode->IsDeleted()) { 765 // Index::UpdateLastModified() will set the new time in the inode 766 Index index(volume); 767 index.UpdateLastModified(transaction, inode, 768 (bigtime_t)stat->st_mtime << INODE_TIME_SHIFT); 769 } 770 } 771 if (mask & B_STAT_CREATION_TIME) { 772 node.create_time = HOST_ENDIAN_TO_BFS_INT64( 773 (bigtime_t)stat->st_crtime << INODE_TIME_SHIFT); 774 } 775 776 status = inode->WriteBack(transaction); 777 if (status == B_OK) 778 transaction.Done(); 779 780 notify_stat_changed(volume->ID(), inode->ID(), mask); 781 782 return status; 783 } 784 785 786 status_t 787 bfs_create(void *_ns, void *_directory, const char *name, int openMode, 788 int mode, void **_cookie, ino_t *_vnodeID) 789 { 790 FUNCTION_START(("name = \"%s\", perms = %d, openMode = %d\n", name, mode, openMode)); 791 792 if (_ns == NULL || _directory == NULL || _cookie == NULL 793 || name == NULL || *name == '\0') 794 RETURN_ERROR(B_BAD_VALUE); 795 796 Volume *volume = (Volume *)_ns; 797 Inode *directory = (Inode *)_directory; 798 799 if (!directory->IsDirectory()) 800 RETURN_ERROR(B_BAD_TYPE); 801 802 // We are creating the cookie at this point, so that we don't have 803 // to remove the inode if we don't have enough free memory later... 804 file_cookie *cookie = (file_cookie *)malloc(sizeof(file_cookie)); 805 if (cookie == NULL) 806 RETURN_ERROR(B_NO_MEMORY); 807 808 // initialize the cookie 809 cookie->open_mode = openMode; 810 cookie->last_size = 0; 811 cookie->last_notification = system_time(); 812 813 Transaction transaction(volume, directory->BlockNumber()); 814 815 bool created; 816 status_t status = Inode::Create(transaction, directory, name, 817 S_FILE | (mode & S_IUMSK), openMode, 0, &created, _vnodeID); 818 819 if (status >= B_OK) { 820 transaction.Done(); 821 822 // register the cookie 823 *_cookie = cookie; 824 825 if (created) 826 notify_entry_created(volume->ID(), directory->ID(), name, *_vnodeID); 827 } else 828 free(cookie); 829 830 return status; 831 } 832 833 834 static status_t 835 bfs_create_symlink(void *_ns, void *_directory, const char *name, 836 const char *path, int mode) 837 { 838 FUNCTION_START(("name = \"%s\", path = \"%s\"\n", name, path)); 839 840 if (_ns == NULL || _directory == NULL || path == NULL 841 || name == NULL || *name == '\0') 842 RETURN_ERROR(B_BAD_VALUE); 843 844 Volume *volume = (Volume *)_ns; 845 Inode *directory = (Inode *)_directory; 846 847 if (!directory->IsDirectory()) 848 RETURN_ERROR(B_BAD_TYPE); 849 850 status_t status = directory->CheckPermissions(W_OK); 851 if (status < B_OK) 852 RETURN_ERROR(status); 853 854 Transaction transaction(volume, directory->BlockNumber()); 855 856 Inode *link; 857 off_t id; 858 status = Inode::Create(transaction, directory, name, S_SYMLINK | 0777, 859 0, 0, NULL, &id, &link); 860 if (status < B_OK) 861 RETURN_ERROR(status); 862 863 size_t length = strlen(path); 864 if (length < SHORT_SYMLINK_NAME_LENGTH) { 865 strcpy(link->Node().short_symlink, path); 866 } else { 867 link->Node().flags |= HOST_ENDIAN_TO_BFS_INT32(INODE_LONG_SYMLINK 868 | INODE_LOGGED); 869 870 // links usually don't have a file cache attached - but we now need one 871 link->SetFileCache(file_cache_create(volume->ID(), link->ID(), 0)); 872 link->SetMap(file_map_create(volume->ID(), link->ID(), 0)); 873 874 // The following call will have to write the inode back, so 875 // we don't have to do that here... 876 status = link->WriteAt(transaction, 0, (const uint8 *)path, &length); 877 } 878 879 if (status == B_OK) 880 status = link->WriteBack(transaction); 881 882 // Inode::Create() left the inode locked in memory, and also doesn't 883 // publish links 884 publish_vnode(volume->ID(), id, link); 885 put_vnode(volume->ID(), id); 886 887 if (status == B_OK) { 888 transaction.Done(); 889 890 notify_entry_created(volume->ID(), directory->ID(), name, id); 891 } 892 893 return status; 894 } 895 896 897 status_t 898 bfs_link(void *ns, void *dir, const char *name, void *node) 899 { 900 FUNCTION_START(("name = \"%s\"\n", name)); 901 902 // This one won't be implemented in a binary compatible BFS 903 904 return B_ERROR; 905 } 906 907 908 status_t 909 bfs_unlink(void *_ns, void *_directory, const char *name) 910 { 911 FUNCTION_START(("name = \"%s\"\n", name)); 912 913 if (_ns == NULL || _directory == NULL || name == NULL || *name == '\0') 914 return B_BAD_VALUE; 915 if (!strcmp(name, "..") || !strcmp(name, ".")) 916 return B_NOT_ALLOWED; 917 918 Volume *volume = (Volume *)_ns; 919 Inode *directory = (Inode *)_directory; 920 921 status_t status = directory->CheckPermissions(W_OK); 922 if (status < B_OK) 923 return status; 924 925 Transaction transaction(volume, directory->BlockNumber()); 926 927 off_t id; 928 if ((status = directory->Remove(transaction, name, &id)) == B_OK) { 929 transaction.Done(); 930 931 notify_entry_removed(volume->ID(), directory->ID(), name, id); 932 } 933 return status; 934 } 935 936 937 status_t 938 bfs_rename(void *_ns, void *_oldDir, const char *oldName, void *_newDir, const char *newName) 939 { 940 FUNCTION_START(("oldDir = %p, oldName = \"%s\", newDir = %p, newName = \"%s\"\n", _oldDir, oldName, _newDir, newName)); 941 942 // there might be some more tests needed?! 943 if (_ns == NULL || _oldDir == NULL || _newDir == NULL 944 || oldName == NULL || *oldName == '\0' 945 || newName == NULL || *newName == '\0' 946 || !strcmp(oldName, ".") || !strcmp(oldName, "..") 947 || !strcmp(newName, ".") || !strcmp(newName, "..") 948 || strchr(newName, '/') != NULL) 949 RETURN_ERROR(B_BAD_VALUE); 950 951 Volume *volume = (Volume *)_ns; 952 Inode *oldDirectory = (Inode *)_oldDir; 953 Inode *newDirectory = (Inode *)_newDir; 954 955 // are we already done? 956 if (oldDirectory == newDirectory && !strcmp(oldName, newName)) 957 return B_OK; 958 959 // are we allowed to do what we've been told? 960 status_t status = oldDirectory->CheckPermissions(W_OK); 961 if (status == B_OK) 962 status = newDirectory->CheckPermissions(W_OK); 963 if (status < B_OK) 964 return status; 965 966 RecursiveLocker locker(volume->Lock()); 967 968 // get the directory's tree, and a pointer to the inode which should be changed 969 BPlusTree *tree; 970 status = oldDirectory->GetTree(&tree); 971 if (status < B_OK) 972 RETURN_ERROR(status); 973 974 off_t id; 975 status = tree->Find((const uint8 *)oldName, strlen(oldName), &id); 976 if (status < B_OK) 977 RETURN_ERROR(status); 978 979 Vnode vnode(volume, id); 980 Inode *inode; 981 if (vnode.Get(&inode) < B_OK) 982 return B_IO_ERROR; 983 984 // Don't move a directory into one of its children - we soar up 985 // from the newDirectory to either the root node or the old 986 // directory, whichever comes first. 987 // If we meet our inode on that way, we have to bail out. 988 989 if (oldDirectory != newDirectory) { 990 ino_t parent = volume->ToVnode(newDirectory->Parent()); 991 ino_t root = volume->RootNode()->ID(); 992 993 while (true) { 994 if (parent == id) 995 return B_BAD_VALUE; 996 else if (parent == root || parent == oldDirectory->ID()) 997 break; 998 999 Vnode vnode(volume, parent); 1000 Inode *parentNode; 1001 if (vnode.Get(&parentNode) < B_OK) 1002 return B_ERROR; 1003 1004 parent = volume->ToVnode(parentNode->Parent()); 1005 } 1006 } 1007 1008 // Everything okay? Then lets get to work... 1009 1010 Transaction transaction(volume, oldDirectory->BlockNumber()); 1011 1012 // First, try to make sure there is nothing that will stop us in 1013 // the target directory - since this is the only non-critical 1014 // failure, we will test this case first 1015 BPlusTree *newTree = tree; 1016 if (newDirectory != oldDirectory) { 1017 status = newDirectory->GetTree(&newTree); 1018 if (status < B_OK) 1019 RETURN_ERROR(status); 1020 } 1021 1022 status = newTree->Insert(transaction, (const uint8 *)newName, strlen(newName), id); 1023 if (status == B_NAME_IN_USE) { 1024 // If there is already a file with that name, we have to remove 1025 // it, as long it's not a directory with files in it 1026 off_t clobber; 1027 if (newTree->Find((const uint8 *)newName, strlen(newName), &clobber) < B_OK) 1028 return B_NAME_IN_USE; 1029 if (clobber == id) 1030 return B_BAD_VALUE; 1031 1032 Vnode vnode(volume, clobber); 1033 Inode *other; 1034 if (vnode.Get(&other) < B_OK) 1035 return B_NAME_IN_USE; 1036 1037 status = newDirectory->Remove(transaction, newName, NULL, other->IsDirectory()); 1038 if (status < B_OK) 1039 return status; 1040 1041 notify_entry_removed(volume->ID(), newDirectory->ID(), newName, clobber); 1042 1043 status = newTree->Insert(transaction, (const uint8 *)newName, strlen(newName), id); 1044 } 1045 if (status < B_OK) 1046 return status; 1047 1048 // If anything fails now, we have to remove the inode from the 1049 // new directory in any case to restore the previous state 1050 status_t bailStatus = B_OK; 1051 1052 // update the name only when they differ 1053 bool nameUpdated = false; 1054 if (strcmp(oldName, newName)) { 1055 status = inode->SetName(transaction, newName); 1056 if (status == B_OK) { 1057 Index index(volume); 1058 index.UpdateName(transaction, oldName, newName, inode); 1059 nameUpdated = true; 1060 } 1061 } 1062 1063 if (status == B_OK) { 1064 status = tree->Remove(transaction, (const uint8 *)oldName, strlen(oldName), id); 1065 if (status == B_OK) { 1066 inode->Parent() = newDirectory->BlockRun(); 1067 1068 // if it's a directory, update the parent directory pointer 1069 // in its tree if necessary 1070 BPlusTree *movedTree = NULL; 1071 if (oldDirectory != newDirectory 1072 && inode->IsDirectory() 1073 && (status = inode->GetTree(&movedTree)) == B_OK) 1074 status = movedTree->Replace(transaction, (const uint8 *)"..", 2, newDirectory->ID()); 1075 1076 if (status == B_OK) 1077 status = inode->WriteBack(transaction); 1078 1079 if (status == B_OK) { 1080 transaction.Done(); 1081 1082 notify_entry_moved(volume->ID(), oldDirectory->ID(), oldName, 1083 newDirectory->ID(), newName, id); 1084 return B_OK; 1085 } 1086 // If we get here, something has gone wrong already! 1087 1088 // Those better don't fail, or we switch to a read-only 1089 // device for safety reasons (Volume::Panic() does this 1090 // for us) 1091 // Anyway, if we overwrote a file in the target directory 1092 // this is lost now (only in-memory, not on-disk)... 1093 bailStatus = tree->Insert(transaction, (const uint8 *)oldName, strlen(oldName), id); 1094 if (movedTree != NULL) 1095 movedTree->Replace(transaction, (const uint8 *)"..", 2, oldDirectory->ID()); 1096 } 1097 } 1098 1099 if (bailStatus == B_OK && nameUpdated) { 1100 bailStatus = inode->SetName(transaction, oldName); 1101 if (status == B_OK) { 1102 // update inode and index 1103 inode->WriteBack(transaction); 1104 1105 Index index(volume); 1106 index.UpdateName(transaction, newName, oldName, inode); 1107 } 1108 } 1109 1110 if (bailStatus == B_OK) 1111 bailStatus = newTree->Remove(transaction, (const uint8 *)newName, strlen(newName), id); 1112 1113 if (bailStatus < B_OK) 1114 volume->Panic(); 1115 1116 return status; 1117 } 1118 1119 1120 static status_t 1121 bfs_open(void *_fs, void *_node, int openMode, void **_cookie) 1122 { 1123 FUNCTION(); 1124 1125 Volume *volume = (Volume *)_fs; 1126 Inode *inode = (Inode *)_node; 1127 1128 // opening a directory read-only is allowed, although you can't read 1129 // any data from it. 1130 if (inode->IsDirectory() && openMode & O_RWMASK) { 1131 openMode = openMode & ~O_RWMASK; 1132 // ToDo: for compatibility reasons, we don't return an error here... 1133 // e.g. "copyattr" tries to do that 1134 //return B_IS_A_DIRECTORY; 1135 } 1136 1137 status_t status = inode->CheckPermissions(openModeToAccess(openMode) 1138 | (openMode & O_TRUNC ? W_OK : 0)); 1139 if (status < B_OK) 1140 RETURN_ERROR(status); 1141 1142 // we could actually use the cookie to keep track of: 1143 // - the last block_run 1144 // - the location in the data_stream (indirect, double indirect, 1145 // position in block_run array) 1146 // 1147 // This could greatly speed up continuous reads of big files, especially 1148 // in the indirect block section. 1149 1150 file_cookie *cookie = (file_cookie *)malloc(sizeof(file_cookie)); 1151 if (cookie == NULL) 1152 RETURN_ERROR(B_NO_MEMORY); 1153 1154 // initialize the cookie 1155 cookie->open_mode = openMode; 1156 // needed by e.g. bfs_write() for O_APPEND 1157 cookie->last_size = inode->Size(); 1158 cookie->last_notification = system_time(); 1159 1160 // Should we truncate the file? 1161 if (openMode & O_TRUNC) { 1162 Transaction transaction(volume, inode->BlockNumber()); 1163 WriteLocked locked(inode->Lock()); 1164 1165 status_t status = inode->SetFileSize(transaction, 0); 1166 if (status >= B_OK) 1167 status = inode->WriteBack(transaction); 1168 1169 if (status < B_OK) { 1170 // bfs_free_cookie() is only called if this function is successful 1171 free(cookie); 1172 return status; 1173 } 1174 1175 transaction.Done(); 1176 } 1177 1178 *_cookie = cookie; 1179 return B_OK; 1180 } 1181 1182 1183 static status_t 1184 bfs_read(void *_ns, void *_node, void *_cookie, off_t pos, void *buffer, 1185 size_t *_length) 1186 { 1187 //FUNCTION(); 1188 Inode *inode = (Inode *)_node; 1189 1190 if (!inode->HasUserAccessableStream()) { 1191 *_length = 0; 1192 RETURN_ERROR(B_BAD_VALUE); 1193 } 1194 1195 return inode->ReadAt(pos, (uint8 *)buffer, _length); 1196 } 1197 1198 1199 static status_t 1200 bfs_write(void *_ns, void *_node, void *_cookie, off_t pos, const void *buffer, 1201 size_t *_length) 1202 { 1203 //FUNCTION(); 1204 Volume *volume = (Volume *)_ns; 1205 Inode *inode = (Inode *)_node; 1206 1207 if (!inode->HasUserAccessableStream()) { 1208 *_length = 0; 1209 RETURN_ERROR(B_BAD_VALUE); 1210 } 1211 1212 file_cookie *cookie = (file_cookie *)_cookie; 1213 1214 if (cookie->open_mode & O_APPEND) 1215 pos = inode->Size(); 1216 1217 Transaction transaction; 1218 // We are not starting the transaction here, since 1219 // it might not be needed at all (the contents of 1220 // regular files aren't logged) 1221 1222 status_t status = inode->WriteAt(transaction, pos, (const uint8 *)buffer, 1223 _length); 1224 1225 if (status == B_OK) 1226 transaction.Done(); 1227 1228 if (status == B_OK) { 1229 ReadLocked locker(inode->Lock()); 1230 1231 // periodically notify if the file size has changed 1232 // ToDo: should we better test for a change in the last_modified time only? 1233 if (!inode->IsDeleted() && cookie->last_size != inode->Size() 1234 && system_time() > cookie->last_notification + INODE_NOTIFICATION_INTERVAL) { 1235 notify_stat_changed(volume->ID(), inode->ID(), 1236 B_STAT_MODIFICATION_TIME | B_STAT_SIZE); 1237 cookie->last_size = inode->Size(); 1238 cookie->last_notification = system_time(); 1239 } 1240 } 1241 1242 return status; 1243 } 1244 1245 1246 /** Do whatever is necessary to close a file, EXCEPT for freeing 1247 * the cookie! 1248 */ 1249 1250 static status_t 1251 bfs_close(void *_ns, void *_node, void *_cookie) 1252 { 1253 FUNCTION(); 1254 if (_ns == NULL || _node == NULL || _cookie == NULL) 1255 return B_BAD_VALUE; 1256 1257 return B_OK; 1258 } 1259 1260 1261 static status_t 1262 bfs_free_cookie(void *_ns, void *_node, void *_cookie) 1263 { 1264 FUNCTION(); 1265 1266 if (_ns == NULL || _node == NULL || _cookie == NULL) 1267 return B_BAD_VALUE; 1268 1269 file_cookie *cookie = (file_cookie *)_cookie; 1270 Volume *volume = (Volume *)_ns; 1271 Inode *inode = (Inode *)_node; 1272 1273 Transaction transaction; 1274 bool needsTrimming; 1275 1276 { 1277 ReadLocked locker(inode->Lock()); 1278 needsTrimming = inode->NeedsTrimming(); 1279 1280 if ((cookie->open_mode & O_RWMASK) != 0 1281 && !inode->IsDeleted() 1282 && (needsTrimming 1283 || inode->OldLastModified() != inode->LastModified() 1284 || inode->OldSize() != inode->Size())) { 1285 locker.Unlock(); 1286 transaction.Start(volume, inode->BlockNumber()); 1287 } 1288 } 1289 1290 WriteLocked locker(inode->Lock()); 1291 status_t status = transaction.IsStarted() ? B_OK : B_ERROR; 1292 1293 if (status == B_OK) { 1294 // trim the preallocated blocks and update the size, 1295 // and last_modified indices if needed 1296 bool changedSize = false, changedTime = false; 1297 Index index(volume); 1298 1299 if (needsTrimming) { 1300 status = inode->TrimPreallocation(transaction); 1301 if (status < B_OK) { 1302 FATAL(("Could not trim preallocated blocks: inode %Ld, transaction %d: %s!\n", 1303 inode->ID(), (int)transaction.ID(), strerror(status))); 1304 1305 // we still want this transaction to succeed 1306 status = B_OK; 1307 } 1308 } 1309 if (needsTrimming || inode->OldSize() != inode->Size()) { 1310 index.UpdateSize(transaction, inode); 1311 changedSize = true; 1312 } 1313 if (inode->OldLastModified() != inode->LastModified()) { 1314 index.UpdateLastModified(transaction, inode, inode->LastModified()); 1315 changedTime = true; 1316 1317 // updating the index doesn't write back the inode 1318 inode->WriteBack(transaction); 1319 } 1320 1321 if (changedSize || changedTime) { 1322 notify_stat_changed(volume->ID(), inode->ID(), 1323 (changedTime ? B_STAT_MODIFICATION_TIME : 0) 1324 | (changedSize ? B_STAT_SIZE : 0)); 1325 } 1326 } 1327 if (status == B_OK) 1328 transaction.Done(); 1329 1330 if (inode->Flags() & INODE_CHKBFS_RUNNING) { 1331 // "chkbfs" exited abnormally, so we have to stop it here... 1332 FATAL(("check process was aborted!\n")); 1333 volume->Allocator().StopChecking(NULL); 1334 } 1335 1336 return B_OK; 1337 } 1338 1339 1340 /** Checks access permissions, return B_NOT_ALLOWED if the action 1341 * is not allowed. 1342 */ 1343 1344 static status_t 1345 bfs_access(void *_ns, void *_node, int accessMode) 1346 { 1347 //FUNCTION(); 1348 1349 if (_ns == NULL || _node == NULL) 1350 return B_BAD_VALUE; 1351 1352 Inode *inode = (Inode *)_node; 1353 status_t status = inode->CheckPermissions(accessMode); 1354 if (status < B_OK) 1355 RETURN_ERROR(status); 1356 1357 return B_OK; 1358 } 1359 1360 1361 static status_t 1362 bfs_read_link(void *_ns, void *_node, char *buffer, size_t *_bufferSize) 1363 { 1364 FUNCTION(); 1365 1366 Inode *inode = (Inode *)_node; 1367 size_t bufferSize = *_bufferSize; 1368 1369 if (!inode->IsSymLink()) 1370 RETURN_ERROR(B_BAD_VALUE); 1371 1372 if (inode->Flags() & INODE_LONG_SYMLINK) { 1373 // we also need space for the terminating null byte 1374 if (inode->Size() >= bufferSize) { 1375 *_bufferSize = inode->Size() + 1; 1376 return B_BUFFER_OVERFLOW; 1377 } 1378 1379 status_t status = inode->ReadAt(0, (uint8 *)buffer, _bufferSize); 1380 if (status < B_OK) 1381 RETURN_ERROR(status); 1382 1383 buffer[++*_bufferSize] = '\0'; 1384 return B_OK; 1385 } 1386 1387 *_bufferSize = strlcpy(buffer, inode->Node().short_symlink, bufferSize) + 1; 1388 if (*_bufferSize > bufferSize) 1389 return B_BUFFER_OVERFLOW; 1390 1391 return B_OK; 1392 } 1393 1394 1395 // #pragma mark - 1396 // Directory functions 1397 1398 1399 static status_t 1400 bfs_create_dir(void *_ns, void *_directory, const char *name, int mode, 1401 ino_t *_newVnodeID) 1402 { 1403 FUNCTION_START(("name = \"%s\", perms = %d\n", name, mode)); 1404 1405 if (_ns == NULL || _directory == NULL 1406 || name == NULL || *name == '\0') 1407 RETURN_ERROR(B_BAD_VALUE); 1408 1409 Volume *volume = (Volume *)_ns; 1410 Inode *directory = (Inode *)_directory; 1411 1412 if (!directory->IsDirectory()) 1413 RETURN_ERROR(B_BAD_TYPE); 1414 1415 status_t status = directory->CheckPermissions(W_OK); 1416 if (status < B_OK) 1417 RETURN_ERROR(status); 1418 1419 Transaction transaction(volume, directory->BlockNumber()); 1420 1421 // Inode::Create() locks the inode if we pass the "id" parameter, but we 1422 // need it anyway 1423 off_t id; 1424 status = Inode::Create(transaction, directory, name, 1425 S_DIRECTORY | (mode & S_IUMSK), 0, 0, NULL, &id); 1426 if (status == B_OK) { 1427 *_newVnodeID = id; 1428 put_vnode(volume->ID(), id); 1429 transaction.Done(); 1430 1431 notify_entry_created(volume->ID(), directory->ID(), name, id); 1432 } 1433 1434 return status; 1435 } 1436 1437 1438 static status_t 1439 bfs_remove_dir(void *_ns, void *_directory, const char *name) 1440 { 1441 FUNCTION_START(("name = \"%s\"\n", name)); 1442 1443 if (_ns == NULL || _directory == NULL || name == NULL || *name == '\0') 1444 return B_BAD_VALUE; 1445 1446 Volume *volume = (Volume *)_ns; 1447 Inode *directory = (Inode *)_directory; 1448 1449 Transaction transaction(volume, directory->BlockNumber()); 1450 1451 off_t id; 1452 status_t status = directory->Remove(transaction, name, &id, true); 1453 if (status == B_OK) { 1454 transaction.Done(); 1455 1456 notify_entry_removed(volume->ID(), directory->ID(), name, id); 1457 } 1458 1459 return status; 1460 } 1461 1462 1463 /** Opens a directory ready to be traversed. 1464 * bfs_open_dir() is also used by bfs_open_index_dir(). 1465 */ 1466 1467 static status_t 1468 bfs_open_dir(void *_ns, void *_node, void **_cookie) 1469 { 1470 FUNCTION(); 1471 1472 if (_ns == NULL || _node == NULL || _cookie == NULL) 1473 RETURN_ERROR(B_BAD_VALUE); 1474 1475 Inode *inode = (Inode *)_node; 1476 1477 status_t status = inode->CheckPermissions(R_OK); 1478 if (status < B_OK) 1479 RETURN_ERROR(status); 1480 1481 // we don't ask here for directories only, because the bfs_open_index_dir() 1482 // function utilizes us (so we must be able to open indices as well) 1483 if (!inode->IsContainer()) 1484 RETURN_ERROR(B_BAD_VALUE); 1485 1486 BPlusTree *tree; 1487 if (inode->GetTree(&tree) != B_OK) 1488 RETURN_ERROR(B_BAD_VALUE); 1489 1490 TreeIterator *iterator = new TreeIterator(tree); 1491 if (iterator == NULL) 1492 RETURN_ERROR(B_NO_MEMORY); 1493 1494 *_cookie = iterator; 1495 return B_OK; 1496 } 1497 1498 1499 static status_t 1500 bfs_read_dir(void *_ns, void *_node, void *_cookie, struct dirent *dirent, 1501 size_t bufferSize, uint32 *_num) 1502 { 1503 FUNCTION(); 1504 1505 TreeIterator *iterator = (TreeIterator *)_cookie; 1506 if (iterator == NULL) 1507 RETURN_ERROR(B_BAD_VALUE); 1508 1509 uint16 length; 1510 ino_t id; 1511 status_t status = iterator->GetNextEntry(dirent->d_name, &length, bufferSize, &id); 1512 if (status == B_ENTRY_NOT_FOUND) { 1513 *_num = 0; 1514 return B_OK; 1515 } else if (status != B_OK) 1516 RETURN_ERROR(status); 1517 1518 Volume *volume = (Volume *)_ns; 1519 1520 dirent->d_dev = volume->ID(); 1521 dirent->d_ino = id; 1522 1523 dirent->d_reclen = sizeof(struct dirent) + length; 1524 1525 *_num = 1; 1526 return B_OK; 1527 } 1528 1529 1530 /** Sets the TreeIterator back to the beginning of the directory 1531 */ 1532 1533 static status_t 1534 bfs_rewind_dir(void * /*ns*/, void * /*node*/, void *_cookie) 1535 { 1536 FUNCTION(); 1537 TreeIterator *iterator = (TreeIterator *)_cookie; 1538 1539 if (iterator == NULL) 1540 RETURN_ERROR(B_BAD_VALUE); 1541 1542 return iterator->Rewind(); 1543 } 1544 1545 1546 static status_t 1547 bfs_close_dir(void * /*ns*/, void * /*node*/, void * /*_cookie*/) 1548 { 1549 FUNCTION(); 1550 // Do whatever you need to to close a directory, but DON'T free the cookie! 1551 return B_OK; 1552 } 1553 1554 1555 static status_t 1556 bfs_free_dir_cookie(void *ns, void *node, void *_cookie) 1557 { 1558 TreeIterator *iterator = (TreeIterator *)_cookie; 1559 1560 if (iterator == NULL) 1561 RETURN_ERROR(B_BAD_VALUE); 1562 1563 delete iterator; 1564 return B_OK; 1565 } 1566 1567 1568 // #pragma mark - 1569 // Attribute functions 1570 1571 1572 static status_t 1573 bfs_open_attr_dir(void *_ns, void *_node, void **_cookie) 1574 { 1575 Inode *inode = (Inode *)_node; 1576 1577 FUNCTION(); 1578 1579 AttributeIterator *iterator = new AttributeIterator(inode); 1580 if (iterator == NULL) 1581 RETURN_ERROR(B_NO_MEMORY); 1582 1583 *_cookie = iterator; 1584 return B_OK; 1585 } 1586 1587 1588 static status_t 1589 bfs_close_attr_dir(void *ns, void *node, void *cookie) 1590 { 1591 FUNCTION(); 1592 return B_OK; 1593 } 1594 1595 1596 static status_t 1597 bfs_free_attr_dir_cookie(void *ns, void *node, void *_cookie) 1598 { 1599 FUNCTION(); 1600 AttributeIterator *iterator = (AttributeIterator *)_cookie; 1601 1602 if (iterator == NULL) 1603 RETURN_ERROR(B_BAD_VALUE); 1604 1605 delete iterator; 1606 return B_OK; 1607 } 1608 1609 1610 static status_t 1611 bfs_rewind_attr_dir(void *_ns, void *_node, void *_cookie) 1612 { 1613 FUNCTION(); 1614 1615 AttributeIterator *iterator = (AttributeIterator *)_cookie; 1616 if (iterator == NULL) 1617 RETURN_ERROR(B_BAD_VALUE); 1618 1619 RETURN_ERROR(iterator->Rewind()); 1620 } 1621 1622 1623 static status_t 1624 bfs_read_attr_dir(void *_ns, void *node, void *_cookie, struct dirent *dirent, 1625 size_t bufferSize, uint32 *_num) 1626 { 1627 FUNCTION(); 1628 AttributeIterator *iterator = (AttributeIterator *)_cookie; 1629 1630 if (iterator == NULL) 1631 RETURN_ERROR(B_BAD_VALUE); 1632 1633 uint32 type; 1634 size_t length; 1635 status_t status = iterator->GetNext(dirent->d_name, &length, &type, &dirent->d_ino); 1636 if (status == B_ENTRY_NOT_FOUND) { 1637 *_num = 0; 1638 return B_OK; 1639 } else if (status != B_OK) { 1640 RETURN_ERROR(status); 1641 } 1642 1643 Volume *volume = (Volume *)_ns; 1644 1645 dirent->d_dev = volume->ID(); 1646 dirent->d_reclen = sizeof(struct dirent) + length; 1647 1648 *_num = 1; 1649 return B_OK; 1650 } 1651 1652 1653 static status_t 1654 bfs_create_attr(fs_volume _fs, fs_vnode _node, const char *name, uint32 type, 1655 int openMode, fs_cookie *_cookie) 1656 { 1657 FUNCTION(); 1658 1659 Inode *inode = (Inode *)_node; 1660 Attribute attribute(inode); 1661 1662 return attribute.Create(name, type, openMode, (attr_cookie **)_cookie); 1663 } 1664 1665 1666 static status_t 1667 bfs_open_attr(fs_volume _fs, fs_vnode _node, const char *name, int openMode, 1668 fs_cookie *_cookie) 1669 { 1670 FUNCTION(); 1671 1672 Inode *inode = (Inode *)_node; 1673 Attribute attribute(inode); 1674 1675 return attribute.Open(name, openMode, (attr_cookie **)_cookie); 1676 } 1677 1678 1679 static status_t 1680 bfs_close_attr(fs_volume _fs, fs_vnode _file, fs_cookie cookie) 1681 { 1682 return B_OK; 1683 } 1684 1685 1686 static status_t 1687 bfs_free_attr_cookie(fs_volume _fs, fs_vnode _file, fs_cookie cookie) 1688 { 1689 free(cookie); 1690 return B_OK; 1691 } 1692 1693 1694 static status_t 1695 bfs_read_attr(fs_volume _fs, fs_vnode _file, fs_cookie _cookie, off_t pos, 1696 void *buffer, size_t *_length) 1697 { 1698 FUNCTION(); 1699 1700 attr_cookie *cookie = (attr_cookie *)_cookie; 1701 Inode *inode = (Inode *)_file; 1702 1703 Attribute attribute(inode, cookie); 1704 1705 return attribute.Read(cookie, pos, (uint8 *)buffer, _length); 1706 } 1707 1708 1709 static status_t 1710 bfs_write_attr(fs_volume _fs, fs_vnode _file, fs_cookie _cookie, off_t pos, 1711 const void *buffer, size_t *_length) 1712 { 1713 FUNCTION(); 1714 1715 attr_cookie *cookie = (attr_cookie *)_cookie; 1716 Volume *volume = (Volume *)_fs; 1717 Inode *inode = (Inode *)_file; 1718 1719 Transaction transaction(volume, inode->BlockNumber()); 1720 Attribute attribute(inode, cookie); 1721 1722 status_t status = attribute.Write(transaction, cookie, pos, (const uint8 *)buffer, _length); 1723 if (status == B_OK) { 1724 transaction.Done(); 1725 1726 notify_attribute_changed(volume->ID(), inode->ID(), cookie->name, B_ATTR_CHANGED); 1727 // ToDo: B_ATTR_CREATED is not yet taken into account 1728 // (we don't know what Attribute::Write() does exactly) 1729 } 1730 1731 return status; 1732 } 1733 1734 1735 static status_t 1736 bfs_read_attr_stat(fs_volume _fs, fs_vnode _file, fs_cookie _cookie, struct stat *stat) 1737 { 1738 FUNCTION(); 1739 1740 attr_cookie *cookie = (attr_cookie *)_cookie; 1741 Inode *inode = (Inode *)_file; 1742 1743 Attribute attribute(inode, cookie); 1744 1745 return attribute.Stat(*stat); 1746 } 1747 1748 1749 static status_t 1750 bfs_write_attr_stat(fs_volume fs, fs_vnode file, fs_cookie cookie, 1751 const struct stat *stat, int statMask) 1752 { 1753 return EOPNOTSUPP; 1754 } 1755 1756 1757 static status_t 1758 bfs_rename_attr(fs_volume _fs, fs_vnode fromFile, const char *fromName, 1759 fs_vnode toFile, const char *toName) 1760 { 1761 FUNCTION_START(("name = \"%s\", to = \"%s\"\n", fromName, toName)); 1762 1763 // ToDo: implement bfs_rename_attr()! 1764 // I'll skip the implementation here, and will do it for Haiku - at least 1765 // there will be an API to move one attribute to another file, making that 1766 // function much more complicated - oh joy ;-) 1767 1768 RETURN_ERROR(B_ENTRY_NOT_FOUND); 1769 } 1770 1771 1772 static status_t 1773 bfs_remove_attr(fs_volume _fs, fs_vnode _node, const char *name) 1774 { 1775 FUNCTION_START(("name = \"%s\"\n", name)); 1776 1777 if (name == NULL) 1778 return B_BAD_VALUE; 1779 1780 Volume *volume = (Volume *)_fs; 1781 Inode *inode = (Inode *)_node; 1782 1783 status_t status = inode->CheckPermissions(W_OK); 1784 if (status < B_OK) 1785 return status; 1786 1787 Transaction transaction(volume, inode->BlockNumber()); 1788 1789 status = inode->RemoveAttribute(transaction, name); 1790 if (status == B_OK) { 1791 transaction.Done(); 1792 1793 notify_attribute_changed(volume->ID(), inode->ID(), name, B_ATTR_REMOVED); 1794 } 1795 1796 return status; 1797 } 1798 1799 1800 // #pragma mark - 1801 // Index functions 1802 1803 1804 static status_t 1805 bfs_open_index_dir(void *_ns, void **_cookie) 1806 { 1807 FUNCTION(); 1808 if (_ns == NULL || _cookie == NULL) 1809 RETURN_ERROR(B_BAD_VALUE); 1810 1811 Volume *volume = (Volume *)_ns; 1812 1813 if (volume->IndicesNode() == NULL) 1814 RETURN_ERROR(B_ENTRY_NOT_FOUND); 1815 1816 // Since the indices root node is just a directory, and we are storing 1817 // a pointer to it in our Volume object, we can just use the directory 1818 // traversal functions. 1819 // In fact we're storing it in the Volume object for that reason. 1820 1821 RETURN_ERROR(bfs_open_dir(_ns, volume->IndicesNode(), _cookie)); 1822 } 1823 1824 1825 static status_t 1826 bfs_close_index_dir(fs_volume _fs, fs_cookie _cookie) 1827 { 1828 FUNCTION(); 1829 if (_fs == NULL || _cookie == NULL) 1830 RETURN_ERROR(B_BAD_VALUE); 1831 1832 Volume *volume = (Volume *)_fs; 1833 RETURN_ERROR(bfs_close_dir(_fs, volume->IndicesNode(), _cookie)); 1834 } 1835 1836 1837 static status_t 1838 bfs_free_index_dir_cookie(fs_volume _fs, fs_cookie _cookie) 1839 { 1840 FUNCTION(); 1841 if (_fs == NULL || _cookie == NULL) 1842 RETURN_ERROR(B_BAD_VALUE); 1843 1844 Volume *volume = (Volume *)_fs; 1845 RETURN_ERROR(bfs_free_dir_cookie(_fs, volume->IndicesNode(), _cookie)); 1846 } 1847 1848 1849 static status_t 1850 bfs_rewind_index_dir(fs_volume _fs, fs_cookie _cookie) 1851 { 1852 FUNCTION(); 1853 if (_fs == NULL || _cookie == NULL) 1854 RETURN_ERROR(B_BAD_VALUE); 1855 1856 Volume *volume = (Volume *)_fs; 1857 RETURN_ERROR(bfs_rewind_dir(_fs, volume->IndicesNode(), _cookie)); 1858 } 1859 1860 1861 static status_t 1862 bfs_read_index_dir(fs_volume _fs, fs_cookie _cookie, struct dirent *dirent, 1863 size_t bufferSize, uint32 *_num) 1864 { 1865 FUNCTION(); 1866 if (_fs == NULL || _cookie == NULL) 1867 RETURN_ERROR(B_BAD_VALUE); 1868 1869 Volume *volume = (Volume *)_fs; 1870 RETURN_ERROR(bfs_read_dir(_fs, volume->IndicesNode(), _cookie, dirent, bufferSize, _num)); 1871 } 1872 1873 1874 static status_t 1875 bfs_create_index(fs_volume _fs, const char *name, uint32 type, uint32 flags) 1876 { 1877 FUNCTION_START(("name = \"%s\", type = %ld, flags = %ld\n", name, type, flags)); 1878 if (_fs == NULL || name == NULL || *name == '\0') 1879 return B_BAD_VALUE; 1880 1881 Volume *volume = (Volume *)_fs; 1882 1883 if (volume->IsReadOnly()) 1884 return B_READ_ONLY_DEVICE; 1885 1886 // only root users are allowed to create indices 1887 if (geteuid() != 0) 1888 return B_NOT_ALLOWED; 1889 1890 Transaction transaction(volume, volume->Indices()); 1891 1892 Index index(volume); 1893 status_t status = index.Create(transaction, name, type); 1894 1895 if (status == B_OK) 1896 transaction.Done(); 1897 1898 RETURN_ERROR(status); 1899 } 1900 1901 1902 static status_t 1903 bfs_remove_index(void *_ns, const char *name) 1904 { 1905 FUNCTION(); 1906 if (_ns == NULL || name == NULL || *name == '\0') 1907 return B_BAD_VALUE; 1908 1909 Volume *volume = (Volume *)_ns; 1910 1911 if (volume->IsReadOnly()) 1912 return B_READ_ONLY_DEVICE; 1913 1914 // only root users are allowed to remove indices 1915 if (geteuid() != 0) 1916 return B_NOT_ALLOWED; 1917 1918 Inode *indices; 1919 if ((indices = volume->IndicesNode()) == NULL) 1920 return B_ENTRY_NOT_FOUND; 1921 1922 Transaction transaction(volume, volume->Indices()); 1923 1924 status_t status = indices->Remove(transaction, name); 1925 if (status == B_OK) 1926 transaction.Done(); 1927 1928 RETURN_ERROR(status); 1929 } 1930 1931 1932 static status_t 1933 bfs_stat_index(fs_volume _fs, const char *name, struct stat *stat) 1934 { 1935 FUNCTION_START(("name = %s\n", name)); 1936 if (_fs == NULL || name == NULL || stat == NULL) 1937 RETURN_ERROR(B_BAD_VALUE); 1938 1939 Volume *volume = (Volume *)_fs; 1940 Index index(volume); 1941 status_t status = index.SetTo(name); 1942 if (status < B_OK) 1943 RETURN_ERROR(status); 1944 1945 bfs_inode &node = index.Node()->Node(); 1946 1947 stat->st_type = index.Type(); 1948 stat->st_size = node.data.Size(); 1949 stat->st_mode = node.Mode(); 1950 1951 stat->st_nlink = 1; 1952 stat->st_blksize = 65536; 1953 1954 stat->st_uid = node.UserID(); 1955 stat->st_gid = node.GroupID(); 1956 1957 stat->st_atime = time(NULL); 1958 stat->st_mtime = stat->st_ctime = (time_t)(node.LastModifiedTime() >> INODE_TIME_SHIFT); 1959 stat->st_crtime = (time_t)(node.CreateTime() >> INODE_TIME_SHIFT); 1960 1961 return B_OK; 1962 } 1963 1964 1965 // #pragma mark - 1966 // Query functions 1967 1968 1969 static status_t 1970 bfs_open_query(void *_fs, const char *queryString, uint32 flags, port_id port, 1971 uint32 token, void **_cookie) 1972 { 1973 FUNCTION_START(("bfs_open_query(\"%s\", flags = %lu, port_id = %ld, token = %ld)\n", 1974 queryString, flags, port, token)); 1975 1976 Volume *volume = (Volume *)_fs; 1977 1978 Expression *expression = new Expression((char *)queryString); 1979 if (expression == NULL) 1980 RETURN_ERROR(B_NO_MEMORY); 1981 1982 if (expression->InitCheck() < B_OK) { 1983 INFORM(("Could not parse query \"%s\", stopped at: \"%s\"\n", 1984 queryString, expression->Position())); 1985 1986 delete expression; 1987 RETURN_ERROR(B_BAD_VALUE); 1988 } 1989 1990 Query *query = new Query(volume, expression, flags); 1991 if (query == NULL) { 1992 delete expression; 1993 RETURN_ERROR(B_NO_MEMORY); 1994 } 1995 1996 if (flags & B_LIVE_QUERY) 1997 query->SetLiveMode(port, token); 1998 1999 *_cookie = (void *)query; 2000 2001 return B_OK; 2002 } 2003 2004 2005 static status_t 2006 bfs_close_query(void *fs, void *cookie) 2007 { 2008 FUNCTION(); 2009 return B_OK; 2010 } 2011 2012 2013 static status_t 2014 bfs_free_query_cookie(void *fs, void *cookie) 2015 { 2016 FUNCTION(); 2017 if (cookie == NULL) 2018 RETURN_ERROR(B_BAD_VALUE); 2019 2020 Query *query = (Query *)cookie; 2021 Expression *expression = query->GetExpression(); 2022 delete query; 2023 delete expression; 2024 2025 return B_OK; 2026 } 2027 2028 2029 static status_t 2030 bfs_read_query(void */*fs*/, void *cookie, struct dirent *dirent, size_t bufferSize, uint32 *_num) 2031 { 2032 FUNCTION(); 2033 Query *query = (Query *)cookie; 2034 if (query == NULL) 2035 RETURN_ERROR(B_BAD_VALUE); 2036 2037 status_t status = query->GetNextEntry(dirent, bufferSize); 2038 if (status == B_OK) 2039 *_num = 1; 2040 else if (status == B_ENTRY_NOT_FOUND) 2041 *_num = 0; 2042 else 2043 return status; 2044 2045 return B_OK; 2046 } 2047 2048 2049 static status_t 2050 bfs_rewind_query(void */*fs*/, void *cookie) 2051 { 2052 FUNCTION(); 2053 Query *query = (Query *)cookie; 2054 if (query == NULL) 2055 RETURN_ERROR(B_BAD_VALUE); 2056 2057 return query->Rewind(); 2058 } 2059 2060 2061 // #pragma mark - 2062 2063 2064 static uint32 2065 bfs_get_supported_operations(partition_data* partition, uint32 mask) 2066 { 2067 // TODO: We should at least check the partition size. 2068 return B_DISK_SYSTEM_SUPPORTS_INITIALIZING 2069 | B_DISK_SYSTEM_SUPPORTS_CONTENT_NAME; 2070 } 2071 2072 2073 static status_t 2074 bfs_initialize(int fd, partition_id partitionID, const char *name, 2075 const char *parameterString, off_t /*partitionSize*/, disk_job_id job) 2076 { 2077 // check name 2078 status_t status = check_volume_name(name); 2079 if (status != B_OK) 2080 return status; 2081 2082 // parse parameters 2083 initialize_parameters parameters; 2084 status = parse_initialize_parameters(parameterString, parameters); 2085 if (status != B_OK) 2086 return status; 2087 2088 update_disk_device_job_progress(job, 0); 2089 2090 // initialize the volume 2091 Volume volume(-1); 2092 status = volume.Initialize(fd, name, parameters.blockSize, 2093 parameters.flags); 2094 if (status < B_OK) { 2095 INFORM(("Initializing volume failed: %s\n", strerror(status))); 2096 return status; 2097 } 2098 2099 // rescan partition 2100 status = scan_partition(partitionID); 2101 if (status != B_OK) 2102 return status; 2103 2104 update_disk_device_job_progress(job, 1); 2105 2106 // print some info, if desired 2107 if (parameters.verbose) { 2108 disk_super_block super = volume.SuperBlock(); 2109 2110 INFORM(("Disk was initialized successfully.\n")); 2111 INFORM(("\tname: \"%s\"\n", super.name)); 2112 INFORM(("\tnum blocks: %Ld\n", super.NumBlocks())); 2113 INFORM(("\tused blocks: %Ld\n", super.UsedBlocks())); 2114 INFORM(("\tblock size: %u bytes\n", (unsigned)super.BlockSize())); 2115 INFORM(("\tnum allocation groups: %d\n", 2116 (int)super.AllocationGroups())); 2117 INFORM(("\tallocation group size: %ld blocks\n", 2118 1L << super.AllocationGroupShift())); 2119 INFORM(("\tlog size: %u blocks\n", super.log_blocks.Length())); 2120 } 2121 2122 return B_OK; 2123 } 2124 2125 2126 // #pragma mark - 2127 2128 2129 static status_t 2130 bfs_std_ops(int32 op, ...) 2131 { 2132 switch (op) { 2133 case B_MODULE_INIT: 2134 #ifdef DEBUG 2135 add_debugger_commands(); 2136 #endif 2137 return B_OK; 2138 case B_MODULE_UNINIT: 2139 #ifdef DEBUG 2140 remove_debugger_commands(); 2141 #endif 2142 return B_OK; 2143 2144 default: 2145 return B_ERROR; 2146 } 2147 } 2148 2149 2150 static file_system_module_info sBeFileSystem = { 2151 { 2152 "file_systems/bfs" B_CURRENT_FS_API_VERSION, 2153 0, 2154 bfs_std_ops, 2155 }, 2156 2157 "Be File System", 2158 2159 // DDM flags 2160 0 2161 // | B_DISK_SYSTEM_SUPPORTS_CHECKING 2162 // | B_DISK_SYSTEM_SUPPORTS_REPAIRING 2163 // | B_DISK_SYSTEM_SUPPORTS_RESIZING 2164 // | B_DISK_SYSTEM_SUPPORTS_MOVING 2165 // | B_DISK_SYSTEM_SUPPORTS_SETTING_CONTENT_NAME 2166 // | B_DISK_SYSTEM_SUPPORTS_SETTING_CONTENT_PARAMETERS 2167 | B_DISK_SYSTEM_SUPPORTS_INITIALIZING 2168 | B_DISK_SYSTEM_SUPPORTS_CONTENT_NAME 2169 // | B_DISK_SYSTEM_SUPPORTS_DEFRAGMENTING 2170 // | B_DISK_SYSTEM_SUPPORTS_DEFRAGMENTING_WHILE_MOUNTED 2171 // | B_DISK_SYSTEM_SUPPORTS_CHECKING_WHILE_MOUNTED 2172 // | B_DISK_SYSTEM_SUPPORTS_REPAIRING_WHILE_MOUNTED 2173 // | B_DISK_SYSTEM_SUPPORTS_RESIZING_WHILE_MOUNTED 2174 // | B_DISK_SYSTEM_SUPPORTS_MOVING_WHILE_MOUNTED 2175 // | B_DISK_SYSTEM_SUPPORTS_SETTING_CONTENT_NAME_WHILE_MOUNTED 2176 // | B_DISK_SYSTEM_SUPPORTS_SETTING_CONTENT_PARAMETERS_WHILE_MOUNTED 2177 , 2178 2179 // scanning 2180 bfs_identify_partition, 2181 bfs_scan_partition, 2182 bfs_free_identify_partition_cookie, 2183 NULL, // free_partition_content_cookie() 2184 2185 &bfs_mount, 2186 &bfs_unmount, 2187 &bfs_read_fs_stat, 2188 &bfs_write_fs_stat, 2189 &bfs_sync, 2190 2191 /* vnode operations */ 2192 &bfs_lookup, 2193 &bfs_get_vnode_name, 2194 &bfs_get_vnode, 2195 &bfs_put_vnode, 2196 &bfs_remove_vnode, 2197 2198 /* VM file access */ 2199 &bfs_can_page, 2200 &bfs_read_pages, 2201 &bfs_write_pages, 2202 2203 &bfs_get_file_map, 2204 2205 &bfs_ioctl, 2206 &bfs_set_flags, 2207 NULL, // &bfs_select 2208 NULL, // &bfs_deselect 2209 &bfs_fsync, 2210 2211 &bfs_read_link, 2212 &bfs_create_symlink, 2213 2214 &bfs_link, 2215 &bfs_unlink, 2216 &bfs_rename, 2217 2218 &bfs_access, 2219 &bfs_read_stat, 2220 &bfs_write_stat, 2221 2222 /* file operations */ 2223 &bfs_create, 2224 &bfs_open, 2225 &bfs_close, 2226 &bfs_free_cookie, 2227 &bfs_read, 2228 &bfs_write, 2229 2230 /* directory operations */ 2231 &bfs_create_dir, 2232 &bfs_remove_dir, 2233 &bfs_open_dir, 2234 &bfs_close_dir, 2235 &bfs_free_dir_cookie, 2236 &bfs_read_dir, 2237 &bfs_rewind_dir, 2238 2239 /* attribute directory operations */ 2240 &bfs_open_attr_dir, 2241 &bfs_close_attr_dir, 2242 &bfs_free_attr_dir_cookie, 2243 &bfs_read_attr_dir, 2244 &bfs_rewind_attr_dir, 2245 2246 /* attribute operations */ 2247 &bfs_create_attr, 2248 &bfs_open_attr, 2249 &bfs_close_attr, 2250 &bfs_free_attr_cookie, 2251 &bfs_read_attr, 2252 &bfs_write_attr, 2253 2254 &bfs_read_attr_stat, 2255 &bfs_write_attr_stat, 2256 &bfs_rename_attr, 2257 &bfs_remove_attr, 2258 2259 /* index directory & index operations */ 2260 &bfs_open_index_dir, 2261 &bfs_close_index_dir, 2262 &bfs_free_index_dir_cookie, 2263 &bfs_read_index_dir, 2264 &bfs_rewind_index_dir, 2265 2266 &bfs_create_index, 2267 &bfs_remove_index, 2268 &bfs_stat_index, 2269 2270 /* query operations */ 2271 &bfs_open_query, 2272 &bfs_close_query, 2273 &bfs_free_query_cookie, 2274 &bfs_read_query, 2275 &bfs_rewind_query, 2276 2277 /* capability querying operations */ 2278 &bfs_get_supported_operations, 2279 2280 NULL, // validate_resize 2281 NULL, // validate_move 2282 NULL, // validate_set_content_name 2283 NULL, // validate_set_content_parameters 2284 NULL, // validate_initialize, 2285 2286 /* shadow partition modification */ 2287 NULL, // shadow_changed 2288 2289 /* writing */ 2290 NULL, // defragment 2291 NULL, // repair 2292 NULL, // resize 2293 NULL, // move 2294 NULL, // set_content_name 2295 NULL, // set_content_parameters 2296 bfs_initialize, 2297 }; 2298 2299 module_info *modules[] = { 2300 (module_info *)&sBeFileSystem, 2301 NULL, 2302 }; 2303