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