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