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