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