* Copyright 2008, Axel Dörfler, axeld@pinc-software.de.
* This file may be used under the terms of the MIT License.
*/
#include "DirectoryIterator.h"
#include <string.h>
#include <AutoDeleter.h>
#include <util/VectorSet.h>
#include "CachedBlock.h"
#include "CRCTable.h"
#include "HTree.h"
#include "Inode.h"
#undef ASSERT
#ifdef TRACE_EXT2
# define TRACE(x...) dprintf("\33[34mext2:\33[0m " x)
# define ASSERT(x) { if (!(x)) kernel_debugger("ext2: assert failed: " #x "\n"); }
#else
# define TRACE(x...) ;
# define ASSERT(x) ;
#endif
struct HashedEntry
{
uint8* position;
uint32 hash;
bool operator<(const HashedEntry& other) const
{
return hash <= other.hash;
}
bool operator>(const HashedEntry& other) const
{
return hash >= other.hash;
}
};
DirectoryIterator::DirectoryIterator(Inode* directory, off_t start,
HTreeEntryIterator* parent)
:
fDirectory(directory),
fVolume(directory->GetVolume()),
fBlockSize(fVolume->BlockSize()),
fParent(parent),
fNumBlocks(directory->Size() == 0 ? 0
: (directory->Size() - 1) / fBlockSize + 1),
fLogicalBlock(start / fBlockSize),
fDisplacement(start % fBlockSize),
fPreviousDisplacement(fDisplacement),
fStartLogicalBlock(fLogicalBlock),
fStartDisplacement(fDisplacement)
{
TRACE("DirectoryIterator::DirectoryIterator() %" B_PRIdINO ": num blocks: "
"%" B_PRIu32 "\n", fDirectory->ID(), fNumBlocks);
fIndexing = parent != NULL;
fInitStatus = fDirectory->FindBlock(start, fPhysicalBlock);
fStartPhysicalBlock = fPhysicalBlock;
}
DirectoryIterator::~DirectoryIterator()
{
TRACE("DirectoryIterator::~DirectoryIterator(): %p, parent: %p\n", this,
fParent);
delete fParent;
TRACE("DirectoryIterator::~DirectoryIterator(): Deleted the parent\n");
}
status_t
DirectoryIterator::InitCheck()
{
return fInitStatus;
}
status_t
DirectoryIterator::Get(char* name, size_t* _nameLength, ino_t* _id)
{
TRACE("DirectoryIterator::Get() ID %" B_PRIdINO "\n", fDirectory->ID());
if (_Offset() >= fDirectory->Size()) {
TRACE("DirectoryIterator::Get() out of entries\n");
return B_ENTRY_NOT_FOUND;
}
CachedBlock cached(fVolume);
const uint8* block = cached.SetTo(fPhysicalBlock);
if (block == NULL)
return B_IO_ERROR;
ASSERT(_CheckBlock(block) == B_OK);
TRACE("DirectoryIterator::Get(): Displacement: %" B_PRIu32 "\n",
fDisplacement);
const ext2_dir_entry* entry = (const ext2_dir_entry*)&block[fDisplacement];
if (entry->Length() == 0 || entry->InodeID() == 0)
return B_BAD_DATA;
if (entry->NameLength() != 0) {
size_t length = entry->NameLength();
TRACE("block %" B_PRIu32 ", displacement %" B_PRIu32 ": entry ino %"
B_PRIu32 ", length %u, name length %" B_PRIuSIZE ", type %u\n",
fLogicalBlock, fDisplacement, entry->InodeID(), entry->Length(),
length, entry->FileType());
if (*_nameLength > 0) {
if (length + 1 > *_nameLength)
return B_BUFFER_OVERFLOW;
memcpy(name, entry->name, length);
name[length] = '\0';
*_nameLength = length;
}
*_id = entry->InodeID();
} else
*_nameLength = 0;
return B_OK;
}
status_t
DirectoryIterator::GetNext(char* name, size_t* _nameLength, ino_t* _id)
{
status_t status;
while ((status = Get(name, _nameLength, _id)) == B_BAD_DATA) {
status = Next();
if (status != B_OK)
return status;
}
return status;
}
status_t
DirectoryIterator::Next()
{
TRACE("DirectoryIterator::Next() fDirectory->ID() %" B_PRIdINO "\n",
fDirectory->ID());
if (_Offset() >= fDirectory->Size()) {
TRACE("DirectoryIterator::Next() out of entries\n");
return B_ENTRY_NOT_FOUND;
}
TRACE("DirectoryIterator::Next(): Creating cached block\n");
CachedBlock cached(fVolume);
ext2_dir_entry* entry;
TRACE("DirectoryIterator::Next(): Loading cached block\n");
const uint8* block = cached.SetTo(fPhysicalBlock);
if (block == NULL)
return B_IO_ERROR;
ASSERT(_CheckBlock(block) == B_OK);
entry = (ext2_dir_entry*)(block + fDisplacement);
do {
TRACE("Checking entry at block %" B_PRIu64 ", displacement %" B_PRIu32
" entry inodeid %" B_PRIu32 "\n", fPhysicalBlock, fDisplacement,
entry->InodeID());
if (entry->Length() != 0) {
if (!entry->IsValid()) {
TRACE("invalid entry.\n");
return B_BAD_DATA;
}
fPreviousDisplacement = fDisplacement;
fDisplacement += entry->Length();
} else
fDisplacement = fBlockSize;
if (fDisplacement == fBlockSize) {
TRACE("Reached end of block\n");
fDisplacement = 0;
status_t status = _NextBlock();
if (status != B_OK)
return status;
if ((off_t)(_Offset() + ext2_dir_entry::MinimumSize())
>= fDirectory->Size()) {
TRACE("DirectoryIterator::Next() end of directory file\n");
return B_ENTRY_NOT_FOUND;
}
status = fDirectory->FindBlock(_Offset(), fPhysicalBlock);
if (status != B_OK)
return status;
block = cached.SetTo(fPhysicalBlock);
if (block == NULL)
return B_IO_ERROR;
ASSERT(_CheckBlock(block) == B_OK);
} else if (fDisplacement > fBlockSize) {
TRACE("The entry isn't block aligned.\n");
return B_BAD_DATA;
}
entry = (ext2_dir_entry*)(block + fDisplacement);
TRACE("DirectoryIterator::Next() skipping entry %d %" B_PRIu32 "\n",
entry->Length(), entry->InodeID());
} while (entry->Length() == 0 || entry->InodeID() == 0);
TRACE("DirectoryIterator::Next() entry->Length() %d entry->name %*s\n",
entry->Length(), entry->NameLength(), entry->name);
return B_OK;
}
status_t
DirectoryIterator::Rewind()
{
fDisplacement = 0;
fPreviousDisplacement = 0;
fLogicalBlock = 0;
return fDirectory->FindBlock(0, fPhysicalBlock);
}
void
DirectoryIterator::Restart()
{
TRACE("DirectoryIterator::Restart(): (logical, physical, displacement): "
"current: (%" B_PRIu32 ", %" B_PRIu64 ", %" B_PRIu32 "), start: (%"
B_PRIu32 ", %" B_PRIu64 ", %" B_PRIu32 ")\n", fLogicalBlock,
fPhysicalBlock, fDisplacement, fStartLogicalBlock, fStartPhysicalBlock,
fStartDisplacement);
fLogicalBlock = fStartLogicalBlock;
fPhysicalBlock = fStartPhysicalBlock;
fDisplacement = fPreviousDisplacement = fStartDisplacement;
}
status_t
DirectoryIterator::AddEntry(Transaction& transaction, const char* name,
size_t _nameLength, ino_t id, uint8 type)
{
TRACE("DirectoryIterator::AddEntry(%s, ...)\n", name);
uint8 nameLength = _nameLength > EXT2_NAME_LENGTH ? EXT2_NAME_LENGTH
: _nameLength;
status_t status = B_OK;
while (status == B_OK) {
uint16 pos = 0;
uint16 newLength;
status = _AllocateBestEntryInBlock(nameLength, pos, newLength);
if (status == B_OK) {
return _AddEntry(transaction, name, nameLength, id, type, newLength,
pos);
} else if (status != B_DEVICE_FULL)
return status;
fDisplacement = 0;
status = _NextBlock();
if (status == B_OK)
status = fDirectory->FindBlock(_Offset(), fPhysicalBlock);
}
if (status != B_ENTRY_NOT_FOUND)
return status;
bool firstSplit = fNumBlocks == 1 && fVolume->IndexedDirectories();
fNumBlocks++;
if (fIndexing) {
TRACE("DirectoryIterator::AddEntry(): Adding another HTree leaf\n");
fNumBlocks += fParent->BlocksNeededForNewEntry();
} else if (firstSplit) {
TRACE("DirectoryIterator::AddEntry(): Creating index for directory\n");
fNumBlocks++;
} else
TRACE("DirectoryIterator::AddEntry(): Enlarging directory\n");
status = fDirectory->Resize(transaction, fNumBlocks * fBlockSize);
if (status != B_OK)
return status;
if (firstSplit || fIndexing) {
return _SplitIndexedBlock(transaction, name, nameLength, id, type,
fNumBlocks - 1, firstSplit);
}
fLogicalBlock = fNumBlocks - 1;
status = fDirectory->FindBlock(fLogicalBlock * fBlockSize, fPhysicalBlock);
if (status != B_OK)
return status;
return _AddEntry(transaction, name, nameLength, id, type, fBlockSize, 0,
false);
}
status_t
DirectoryIterator::FindEntry(const char* name, ino_t* _id)
{
TRACE("DirectoryIterator::FindEntry(): %p %p\n", this, name);
char buffer[EXT2_NAME_LENGTH + 1];
ino_t id;
status_t status = B_OK;
size_t length = strlen(name);
while (status == B_OK) {
size_t nameLength = EXT2_NAME_LENGTH;
status = Get(buffer, &nameLength, &id);
if (status == B_OK) {
if (length == nameLength
&& strncmp(name, buffer, nameLength) == 0) {
if (_id != NULL)
*_id = id;
return B_OK;
}
} else if (status != B_BAD_DATA)
break;
status = Next();
}
return status;
}
status_t
DirectoryIterator::RemoveEntry(Transaction& transaction)
{
TRACE("DirectoryIterator::RemoveEntry()\n");
ext2_dir_entry* previousEntry;
ext2_dir_entry* dirEntry;
CachedBlock cached(fVolume);
uint8* block = cached.SetToWritable(transaction, fPhysicalBlock);
uint32 maxSize = _MaxSize();
if (fDisplacement == 0) {
previousEntry = (ext2_dir_entry*)&block[fDisplacement];
fPreviousDisplacement = fDisplacement;
fDisplacement += previousEntry->Length();
if (fDisplacement == maxSize) {
previousEntry->SetInodeID(0);
fDisplacement = 0;
return B_OK;
} else if (fDisplacement > maxSize) {
TRACE("DirectoryIterator::RemoveEntry(): Entry isn't aligned to "
"block entry.");
return B_BAD_DATA;
}
dirEntry = (ext2_dir_entry*)&block[fDisplacement];
memcpy(&block[fPreviousDisplacement], &block[fDisplacement],
dirEntry->Length());
previousEntry->SetLength(fDisplacement - fPreviousDisplacement
+ previousEntry->Length());
fDirectory->SetDirEntryChecksum(block);
ASSERT(_CheckBlock(block) == B_OK);
return B_OK;
}
TRACE("DirectoryIterator::RemoveEntry() fDisplacement %" B_PRIu32 "\n",
fDisplacement);
if (fPreviousDisplacement == fDisplacement) {
char buffer[EXT2_NAME_LENGTH + 1];
dirEntry = (ext2_dir_entry*)&block[fDisplacement];
memcpy(buffer, dirEntry->name, (uint32)dirEntry->name_length);
fDisplacement = 0;
status_t status = FindEntry(dirEntry->name);
if (status == B_ENTRY_NOT_FOUND)
return B_BAD_DATA;
if (status != B_OK)
return status;
}
previousEntry = (ext2_dir_entry*)&block[fPreviousDisplacement];
dirEntry = (ext2_dir_entry*)&block[fDisplacement];
previousEntry->SetLength(previousEntry->Length() + dirEntry->Length());
memset(&block[fDisplacement], 0,
fPreviousDisplacement + previousEntry->Length() - fDisplacement);
fDirectory->SetDirEntryChecksum(block);
ASSERT(_CheckBlock(block) == B_OK);
return B_OK;
}
status_t
DirectoryIterator::ChangeEntry(Transaction& transaction, ino_t id,
uint8 fileType)
{
CachedBlock cached(fVolume);
uint8* block = cached.SetToWritable(transaction, fPhysicalBlock);
if (block == NULL)
return B_IO_ERROR;
ext2_dir_entry* dirEntry = (ext2_dir_entry*)&block[fDisplacement];
dirEntry->SetInodeID(id);
dirEntry->file_type = fileType;
fDirectory->SetDirEntryChecksum(block);
ASSERT(_CheckBlock(block) == B_OK);
return B_OK;
}
status_t
DirectoryIterator::_AllocateBestEntryInBlock(uint8 nameLength, uint16& pos,
uint16& newLength)
{
TRACE("DirectoryIterator::_AllocateBestEntryInBlock()\n");
CachedBlock cached(fVolume);
const uint8* block = cached.SetTo(fPhysicalBlock);
ASSERT(_CheckBlock(block) == B_OK);
uint16 requiredLength = EXT2_DIR_REC_LEN(nameLength);
uint32 maxSize = _MaxSize();
uint16 bestPos = maxSize;
uint16 bestLength = maxSize;
uint16 bestRealLength = maxSize;
ext2_dir_entry* dirEntry;
while (pos < maxSize) {
dirEntry = (ext2_dir_entry*)&block[pos];
if (!_CheckDirEntry(dirEntry, block)) {
TRACE("DirectoryIterator::_AllocateBestEntryInBlock(): invalid "
"dirEntry->Length() pos %d\n", pos);
return B_BAD_DATA;
}
uint16 realLength = EXT2_DIR_REC_LEN(dirEntry->NameLength());
uint16 emptySpace = dirEntry->Length();
if (dirEntry->InodeID() != 0)
emptySpace -= realLength;
if (emptySpace == requiredLength) {
TRACE("DirectoryIterator::_AllocateBestEntryInBlock(): Found an "
"exact length match\n");
newLength = realLength;
return B_OK;
} else if (emptySpace > requiredLength && emptySpace < bestLength) {
bestPos = pos;
bestLength = emptySpace;
bestRealLength = realLength;
}
pos += dirEntry->Length();
}
if (bestPos == maxSize)
return B_DEVICE_FULL;
TRACE("DirectoryIterator::_AllocateBestEntryInBlock(): Found a suitable "
"location: %u\n", bestPos);
pos = bestPos;
newLength = bestRealLength;
return B_OK;
}
status_t
DirectoryIterator::_AddEntry(Transaction& transaction, const char* name,
uint8 nameLength, ino_t id, uint8 type, uint16 newLength, uint16 pos,
bool hasPrevious)
{
TRACE("DirectoryIterator::_AddEntry(%s, %d, %" B_PRIdINO ", %d, %d, %d, "
"%c)\n", name, nameLength, id, type, newLength, pos,
hasPrevious ? 't' : 'f');
CachedBlock cached(fVolume);
uint8* block = cached.SetToWritable(transaction, fPhysicalBlock);
if (block == NULL)
return B_IO_ERROR;
ext2_dir_entry* dirEntry = (ext2_dir_entry*)&block[pos];
if (hasPrevious) {
uint16 previousLength = dirEntry->Length();
dirEntry->SetLength(newLength);
dirEntry = (ext2_dir_entry*)&block[pos + newLength];
newLength = previousLength - newLength;
}
dirEntry->SetLength(newLength);
dirEntry->name_length = nameLength;
dirEntry->SetInodeID(id);
dirEntry->file_type = type;
memcpy(dirEntry->name, name, nameLength);
fDirectory->SetDirEntryChecksum(block);
ASSERT(_CheckBlock(block) == B_OK);
TRACE("DirectoryIterator::_AddEntry(): Done\n");
return B_OK;
}
status_t
DirectoryIterator::_SplitIndexedBlock(Transaction& transaction,
const char* name, uint8 nameLength, ino_t id, uint8 type,
uint32 newBlocksPos, bool firstSplit)
{
TRACE("DirectoryIterator::_SplitIndexedBlock(.., %s, %u, %" B_PRIdINO ", %"
B_PRIu32 ", %c)\n", name, nameLength, id, newBlocksPos,
firstSplit ? 't' : 'f');
uint8* buffer = new(std::nothrow) uint8[fBlockSize];
if (buffer == NULL)
return B_NO_MEMORY;
ArrayDeleter<uint8> bufferDeleter(buffer);
fsblock_t firstPhysicalBlock = 0;
uint32 maxSize = _MaxSize();
CachedBlock cachedFirst(fVolume);
if (firstSplit) {
status_t status = fDirectory->FindBlock(0, firstPhysicalBlock);
if (status != B_OK)
return status;
const uint8* srcBlock = cachedFirst.SetTo(firstPhysicalBlock);
if (srcBlock == NULL)
return B_IO_ERROR;
memcpy(buffer, srcBlock, fBlockSize);
status = fDirectory->FindBlock(fBlockSize, fPhysicalBlock);
if (status != B_OK)
return status;
}
uint8* firstBlock = cachedFirst.SetToWritable(transaction, fPhysicalBlock);
uint8* secondBlock = NULL;
if (firstBlock == NULL)
return B_IO_ERROR;
status_t status;
if (!firstSplit) {
memcpy(buffer, firstBlock, fBlockSize);
} else {
fDirectory->Node().SetFlag(EXT2_INODE_INDEXED);
HTreeRoot* root;
secondBlock = cachedFirst.SetToWritable(transaction,
firstPhysicalBlock, true);
if (secondBlock == NULL)
return B_IO_ERROR;
status = fDirectory->WriteBack(transaction);
if (status != B_OK)
return status;
memcpy(secondBlock, buffer, 2 * (sizeof(HTreeFakeDirEntry) + 4));
root = (HTreeRoot*)secondBlock;
HTreeFakeDirEntry* dotdot = &root->dotdot;
dotdot->SetEntryLength(fBlockSize - (sizeof(HTreeFakeDirEntry) + 4));
root->hash_version = fVolume->DefaultHashVersion();
root->root_info_length = 8;
root->indirection_levels = 0;
uint32 rootMaxSize = fBlockSize;
if (fVolume->HasMetaGroupChecksumFeature())
rootMaxSize -= sizeof(ext2_htree_tail);
root->count_limit->SetLimit((rootMaxSize
- ((uint8*)root->count_limit - secondBlock)) / sizeof(HTreeEntry));
root->count_limit->SetCount(2);
}
VectorSet<HashedEntry> entrySet;
HTree htree(fVolume, fDirectory);
status = htree.PrepareForHash();
if (status != B_OK)
return status;
uint32 displacement = firstSplit ? 2 * (sizeof(HTreeFakeDirEntry) + 4) : 0;
HashedEntry entry;
ext2_dir_entry* dirEntry = NULL;
while (displacement < maxSize) {
entry.position = &buffer[displacement];
dirEntry = (ext2_dir_entry*)entry.position;
TRACE("DirectoryIterator::_SplitIndexedBlock(): pos: %p, name "
"length: %u, entry length: %u\n", entry.position,
dirEntry->name_length,
dirEntry->Length());
char cbuffer[256];
memcpy(cbuffer, dirEntry->name, dirEntry->name_length);
cbuffer[dirEntry->name_length] = '\0';
entry.hash = htree.Hash(dirEntry->name, dirEntry->name_length);
TRACE("DirectoryIterator::_SplitIndexedBlock(): %s -> %" B_PRIu32 "\n",
cbuffer, entry.hash);
status = entrySet.Insert(entry);
if (status != B_OK)
return status;
displacement += dirEntry->Length();
}
ext2_dir_entry newEntry;
uint16 newLength = EXT2_DIR_REC_LEN(nameLength);
newEntry.name_length = nameLength;
newEntry.SetLength(newLength);
newEntry.SetInodeID(id);
newEntry.file_type = type;
memcpy(newEntry.name, name, nameLength);
entry.position = (uint8*)&newEntry;
entry.hash = htree.Hash(name, nameLength);
TRACE("DirectoryIterator::_SplitIndexedBlock(): %s -> %" B_PRIu32 "\n",
name, entry.hash);
entrySet.Insert(entry);
VectorSet<HashedEntry>::Iterator iterator = entrySet.Begin();
int32 median = entrySet.Count() / 2;
displacement = 0;
TRACE("DirectoryIterator::_SplitIndexedBlock(): Count: %" B_PRId32
", median: %" B_PRId32 "\n", entrySet.Count(), median);
uint32 previousHash = (*iterator).hash;
for (int32 i = 0; i < median; ++i) {
dirEntry = (ext2_dir_entry*)(*iterator).position;
previousHash = (*iterator).hash;
uint32 realLength = EXT2_DIR_REC_LEN(dirEntry->name_length);
dirEntry->SetLength((uint16)realLength);
memcpy(&firstBlock[displacement], dirEntry, realLength);
displacement += realLength;
iterator++;
}
uint16 oldLength = dirEntry->Length();
dirEntry = (ext2_dir_entry*)&firstBlock[displacement - oldLength];
dirEntry->SetLength(maxSize - displacement + oldLength);
fDirectory->SetDirEntryChecksum(firstBlock);
bool collision = false;
while (iterator != entrySet.End() && (*iterator).hash == previousHash) {
TRACE("DirectoryIterator::_SplitIndexedBlock(): Handling collisions\n");
dirEntry = (ext2_dir_entry*)(*iterator).position;
if (displacement + dirEntry->Length() > maxSize) {
collision = true;
break;
}
memcpy(&firstBlock[displacement], dirEntry, dirEntry->Length());
displacement += dirEntry->Length();
iterator++;
}
uint32 medianHash = (*iterator).hash;
if (firstSplit) {
TRACE("DirectoryIterator::_SplitIndexedBlock(): Updating root\n");
HTreeRoot* root = (HTreeRoot*)secondBlock;
HTreeEntry* htreeEntry = (HTreeEntry*)root->count_limit;
htreeEntry->SetBlock(1);
++htreeEntry;
htreeEntry->SetBlock(2);
htreeEntry->SetHash(medianHash);
off_t start = (off_t)root->root_info_length
+ 2 * (sizeof(HTreeFakeDirEntry) + 4);
_SetHTreeEntryChecksum(secondBlock, start, 2);
fParent = new(std::nothrow) HTreeEntryIterator(start, fDirectory);
if (fParent == NULL)
return B_NO_MEMORY;
fLogicalBlock = 1;
status = fDirectory->FindBlock(fLogicalBlock * fBlockSize,
fPhysicalBlock);
fPreviousDisplacement = fDisplacement = 0;
status = fParent->Init();
}
else {
status = fParent->InsertEntry(transaction, medianHash, fNumBlocks - 1,
newBlocksPos, collision);
}
if (status != B_OK)
return status;
TRACE("DirectoryIterator::_SplitIndexedBlock(): Preparing second leaf "
"block\n");
fDisplacement = 0;
status = fDirectory->FindBlock(fDirectory->Size() - 1, fPhysicalBlock);
if (status != B_OK)
return status;
CachedBlock cachedSecond(fVolume);
secondBlock = cachedSecond.SetToWritable(transaction,
fPhysicalBlock);
if (secondBlock == NULL)
return B_IO_ERROR;
VectorSet<HashedEntry>::Iterator end = entrySet.End();
displacement = 0;
while (iterator != end) {
dirEntry = (ext2_dir_entry*)(*iterator).position;
uint32 realLength = EXT2_DIR_REC_LEN(dirEntry->name_length);
dirEntry->SetLength((uint16)realLength);
memcpy(&secondBlock[displacement], dirEntry, realLength);
displacement += realLength;
iterator++;
}
oldLength = dirEntry->Length();
dirEntry = (ext2_dir_entry*)&secondBlock[displacement - oldLength];
dirEntry->SetLength(maxSize - displacement + oldLength);
fDirectory->SetDirEntryChecksum(secondBlock);
ASSERT(_CheckBlock(firstBlock) == B_OK);
ASSERT(_CheckBlock(secondBlock) == B_OK);
TRACE("DirectoryIterator::_SplitIndexedBlock(): Done\n");
return B_OK;
}
status_t
DirectoryIterator::_NextBlock()
{
TRACE("DirectoryIterator::_NextBlock()\n");
if (fIndexing) {
TRACE("DirectoryIterator::_NextBlock(): Indexing\n");
if (!fParent->HasCollision()) {
TRACE("DirectoryIterator::_NextBlock(): next block doesn't "
"contain collisions from previous block\n");
#ifndef COLLISION_TEST
return B_ENTRY_NOT_FOUND;
#endif
}
return fParent->GetNext(fLogicalBlock);
}
if (++fLogicalBlock > fNumBlocks)
return B_ENTRY_NOT_FOUND;
return B_OK;
}
bool
DirectoryIterator::_CheckDirEntry(const ext2_dir_entry* dirEntry, const uint8* buffer)
{
const char *errmsg = NULL;
if (dirEntry->Length() < EXT2_DIR_REC_LEN(1))
errmsg = "Length is too small";
else if (dirEntry->Length() % 4 != 0)
errmsg = "Length is not a multiple of 4";
else if (dirEntry->Length() < EXT2_DIR_REC_LEN(dirEntry->NameLength()))
errmsg = "Length is too short for the name";
else if (((uint8*)dirEntry - buffer) + dirEntry->Length()
> (ptrdiff_t)fBlockSize) {
errmsg = "Length is too big for the blocksize";
}
if (errmsg != NULL) {
TRACE("DirectoryIterator::_CheckDirEntry() %s\n", errmsg);
}
return errmsg == NULL;
}
status_t
DirectoryIterator::_CheckBlock(const uint8* buffer)
{
uint32 maxSize = fBlockSize;
if (fVolume->HasMetaGroupChecksumFeature())
maxSize -= sizeof(ext2_dir_entry_tail);
status_t err = B_OK;
uint16 pos = 0;
while (pos < maxSize) {
const ext2_dir_entry *dirEntry = (const ext2_dir_entry*)&buffer[pos];
if (!_CheckDirEntry(dirEntry, buffer)) {
TRACE("DirectoryIterator::_CheckBlock(): invalid "
"dirEntry pos %d\n", pos);
err = B_BAD_DATA;
}
pos += dirEntry->Length();
}
return err;
}
ext2_htree_tail*
DirectoryIterator::_HTreeEntryTail(uint8* block, uint16 offset) const
{
HTreeEntry* entries = (HTreeEntry*)block;
uint16 firstEntry = offset % fBlockSize / sizeof(HTreeEntry);
HTreeCountLimit* countLimit = (HTreeCountLimit*)&entries[firstEntry];
uint16 limit = countLimit->Limit();
TRACE("DirectoryIterator::_HTreeEntryTail() %p %p\n", block, &entries[firstEntry + limit]);
return (ext2_htree_tail*)(&entries[firstEntry + limit]);
}
uint32
DirectoryIterator::_HTreeRootChecksum(uint8* block, uint16 offset, uint16 count) const
{
uint32 number = fDirectory->ID();
uint32 checksum = calculate_crc32c(fVolume->ChecksumSeed(),
(uint8*)&number, sizeof(number));
uint32 gen = fDirectory->Node().generation;
checksum = calculate_crc32c(checksum, (uint8*)&gen, sizeof(gen));
checksum = calculate_crc32c(checksum, block,
offset + count * sizeof(HTreeEntry));
TRACE("DirectoryIterator::_HTreeRootChecksum() size %" B_PRIu64 "\n",
offset + count * sizeof(HTreeEntry));
ext2_htree_tail dummy = {};
checksum = calculate_crc32c(checksum, (uint8*)&dummy, sizeof(dummy));
return checksum;
}
void
DirectoryIterator::_SetHTreeEntryChecksum(uint8* block, uint16 offset, uint16 count)
{
TRACE("DirectoryIterator::_SetHTreeEntryChecksum()\n");
if (fVolume->HasMetaGroupChecksumFeature()) {
ext2_htree_tail* tail = _HTreeEntryTail(block, offset);
tail->reserved = 0x0;
tail->checksum = _HTreeRootChecksum(block, offset, count);
}
}
uint32
DirectoryIterator::_MaxSize()
{
uint32 maxSize = fBlockSize;
if (fVolume->HasMetaGroupChecksumFeature())
maxSize -= sizeof(ext2_dir_entry_tail);
return maxSize;
}