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