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