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