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