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