116359Sasami/* 256711Snyan * Copyright 2010, J��r��me Duval, korli@users.berlios.de. 316359Sasami * Copyright 2008, Axel D��rfler, axeld@pinc-software.de. 450518Snyan * This file may be used under the terms of the MIT License. 550518Snyan */ 616359Sasami 760717Snyan 850518Snyan#include <algorithm> 950518Snyan#include <dirent.h> 1050518Snyan#include <sys/ioctl.h> 1160717Snyan#include <util/kernel_cpp.h> 1250518Snyan#include <string.h> 1350518Snyan 1450518Snyan#include <AutoDeleter.h> 1550518Snyan#include <fs_cache.h> 1617973Sasami#include <fs_info.h> 1717973Sasami#include <io_requests.h> 1850477Speter#include <NodeMonitor.h> 1916359Sasami#include <util/AutoLock.h> 2046044Skato 2146044Skato#include "Attribute.h" 2246044Skato#include "CachedBlock.h" 2346044Skato#include "DirectoryIterator.h" 2446044Skato#include "ext2.h" 2560027Snyan#include "HTree.h" 2638832Skato#include "Inode.h" 2716359Sasami#include "Journal.h" 2861640Speter#include "Utility.h" 2961640Speter 3046044Skato 3145783Skato//#define TRACE_EXT2 3250518Snyan#ifdef TRACE_EXT2 3350518Snyan# define TRACE(x...) dprintf("\33[34mext2:\33[0m " x) 3450518Snyan#else 3557655Skato# define TRACE(x...) ; 3650518Snyan#endif 3750518Snyan#define ERROR(x...) dprintf("\33[34mext2:\33[0m " x) 3854877Skato 3953744Snyan 4050518Snyan#define EXT2_IO_SIZE 65536 4156533Skato 4250518Snyan 4350518Snyanstruct identify_cookie { 4456533Skato ext2_super_block super_block; 4550518Snyan}; 4650518Snyan 4756711Snyan 4850518Snyan// #pragma mark - Scanning 4950518Snyan 5050518Snyan 5150518Snyanstatic float 5250518Snyanext2_identify_partition(int fd, partition_data *partition, void **_cookie) 5350518Snyan{ 5456533Skato STATIC_ASSERT(sizeof(struct ext2_super_block) == 1024); 5552833Snyan STATIC_ASSERT(sizeof(struct ext2_block_group) == 64); 5652833Snyan 5756327Snyan ext2_super_block superBlock; 5859530Snyan status_t status = Volume::Identify(fd, &superBlock); 5956323Skato if (status != B_OK) 6060717Snyan return -1; 6118846Sasami 6242157Skato identify_cookie *cookie = new identify_cookie; 6350518Snyan memcpy(&cookie->super_block, &superBlock, sizeof(ext2_super_block)); 6450518Snyan 6542157Skato *_cookie = cookie; 6650518Snyan return 0.8f; 6750518Snyan} 6850518Snyan 6950518Snyan 7042157Skatostatic status_t 7156442Speterext2_scan_partition(int fd, partition_data *partition, void *_cookie) 7256442Speter{ 7358290Skato identify_cookie *cookie = (identify_cookie *)_cookie; 7458290Skato 7516359Sasami partition->status = B_PARTITION_VALID; 7650518Snyan partition->flags |= B_PARTITION_FILE_SYSTEM; 7761640Speter partition->content_size = cookie->super_block.NumBlocks( 7816359Sasami (cookie->super_block.CompatibleFeatures() 7950518Snyan & EXT2_INCOMPATIBLE_FEATURE_64BIT) != 0) 8061640Speter << cookie->super_block.BlockShift(); 8116359Sasami partition->block_size = 1UL << cookie->super_block.BlockShift(); 8256516Speter partition->content_name = strdup(cookie->super_block.name); 8356442Speter if (partition->content_name == NULL) 8456442Speter return B_NO_MEMORY; 8556442Speter 8616359Sasami return B_OK; 8750518Snyan} 8856442Speter 8956442Speter 9056442Speterstatic void 9156442Speterext2_free_identify_partition_cookie(partition_data* partition, void* _cookie) 9256442Speter{ 9356442Speter delete (identify_cookie*)_cookie; 9418846Sasami} 9520128Sasami 9620128Sasami 9720128Sasami// #pragma mark - 9820128Sasami 9920128Sasami 10020128Sasamistatic status_t 10161741Skatoext2_mount(fs_volume* _volume, const char* device, uint32 flags, 10220128Sasami const char* args, ino_t* _rootID) 10361640Speter{ 10461640Speter Volume* volume = new(std::nothrow) Volume(_volume); 10561640Speter if (volume == NULL) 10661640Speter return B_NO_MEMORY; 10761640Speter 10820128Sasami // TODO: this is a bit hacky: we can't use publish_vnode() to publish 10961640Speter // the root node, or else its file cache cannot be created (we could 11061640Speter // create it later, though). Therefore we're using get_vnode() in Mount(), 11161640Speter // but that requires us to export our volume data before calling it. 11261640Speter _volume->private_volume = volume; 11361640Speter _volume->ops = &gExt2VolumeOps; 11420128Sasami 11561640Speter status_t status = volume->Mount(device, flags); 11661640Speter if (status != B_OK) { 11761640Speter ERROR("Failed mounting the volume. Error: %s\n", strerror(status)); 11861640Speter delete volume; 11961640Speter return status; 12020128Sasami } 12161640Speter 12261640Speter *_rootID = volume->RootNode()->ID(); 12361640Speter return B_OK; 12461640Speter} 12561640Speter 12620128Sasami 12761640Speterstatic status_t 12861640Speterext2_unmount(fs_volume *_volume) 12961640Speter{ 13061640Speter Volume* volume = (Volume *)_volume->private_volume; 13161640Speter 13261640Speter status_t status = volume->Unmount(); 13361640Speter delete volume; 13421272Skato 13561640Speter return status; 13661640Speter} 13761640Speter 13861640Speter 13961640Speterstatic status_t 14020128Sasamiext2_read_fs_info(fs_volume* _volume, struct fs_info* info) 14158789Snyan{ 14261640Speter Volume* volume = (Volume*)_volume->private_volume; 14358789Snyan 14450518Snyan // File system flags 14556442Speter info->flags = B_FS_IS_PERSISTENT | B_FS_HAS_ATTR 14656442Speter | (volume->IsReadOnly() ? B_FS_IS_READONLY : 0); 14756442Speter info->io_size = EXT2_IO_SIZE; 14856442Speter info->block_size = volume->BlockSize(); 14956442Speter info->total_blocks = volume->NumBlocks(); 15016359Sasami info->free_blocks = volume->NumFreeBlocks(); 15154030Snyan 15260717Snyan // Volume name 15356442Speter strlcpy(info->volume_name, volume->Name(), sizeof(info->volume_name)); 15456442Speter 15554030Snyan // File system name 15642795Skato if (volume->HasExtentsFeature()) 15761741Skato strlcpy(info->fsh_name, "ext4", sizeof(info->fsh_name)); 15816359Sasami else if (volume->HasJournalFeature()) 15961640Speter strlcpy(info->fsh_name, "ext3", sizeof(info->fsh_name)); 16042795Skato else 16145783Skato strlcpy(info->fsh_name, "ext2", sizeof(info->fsh_name)); 16261640Speter 16342795Skato return B_OK; 16453120Snyan} 16561640Speter 16642795Skato 16750518Snyanstatic status_t 16861640Speterext2_write_fs_info(fs_volume* _volume, const struct fs_info* info, uint32 mask) 16916359Sasami{ 17050518Snyan Volume* volume = (Volume*)_volume->private_volume; 17161640Speter 17224432Skato if (volume->IsReadOnly()) 17316359Sasami return B_READ_ONLY_DEVICE; 17456442Speter 17561640Speter MutexLocker locker(volume->Lock()); 17616359Sasami 17750518Snyan status_t status = B_BAD_VALUE; 17842730Skato 17953120Snyan if (mask & FS_WRITE_FSINFO_NAME) { 18053120Snyan Transaction transaction(volume->GetJournal()); 18142730Skato volume->SetName(info->volume_name); 18261640Speter status = volume->WriteSuperBlock(transaction); 18342730Skato transaction.Done(); 18461640Speter } 18561640Speter return status; 18661640Speter} 18761640Speter 18842730Skato 18961640Speterstatic status_t 19061640Speterext2_sync(fs_volume* _volume) 19161640Speter{ 19261640Speter Volume* volume = (Volume*)_volume->private_volume; 19361640Speter return volume->Sync(); 19461640Speter} 19561640Speter 19661640Speter 19742730Skato// #pragma mark - 19861640Speter 19961640Speter 20061640Speterstatic status_t 20161640Speterext2_get_vnode(fs_volume* _volume, ino_t id, fs_vnode* _node, int* _type, 20242730Skato uint32* _flags, bool reenter) 20361640Speter{ 20461640Speter Volume* volume = (Volume*)_volume->private_volume; 20561640Speter 20661640Speter if (id < 2 || id > volume->NumInodes()) { 20761640Speter ERROR("invalid inode id %" B_PRIdINO " requested!\n", id); 20861640Speter return B_BAD_VALUE; 20961640Speter } 21042730Skato 21161640Speter Inode* inode = new(std::nothrow) Inode(volume, id); 21261640Speter if (inode == NULL) 21361640Speter return B_NO_MEMORY; 21461640Speter 21561640Speter status_t status = inode->InitCheck(); 21661640Speter if (status != B_OK) 21742730Skato delete inode; 21861640Speter 21961640Speter if (status == B_OK) { 22061640Speter _node->private_node = inode; 22161640Speter _node->ops = &gExt2VnodeOps; 22261640Speter *_type = inode->Mode(); 22361640Speter *_flags = 0; 22442730Skato } else 22561640Speter ERROR("get_vnode: InitCheck() failed. Error: %s\n", strerror(status)); 22661640Speter 22761640Speter return status; 22861640Speter} 22961640Speter 23061640Speter 23161640Speterstatic status_t 23242730Skatoext2_put_vnode(fs_volume* _volume, fs_vnode* _node, bool reenter) 23353120Snyan{ 23461640Speter delete (Inode*)_node->private_node; 23561640Speter return B_OK; 23661640Speter} 23761640Speter 23816359Sasami 23961640Speterstatic status_t 24050518Snyanext2_remove_vnode(fs_volume* _volume, fs_vnode* _node, bool reenter) 24148006Skato{ 24261640Speter TRACE("ext2_remove_vnode()\n"); 24356711Snyan Volume* volume = (Volume*)_volume->private_volume; 24456711Snyan Inode* inode = (Inode*)_node->private_node; 24556711Snyan ObjectDeleter<Inode> inodeDeleter(inode); 24656711Snyan 24756711Snyan if (!inode->IsDeleted()) 24848006Skato return B_OK; 24948006Skato 25061741Skato TRACE("ext2_remove_vnode(): Starting transaction\n"); 25116359Sasami Transaction transaction(volume->GetJournal()); 25250513Skato 25350518Snyan if (!inode->IsSymLink() || inode->Size() >= EXT2_SHORT_SYMLINK_LENGTH) { 25456442Speter TRACE("ext2_remove_vnode(): Truncating\n"); 25556442Speter status_t status = inode->Resize(transaction, 0); 25656442Speter if (status != B_OK) 25756442Speter return status; 25856442Speter } 25951222Skato 26051222Skato TRACE("ext2_remove_vnode(): Removing from orphan list\n"); 26156442Speter status_t status = volume->RemoveOrphan(transaction, inode->ID()); 26256442Speter if (status != B_OK) 26356442Speter return status; 26456442Speter 26556442Speter TRACE("ext2_remove_vnode(): Setting deletion time\n"); 26656442Speter inode->Node().SetDeletionTime(real_time_clock()); 26756442Speter 26856442Speter status = inode->WriteBack(transaction); 26956442Speter if (status != B_OK) 27056442Speter return status; 27116359Sasami 27253056Snyan TRACE("ext2_remove_vnode(): Freeing inode\n"); 27318095Sasami status = volume->FreeInode(transaction, inode->ID(), inode->IsDirectory()); 27418095Sasami 27540071Skato // TODO: When Transaction::Done() fails, do we have to re-add the vnode? 27656372Snyan if (status == B_OK) 27718095Sasami status = transaction.Done(); 27818095Sasami 27918095Sasami return status; 28018095Sasami} 28118095Sasami 28218095Sasami 28318095Sasamistatic bool 28418846Sasamiext2_can_page(fs_volume* _volume, fs_vnode* _node, void* _cookie) 28518846Sasami{ 28656372Snyan return true; 28756372Snyan} 28853120Snyan 28961640Speter 29055107Skatostatic status_t 29156372Snyanext2_read_pages(fs_volume* _volume, fs_vnode* _node, void* _cookie, 29255107Skato off_t pos, const iovec* vecs, size_t count, size_t* _numBytes) 29355107Skato{ 29455107Skato Volume* volume = (Volume*)_volume->private_volume; 29555107Skato Inode* inode = (Inode*)_node->private_node; 29655107Skato 29755107Skato if (inode->FileCache() == NULL) 29855107Skato return B_BAD_VALUE; 29955107Skato 30055107Skato rw_lock_read_lock(inode->Lock()); 30156372Snyan 30255107Skato uint32 vecIndex = 0; 30356372Snyan size_t vecOffset = 0; 30456372Snyan size_t bytesLeft = *_numBytes; 30556372Snyan status_t status; 30656372Snyan 30756372Snyan while (true) { 30856372Snyan file_io_vec fileVecs[8]; 30956372Snyan size_t fileVecCount = 8; 31056372Snyan 31155326Snyan status = file_map_translate(inode->Map(), pos, bytesLeft, fileVecs, 31261640Speter &fileVecCount, 0); 31355326Snyan if (status != B_OK && status != B_BUFFER_OVERFLOW) 31461640Speter break; 31561640Speter 31660717Snyan bool bufferOverflow = status == B_BUFFER_OVERFLOW; 31755326Snyan 31855086Skato size_t bytes = bytesLeft; 31954877Skato status = read_file_io_vec_pages(volume->Device(), fileVecs, 32054877Skato fileVecCount, vecs, count, &vecIndex, &vecOffset, &bytes); 32156711Snyan if (status != B_OK || !bufferOverflow) 32256323Skato break; 32356323Skato 32456323Skato pos += bytes; 32556323Skato bytesLeft -= bytes; 32656323Skato } 32756711Snyan 32860717Snyan rw_lock_read_unlock(inode->Lock()); 32960717Snyan 33060717Snyan return status; 33160717Snyan} 33218095Sasami 33356372Snyan 33456442Speterstatic status_t 33561640Speterext2_write_pages(fs_volume* _volume, fs_vnode* _node, void* _cookie, 33661640Speter off_t pos, const iovec* vecs, size_t count, size_t* _numBytes) 33761640Speter{ 33861640Speter Volume* volume = (Volume*)_volume->private_volume; 33961640Speter Inode* inode = (Inode*)_node->private_node; 34061640Speter 34161640Speter if (volume->IsReadOnly()) 34216359Sasami return B_READ_ONLY_DEVICE; 34361640Speter 34458458Snyan if (inode->FileCache() == NULL) 34560711Snyan return B_BAD_VALUE; 34660711Snyan 34760711Snyan rw_lock_read_lock(inode->Lock()); 34861640Speter 34960711Snyan uint32 vecIndex = 0; 35060711Snyan size_t vecOffset = 0; 35160711Snyan size_t bytesLeft = *_numBytes; 35261640Speter status_t status; 35360711Snyan 35450518Snyan while (true) { 35561640Speter file_io_vec fileVecs[8]; 35661640Speter size_t fileVecCount = 8; 35761640Speter 35861640Speter status = file_map_translate(inode->Map(), pos, bytesLeft, fileVecs, 35961640Speter &fileVecCount, 0); 36061640Speter if (status != B_OK && status != B_BUFFER_OVERFLOW) 36161640Speter break; 36261640Speter 36361640Speter bool bufferOverflow = status == B_BUFFER_OVERFLOW; 36417256Sasami 36561640Speter size_t bytes = bytesLeft; 36649519Skato status = write_file_io_vec_pages(volume->Device(), fileVecs, 36761640Speter fileVecCount, vecs, count, &vecIndex, &vecOffset, &bytes); 36850551Sphk if (status != B_OK || !bufferOverflow) 36961640Speter break; 37055326Snyan 37155326Snyan pos += bytes; 37256442Speter bytesLeft -= bytes; 37356442Speter } 37456442Speter 37560717Snyan rw_lock_read_unlock(inode->Lock()); 37656442Speter 37756442Speter return status; 37856442Speter} 37956442Speter 38056978Skato 38156442Speterstatic status_t 38260717Snyanext2_get_file_map(fs_volume* _volume, fs_vnode* _node, off_t offset, 38357655Skato size_t size, struct file_io_vec* vecs, size_t* _count) 38456442Speter{ 38556442Speter TRACE("ext2_get_file_map()\n"); 38656442Speter Volume* volume = (Volume*)_volume->private_volume; 387 Inode* inode = (Inode*)_node->private_node; 388 size_t index = 0, max = *_count; 389 390 while (true) { 391 fsblock_t block; 392 uint32 count = 1; 393 status_t status = inode->FindBlock(offset, block, &count); 394 if (status != B_OK) 395 return status; 396 397 if (block > volume->NumBlocks()) { 398 panic("ext2_get_file_map() found block %" B_PRIu64 " for offset %" 399 B_PRIdOFF "\n", block, offset); 400 } 401 402 off_t blockOffset = block << volume->BlockShift(); 403 uint32 blockLength = volume->BlockSize() * count; 404 405 if (index > 0 && (vecs[index - 1].offset 406 == blockOffset - vecs[index - 1].length 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 (offset >= inode->Size() || size <= blockLength) { 429 // We're done! 430 *_count = index; 431 TRACE("ext2_get_file_map for inode %" B_PRIdINO "\n", inode->ID()); 432 return B_OK; 433 } 434 435 size -= blockLength; 436 } 437 438 // can never get here 439 return B_ERROR; 440} 441 442 443// #pragma mark - 444 445 446static status_t 447ext2_lookup(fs_volume* _volume, fs_vnode* _directory, const char* name, 448 ino_t* _vnodeID) 449{ 450 TRACE("ext2_lookup: name address: %p\n", name); 451 TRACE("ext2_lookup: name: %s\n", name); 452 Volume* volume = (Volume*)_volume->private_volume; 453 Inode* directory = (Inode*)_directory->private_node; 454 455 // check access permissions 456 status_t status = directory->CheckPermissions(X_OK); 457 if (status < B_OK) 458 return status; 459 460 HTree htree(volume, directory); 461 DirectoryIterator* iterator; 462 463 status = htree.Lookup(name, &iterator); 464 if (status != B_OK) 465 return status; 466 467 ObjectDeleter<DirectoryIterator> iteratorDeleter(iterator); 468 469 status = iterator->FindEntry(name, _vnodeID); 470 if (status != B_OK) { 471 if (status == B_ENTRY_NOT_FOUND) 472 entry_cache_add_missing(volume->ID(), directory->ID(), name); 473 return status; 474 } 475 entry_cache_add(volume->ID(), directory->ID(), name, *_vnodeID); 476 477 return get_vnode(volume->FSVolume(), *_vnodeID, NULL); 478} 479 480 481static status_t 482ext2_ioctl(fs_volume* _volume, fs_vnode* _node, void* _cookie, uint32 cmd, 483 void* buffer, size_t bufferLength) 484{ 485 TRACE("ioctl: %" B_PRIu32 "\n", cmd); 486 487 Volume* volume = (Volume*)_volume->private_volume; 488 switch (cmd) { 489 case 56742: 490 { 491 TRACE("ioctl: Test the block allocator\n"); 492 // Test the block allocator 493 TRACE("ioctl: Creating transaction\n"); 494 Transaction transaction(volume->GetJournal()); 495 TRACE("ioctl: Creating cached block\n"); 496 CachedBlock cached(volume); 497 uint32 blocksPerGroup = volume->BlocksPerGroup(); 498 uint32 blockSize = volume->BlockSize(); 499 uint32 firstBlock = volume->FirstDataBlock(); 500 fsblock_t start = 0; 501 uint32 group = 0; 502 uint32 length; 503 504 TRACE("ioctl: blocks per group: %" B_PRIu32 ", block size: %" 505 B_PRIu32 ", first block: %" B_PRIu32 ", start: %" B_PRIu64 506 ", group: %" B_PRIu32 "\n", blocksPerGroup, 507 blockSize, firstBlock, start, group); 508 509 while (volume->AllocateBlocks(transaction, 1, 2048, group, start, 510 length) == B_OK) { 511 TRACE("ioctl: Allocated blocks in group %" B_PRIu32 ": %" 512 B_PRIu64 "-%" B_PRIu64 "\n", group, start, start + length); 513 off_t blockNum = start + group * blocksPerGroup - firstBlock; 514 515 for (uint32 i = 0; i < length; ++i) { 516 uint8* block = cached.SetToWritable(transaction, blockNum); 517 memset(block, 0, blockSize); 518 blockNum++; 519 } 520 521 TRACE("ioctl: Blocks cleared\n"); 522 523 transaction.Done(); 524 transaction.Start(volume->GetJournal()); 525 } 526 527 TRACE("ioctl: Done\n"); 528 529 return B_OK; 530 } 531 532 case FIOSEEKDATA: 533 case FIOSEEKHOLE: 534 { 535 off_t* offset = (off_t*)buffer; 536 Inode* inode = (Inode*)_node->private_node; 537 538 if (*offset >= inode->Size()) 539 return ENXIO; 540 541 while (*offset < inode->Size()) { 542 fsblock_t block; 543 uint32 count = 1; 544 status_t status = inode->FindBlock(*offset, block, &count); 545 if (status != B_OK) 546 return status; 547 if ((block != 0 && cmd == FIOSEEKDATA) 548 || (block == 0 && cmd == FIOSEEKHOLE)) { 549 return B_OK; 550 } 551 *offset += count * volume->BlockSize(); 552 } 553 554 if (*offset > inode->Size()) 555 *offset = inode->Size(); 556 return cmd == FIOSEEKDATA ? ENXIO : B_OK; 557 } 558 } 559 560 return B_DEV_INVALID_IOCTL; 561} 562 563 564/*! Sets the open-mode flags for the open file cookie - only 565 supports O_APPEND currently, but that should be sufficient 566 for a file system. 567*/ 568static status_t 569ext2_set_flags(fs_volume* _volume, fs_vnode* _node, void* _cookie, int flags) 570{ 571 file_cookie* cookie = (file_cookie*)_cookie; 572 cookie->open_mode = (cookie->open_mode & ~O_APPEND) | (flags & O_APPEND); 573 574 return B_OK; 575} 576 577 578static status_t 579ext2_fsync(fs_volume* _volume, fs_vnode* _node) 580{ 581 Inode* inode = (Inode*)_node->private_node; 582 return inode->Sync(); 583} 584 585 586static status_t 587ext2_read_stat(fs_volume* _volume, fs_vnode* _node, struct stat* stat) 588{ 589 Inode* inode = (Inode*)_node->private_node; 590 const ext2_inode& node = inode->Node(); 591 592 stat->st_dev = inode->GetVolume()->ID(); 593 stat->st_ino = inode->ID(); 594 stat->st_nlink = node.NumLinks(); 595 stat->st_blksize = EXT2_IO_SIZE; 596 597 stat->st_uid = node.UserID(); 598 stat->st_gid = node.GroupID(); 599 stat->st_mode = node.Mode(); 600 stat->st_type = 0; 601 602 inode->GetAccessTime(&stat->st_atim); 603 inode->GetModificationTime(&stat->st_mtim); 604 inode->GetChangeTime(&stat->st_ctim); 605 inode->GetCreationTime(&stat->st_crtim); 606 607 stat->st_size = inode->Size(); 608 stat->st_blocks = inode->NumBlocks(); 609 610 return B_OK; 611} 612 613 614status_t 615ext2_write_stat(fs_volume* _volume, fs_vnode* _node, const struct stat* stat, 616 uint32 mask) 617{ 618 TRACE("ext2_write_stat\n"); 619 Volume* volume = (Volume*)_volume->private_volume; 620 621 if (volume->IsReadOnly()) 622 return B_READ_ONLY_DEVICE; 623 624 Inode* inode = (Inode*)_node->private_node; 625 626 ext2_inode& node = inode->Node(); 627 bool updateTime = false; 628 uid_t uid = geteuid(); 629 630 bool isOwnerOrRoot = uid == 0 || uid == (uid_t)node.UserID(); 631 bool hasWriteAccess = inode->CheckPermissions(W_OK) == B_OK; 632 633 TRACE("ext2_write_stat: Starting transaction\n"); 634 Transaction transaction(volume->GetJournal()); 635 inode->WriteLockInTransaction(transaction); 636 637 if ((mask & B_STAT_SIZE) != 0 && inode->Size() != stat->st_size) { 638 if (inode->IsDirectory()) 639 return B_IS_A_DIRECTORY; 640 if (!inode->IsFile()) 641 return B_BAD_VALUE; 642 if (!hasWriteAccess) 643 return B_NOT_ALLOWED; 644 645 TRACE("ext2_write_stat: Old size: %ld, new size: %ld\n", 646 (long)inode->Size(), (long)stat->st_size); 647 648 off_t oldSize = inode->Size(); 649 650 status_t status = inode->Resize(transaction, stat->st_size); 651 if (status != B_OK) 652 return status; 653 654 if ((mask & B_STAT_SIZE_INSECURE) == 0) { 655 rw_lock_write_unlock(inode->Lock()); 656 inode->FillGapWithZeros(oldSize, inode->Size()); 657 rw_lock_write_lock(inode->Lock()); 658 } 659 660 updateTime = true; 661 } 662 663 if ((mask & B_STAT_MODE) != 0) { 664 // only the user or root can do that 665 if (!isOwnerOrRoot) 666 return B_NOT_ALLOWED; 667 node.UpdateMode(stat->st_mode, S_IUMSK); 668 updateTime = true; 669 } 670 671 if ((mask & B_STAT_UID) != 0) { 672 // only root should be allowed 673 if (uid != 0) 674 return B_NOT_ALLOWED; 675 node.SetUserID(stat->st_uid); 676 updateTime = true; 677 } 678 679 if ((mask & B_STAT_GID) != 0) { 680 // only the user or root can do that 681 if (!isOwnerOrRoot) 682 return B_NOT_ALLOWED; 683 node.SetGroupID(stat->st_gid); 684 updateTime = true; 685 } 686 687 if ((mask & B_STAT_MODIFICATION_TIME) != 0 || updateTime 688 || (mask & B_STAT_CHANGE_TIME) != 0) { 689 // the user or root can do that or any user with write access 690 if (!isOwnerOrRoot && !hasWriteAccess) 691 return B_NOT_ALLOWED; 692 struct timespec newTimespec = { 0, 0}; 693 694 if ((mask & B_STAT_MODIFICATION_TIME) != 0) 695 newTimespec = stat->st_mtim; 696 697 if ((mask & B_STAT_CHANGE_TIME) != 0 698 && stat->st_ctim.tv_sec > newTimespec.tv_sec) 699 newTimespec = stat->st_ctim; 700 701 if (newTimespec.tv_sec == 0) 702 Inode::_BigtimeToTimespec(real_time_clock_usecs(), &newTimespec); 703 704 inode->SetModificationTime(&newTimespec); 705 } 706 if ((mask & B_STAT_CREATION_TIME) != 0) { 707 // the user or root can do that or any user with write access 708 if (!isOwnerOrRoot && !hasWriteAccess) 709 return B_NOT_ALLOWED; 710 inode->SetCreationTime(&stat->st_crtim); 711 } 712 713 status_t status = inode->WriteBack(transaction); 714 if (status == B_OK) 715 status = transaction.Done(); 716 if (status == B_OK) 717 notify_stat_changed(volume->ID(), -1, inode->ID(), mask); 718 719 return status; 720} 721 722 723static status_t 724ext2_create(fs_volume* _volume, fs_vnode* _directory, const char* name, 725 int openMode, int mode, void** _cookie, ino_t* _vnodeID) 726{ 727 Volume* volume = (Volume*)_volume->private_volume; 728 Inode* directory = (Inode*)_directory->private_node; 729 730 TRACE("ext2_create()\n"); 731 732 if (volume->IsReadOnly()) 733 return B_READ_ONLY_DEVICE; 734 735 if (!directory->IsDirectory()) 736 return B_BAD_TYPE; 737 738 TRACE("ext2_create(): Creating cookie\n"); 739 740 // Allocate cookie 741 file_cookie* cookie = new(std::nothrow) file_cookie; 742 if (cookie == NULL) 743 return B_NO_MEMORY; 744 ObjectDeleter<file_cookie> cookieDeleter(cookie); 745 746 cookie->open_mode = openMode; 747 cookie->last_size = 0; 748 cookie->last_notification = system_time(); 749 750 TRACE("ext2_create(): Starting transaction\n"); 751 752 Transaction transaction(volume->GetJournal()); 753 754 TRACE("ext2_create(): Creating inode\n"); 755 756 Inode* inode; 757 bool created; 758 status_t status = Inode::Create(transaction, directory, name, 759 S_FILE | (mode & S_IUMSK), openMode, EXT2_TYPE_FILE, &created, _vnodeID, 760 &inode, &gExt2VnodeOps); 761 if (status != B_OK) 762 return status; 763 764 TRACE("ext2_create(): Created inode\n"); 765 766 if ((openMode & O_NOCACHE) != 0) { 767 status = inode->DisableFileCache(); 768 if (status != B_OK) 769 return status; 770 } 771 772 entry_cache_add(volume->ID(), directory->ID(), name, *_vnodeID); 773 774 status = transaction.Done(); 775 if (status != B_OK) { 776 entry_cache_remove(volume->ID(), directory->ID(), name); 777 return status; 778 } 779 780 *_cookie = cookie; 781 cookieDeleter.Detach(); 782 783 if (created) 784 notify_entry_created(volume->ID(), directory->ID(), name, *_vnodeID); 785 786 return B_OK; 787} 788 789 790static status_t 791ext2_create_symlink(fs_volume* _volume, fs_vnode* _directory, const char* name, 792 const char* path, int mode) 793{ 794 TRACE("ext2_create_symlink()\n"); 795 796 Volume* volume = (Volume*)_volume->private_volume; 797 Inode* directory = (Inode*)_directory->private_node; 798 799 if (volume->IsReadOnly()) 800 return B_READ_ONLY_DEVICE; 801 802 if (!directory->IsDirectory()) 803 return B_BAD_TYPE; 804 805 status_t status = directory->CheckPermissions(W_OK); 806 if (status != B_OK) 807 return status; 808 809 TRACE("ext2_create_symlink(): Starting transaction\n"); 810 Transaction transaction(volume->GetJournal()); 811 812 Inode* link; 813 ino_t id; 814 status = Inode::Create(transaction, directory, name, S_SYMLINK | 0777, 815 0, (uint8)EXT2_TYPE_SYMLINK, NULL, &id, &link); 816 if (status != B_OK) 817 return status; 818 819 // TODO: We have to prepare the link before publishing? 820 821 size_t length = strlen(path); 822 TRACE("ext2_create_symlink(): Path (%s) length: %d\n", path, (int)length); 823 if (length < EXT2_SHORT_SYMLINK_LENGTH) { 824 strcpy(link->Node().symlink, path); 825 link->Node().SetSize((uint32)length); 826 } else { 827 if (!link->HasFileCache()) { 828 status = link->CreateFileCache(); 829 if (status != B_OK) 830 return status; 831 } 832 833 size_t written = length; 834 status = link->WriteAt(transaction, 0, (const uint8*)path, &written); 835 if (status == B_OK && written != length) 836 status = B_IO_ERROR; 837 } 838 839 if (status == B_OK) 840 status = link->WriteBack(transaction); 841 842 TRACE("ext2_create_symlink(): Publishing vnode\n"); 843 publish_vnode(volume->FSVolume(), id, link, &gExt2VnodeOps, 844 link->Mode(), 0); 845 put_vnode(volume->FSVolume(), id); 846 847 if (status == B_OK) { 848 entry_cache_add(volume->ID(), directory->ID(), name, id); 849 850 status = transaction.Done(); 851 if (status == B_OK) 852 notify_entry_created(volume->ID(), directory->ID(), name, id); 853 else 854 entry_cache_remove(volume->ID(), directory->ID(), name); 855 } 856 TRACE("ext2_create_symlink(): Done\n"); 857 858 return status; 859} 860 861 862static status_t 863ext2_link(fs_volume* volume, fs_vnode* dir, const char* name, fs_vnode* node) 864{ 865 // TODO 866 867 return B_UNSUPPORTED; 868} 869 870 871static status_t 872ext2_unlink(fs_volume* _volume, fs_vnode* _directory, const char* name) 873{ 874 TRACE("ext2_unlink()\n"); 875 if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0) 876 return B_NOT_ALLOWED; 877 878 Volume* volume = (Volume*)_volume->private_volume; 879 Inode* directory = (Inode*)_directory->private_node; 880 881 status_t status = directory->CheckPermissions(W_OK); 882 if (status != B_OK) 883 return status; 884 885 TRACE("ext2_unlink(): Starting transaction\n"); 886 Transaction transaction(volume->GetJournal()); 887 888 directory->WriteLockInTransaction(transaction); 889 890 TRACE("ext2_unlink(): Looking up for directory entry\n"); 891 HTree htree(volume, directory); 892 DirectoryIterator* directoryIterator; 893 894 status = htree.Lookup(name, &directoryIterator); 895 if (status != B_OK) 896 return status; 897 898 ino_t id; 899 status = directoryIterator->FindEntry(name, &id); 900 if (status != B_OK) 901 return status; 902 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 932static status_t 933ext2_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 TRACE("ext2_rename(): found entry to rename\n"); 972 973 if (oldDirectory != newDirectory) { 974 TRACE("ext2_rename(): Different parent directories\n"); 975 CachedBlock cached(volume); 976 977 ino_t parentID = newDirectory->ID(); 978 ino_t oldDirID = oldDirectory->ID(); 979 980 do { 981 Vnode vnode(volume, parentID); 982 Inode* parent; 983 984 status = vnode.Get(&parent); 985 if (status != B_OK) 986 return B_IO_ERROR; 987 988 fsblock_t blockNum; 989 status = parent->FindBlock(0, blockNum); 990 if (status != B_OK) 991 return status; 992 993 const HTreeRoot* data = (const HTreeRoot*)cached.SetTo(blockNum); 994 parentID = data->dotdot.InodeID(); 995 } while (parentID != oldID && parentID != oldDirID 996 && parentID != EXT2_ROOT_NODE); 997 998 if (parentID == oldID) 999 return B_BAD_VALUE; 1000 } 1001 1002 HTree newHTree(volume, newDirectory); 1003 DirectoryIterator* newIterator; 1004 1005 status = newHTree.Lookup(newName, &newIterator); 1006 if (status != B_OK) 1007 return status; 1008 1009 TRACE("ext2_rename(): found new directory\n"); 1010 1011 ObjectDeleter<DirectoryIterator> newIteratorDeleter(newIterator); 1012 1013 Vnode vnode(volume, oldID); 1014 Inode* inode; 1015 1016 status = vnode.Get(&inode); 1017 if (status != B_OK) 1018 return status; 1019 1020 uint8 fileType; 1021 1022 // TODO: Support all file types? 1023 if (inode->IsDirectory()) 1024 fileType = EXT2_TYPE_DIRECTORY; 1025 else if (inode->IsSymLink()) 1026 fileType = EXT2_TYPE_SYMLINK; 1027 else 1028 fileType = EXT2_TYPE_FILE; 1029 1030 // Add entry in destination directory 1031 ino_t existentID; 1032 status = newIterator->FindEntry(newName, &existentID); 1033 if (status == B_OK) { 1034 TRACE("ext2_rename(): found existing new entry\n"); 1035 if (existentID == oldID) { 1036 // Remove entry in oldID 1037 // return inode->Unlink(); 1038 return B_BAD_VALUE; 1039 } 1040 1041 Vnode vnodeExistent(volume, existentID); 1042 Inode* existent; 1043 1044 if (vnodeExistent.Get(&existent) != B_OK) 1045 return B_NAME_IN_USE; 1046 1047 if (existent->IsDirectory() != inode->IsDirectory()) { 1048 return existent->IsDirectory() ? B_IS_A_DIRECTORY 1049 : B_NOT_A_DIRECTORY; 1050 } 1051 1052 // TODO: Perhaps we have to revert this in case of error? 1053 status = newIterator->ChangeEntry(transaction, oldID, fileType); 1054 if (status != B_OK) 1055 return status; 1056 1057 status = existent->Unlink(transaction); 1058 if (status != B_OK) 1059 ERROR("Error while unlinking existing destination\n"); 1060 1061 entry_cache_remove(volume->ID(), newDirectory->ID(), newName); 1062 1063 notify_entry_removed(volume->ID(), newDirectory->ID(), newName, 1064 existentID); 1065 } else if (status == B_ENTRY_NOT_FOUND) { 1066 newIterator->Restart(); 1067 1068 status = newIterator->AddEntry(transaction, newName, strlen(newName), 1069 oldID, fileType); 1070 if (status != B_OK) 1071 return status; 1072 1073 } else 1074 return status; 1075 1076 if (oldDirectory == newDirectory) { 1077 status = oldHTree.Lookup(oldName, &oldIterator); 1078 if (status != B_OK) 1079 return status; 1080 1081 oldIteratorDeleter.SetTo(oldIterator); 1082 status = oldIterator->FindEntry(oldName, &oldID); 1083 if (status != B_OK) 1084 return status; 1085 } 1086 1087 // Remove entry from source folder 1088 status = oldIterator->RemoveEntry(transaction); 1089 if (status != B_OK) 1090 return status; 1091 1092 inode->WriteLockInTransaction(transaction); 1093 1094 if (oldDirectory != newDirectory && inode->IsDirectory()) { 1095 DirectoryIterator inodeIterator(inode); 1096 1097 status = inodeIterator.FindEntry(".."); 1098 if (status == B_ENTRY_NOT_FOUND) { 1099 ERROR("Corrupt file system. Missing \"..\" in directory %" 1100 B_PRIdINO "\n", inode->ID()); 1101 return B_BAD_DATA; 1102 } else if (status != B_OK) 1103 return status; 1104 1105 inodeIterator.ChangeEntry(transaction, newDirectory->ID(), 1106 (uint8)EXT2_TYPE_DIRECTORY); 1107 // Decrement hardlink count on the source folder 1108 status = oldDirectory->Unlink(transaction); 1109 if (status != B_OK) 1110 ERROR("Error while decrementing hardlink count on the source folder\n"); 1111 // Increment hardlink count on the destination folder 1112 newDirectory->IncrementNumLinks(transaction); 1113 status = newDirectory->WriteBack(transaction); 1114 if (status != B_OK) 1115 ERROR("Error while writing back the destination folder inode\n"); 1116 } 1117 1118 status = inode->WriteBack(transaction); 1119 if (status != B_OK) 1120 return status; 1121 1122 entry_cache_remove(volume->ID(), oldDirectory->ID(), oldName); 1123 entry_cache_add(volume->ID(), newDirectory->ID(), newName, oldID); 1124 1125 status = transaction.Done(); 1126 if (status != B_OK) { 1127 entry_cache_remove(volume->ID(), oldDirectory->ID(), newName); 1128 entry_cache_add(volume->ID(), newDirectory->ID(), oldName, oldID); 1129 1130 return status; 1131 } 1132 1133 notify_entry_moved(volume->ID(), oldDirectory->ID(), oldName, 1134 newDirectory->ID(), newName, oldID); 1135 1136 return B_OK; 1137} 1138 1139 1140static status_t 1141ext2_open(fs_volume* _volume, fs_vnode* _node, int openMode, void** _cookie) 1142{ 1143 Volume* volume = (Volume*)_volume->private_volume; 1144 Inode* inode = (Inode*)_node->private_node; 1145 1146 // opening a directory read-only is allowed, although you can't read 1147 // any data from it. 1148 if (inode->IsDirectory() && (openMode & O_RWMASK) != 0) 1149 return B_IS_A_DIRECTORY; 1150 1151 status_t status = inode->CheckPermissions(open_mode_to_access(openMode) 1152 | (openMode & O_TRUNC ? W_OK : 0)); 1153 if (status != B_OK) 1154 return status; 1155 1156 // Prepare the cookie 1157 file_cookie* cookie = new(std::nothrow) file_cookie; 1158 if (cookie == NULL) 1159 return B_NO_MEMORY; 1160 ObjectDeleter<file_cookie> cookieDeleter(cookie); 1161 1162 cookie->open_mode = openMode & EXT2_OPEN_MODE_USER_MASK; 1163 cookie->last_size = inode->Size(); 1164 cookie->last_notification = system_time(); 1165 1166 MethodDeleter<Inode, status_t, &Inode::EnableFileCache> fileCacheEnabler; 1167 if ((openMode & O_NOCACHE) != 0) { 1168 status = inode->DisableFileCache(); 1169 if (status != B_OK) 1170 return status; 1171 fileCacheEnabler.SetTo(inode); 1172 } 1173 1174 // Should we truncate the file? 1175 if ((openMode & O_TRUNC) != 0) { 1176 if ((openMode & O_RWMASK) == O_RDONLY) 1177 return B_NOT_ALLOWED; 1178 1179 Transaction transaction(volume->GetJournal()); 1180 inode->WriteLockInTransaction(transaction); 1181 1182 status_t status = inode->Resize(transaction, 0); 1183 if (status == B_OK) 1184 status = inode->WriteBack(transaction); 1185 if (status == B_OK) 1186 status = transaction.Done(); 1187 if (status != B_OK) 1188 return status; 1189 1190 // TODO: No need to notify file size changed? 1191 } 1192 1193 fileCacheEnabler.Detach(); 1194 cookieDeleter.Detach(); 1195 *_cookie = cookie; 1196 1197 return B_OK; 1198} 1199 1200 1201static status_t 1202ext2_read(fs_volume* _volume, fs_vnode* _node, void* _cookie, off_t pos, 1203 void* buffer, size_t* _length) 1204{ 1205 Inode* inode = (Inode*)_node->private_node; 1206 1207 if (!inode->IsFile()) { 1208 *_length = 0; 1209 return inode->IsDirectory() ? B_IS_A_DIRECTORY : B_BAD_VALUE; 1210 } 1211 1212 return inode->ReadAt(pos, (uint8*)buffer, _length); 1213} 1214 1215 1216static status_t 1217ext2_write(fs_volume* _volume, fs_vnode* _node, void* _cookie, off_t pos, 1218 const void* buffer, size_t* _length) 1219{ 1220 TRACE("ext2_write()\n"); 1221 Volume* volume = (Volume*)_volume->private_volume; 1222 Inode* inode = (Inode*)_node->private_node; 1223 1224 if (volume->IsReadOnly()) 1225 return B_READ_ONLY_DEVICE; 1226 1227 if (inode->IsDirectory()) { 1228 *_length = 0; 1229 return B_IS_A_DIRECTORY; 1230 } 1231 1232 TRACE("ext2_write(): Preparing cookie\n"); 1233 1234 file_cookie* cookie = (file_cookie*)_cookie; 1235 1236 if ((cookie->open_mode & O_APPEND) != 0) 1237 pos = inode->Size(); 1238 1239 TRACE("ext2_write(): Creating transaction\n"); 1240 Transaction transaction; 1241 1242 status_t status = inode->WriteAt(transaction, pos, (const uint8*)buffer, 1243 _length); 1244 if (status == B_OK) 1245 status = transaction.Done(); 1246 if (status == B_OK) { 1247 TRACE("ext2_write(): Finalizing\n"); 1248 ReadLocker lock(*inode->Lock()); 1249 1250 if (cookie->last_size != inode->Size() 1251 && system_time() > cookie->last_notification 1252 + INODE_NOTIFICATION_INTERVAL) { 1253 notify_stat_changed(volume->ID(), -1, inode->ID(), 1254 B_STAT_MODIFICATION_TIME | B_STAT_SIZE | B_STAT_INTERIM_UPDATE); 1255 cookie->last_size = inode->Size(); 1256 cookie->last_notification = system_time(); 1257 } 1258 } 1259 1260 TRACE("ext2_write(): Done\n"); 1261 1262 return status; 1263} 1264 1265 1266static status_t 1267ext2_close(fs_volume *_volume, fs_vnode *_node, void *_cookie) 1268{ 1269 return B_OK; 1270} 1271 1272 1273static status_t 1274ext2_free_cookie(fs_volume* _volume, fs_vnode* _node, void* _cookie) 1275{ 1276 file_cookie* cookie = (file_cookie*)_cookie; 1277 Volume* volume = (Volume*)_volume->private_volume; 1278 Inode* inode = (Inode*)_node->private_node; 1279 1280 if (inode->Size() != cookie->last_size) 1281 notify_stat_changed(volume->ID(), -1, inode->ID(), B_STAT_SIZE); 1282 1283 if ((cookie->open_mode & O_NOCACHE) != 0) 1284 inode->EnableFileCache(); 1285 1286 delete cookie; 1287 return B_OK; 1288} 1289 1290 1291static status_t 1292ext2_access(fs_volume* _volume, fs_vnode* _node, int accessMode) 1293{ 1294 Inode* inode = (Inode*)_node->private_node; 1295 return inode->CheckPermissions(accessMode); 1296} 1297 1298 1299static status_t 1300ext2_read_link(fs_volume *_volume, fs_vnode *_node, char *buffer, 1301 size_t *_bufferSize) 1302{ 1303 Inode* inode = (Inode*)_node->private_node; 1304 1305 if (!inode->IsSymLink()) 1306 return B_BAD_VALUE; 1307 1308 if (inode->Size() > EXT2_SHORT_SYMLINK_LENGTH) { 1309 status_t result = inode->ReadAt(0, reinterpret_cast<uint8*>(buffer), 1310 _bufferSize); 1311 if (result != B_OK) 1312 return result; 1313 } else { 1314 size_t bytesToCopy = std::min(static_cast<size_t>(inode->Size()), 1315 *_bufferSize); 1316 1317 memcpy(buffer, inode->Node().symlink, bytesToCopy); 1318 } 1319 1320 *_bufferSize = inode->Size(); 1321 return B_OK; 1322} 1323 1324 1325// #pragma mark - Directory functions 1326 1327 1328static status_t 1329ext2_create_dir(fs_volume* _volume, fs_vnode* _directory, const char* name, 1330 int mode) 1331{ 1332 TRACE("ext2_create_dir()\n"); 1333 Volume* volume = (Volume*)_volume->private_volume; 1334 Inode* directory = (Inode*)_directory->private_node; 1335 1336 if (volume->IsReadOnly()) 1337 return B_READ_ONLY_DEVICE; 1338 1339 if (!directory->IsDirectory()) 1340 return B_BAD_TYPE; 1341 1342 status_t status = directory->CheckPermissions(W_OK); 1343 if (status != B_OK) 1344 return status; 1345 1346 TRACE("ext2_create_dir(): Starting transaction\n"); 1347 Transaction transaction(volume->GetJournal()); 1348 1349 ino_t id; 1350 status = Inode::Create(transaction, directory, name, 1351 S_DIRECTORY | (mode & S_IUMSK), 0, EXT2_TYPE_DIRECTORY, NULL, &id); 1352 if (status != B_OK) 1353 return status; 1354 1355 put_vnode(volume->FSVolume(), id); 1356 1357 entry_cache_add(volume->ID(), directory->ID(), name, id); 1358 1359 status = transaction.Done(); 1360 if (status != B_OK) { 1361 entry_cache_remove(volume->ID(), directory->ID(), name); 1362 return status; 1363 } 1364 1365 notify_entry_created(volume->ID(), directory->ID(), name, id); 1366 1367 TRACE("ext2_create_dir(): Done\n"); 1368 1369 return B_OK; 1370} 1371 1372 1373static status_t 1374ext2_remove_dir(fs_volume* _volume, fs_vnode* _directory, const char* name) 1375{ 1376 TRACE("ext2_remove_dir()\n"); 1377 1378 Volume* volume = (Volume*)_volume->private_volume; 1379 Inode* directory = (Inode*)_directory->private_node; 1380 1381 status_t status = directory->CheckPermissions(W_OK); 1382 if (status != B_OK) 1383 return status; 1384 1385 TRACE("ext2_remove_dir(): Starting transaction\n"); 1386 Transaction transaction(volume->GetJournal()); 1387 1388 directory->WriteLockInTransaction(transaction); 1389 1390 TRACE("ext2_remove_dir(): Looking up for directory entry\n"); 1391 HTree htree(volume, directory); 1392 DirectoryIterator* directoryIterator; 1393 1394 status = htree.Lookup(name, &directoryIterator); 1395 if (status != B_OK) 1396 return status; 1397 1398 ino_t id; 1399 status = directoryIterator->FindEntry(name, &id); 1400 if (status != B_OK) 1401 return status; 1402 1403 Vnode vnode(volume, id); 1404 Inode* inode; 1405 status = vnode.Get(&inode); 1406 if (status != B_OK) 1407 return status; 1408 1409 inode->WriteLockInTransaction(transaction); 1410 1411 status = inode->Unlink(transaction); 1412 if (status != B_OK) 1413 return status; 1414 1415 status = directory->Unlink(transaction); 1416 if (status != B_OK) 1417 return status; 1418 1419 status = directoryIterator->RemoveEntry(transaction); 1420 if (status != B_OK) 1421 return status; 1422 1423 entry_cache_remove(volume->ID(), directory->ID(), name); 1424 entry_cache_remove(volume->ID(), id, ".."); 1425 1426 status = transaction.Done(); 1427 if (status != B_OK) { 1428 entry_cache_add(volume->ID(), directory->ID(), name, id); 1429 entry_cache_add(volume->ID(), id, "..", id); 1430 } else 1431 notify_entry_removed(volume->ID(), directory->ID(), name, id); 1432 1433 return status; 1434} 1435 1436 1437static status_t 1438ext2_open_dir(fs_volume* _volume, fs_vnode* _node, void** _cookie) 1439{ 1440 Inode* inode = (Inode*)_node->private_node; 1441 status_t status = inode->CheckPermissions(R_OK); 1442 if (status < B_OK) 1443 return status; 1444 1445 if (!inode->IsDirectory()) 1446 return B_NOT_A_DIRECTORY; 1447 1448 DirectoryIterator* iterator = new(std::nothrow) DirectoryIterator(inode); 1449 if (iterator == NULL) 1450 return B_NO_MEMORY; 1451 1452 *_cookie = iterator; 1453 return B_OK; 1454} 1455 1456 1457static status_t 1458ext2_read_dir(fs_volume *_volume, fs_vnode *_node, void *_cookie, 1459 struct dirent *dirent, size_t bufferSize, uint32 *_num) 1460{ 1461 DirectoryIterator *iterator = (DirectoryIterator *)_cookie; 1462 Volume* volume = (Volume*)_volume->private_volume; 1463 1464 uint32 maxCount = *_num; 1465 uint32 count = 0; 1466 1467 while (count < maxCount && bufferSize > sizeof(struct dirent)) { 1468 1469 size_t length = bufferSize - offsetof(struct dirent, d_name); 1470 ino_t id; 1471 1472 status_t status = iterator->GetNext(dirent->d_name, &length, &id); 1473 if (status == B_ENTRY_NOT_FOUND) 1474 break; 1475 1476 if (status == B_BUFFER_OVERFLOW) { 1477 // the remaining name buffer length was too small 1478 if (count == 0) 1479 return B_BUFFER_OVERFLOW; 1480 break; 1481 } 1482 1483 if (status != B_OK) 1484 return status; 1485 1486 status = iterator->Next(); 1487 if (status != B_OK && status != B_ENTRY_NOT_FOUND) 1488 return status; 1489 1490 dirent->d_dev = volume->ID(); 1491 dirent->d_ino = id; 1492 1493 dirent = next_dirent(dirent, length, bufferSize); 1494 count++; 1495 } 1496 1497 *_num = count; 1498 return B_OK; 1499} 1500 1501 1502static status_t 1503ext2_rewind_dir(fs_volume * /*_volume*/, fs_vnode * /*node*/, void *_cookie) 1504{ 1505 DirectoryIterator *iterator = (DirectoryIterator *)_cookie; 1506 return iterator->Rewind(); 1507} 1508 1509 1510static status_t 1511ext2_close_dir(fs_volume * /*_volume*/, fs_vnode * /*node*/, void * /*_cookie*/) 1512{ 1513 return B_OK; 1514} 1515 1516 1517static status_t 1518ext2_free_dir_cookie(fs_volume *_volume, fs_vnode *_node, void *_cookie) 1519{ 1520 delete (DirectoryIterator*)_cookie; 1521 return B_OK; 1522} 1523 1524 1525static status_t 1526ext2_open_attr_dir(fs_volume *_volume, fs_vnode *_node, void **_cookie) 1527{ 1528 Inode* inode = (Inode*)_node->private_node; 1529 Volume* volume = (Volume*)_volume->private_volume; 1530 TRACE("%s()\n", __FUNCTION__); 1531 1532 if (!(volume->SuperBlock().CompatibleFeatures() & EXT2_FEATURE_EXT_ATTR)) 1533 return ENOSYS; 1534 1535 // on directories too ? 1536 if (!inode->IsFile()) 1537 return EINVAL; 1538 1539 int32 *index = new(std::nothrow) int32; 1540 if (index == NULL) 1541 return B_NO_MEMORY; 1542 *index = 0; 1543 *(int32**)_cookie = index; 1544 return B_OK; 1545} 1546 1547static status_t 1548ext2_close_attr_dir(fs_volume* _volume, fs_vnode* _node, void* cookie) 1549{ 1550 TRACE("%s()\n", __FUNCTION__); 1551 return B_OK; 1552} 1553 1554 1555static status_t 1556ext2_free_attr_dir_cookie(fs_volume* _volume, fs_vnode* _node, void* _cookie) 1557{ 1558 TRACE("%s()\n", __FUNCTION__); 1559 delete (int32 *)_cookie; 1560 return B_OK; 1561} 1562 1563 1564static status_t 1565ext2_read_attr_dir(fs_volume* _volume, fs_vnode* _node, 1566 void* _cookie, struct dirent* dirent, size_t bufferSize, 1567 uint32* _num) 1568{ 1569 Inode* inode = (Inode*)_node->private_node; 1570 int32 index = *(int32 *)_cookie; 1571 Attribute attribute(inode); 1572 TRACE("%s()\n", __FUNCTION__); 1573 1574 size_t length = bufferSize; 1575 status_t status = attribute.Find(index); 1576 if (status == B_ENTRY_NOT_FOUND) { 1577 *_num = 0; 1578 return B_OK; 1579 } else if (status != B_OK) 1580 return status; 1581 1582 status = attribute.GetName(dirent->d_name, &length); 1583 if (status != B_OK) 1584 return B_OK; 1585 1586 Volume* volume = (Volume*)_volume->private_volume; 1587 1588 dirent->d_dev = volume->ID(); 1589 dirent->d_ino = inode->ID(); 1590 dirent->d_reclen = offsetof(struct dirent, d_name) + length + 1; 1591 1592 *_num = 1; 1593 *(int32*)_cookie = index + 1; 1594 return B_OK; 1595} 1596 1597 1598static status_t 1599ext2_rewind_attr_dir(fs_volume* _volume, fs_vnode* _node, void* _cookie) 1600{ 1601 *(int32*)_cookie = 0; 1602 TRACE("%s()\n", __FUNCTION__); 1603 return B_OK; 1604} 1605 1606 1607 /* attribute operations */ 1608static status_t 1609ext2_open_attr(fs_volume* _volume, fs_vnode* _node, const char* name, 1610 int openMode, void** _cookie) 1611{ 1612 TRACE("%s()\n", __FUNCTION__); 1613 1614 Volume* volume = (Volume*)_volume->private_volume; 1615 Inode* inode = (Inode*)_node->private_node; 1616 Attribute attribute(inode); 1617 1618 if (!(volume->SuperBlock().CompatibleFeatures() & EXT2_FEATURE_EXT_ATTR)) 1619 return ENOSYS; 1620 1621 return attribute.Open(name, openMode, (attr_cookie**)_cookie); 1622} 1623 1624 1625static status_t 1626ext2_close_attr(fs_volume* _volume, fs_vnode* _node, 1627 void* cookie) 1628{ 1629 return B_OK; 1630} 1631 1632 1633static status_t 1634ext2_free_attr_cookie(fs_volume* _volume, fs_vnode* _node, 1635 void* cookie) 1636{ 1637 delete (attr_cookie*)cookie; 1638 return B_OK; 1639} 1640 1641 1642static status_t 1643ext2_read_attr(fs_volume* _volume, fs_vnode* _node, void* _cookie, 1644 off_t pos, void* buffer, size_t* _length) 1645{ 1646 TRACE("%s()\n", __FUNCTION__); 1647 1648 attr_cookie* cookie = (attr_cookie*)_cookie; 1649 Inode* inode = (Inode*)_node->private_node; 1650 1651 Attribute attribute(inode, cookie); 1652 1653 return attribute.Read(cookie, pos, (uint8*)buffer, _length); 1654} 1655 1656 1657static status_t 1658ext2_read_attr_stat(fs_volume* _volume, fs_vnode* _node, 1659 void* _cookie, struct stat* stat) 1660{ 1661 attr_cookie* cookie = (attr_cookie*)_cookie; 1662 Inode* inode = (Inode*)_node->private_node; 1663 1664 Attribute attribute(inode, cookie); 1665 1666 return attribute.Stat(*stat); 1667} 1668 1669 1670fs_volume_ops gExt2VolumeOps = { 1671 &ext2_unmount, 1672 &ext2_read_fs_info, 1673 &ext2_write_fs_info, 1674 &ext2_sync, 1675 &ext2_get_vnode, 1676}; 1677 1678 1679fs_vnode_ops gExt2VnodeOps = { 1680 /* vnode operations */ 1681 &ext2_lookup, 1682 NULL, 1683 &ext2_put_vnode, 1684 &ext2_remove_vnode, 1685 1686 /* VM file access */ 1687 &ext2_can_page, 1688 &ext2_read_pages, 1689 &ext2_write_pages, 1690 1691 NULL, // io() 1692 NULL, // cancel_io() 1693 1694 &ext2_get_file_map, 1695 1696 &ext2_ioctl, 1697 &ext2_set_flags, 1698 NULL, // fs_select 1699 NULL, // fs_deselect 1700 &ext2_fsync, 1701 1702 &ext2_read_link, 1703 &ext2_create_symlink, 1704 1705 &ext2_link, 1706 &ext2_unlink, 1707 &ext2_rename, 1708 1709 &ext2_access, 1710 &ext2_read_stat, 1711 &ext2_write_stat, 1712 NULL, // fs_preallocate 1713 1714 /* file operations */ 1715 &ext2_create, 1716 &ext2_open, 1717 &ext2_close, 1718 &ext2_free_cookie, 1719 &ext2_read, 1720 &ext2_write, 1721 1722 /* directory operations */ 1723 &ext2_create_dir, 1724 &ext2_remove_dir, 1725 &ext2_open_dir, 1726 &ext2_close_dir, 1727 &ext2_free_dir_cookie, 1728 &ext2_read_dir, 1729 &ext2_rewind_dir, 1730 1731 /* attribute directory operations */ 1732 &ext2_open_attr_dir, 1733 &ext2_close_attr_dir, 1734 &ext2_free_attr_dir_cookie, 1735 &ext2_read_attr_dir, 1736 &ext2_rewind_attr_dir, 1737 1738 /* attribute operations */ 1739 NULL, 1740 &ext2_open_attr, 1741 &ext2_close_attr, 1742 &ext2_free_attr_cookie, 1743 &ext2_read_attr, 1744 NULL, 1745 &ext2_read_attr_stat, 1746 NULL, 1747 NULL, 1748 NULL, 1749}; 1750 1751 1752static file_system_module_info sExt2FileSystem = { 1753 { 1754 "file_systems/ext2" B_CURRENT_FS_API_VERSION, 1755 0, 1756 NULL, 1757 }, 1758 1759 "ext4", // short_name 1760 "Linux Extended File System 2/3/4", // pretty_name 1761 B_DISK_SYSTEM_SUPPORTS_WRITING 1762 | B_DISK_SYSTEM_SUPPORTS_CONTENT_NAME, // DDM flags 1763 1764 // scanning 1765 ext2_identify_partition, 1766 ext2_scan_partition, 1767 ext2_free_identify_partition_cookie, 1768 NULL, // free_partition_content_cookie() 1769 1770 &ext2_mount, 1771 1772 NULL, 1773}; 1774 1775 1776module_info *modules[] = { 1777 (module_info *)&sExt2FileSystem, 1778 NULL, 1779}; 1780