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