1 /* 2 * Copyright 2010, Jérôme Duval, korli@users.berlios.de. 3 * Copyright 2008, 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 <algorithm> 9 #include <dirent.h> 10 #include <sys/ioctl.h> 11 #include <util/kernel_cpp.h> 12 #include <string.h> 13 14 #include <AutoDeleter.h> 15 #include <fs_cache.h> 16 #include <fs_info.h> 17 #include <io_requests.h> 18 #include <NodeMonitor.h> 19 #include <util/AutoLock.h> 20 21 #include "Attribute.h" 22 #include "CachedBlock.h" 23 #include "DirectoryIterator.h" 24 #include "ext2.h" 25 #include "HTree.h" 26 #include "Inode.h" 27 #include "Journal.h" 28 #include "Utility.h" 29 30 31 //#define TRACE_EXT2 32 #ifdef TRACE_EXT2 33 # define TRACE(x...) dprintf("\33[34mext2:\33[0m " x) 34 #else 35 # define TRACE(x...) ; 36 #endif 37 #define ERROR(x...) dprintf("\33[34mext2:\33[0m " x) 38 39 40 #define EXT2_IO_SIZE 65536 41 42 43 struct identify_cookie { 44 ext2_super_block super_block; 45 }; 46 47 48 //! ext2_io() callback hook 49 static status_t 50 iterative_io_get_vecs_hook(void* cookie, io_request* request, off_t offset, 51 size_t size, struct file_io_vec* vecs, size_t* _count) 52 { 53 Inode* inode = (Inode*)cookie; 54 55 return file_map_translate(inode->Map(), offset, size, vecs, _count, 56 inode->GetVolume()->BlockSize()); 57 } 58 59 60 //! ext2_io() callback hook 61 static status_t 62 iterative_io_finished_hook(void* cookie, io_request* request, status_t status, 63 bool partialTransfer, size_t bytesTransferred) 64 { 65 Inode* inode = (Inode*)cookie; 66 rw_lock_read_unlock(inode->Lock()); 67 return B_OK; 68 } 69 70 71 // #pragma mark - Scanning 72 73 74 static float 75 ext2_identify_partition(int fd, partition_data *partition, void **_cookie) 76 { 77 STATIC_ASSERT(sizeof(struct ext2_super_block) == 1024); 78 STATIC_ASSERT(sizeof(struct ext2_block_group) == 64); 79 80 ext2_super_block superBlock; 81 status_t status = Volume::Identify(fd, &superBlock); 82 if (status != B_OK) 83 return -1; 84 85 identify_cookie *cookie = new identify_cookie; 86 memcpy(&cookie->super_block, &superBlock, sizeof(ext2_super_block)); 87 88 *_cookie = cookie; 89 return 0.8f; 90 } 91 92 93 static status_t 94 ext2_scan_partition(int fd, partition_data *partition, void *_cookie) 95 { 96 identify_cookie *cookie = (identify_cookie *)_cookie; 97 98 partition->status = B_PARTITION_VALID; 99 partition->flags |= B_PARTITION_FILE_SYSTEM; 100 partition->content_size = cookie->super_block.NumBlocks( 101 (cookie->super_block.CompatibleFeatures() 102 & EXT2_INCOMPATIBLE_FEATURE_64BIT) != 0) 103 << cookie->super_block.BlockShift(); 104 partition->block_size = 1UL << cookie->super_block.BlockShift(); 105 partition->content_name = strdup(cookie->super_block.name); 106 if (partition->content_name == NULL) 107 return B_NO_MEMORY; 108 109 return B_OK; 110 } 111 112 113 static void 114 ext2_free_identify_partition_cookie(partition_data* partition, void* _cookie) 115 { 116 delete (identify_cookie*)_cookie; 117 } 118 119 120 // #pragma mark - 121 122 123 static status_t 124 ext2_mount(fs_volume* _volume, const char* device, uint32 flags, 125 const char* args, ino_t* _rootID) 126 { 127 Volume* volume = new(std::nothrow) Volume(_volume); 128 if (volume == NULL) 129 return B_NO_MEMORY; 130 131 // TODO: this is a bit hacky: we can't use publish_vnode() to publish 132 // the root node, or else its file cache cannot be created (we could 133 // create it later, though). Therefore we're using get_vnode() in Mount(), 134 // but that requires us to export our volume data before calling it. 135 _volume->private_volume = volume; 136 _volume->ops = &gExt2VolumeOps; 137 138 status_t status = volume->Mount(device, flags); 139 if (status != B_OK) { 140 ERROR("Failed mounting the volume. Error: %s\n", strerror(status)); 141 delete volume; 142 return status; 143 } 144 145 *_rootID = volume->RootNode()->ID(); 146 return B_OK; 147 } 148 149 150 static status_t 151 ext2_unmount(fs_volume *_volume) 152 { 153 Volume* volume = (Volume *)_volume->private_volume; 154 155 status_t status = volume->Unmount(); 156 delete volume; 157 158 return status; 159 } 160 161 162 static status_t 163 ext2_read_fs_info(fs_volume* _volume, struct fs_info* info) 164 { 165 Volume* volume = (Volume*)_volume->private_volume; 166 167 // File system flags 168 info->flags = B_FS_IS_PERSISTENT | B_FS_HAS_ATTR 169 | (volume->IsReadOnly() ? B_FS_IS_READONLY : 0); 170 info->io_size = EXT2_IO_SIZE; 171 info->block_size = volume->BlockSize(); 172 info->total_blocks = volume->NumBlocks(); 173 info->free_blocks = volume->NumFreeBlocks(); 174 175 // Volume name 176 strlcpy(info->volume_name, volume->Name(), sizeof(info->volume_name)); 177 178 // File system name 179 if (volume->HasExtentsFeature()) 180 strlcpy(info->fsh_name, "ext4", sizeof(info->fsh_name)); 181 else if (volume->HasJournalFeature()) 182 strlcpy(info->fsh_name, "ext3", sizeof(info->fsh_name)); 183 else 184 strlcpy(info->fsh_name, "ext2", sizeof(info->fsh_name)); 185 186 return B_OK; 187 } 188 189 190 static status_t 191 ext2_write_fs_info(fs_volume* _volume, const struct fs_info* info, uint32 mask) 192 { 193 Volume* volume = (Volume*)_volume->private_volume; 194 195 if (volume->IsReadOnly()) 196 return B_READ_ONLY_DEVICE; 197 198 MutexLocker locker(volume->Lock()); 199 200 status_t status = B_BAD_VALUE; 201 202 if (mask & FS_WRITE_FSINFO_NAME) { 203 Transaction transaction(volume->GetJournal()); 204 volume->SetName(info->volume_name); 205 status = volume->WriteSuperBlock(transaction); 206 transaction.Done(); 207 } 208 return status; 209 } 210 211 212 static status_t 213 ext2_sync(fs_volume* _volume) 214 { 215 Volume* volume = (Volume*)_volume->private_volume; 216 return volume->Sync(); 217 } 218 219 220 // #pragma mark - 221 222 223 static status_t 224 ext2_get_vnode(fs_volume* _volume, ino_t id, fs_vnode* _node, int* _type, 225 uint32* _flags, bool reenter) 226 { 227 Volume* volume = (Volume*)_volume->private_volume; 228 229 if (id < 2 || id > volume->NumInodes()) { 230 ERROR("invalid inode id %" B_PRIdINO " requested!\n", id); 231 return B_BAD_VALUE; 232 } 233 234 Inode* inode = new(std::nothrow) Inode(volume, id); 235 if (inode == NULL) 236 return B_NO_MEMORY; 237 238 status_t status = inode->InitCheck(); 239 if (status != B_OK) 240 delete inode; 241 242 if (status == B_OK) { 243 _node->private_node = inode; 244 _node->ops = &gExt2VnodeOps; 245 *_type = inode->Mode(); 246 *_flags = 0; 247 } else 248 ERROR("get_vnode: InitCheck() failed. Error: %s\n", strerror(status)); 249 250 return status; 251 } 252 253 254 static status_t 255 ext2_put_vnode(fs_volume* _volume, fs_vnode* _node, bool reenter) 256 { 257 delete (Inode*)_node->private_node; 258 return B_OK; 259 } 260 261 262 static status_t 263 ext2_remove_vnode(fs_volume* _volume, fs_vnode* _node, bool reenter) 264 { 265 TRACE("ext2_remove_vnode()\n"); 266 Volume* volume = (Volume*)_volume->private_volume; 267 Inode* inode = (Inode*)_node->private_node; 268 ObjectDeleter<Inode> inodeDeleter(inode); 269 270 if (!inode->IsDeleted()) 271 return B_OK; 272 273 TRACE("ext2_remove_vnode(): Starting transaction\n"); 274 Transaction transaction(volume->GetJournal()); 275 276 if (!inode->IsSymLink() || inode->Size() >= EXT2_SHORT_SYMLINK_LENGTH) { 277 TRACE("ext2_remove_vnode(): Truncating\n"); 278 status_t status = inode->Resize(transaction, 0); 279 if (status != B_OK) 280 return status; 281 } 282 283 TRACE("ext2_remove_vnode(): Removing from orphan list\n"); 284 status_t status = volume->RemoveOrphan(transaction, inode->ID()); 285 if (status != B_OK) 286 return status; 287 288 TRACE("ext2_remove_vnode(): Setting deletion time\n"); 289 inode->Node().SetDeletionTime(real_time_clock()); 290 291 status = inode->WriteBack(transaction); 292 if (status != B_OK) 293 return status; 294 295 TRACE("ext2_remove_vnode(): Freeing inode\n"); 296 status = volume->FreeInode(transaction, inode->ID(), inode->IsDirectory()); 297 298 // TODO: When Transaction::Done() fails, do we have to re-add the vnode? 299 if (status == B_OK) 300 status = transaction.Done(); 301 302 return status; 303 } 304 305 306 static bool 307 ext2_can_page(fs_volume* _volume, fs_vnode* _node, void* _cookie) 308 { 309 return true; 310 } 311 312 313 static status_t 314 ext2_read_pages(fs_volume* _volume, fs_vnode* _node, void* _cookie, 315 off_t pos, const iovec* vecs, size_t count, size_t* _numBytes) 316 { 317 Volume* volume = (Volume*)_volume->private_volume; 318 Inode* inode = (Inode*)_node->private_node; 319 320 if (inode->FileCache() == NULL) 321 return B_BAD_VALUE; 322 323 rw_lock_read_lock(inode->Lock()); 324 325 uint32 vecIndex = 0; 326 size_t vecOffset = 0; 327 size_t bytesLeft = *_numBytes; 328 status_t status; 329 330 while (true) { 331 file_io_vec fileVecs[8]; 332 size_t fileVecCount = 8; 333 334 status = file_map_translate(inode->Map(), pos, bytesLeft, fileVecs, 335 &fileVecCount, 0); 336 if (status != B_OK && status != B_BUFFER_OVERFLOW) 337 break; 338 339 bool bufferOverflow = status == B_BUFFER_OVERFLOW; 340 341 size_t bytes = bytesLeft; 342 status = read_file_io_vec_pages(volume->Device(), fileVecs, 343 fileVecCount, vecs, count, &vecIndex, &vecOffset, &bytes); 344 if (status != B_OK || !bufferOverflow) 345 break; 346 347 pos += bytes; 348 bytesLeft -= bytes; 349 } 350 351 rw_lock_read_unlock(inode->Lock()); 352 353 return status; 354 } 355 356 357 static status_t 358 ext2_write_pages(fs_volume* _volume, fs_vnode* _node, void* _cookie, 359 off_t pos, const iovec* vecs, size_t count, size_t* _numBytes) 360 { 361 Volume* volume = (Volume*)_volume->private_volume; 362 Inode* inode = (Inode*)_node->private_node; 363 364 if (volume->IsReadOnly()) 365 return B_READ_ONLY_DEVICE; 366 367 if (inode->FileCache() == NULL) 368 return B_BAD_VALUE; 369 370 rw_lock_read_lock(inode->Lock()); 371 372 uint32 vecIndex = 0; 373 size_t vecOffset = 0; 374 size_t bytesLeft = *_numBytes; 375 status_t status; 376 377 while (true) { 378 file_io_vec fileVecs[8]; 379 size_t fileVecCount = 8; 380 381 status = file_map_translate(inode->Map(), pos, bytesLeft, fileVecs, 382 &fileVecCount, 0); 383 if (status != B_OK && status != B_BUFFER_OVERFLOW) 384 break; 385 386 bool bufferOverflow = status == B_BUFFER_OVERFLOW; 387 388 size_t bytes = bytesLeft; 389 status = write_file_io_vec_pages(volume->Device(), fileVecs, 390 fileVecCount, vecs, count, &vecIndex, &vecOffset, &bytes); 391 if (status != B_OK || !bufferOverflow) 392 break; 393 394 pos += bytes; 395 bytesLeft -= bytes; 396 } 397 398 rw_lock_read_unlock(inode->Lock()); 399 400 return status; 401 } 402 403 404 static status_t 405 ext2_io(fs_volume* _volume, fs_vnode* _node, void* _cookie, io_request* request) 406 { 407 Volume* volume = (Volume*)_volume->private_volume; 408 Inode* inode = (Inode*)_node->private_node; 409 410 #ifndef EXT2_SHELL 411 if (io_request_is_write(request) && volume->IsReadOnly()) { 412 notify_io_request(request, B_READ_ONLY_DEVICE); 413 return B_READ_ONLY_DEVICE; 414 } 415 #endif 416 417 if (inode->FileCache() == NULL) { 418 #ifndef EXT2_SHELL 419 notify_io_request(request, B_BAD_VALUE); 420 #endif 421 return B_BAD_VALUE; 422 } 423 424 // We lock the node here and will unlock it in the "finished" hook. 425 rw_lock_read_lock(inode->Lock()); 426 427 return do_iterative_fd_io(volume->Device(), request, 428 iterative_io_get_vecs_hook, iterative_io_finished_hook, inode); 429 } 430 431 432 static status_t 433 ext2_get_file_map(fs_volume* _volume, fs_vnode* _node, off_t offset, 434 size_t size, struct file_io_vec* vecs, size_t* _count) 435 { 436 TRACE("ext2_get_file_map()\n"); 437 Volume* volume = (Volume*)_volume->private_volume; 438 Inode* inode = (Inode*)_node->private_node; 439 size_t index = 0, max = *_count; 440 441 while (true) { 442 fsblock_t block; 443 uint32 count = 1; 444 status_t status = inode->FindBlock(offset, block, &count); 445 if (status != B_OK) 446 return status; 447 448 if (block > volume->NumBlocks()) { 449 panic("ext2_get_file_map() found block %" B_PRIu64 " for offset %" 450 B_PRIdOFF "\n", block, offset); 451 } 452 453 off_t blockOffset = block << volume->BlockShift(); 454 uint32 blockLength = volume->BlockSize() * count; 455 456 if (index > 0 && (vecs[index - 1].offset 457 == blockOffset - vecs[index - 1].length 458 || (vecs[index - 1].offset == -1 && block == 0))) { 459 vecs[index - 1].length += blockLength; 460 } else { 461 if (index >= max) { 462 // we're out of file_io_vecs; let's bail out 463 *_count = index; 464 return B_BUFFER_OVERFLOW; 465 } 466 467 // 'block' is 0 for sparse blocks 468 if (block != 0) 469 vecs[index].offset = blockOffset; 470 else 471 vecs[index].offset = -1; 472 473 vecs[index].length = blockLength; 474 index++; 475 } 476 477 offset += blockLength; 478 479 if (offset >= inode->Size() || size <= blockLength) { 480 // We're done! 481 *_count = index; 482 TRACE("ext2_get_file_map for inode %" B_PRIdINO "\n", inode->ID()); 483 return B_OK; 484 } 485 486 size -= blockLength; 487 } 488 489 // can never get here 490 return B_ERROR; 491 } 492 493 494 // #pragma mark - 495 496 497 static status_t 498 ext2_lookup(fs_volume* _volume, fs_vnode* _directory, const char* name, 499 ino_t* _vnodeID) 500 { 501 TRACE("ext2_lookup: name address: %p\n", name); 502 TRACE("ext2_lookup: name: %s\n", name); 503 Volume* volume = (Volume*)_volume->private_volume; 504 Inode* directory = (Inode*)_directory->private_node; 505 506 // check access permissions 507 status_t status = directory->CheckPermissions(X_OK); 508 if (status < B_OK) 509 return status; 510 511 HTree htree(volume, directory); 512 DirectoryIterator* iterator; 513 514 status = htree.Lookup(name, &iterator); 515 if (status != B_OK) 516 return status; 517 518 ObjectDeleter<DirectoryIterator> iteratorDeleter(iterator); 519 520 status = iterator->FindEntry(name, _vnodeID); 521 if (status != B_OK) 522 return status; 523 524 return get_vnode(volume->FSVolume(), *_vnodeID, NULL); 525 } 526 527 528 static status_t 529 ext2_ioctl(fs_volume* _volume, fs_vnode* _node, void* _cookie, uint32 cmd, 530 void* buffer, size_t bufferLength) 531 { 532 TRACE("ioctl: %" B_PRIu32 "\n", cmd); 533 534 Volume* volume = (Volume*)_volume->private_volume; 535 switch (cmd) { 536 case 56742: 537 { 538 TRACE("ioctl: Test the block allocator\n"); 539 // Test the block allocator 540 TRACE("ioctl: Creating transaction\n"); 541 Transaction transaction(volume->GetJournal()); 542 TRACE("ioctl: Creating cached block\n"); 543 CachedBlock cached(volume); 544 uint32 blocksPerGroup = volume->BlocksPerGroup(); 545 uint32 blockSize = volume->BlockSize(); 546 uint32 firstBlock = volume->FirstDataBlock(); 547 fsblock_t start = 0; 548 uint32 group = 0; 549 uint32 length; 550 551 TRACE("ioctl: blocks per group: %" B_PRIu32 ", block size: %" 552 B_PRIu32 ", first block: %" B_PRIu32 ", start: %" B_PRIu64 553 ", group: %" B_PRIu32 "\n", blocksPerGroup, 554 blockSize, firstBlock, start, group); 555 556 while (volume->AllocateBlocks(transaction, 1, 2048, group, start, 557 length) == B_OK) { 558 TRACE("ioctl: Allocated blocks in group %" B_PRIu32 ": %" 559 B_PRIu64 "-%" B_PRIu64 "\n", group, start, start + length); 560 off_t blockNum = start + group * blocksPerGroup - firstBlock; 561 562 for (uint32 i = 0; i < length; ++i) { 563 uint8* block = cached.SetToWritable(transaction, blockNum); 564 memset(block, 0, blockSize); 565 blockNum++; 566 } 567 568 TRACE("ioctl: Blocks cleared\n"); 569 570 transaction.Done(); 571 transaction.Start(volume->GetJournal()); 572 } 573 574 TRACE("ioctl: Done\n"); 575 576 return B_OK; 577 } 578 579 case FIOSEEKDATA: 580 case FIOSEEKHOLE: 581 { 582 off_t* offset = (off_t*)buffer; 583 Inode* inode = (Inode*)_node->private_node; 584 585 if (*offset >= inode->Size()) 586 return ENXIO; 587 588 while (*offset < inode->Size()) { 589 fsblock_t block; 590 uint32 count = 1; 591 status_t status = inode->FindBlock(*offset, block, &count); 592 if (status != B_OK) 593 return status; 594 if ((block != 0 && cmd == FIOSEEKDATA) 595 || (block == 0 && cmd == FIOSEEKHOLE)) { 596 return B_OK; 597 } 598 *offset += count * volume->BlockSize(); 599 } 600 601 if (*offset > inode->Size()) 602 *offset = inode->Size(); 603 return cmd == FIOSEEKDATA ? ENXIO : B_OK; 604 } 605 } 606 607 return B_DEV_INVALID_IOCTL; 608 } 609 610 611 static status_t 612 ext2_fsync(fs_volume* _volume, fs_vnode* _node) 613 { 614 Inode* inode = (Inode*)_node->private_node; 615 return inode->Sync(); 616 } 617 618 619 static status_t 620 ext2_read_stat(fs_volume* _volume, fs_vnode* _node, struct stat* stat) 621 { 622 Inode* inode = (Inode*)_node->private_node; 623 const ext2_inode& node = inode->Node(); 624 625 stat->st_dev = inode->GetVolume()->ID(); 626 stat->st_ino = inode->ID(); 627 stat->st_nlink = node.NumLinks(); 628 stat->st_blksize = EXT2_IO_SIZE; 629 630 stat->st_uid = node.UserID(); 631 stat->st_gid = node.GroupID(); 632 stat->st_mode = node.Mode(); 633 stat->st_type = 0; 634 635 inode->GetAccessTime(&stat->st_atim); 636 inode->GetModificationTime(&stat->st_mtim); 637 inode->GetChangeTime(&stat->st_ctim); 638 inode->GetCreationTime(&stat->st_crtim); 639 640 stat->st_size = inode->Size(); 641 stat->st_blocks = (inode->Size() + 511) / 512; 642 643 return B_OK; 644 } 645 646 647 status_t 648 ext2_write_stat(fs_volume* _volume, fs_vnode* _node, const struct stat* stat, 649 uint32 mask) 650 { 651 TRACE("ext2_write_stat\n"); 652 Volume* volume = (Volume*)_volume->private_volume; 653 654 if (volume->IsReadOnly()) 655 return B_READ_ONLY_DEVICE; 656 657 Inode* inode = (Inode*)_node->private_node; 658 659 ext2_inode& node = inode->Node(); 660 bool updateTime = false; 661 uid_t uid = geteuid(); 662 663 bool isOwnerOrRoot = uid == 0 || uid == (uid_t)node.UserID(); 664 bool hasWriteAccess = inode->CheckPermissions(W_OK) == B_OK; 665 666 TRACE("ext2_write_stat: Starting transaction\n"); 667 Transaction transaction(volume->GetJournal()); 668 inode->WriteLockInTransaction(transaction); 669 670 if ((mask & B_STAT_SIZE) != 0 && inode->Size() != stat->st_size) { 671 if (inode->IsDirectory()) 672 return B_IS_A_DIRECTORY; 673 if (!inode->IsFile()) 674 return B_BAD_VALUE; 675 if (!hasWriteAccess) 676 return B_NOT_ALLOWED; 677 678 TRACE("ext2_write_stat: Old size: %ld, new size: %ld\n", 679 (long)inode->Size(), (long)stat->st_size); 680 681 off_t oldSize = inode->Size(); 682 683 status_t status = inode->Resize(transaction, stat->st_size); 684 if (status != B_OK) 685 return status; 686 687 if ((mask & B_STAT_SIZE_INSECURE) == 0) { 688 rw_lock_write_unlock(inode->Lock()); 689 inode->FillGapWithZeros(oldSize, inode->Size()); 690 rw_lock_write_lock(inode->Lock()); 691 } 692 693 updateTime = true; 694 } 695 696 if ((mask & B_STAT_MODE) != 0) { 697 // only the user or root can do that 698 if (!isOwnerOrRoot) 699 return B_NOT_ALLOWED; 700 node.UpdateMode(stat->st_mode, S_IUMSK); 701 updateTime = true; 702 } 703 704 if ((mask & B_STAT_UID) != 0) { 705 // only root should be allowed 706 if (uid != 0) 707 return B_NOT_ALLOWED; 708 node.SetUserID(stat->st_uid); 709 updateTime = true; 710 } 711 712 if ((mask & B_STAT_GID) != 0) { 713 // only the user or root can do that 714 if (!isOwnerOrRoot) 715 return B_NOT_ALLOWED; 716 node.SetGroupID(stat->st_gid); 717 updateTime = true; 718 } 719 720 if ((mask & B_STAT_MODIFICATION_TIME) != 0 || updateTime 721 || (mask & B_STAT_CHANGE_TIME) != 0) { 722 // the user or root can do that or any user with write access 723 if (!isOwnerOrRoot && !hasWriteAccess) 724 return B_NOT_ALLOWED; 725 struct timespec newTimespec = { 0, 0}; 726 727 if ((mask & B_STAT_MODIFICATION_TIME) != 0) 728 newTimespec = stat->st_mtim; 729 730 if ((mask & B_STAT_CHANGE_TIME) != 0 731 && stat->st_ctim.tv_sec > newTimespec.tv_sec) 732 newTimespec = stat->st_ctim; 733 734 if (newTimespec.tv_sec == 0) 735 Inode::_BigtimeToTimespec(real_time_clock_usecs(), &newTimespec); 736 737 inode->SetModificationTime(&newTimespec); 738 } 739 if ((mask & B_STAT_CREATION_TIME) != 0) { 740 // the user or root can do that or any user with write access 741 if (!isOwnerOrRoot && !hasWriteAccess) 742 return B_NOT_ALLOWED; 743 inode->SetCreationTime(&stat->st_crtim); 744 } 745 746 status_t status = inode->WriteBack(transaction); 747 if (status == B_OK) 748 status = transaction.Done(); 749 if (status == B_OK) 750 notify_stat_changed(volume->ID(), -1, inode->ID(), mask); 751 752 return status; 753 } 754 755 756 static status_t 757 ext2_create(fs_volume* _volume, fs_vnode* _directory, const char* name, 758 int openMode, int mode, void** _cookie, ino_t* _vnodeID) 759 { 760 Volume* volume = (Volume*)_volume->private_volume; 761 Inode* directory = (Inode*)_directory->private_node; 762 763 TRACE("ext2_create()\n"); 764 765 if (volume->IsReadOnly()) 766 return B_READ_ONLY_DEVICE; 767 768 if (!directory->IsDirectory()) 769 return B_BAD_TYPE; 770 771 TRACE("ext2_create(): Creating cookie\n"); 772 773 // Allocate cookie 774 file_cookie* cookie = new(std::nothrow) file_cookie; 775 if (cookie == NULL) 776 return B_NO_MEMORY; 777 ObjectDeleter<file_cookie> cookieDeleter(cookie); 778 779 cookie->open_mode = openMode; 780 cookie->last_size = 0; 781 cookie->last_notification = system_time(); 782 783 TRACE("ext2_create(): Starting transaction\n"); 784 785 Transaction transaction(volume->GetJournal()); 786 787 TRACE("ext2_create(): Creating inode\n"); 788 789 Inode* inode; 790 bool created; 791 status_t status = Inode::Create(transaction, directory, name, 792 S_FILE | (mode & S_IUMSK), openMode, EXT2_TYPE_FILE, &created, _vnodeID, 793 &inode, &gExt2VnodeOps); 794 if (status != B_OK) 795 return status; 796 797 TRACE("ext2_create(): Created inode\n"); 798 799 if ((openMode & O_NOCACHE) != 0) { 800 status = inode->DisableFileCache(); 801 if (status != B_OK) 802 return status; 803 } 804 805 entry_cache_add(volume->ID(), directory->ID(), name, *_vnodeID); 806 807 status = transaction.Done(); 808 if (status != B_OK) { 809 entry_cache_remove(volume->ID(), directory->ID(), name); 810 return status; 811 } 812 813 *_cookie = cookie; 814 cookieDeleter.Detach(); 815 816 if (created) 817 notify_entry_created(volume->ID(), directory->ID(), name, *_vnodeID); 818 819 return B_OK; 820 } 821 822 823 static status_t 824 ext2_create_symlink(fs_volume* _volume, fs_vnode* _directory, const char* name, 825 const char* path, int mode) 826 { 827 TRACE("ext2_create_symlink()\n"); 828 829 Volume* volume = (Volume*)_volume->private_volume; 830 Inode* directory = (Inode*)_directory->private_node; 831 832 if (volume->IsReadOnly()) 833 return B_READ_ONLY_DEVICE; 834 835 if (!directory->IsDirectory()) 836 return B_BAD_TYPE; 837 838 status_t status = directory->CheckPermissions(W_OK); 839 if (status != B_OK) 840 return status; 841 842 TRACE("ext2_create_symlink(): Starting transaction\n"); 843 Transaction transaction(volume->GetJournal()); 844 845 Inode* link; 846 ino_t id; 847 status = Inode::Create(transaction, directory, name, S_SYMLINK | 0777, 848 0, (uint8)EXT2_TYPE_SYMLINK, NULL, &id, &link); 849 if (status != B_OK) 850 return status; 851 852 // TODO: We have to prepare the link before publishing? 853 854 size_t length = strlen(path); 855 TRACE("ext2_create_symlink(): Path (%s) length: %d\n", path, (int)length); 856 if (length < EXT2_SHORT_SYMLINK_LENGTH) { 857 strcpy(link->Node().symlink, path); 858 link->Node().SetSize((uint32)length); 859 } else { 860 if (!link->HasFileCache()) { 861 status = link->CreateFileCache(); 862 if (status != B_OK) 863 return status; 864 } 865 866 size_t written = length; 867 status = link->WriteAt(transaction, 0, (const uint8*)path, &written); 868 if (status == B_OK && written != length) 869 status = B_IO_ERROR; 870 } 871 872 if (status == B_OK) 873 status = link->WriteBack(transaction); 874 875 TRACE("ext2_create_symlink(): Publishing vnode\n"); 876 publish_vnode(volume->FSVolume(), id, link, &gExt2VnodeOps, 877 link->Mode(), 0); 878 put_vnode(volume->FSVolume(), id); 879 880 if (status == B_OK) { 881 entry_cache_add(volume->ID(), directory->ID(), name, id); 882 883 status = transaction.Done(); 884 if (status == B_OK) 885 notify_entry_created(volume->ID(), directory->ID(), name, id); 886 else 887 entry_cache_remove(volume->ID(), directory->ID(), name); 888 } 889 TRACE("ext2_create_symlink(): Done\n"); 890 891 return status; 892 } 893 894 895 static status_t 896 ext2_link(fs_volume* volume, fs_vnode* dir, const char* name, fs_vnode* node) 897 { 898 // TODO 899 900 return B_UNSUPPORTED; 901 } 902 903 904 static status_t 905 ext2_unlink(fs_volume* _volume, fs_vnode* _directory, const char* name) 906 { 907 TRACE("ext2_unlink()\n"); 908 if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0) 909 return B_NOT_ALLOWED; 910 911 Volume* volume = (Volume*)_volume->private_volume; 912 Inode* directory = (Inode*)_directory->private_node; 913 914 status_t status = directory->CheckPermissions(W_OK); 915 if (status != B_OK) 916 return status; 917 918 TRACE("ext2_unlink(): Starting transaction\n"); 919 Transaction transaction(volume->GetJournal()); 920 921 directory->WriteLockInTransaction(transaction); 922 923 TRACE("ext2_unlink(): Looking up for directory entry\n"); 924 HTree htree(volume, directory); 925 DirectoryIterator* directoryIterator; 926 927 status = htree.Lookup(name, &directoryIterator); 928 if (status != B_OK) 929 return status; 930 931 ino_t id; 932 status = directoryIterator->FindEntry(name, &id); 933 if (status != B_OK) 934 return status; 935 936 { 937 Vnode vnode(volume, id); 938 Inode* inode; 939 status = vnode.Get(&inode); 940 if (status != B_OK) 941 return status; 942 943 inode->WriteLockInTransaction(transaction); 944 945 status = inode->Unlink(transaction); 946 if (status != B_OK) 947 return status; 948 949 status = directoryIterator->RemoveEntry(transaction); 950 if (status != B_OK) 951 return status; 952 } 953 entry_cache_remove(volume->ID(), directory->ID(), name); 954 955 status = transaction.Done(); 956 if (status != B_OK) 957 entry_cache_add(volume->ID(), directory->ID(), name, id); 958 else 959 notify_entry_removed(volume->ID(), directory->ID(), name, id); 960 961 return status; 962 } 963 964 965 static status_t 966 ext2_rename(fs_volume* _volume, fs_vnode* _oldDir, const char* oldName, 967 fs_vnode* _newDir, const char* newName) 968 { 969 TRACE("ext2_rename()\n"); 970 971 Volume* volume = (Volume*)_volume->private_volume; 972 Inode* oldDirectory = (Inode*)_oldDir->private_node; 973 Inode* newDirectory = (Inode*)_newDir->private_node; 974 975 if (oldDirectory == newDirectory && strcmp(oldName, newName) == 0) 976 return B_OK; 977 978 Transaction transaction(volume->GetJournal()); 979 980 oldDirectory->WriteLockInTransaction(transaction); 981 if (oldDirectory != newDirectory) 982 newDirectory->WriteLockInTransaction(transaction); 983 984 status_t status = oldDirectory->CheckPermissions(W_OK); 985 if (status == B_OK) 986 status = newDirectory->CheckPermissions(W_OK); 987 if (status != B_OK) 988 return status; 989 990 HTree oldHTree(volume, oldDirectory); 991 DirectoryIterator* oldIterator; 992 993 status = oldHTree.Lookup(oldName, &oldIterator); 994 if (status != B_OK) 995 return status; 996 997 ObjectDeleter<DirectoryIterator> oldIteratorDeleter(oldIterator); 998 999 ino_t oldID; 1000 status = oldIterator->FindEntry(oldName, &oldID); 1001 if (status != B_OK) 1002 return status; 1003 1004 TRACE("ext2_rename(): found entry to rename\n"); 1005 1006 if (oldDirectory != newDirectory) { 1007 TRACE("ext2_rename(): Different parent directories\n"); 1008 CachedBlock cached(volume); 1009 1010 ino_t parentID = newDirectory->ID(); 1011 ino_t oldDirID = oldDirectory->ID(); 1012 1013 do { 1014 Vnode vnode(volume, parentID); 1015 Inode* parent; 1016 1017 status = vnode.Get(&parent); 1018 if (status != B_OK) 1019 return B_IO_ERROR; 1020 1021 fsblock_t blockNum; 1022 status = parent->FindBlock(0, blockNum); 1023 if (status != B_OK) 1024 return status; 1025 1026 const HTreeRoot* data = (const HTreeRoot*)cached.SetTo(blockNum); 1027 parentID = data->dotdot.InodeID(); 1028 } while (parentID != oldID && parentID != oldDirID 1029 && parentID != EXT2_ROOT_NODE); 1030 1031 if (parentID == oldID) 1032 return B_BAD_VALUE; 1033 } 1034 1035 HTree newHTree(volume, newDirectory); 1036 DirectoryIterator* newIterator; 1037 1038 status = newHTree.Lookup(newName, &newIterator); 1039 if (status != B_OK) 1040 return status; 1041 1042 TRACE("ext2_rename(): found new directory\n"); 1043 1044 ObjectDeleter<DirectoryIterator> newIteratorDeleter(newIterator); 1045 1046 Vnode vnode(volume, oldID); 1047 Inode* inode; 1048 1049 status = vnode.Get(&inode); 1050 if (status != B_OK) 1051 return status; 1052 1053 uint8 fileType; 1054 1055 // TODO: Support all file types? 1056 if (inode->IsDirectory()) 1057 fileType = EXT2_TYPE_DIRECTORY; 1058 else if (inode->IsSymLink()) 1059 fileType = EXT2_TYPE_SYMLINK; 1060 else 1061 fileType = EXT2_TYPE_FILE; 1062 1063 // Add entry in destination directory 1064 ino_t existentID; 1065 status = newIterator->FindEntry(newName, &existentID); 1066 if (status == B_OK) { 1067 TRACE("ext2_rename(): found existing new entry\n"); 1068 if (existentID == oldID) { 1069 // Remove entry in oldID 1070 // return inode->Unlink(); 1071 return B_BAD_VALUE; 1072 } 1073 1074 Vnode vnodeExistent(volume, existentID); 1075 Inode* existent; 1076 1077 if (vnodeExistent.Get(&existent) != B_OK) 1078 return B_NAME_IN_USE; 1079 1080 if (existent->IsDirectory() != inode->IsDirectory()) { 1081 return existent->IsDirectory() ? B_IS_A_DIRECTORY 1082 : B_NOT_A_DIRECTORY; 1083 } 1084 1085 // TODO: Perhaps we have to revert this in case of error? 1086 status = newIterator->ChangeEntry(transaction, oldID, fileType); 1087 if (status != B_OK) 1088 return status; 1089 1090 notify_entry_removed(volume->ID(), newDirectory->ID(), newName, 1091 existentID); 1092 } else if (status == B_ENTRY_NOT_FOUND) { 1093 newIterator->Restart(); 1094 1095 status = newIterator->AddEntry(transaction, newName, strlen(newName), 1096 oldID, fileType); 1097 if (status != B_OK) 1098 return status; 1099 1100 } else 1101 return status; 1102 1103 if (oldDirectory == newDirectory) { 1104 status = oldHTree.Lookup(oldName, &oldIterator); 1105 if (status != B_OK) 1106 return status; 1107 1108 oldIteratorDeleter.SetTo(oldIterator); 1109 status = oldIterator->FindEntry(oldName, &oldID); 1110 if (status != B_OK) 1111 return status; 1112 } 1113 1114 // Remove entry from source folder 1115 status = oldIterator->RemoveEntry(transaction); 1116 if (status != B_OK) 1117 return status; 1118 1119 inode->WriteLockInTransaction(transaction); 1120 1121 if (oldDirectory != newDirectory && inode->IsDirectory()) { 1122 DirectoryIterator inodeIterator(inode); 1123 1124 status = inodeIterator.FindEntry(".."); 1125 if (status == B_ENTRY_NOT_FOUND) { 1126 ERROR("Corrupt file system. Missing \"..\" in directory %" 1127 B_PRIdINO "\n", inode->ID()); 1128 return B_BAD_DATA; 1129 } else if (status != B_OK) 1130 return status; 1131 1132 inodeIterator.ChangeEntry(transaction, newDirectory->ID(), 1133 (uint8)EXT2_TYPE_DIRECTORY); 1134 // Decrement hardlink count on the source folder 1135 status = oldDirectory->Unlink(transaction); 1136 if (status != B_OK) 1137 ERROR("Error while decrementing hardlink count on the source folder\n"); 1138 // Increment hardlink count on the destination folder 1139 newDirectory->IncrementNumLinks(transaction); 1140 status = newDirectory->WriteBack(transaction); 1141 if (status != B_OK) 1142 ERROR("Error while writing back the destination folder inode\n"); 1143 } 1144 1145 status = inode->WriteBack(transaction); 1146 if (status != B_OK) 1147 return status; 1148 1149 entry_cache_remove(volume->ID(), oldDirectory->ID(), oldName); 1150 entry_cache_add(volume->ID(), newDirectory->ID(), newName, oldID); 1151 1152 status = transaction.Done(); 1153 if (status != B_OK) { 1154 entry_cache_remove(volume->ID(), oldDirectory->ID(), newName); 1155 entry_cache_add(volume->ID(), newDirectory->ID(), oldName, oldID); 1156 1157 return status; 1158 } 1159 1160 notify_entry_moved(volume->ID(), oldDirectory->ID(), oldName, 1161 newDirectory->ID(), newName, oldID); 1162 1163 return B_OK; 1164 } 1165 1166 1167 static status_t 1168 ext2_open(fs_volume* _volume, fs_vnode* _node, int openMode, void** _cookie) 1169 { 1170 Volume* volume = (Volume*)_volume->private_volume; 1171 Inode* inode = (Inode*)_node->private_node; 1172 1173 // opening a directory read-only is allowed, although you can't read 1174 // any data from it. 1175 if (inode->IsDirectory() && (openMode & O_RWMASK) != 0) 1176 return B_IS_A_DIRECTORY; 1177 1178 status_t status = inode->CheckPermissions(open_mode_to_access(openMode) 1179 | (openMode & O_TRUNC ? W_OK : 0)); 1180 if (status != B_OK) 1181 return status; 1182 1183 // Prepare the cookie 1184 file_cookie* cookie = new(std::nothrow) file_cookie; 1185 if (cookie == NULL) 1186 return B_NO_MEMORY; 1187 ObjectDeleter<file_cookie> cookieDeleter(cookie); 1188 1189 cookie->open_mode = openMode & EXT2_OPEN_MODE_USER_MASK; 1190 cookie->last_size = inode->Size(); 1191 cookie->last_notification = system_time(); 1192 1193 MethodDeleter<Inode, status_t, &Inode::EnableFileCache> fileCacheEnabler; 1194 if ((openMode & O_NOCACHE) != 0) { 1195 status = inode->DisableFileCache(); 1196 if (status != B_OK) 1197 return status; 1198 fileCacheEnabler.SetTo(inode); 1199 } 1200 1201 // Should we truncate the file? 1202 if ((openMode & O_TRUNC) != 0) { 1203 if ((openMode & O_RWMASK) == O_RDONLY) 1204 return B_NOT_ALLOWED; 1205 1206 Transaction transaction(volume->GetJournal()); 1207 inode->WriteLockInTransaction(transaction); 1208 1209 status_t status = inode->Resize(transaction, 0); 1210 if (status == B_OK) 1211 status = inode->WriteBack(transaction); 1212 if (status == B_OK) 1213 status = transaction.Done(); 1214 if (status != B_OK) 1215 return status; 1216 1217 // TODO: No need to notify file size changed? 1218 } 1219 1220 fileCacheEnabler.Detach(); 1221 cookieDeleter.Detach(); 1222 *_cookie = cookie; 1223 1224 return B_OK; 1225 } 1226 1227 1228 static status_t 1229 ext2_read(fs_volume* _volume, fs_vnode* _node, void* _cookie, off_t pos, 1230 void* buffer, size_t* _length) 1231 { 1232 Inode* inode = (Inode*)_node->private_node; 1233 1234 if (!inode->IsFile()) { 1235 *_length = 0; 1236 return inode->IsDirectory() ? B_IS_A_DIRECTORY : B_BAD_VALUE; 1237 } 1238 1239 return inode->ReadAt(pos, (uint8*)buffer, _length); 1240 } 1241 1242 1243 static status_t 1244 ext2_write(fs_volume* _volume, fs_vnode* _node, void* _cookie, off_t pos, 1245 const void* buffer, size_t* _length) 1246 { 1247 TRACE("ext2_write()\n"); 1248 Volume* volume = (Volume*)_volume->private_volume; 1249 Inode* inode = (Inode*)_node->private_node; 1250 1251 if (volume->IsReadOnly()) 1252 return B_READ_ONLY_DEVICE; 1253 1254 if (inode->IsDirectory()) { 1255 *_length = 0; 1256 return B_IS_A_DIRECTORY; 1257 } 1258 1259 TRACE("ext2_write(): Preparing cookie\n"); 1260 1261 file_cookie* cookie = (file_cookie*)_cookie; 1262 1263 if ((cookie->open_mode & O_APPEND) != 0) 1264 pos = inode->Size(); 1265 1266 TRACE("ext2_write(): Creating transaction\n"); 1267 Transaction transaction; 1268 1269 status_t status = inode->WriteAt(transaction, pos, (const uint8*)buffer, 1270 _length); 1271 if (status == B_OK) 1272 status = transaction.Done(); 1273 if (status == B_OK) { 1274 TRACE("ext2_write(): Finalizing\n"); 1275 ReadLocker lock(*inode->Lock()); 1276 1277 if (cookie->last_size != inode->Size() 1278 && system_time() > cookie->last_notification 1279 + INODE_NOTIFICATION_INTERVAL) { 1280 notify_stat_changed(volume->ID(), -1, inode->ID(), 1281 B_STAT_MODIFICATION_TIME | B_STAT_SIZE | B_STAT_INTERIM_UPDATE); 1282 cookie->last_size = inode->Size(); 1283 cookie->last_notification = system_time(); 1284 } 1285 } 1286 1287 TRACE("ext2_write(): Done\n"); 1288 1289 return status; 1290 } 1291 1292 1293 static status_t 1294 ext2_close(fs_volume *_volume, fs_vnode *_node, void *_cookie) 1295 { 1296 return B_OK; 1297 } 1298 1299 1300 static status_t 1301 ext2_free_cookie(fs_volume* _volume, fs_vnode* _node, void* _cookie) 1302 { 1303 file_cookie* cookie = (file_cookie*)_cookie; 1304 Volume* volume = (Volume*)_volume->private_volume; 1305 Inode* inode = (Inode*)_node->private_node; 1306 1307 if (inode->Size() != cookie->last_size) 1308 notify_stat_changed(volume->ID(), -1, inode->ID(), B_STAT_SIZE); 1309 1310 if ((cookie->open_mode & O_NOCACHE) != 0) 1311 inode->EnableFileCache(); 1312 1313 delete cookie; 1314 return B_OK; 1315 } 1316 1317 1318 static status_t 1319 ext2_access(fs_volume* _volume, fs_vnode* _node, int accessMode) 1320 { 1321 Inode* inode = (Inode*)_node->private_node; 1322 return inode->CheckPermissions(accessMode); 1323 } 1324 1325 1326 static status_t 1327 ext2_read_link(fs_volume *_volume, fs_vnode *_node, char *buffer, 1328 size_t *_bufferSize) 1329 { 1330 Inode* inode = (Inode*)_node->private_node; 1331 1332 if (!inode->IsSymLink()) 1333 return B_BAD_VALUE; 1334 1335 if (inode->Size() > EXT2_SHORT_SYMLINK_LENGTH) { 1336 status_t result = inode->ReadAt(0, reinterpret_cast<uint8*>(buffer), 1337 _bufferSize); 1338 if (result != B_OK) 1339 return result; 1340 } else { 1341 size_t bytesToCopy = std::min(static_cast<size_t>(inode->Size()), 1342 *_bufferSize); 1343 1344 memcpy(buffer, inode->Node().symlink, bytesToCopy); 1345 } 1346 1347 *_bufferSize = inode->Size(); 1348 return B_OK; 1349 } 1350 1351 1352 // #pragma mark - Directory functions 1353 1354 1355 static status_t 1356 ext2_create_dir(fs_volume* _volume, fs_vnode* _directory, const char* name, 1357 int mode) 1358 { 1359 TRACE("ext2_create_dir()\n"); 1360 Volume* volume = (Volume*)_volume->private_volume; 1361 Inode* directory = (Inode*)_directory->private_node; 1362 1363 if (volume->IsReadOnly()) 1364 return B_READ_ONLY_DEVICE; 1365 1366 if (!directory->IsDirectory()) 1367 return B_BAD_TYPE; 1368 1369 status_t status = directory->CheckPermissions(W_OK); 1370 if (status != B_OK) 1371 return status; 1372 1373 TRACE("ext2_create_dir(): Starting transaction\n"); 1374 Transaction transaction(volume->GetJournal()); 1375 1376 ino_t id; 1377 status = Inode::Create(transaction, directory, name, 1378 S_DIRECTORY | (mode & S_IUMSK), 0, EXT2_TYPE_DIRECTORY, NULL, &id); 1379 if (status != B_OK) 1380 return status; 1381 1382 put_vnode(volume->FSVolume(), id); 1383 1384 entry_cache_add(volume->ID(), directory->ID(), name, id); 1385 1386 status = transaction.Done(); 1387 if (status != B_OK) { 1388 entry_cache_remove(volume->ID(), directory->ID(), name); 1389 return status; 1390 } 1391 1392 notify_entry_created(volume->ID(), directory->ID(), name, id); 1393 1394 TRACE("ext2_create_dir(): Done\n"); 1395 1396 return B_OK; 1397 } 1398 1399 1400 static status_t 1401 ext2_remove_dir(fs_volume* _volume, fs_vnode* _directory, const char* name) 1402 { 1403 TRACE("ext2_remove_dir()\n"); 1404 1405 Volume* volume = (Volume*)_volume->private_volume; 1406 Inode* directory = (Inode*)_directory->private_node; 1407 1408 status_t status = directory->CheckPermissions(W_OK); 1409 if (status != B_OK) 1410 return status; 1411 1412 TRACE("ext2_remove_dir(): Starting transaction\n"); 1413 Transaction transaction(volume->GetJournal()); 1414 1415 directory->WriteLockInTransaction(transaction); 1416 1417 TRACE("ext2_remove_dir(): Looking up for directory entry\n"); 1418 HTree htree(volume, directory); 1419 DirectoryIterator* directoryIterator; 1420 1421 status = htree.Lookup(name, &directoryIterator); 1422 if (status != B_OK) 1423 return status; 1424 1425 ino_t id; 1426 status = directoryIterator->FindEntry(name, &id); 1427 if (status != B_OK) 1428 return status; 1429 1430 Vnode vnode(volume, id); 1431 Inode* inode; 1432 status = vnode.Get(&inode); 1433 if (status != B_OK) 1434 return status; 1435 1436 inode->WriteLockInTransaction(transaction); 1437 1438 status = inode->Unlink(transaction); 1439 if (status != B_OK) 1440 return status; 1441 1442 status = directory->Unlink(transaction); 1443 if (status != B_OK) 1444 return status; 1445 1446 status = directoryIterator->RemoveEntry(transaction); 1447 if (status != B_OK) 1448 return status; 1449 1450 entry_cache_remove(volume->ID(), directory->ID(), name); 1451 entry_cache_remove(volume->ID(), id, ".."); 1452 1453 status = transaction.Done(); 1454 if (status != B_OK) { 1455 entry_cache_add(volume->ID(), directory->ID(), name, id); 1456 entry_cache_add(volume->ID(), id, "..", id); 1457 } else 1458 notify_entry_removed(volume->ID(), directory->ID(), name, id); 1459 1460 return status; 1461 } 1462 1463 1464 static status_t 1465 ext2_open_dir(fs_volume* _volume, fs_vnode* _node, void** _cookie) 1466 { 1467 Inode* inode = (Inode*)_node->private_node; 1468 status_t status = inode->CheckPermissions(R_OK); 1469 if (status < B_OK) 1470 return status; 1471 1472 if (!inode->IsDirectory()) 1473 return B_NOT_A_DIRECTORY; 1474 1475 DirectoryIterator* iterator = new(std::nothrow) DirectoryIterator(inode); 1476 if (iterator == NULL) 1477 return B_NO_MEMORY; 1478 1479 *_cookie = iterator; 1480 return B_OK; 1481 } 1482 1483 1484 static status_t 1485 ext2_read_dir(fs_volume *_volume, fs_vnode *_node, void *_cookie, 1486 struct dirent *dirent, size_t bufferSize, uint32 *_num) 1487 { 1488 DirectoryIterator *iterator = (DirectoryIterator *)_cookie; 1489 Volume* volume = (Volume*)_volume->private_volume; 1490 1491 uint32 maxCount = *_num; 1492 uint32 count = 0; 1493 1494 while (count < maxCount && bufferSize > sizeof(struct dirent)) { 1495 1496 size_t length = bufferSize - sizeof(struct dirent) + 1; 1497 ino_t id; 1498 1499 status_t status = iterator->GetNext(dirent->d_name, &length, &id); 1500 if (status == B_ENTRY_NOT_FOUND) 1501 break; 1502 1503 if (status == B_BUFFER_OVERFLOW) { 1504 // the remaining name buffer length was too small 1505 if (count == 0) 1506 return B_BUFFER_OVERFLOW; 1507 break; 1508 } 1509 1510 if (status != B_OK) 1511 return status; 1512 1513 status = iterator->Next(); 1514 if (status != B_OK && status != B_ENTRY_NOT_FOUND) 1515 return status; 1516 1517 dirent->d_dev = volume->ID(); 1518 dirent->d_ino = id; 1519 dirent->d_reclen = sizeof(struct dirent) + length; 1520 1521 bufferSize -= dirent->d_reclen; 1522 dirent = (struct dirent*)((uint8*)dirent + dirent->d_reclen); 1523 count++; 1524 } 1525 1526 *_num = count; 1527 return B_OK; 1528 } 1529 1530 1531 static status_t 1532 ext2_rewind_dir(fs_volume * /*_volume*/, fs_vnode * /*node*/, void *_cookie) 1533 { 1534 DirectoryIterator *iterator = (DirectoryIterator *)_cookie; 1535 return iterator->Rewind(); 1536 } 1537 1538 1539 static status_t 1540 ext2_close_dir(fs_volume * /*_volume*/, fs_vnode * /*node*/, void * /*_cookie*/) 1541 { 1542 return B_OK; 1543 } 1544 1545 1546 static status_t 1547 ext2_free_dir_cookie(fs_volume *_volume, fs_vnode *_node, void *_cookie) 1548 { 1549 delete (DirectoryIterator*)_cookie; 1550 return B_OK; 1551 } 1552 1553 1554 static status_t 1555 ext2_open_attr_dir(fs_volume *_volume, fs_vnode *_node, void **_cookie) 1556 { 1557 Inode* inode = (Inode*)_node->private_node; 1558 Volume* volume = (Volume*)_volume->private_volume; 1559 TRACE("%s()\n", __FUNCTION__); 1560 1561 if (!(volume->SuperBlock().CompatibleFeatures() & EXT2_FEATURE_EXT_ATTR)) 1562 return ENOSYS; 1563 1564 // on directories too ? 1565 if (!inode->IsFile()) 1566 return EINVAL; 1567 1568 int32 *index = new(std::nothrow) int32; 1569 if (index == NULL) 1570 return B_NO_MEMORY; 1571 *index = 0; 1572 *(int32**)_cookie = index; 1573 return B_OK; 1574 } 1575 1576 static status_t 1577 ext2_close_attr_dir(fs_volume* _volume, fs_vnode* _node, void* cookie) 1578 { 1579 TRACE("%s()\n", __FUNCTION__); 1580 return B_OK; 1581 } 1582 1583 1584 static status_t 1585 ext2_free_attr_dir_cookie(fs_volume* _volume, fs_vnode* _node, void* _cookie) 1586 { 1587 TRACE("%s()\n", __FUNCTION__); 1588 delete (int32 *)_cookie; 1589 return B_OK; 1590 } 1591 1592 1593 static status_t 1594 ext2_read_attr_dir(fs_volume* _volume, fs_vnode* _node, 1595 void* _cookie, struct dirent* dirent, size_t bufferSize, 1596 uint32* _num) 1597 { 1598 Inode* inode = (Inode*)_node->private_node; 1599 int32 index = *(int32 *)_cookie; 1600 Attribute attribute(inode); 1601 TRACE("%s()\n", __FUNCTION__); 1602 1603 size_t length = bufferSize; 1604 status_t status = attribute.Find(index); 1605 if (status == B_ENTRY_NOT_FOUND) { 1606 *_num = 0; 1607 return B_OK; 1608 } else if (status != B_OK) 1609 return status; 1610 1611 status = attribute.GetName(dirent->d_name, &length); 1612 if (status != B_OK) 1613 return B_OK; 1614 1615 Volume* volume = (Volume*)_volume->private_volume; 1616 1617 dirent->d_dev = volume->ID(); 1618 dirent->d_ino = inode->ID(); 1619 dirent->d_reclen = sizeof(struct dirent) + length; 1620 1621 *_num = 1; 1622 *(int32*)_cookie = index + 1; 1623 return B_OK; 1624 } 1625 1626 1627 static status_t 1628 ext2_rewind_attr_dir(fs_volume* _volume, fs_vnode* _node, void* _cookie) 1629 { 1630 *(int32*)_cookie = 0; 1631 TRACE("%s()\n", __FUNCTION__); 1632 return B_OK; 1633 } 1634 1635 1636 /* attribute operations */ 1637 static status_t 1638 ext2_create_attr(fs_volume* _volume, fs_vnode* _node, 1639 const char* name, uint32 type, int openMode, void** _cookie) 1640 { 1641 return EROFS; 1642 } 1643 1644 1645 static status_t 1646 ext2_open_attr(fs_volume* _volume, fs_vnode* _node, const char* name, 1647 int openMode, void** _cookie) 1648 { 1649 TRACE("%s()\n", __FUNCTION__); 1650 1651 Volume* volume = (Volume*)_volume->private_volume; 1652 Inode* inode = (Inode*)_node->private_node; 1653 Attribute attribute(inode); 1654 1655 if (!(volume->SuperBlock().CompatibleFeatures() & EXT2_FEATURE_EXT_ATTR)) 1656 return ENOSYS; 1657 1658 return attribute.Open(name, openMode, (attr_cookie**)_cookie); 1659 } 1660 1661 1662 static status_t 1663 ext2_close_attr(fs_volume* _volume, fs_vnode* _node, 1664 void* cookie) 1665 { 1666 return B_OK; 1667 } 1668 1669 1670 static status_t 1671 ext2_free_attr_cookie(fs_volume* _volume, fs_vnode* _node, 1672 void* cookie) 1673 { 1674 delete (attr_cookie*)cookie; 1675 return B_OK; 1676 } 1677 1678 1679 static status_t 1680 ext2_read_attr(fs_volume* _volume, fs_vnode* _node, void* _cookie, 1681 off_t pos, void* buffer, size_t* _length) 1682 { 1683 TRACE("%s()\n", __FUNCTION__); 1684 1685 attr_cookie* cookie = (attr_cookie*)_cookie; 1686 Inode* inode = (Inode*)_node->private_node; 1687 1688 Attribute attribute(inode, cookie); 1689 1690 return attribute.Read(cookie, pos, (uint8*)buffer, _length); 1691 } 1692 1693 1694 static status_t 1695 ext2_write_attr(fs_volume* _volume, fs_vnode* _node, void* cookie, 1696 off_t pos, const void* buffer, size_t* length) 1697 { 1698 return EROFS; 1699 } 1700 1701 1702 1703 static status_t 1704 ext2_read_attr_stat(fs_volume* _volume, fs_vnode* _node, 1705 void* _cookie, struct stat* stat) 1706 { 1707 attr_cookie* cookie = (attr_cookie*)_cookie; 1708 Inode* inode = (Inode*)_node->private_node; 1709 1710 Attribute attribute(inode, cookie); 1711 1712 return attribute.Stat(*stat); 1713 } 1714 1715 1716 static status_t 1717 ext2_write_attr_stat(fs_volume* _volume, fs_vnode* _node, 1718 void* cookie, const struct stat* stat, int statMask) 1719 { 1720 return EROFS; 1721 } 1722 1723 1724 static status_t 1725 ext2_rename_attr(fs_volume* _volume, fs_vnode* fromVnode, 1726 const char* fromName, fs_vnode* toVnode, const char* toName) 1727 { 1728 return ENOSYS; 1729 } 1730 1731 1732 static status_t 1733 ext2_remove_attr(fs_volume* _volume, fs_vnode* vnode, 1734 const char* name) 1735 { 1736 return ENOSYS; 1737 } 1738 1739 1740 fs_volume_ops gExt2VolumeOps = { 1741 &ext2_unmount, 1742 &ext2_read_fs_info, 1743 &ext2_write_fs_info, 1744 &ext2_sync, 1745 &ext2_get_vnode, 1746 }; 1747 1748 1749 fs_vnode_ops gExt2VnodeOps = { 1750 /* vnode operations */ 1751 &ext2_lookup, 1752 NULL, 1753 &ext2_put_vnode, 1754 &ext2_remove_vnode, 1755 1756 /* VM file access */ 1757 &ext2_can_page, 1758 &ext2_read_pages, 1759 &ext2_write_pages, 1760 1761 NULL, // io() 1762 NULL, // cancel_io() 1763 1764 &ext2_get_file_map, 1765 1766 &ext2_ioctl, 1767 NULL, 1768 NULL, // fs_select 1769 NULL, // fs_deselect 1770 &ext2_fsync, 1771 1772 &ext2_read_link, 1773 &ext2_create_symlink, 1774 1775 &ext2_link, 1776 &ext2_unlink, 1777 &ext2_rename, 1778 1779 &ext2_access, 1780 &ext2_read_stat, 1781 &ext2_write_stat, 1782 NULL, // fs_preallocate 1783 1784 /* file operations */ 1785 &ext2_create, 1786 &ext2_open, 1787 &ext2_close, 1788 &ext2_free_cookie, 1789 &ext2_read, 1790 &ext2_write, 1791 1792 /* directory operations */ 1793 &ext2_create_dir, 1794 &ext2_remove_dir, 1795 &ext2_open_dir, 1796 &ext2_close_dir, 1797 &ext2_free_dir_cookie, 1798 &ext2_read_dir, 1799 &ext2_rewind_dir, 1800 1801 /* attribute directory operations */ 1802 &ext2_open_attr_dir, 1803 &ext2_close_attr_dir, 1804 &ext2_free_attr_dir_cookie, 1805 &ext2_read_attr_dir, 1806 &ext2_rewind_attr_dir, 1807 1808 /* attribute operations */ 1809 NULL, //&ext2_create_attr, 1810 &ext2_open_attr, 1811 &ext2_close_attr, 1812 &ext2_free_attr_cookie, 1813 &ext2_read_attr, 1814 NULL, //&ext2_write_attr, 1815 &ext2_read_attr_stat, 1816 NULL, //&ext2_write_attr_stat, 1817 NULL, //&ext2_rename_attr, 1818 NULL, //&ext2_remove_attr, 1819 }; 1820 1821 1822 static file_system_module_info sExt2FileSystem = { 1823 { 1824 "file_systems/ext2" B_CURRENT_FS_API_VERSION, 1825 0, 1826 NULL, 1827 }, 1828 1829 "ext2", // short_name 1830 "Ext2 File System", // pretty_name 1831 B_DISK_SYSTEM_SUPPORTS_WRITING 1832 | B_DISK_SYSTEM_SUPPORTS_CONTENT_NAME, // DDM flags 1833 1834 // scanning 1835 ext2_identify_partition, 1836 ext2_scan_partition, 1837 ext2_free_identify_partition_cookie, 1838 NULL, // free_partition_content_cookie() 1839 1840 &ext2_mount, 1841 1842 NULL, 1843 }; 1844 1845 1846 module_info *modules[] = { 1847 (module_info *)&sExt2FileSystem, 1848 NULL, 1849 }; 1850