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