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