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