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