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