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