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