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