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