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