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