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