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