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