xfs: B+Tree GetNext() works
The GetNext() works well for B+Trees and that wraps up the work needed for all kinds of GetNext(). Change-Id: Ie965d3da273364f8fdbdb8faee5cb3c214881130 Reviewed-on: https://review.haiku-os.org/c/haiku/+/3124 Tested-by: Commit checker robot <no-reply+buildbot@haiku-os.org> Reviewed-by: Adrien Destugues <pulkomandy@gmail.com>
This commit is contained in:
parent
7766b5a136
commit
1dcb6c252c
@ -6,7 +6,6 @@
|
||||
|
||||
#include "BPlusTree.h"
|
||||
|
||||
|
||||
TreeDirectory::TreeDirectory(Inode* inode)
|
||||
:
|
||||
fInode(inode),
|
||||
@ -14,7 +13,11 @@ TreeDirectory::TreeDirectory(Inode* inode)
|
||||
fRoot(NULL),
|
||||
fExtents(NULL),
|
||||
fSingleDirBlock(NULL),
|
||||
fCountOfFilledExtents(0)
|
||||
fOffsetOfSingleDirBlock(-1),
|
||||
fCountOfFilledExtents(0),
|
||||
fCurMapIndex(0),
|
||||
fOffset(0),
|
||||
fCurBlockNumber(0)
|
||||
{
|
||||
fRoot = new(std::nothrow) BlockInDataFork;
|
||||
if (fRoot == NULL) {
|
||||
@ -30,6 +33,12 @@ TreeDirectory::TreeDirectory(Inode* inode)
|
||||
|
||||
memcpy((void*)fRoot,
|
||||
DIR_DFORK_PTR(fInode->Buffer()), sizeof(BlockInDataFork));
|
||||
|
||||
for (int i = 0; i < MAX_TREE_DEPTH; i++) {
|
||||
fPathForLeaves[i].blockData = NULL;
|
||||
fPathForData[i].blockData = NULL;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -55,13 +64,6 @@ TreeDirectory::BlockLen()
|
||||
}
|
||||
|
||||
|
||||
TreeKey
|
||||
TreeDirectory::GetKey(int pos)
|
||||
{
|
||||
return BlockLen() + (pos - 1) * XFS_KEY_SIZE;
|
||||
}
|
||||
|
||||
|
||||
size_t
|
||||
TreeDirectory::KeySize()
|
||||
{
|
||||
@ -76,22 +78,198 @@ TreeDirectory::PtrSize()
|
||||
}
|
||||
|
||||
|
||||
TreePointer*
|
||||
TreeDirectory::GetPtr(int pos, LongBlock* curLongBlock)
|
||||
size_t
|
||||
TreeDirectory::MaxRecordsPossibleRoot()
|
||||
{
|
||||
size_t availableSpace = fInode->GetVolume()->BlockSize() - BlockLen();
|
||||
size_t maxRecords = availableSpace / (KeySize() + PtrSize());
|
||||
size_t offsetIntoNode =
|
||||
BlockLen() + maxRecords * KeySize() + (pos - 1) * PtrSize();
|
||||
return (TreePointer*)((char*)curLongBlock + offsetIntoNode);
|
||||
size_t lengthOfDataFork;
|
||||
if (fInode->ForkOffset() != 0)
|
||||
lengthOfDataFork = fInode->ForkOffset() << 3;
|
||||
if (fInode->ForkOffset() == 0) {
|
||||
lengthOfDataFork = fInode->GetVolume()->InodeSize()
|
||||
- INODE_CORE_UNLINKED_SIZE;
|
||||
}
|
||||
|
||||
lengthOfDataFork -= sizeof(BlockInDataFork);
|
||||
return lengthOfDataFork / (KeySize() + PtrSize());
|
||||
}
|
||||
|
||||
|
||||
size_t
|
||||
TreeDirectory::MaxRecordsPossible(size_t len)
|
||||
TreeDirectory::GetPtrOffsetIntoRoot(int pos)
|
||||
{
|
||||
len -= sizeof(BlockInDataFork);
|
||||
return len / (KeySize() + PtrSize());
|
||||
size_t maxRecords = MaxRecordsPossibleRoot();
|
||||
return (sizeof(BlockInDataFork)
|
||||
+ maxRecords * KeySize() + (pos - 1) * PtrSize());
|
||||
}
|
||||
|
||||
|
||||
size_t
|
||||
TreeDirectory::MaxRecordsPossibleNode()
|
||||
{
|
||||
size_t availableSpace = fInode->GetVolume()->BlockSize() - BlockLen();
|
||||
return availableSpace / (KeySize() + PtrSize());
|
||||
}
|
||||
|
||||
|
||||
size_t
|
||||
TreeDirectory::GetPtrOffsetIntoNode(int pos)
|
||||
{
|
||||
size_t maxRecords = MaxRecordsPossibleNode();
|
||||
return BlockLen() + maxRecords * KeySize() + (pos - 1) * PtrSize();
|
||||
}
|
||||
|
||||
|
||||
TreePointer*
|
||||
TreeDirectory::GetPtrFromRoot(int pos)
|
||||
{
|
||||
return (TreePointer*)
|
||||
((char*)DIR_DFORK_PTR(fInode->Buffer()) + GetPtrOffsetIntoRoot(pos));
|
||||
}
|
||||
|
||||
|
||||
TreePointer*
|
||||
TreeDirectory::GetPtrFromNode(int pos, void* buffer)
|
||||
{
|
||||
size_t offsetIntoNode = GetPtrOffsetIntoNode(pos);
|
||||
return (TreePointer*)((char*)buffer + offsetIntoNode);
|
||||
}
|
||||
|
||||
|
||||
TreeKey*
|
||||
TreeDirectory::GetKeyFromNode(int pos, void* buffer)
|
||||
{
|
||||
return (TreeKey*)
|
||||
((char*)buffer + BlockLen() + (pos - 1) * KeySize());
|
||||
}
|
||||
|
||||
|
||||
TreeKey*
|
||||
TreeDirectory::GetKeyFromRoot(int pos)
|
||||
{
|
||||
off_t offset = (pos - 1) * KeySize();
|
||||
char* base = (char*)DIR_DFORK_PTR(fInode->Buffer())
|
||||
+ sizeof(BlockInDataFork);
|
||||
return (TreeKey*) (base + offset);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
TreeDirectory::SearchOffsetInTreeNode(uint32 offset,
|
||||
TreePointer** pointer, int pathIndex)
|
||||
{
|
||||
// This is O(MaxRecords). Next is to implement this using upper bound
|
||||
// binary search and get O(log(MaxRecords))
|
||||
*pointer = NULL;
|
||||
TreeKey* offsetKey = NULL;
|
||||
size_t maxRecords = MaxRecordsPossibleNode();
|
||||
for (int i = maxRecords - 1; i >= 0; i--) {
|
||||
offsetKey
|
||||
= GetKeyFromNode(i + 1, (void*)fPathForLeaves[pathIndex].blockData);
|
||||
if (B_BENDIAN_TO_HOST_INT64(*offsetKey) <= offset) {
|
||||
*pointer = GetPtrFromNode(i + 1, (void*)
|
||||
fPathForLeaves[pathIndex].blockData);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (offsetKey == NULL || *pointer == NULL)
|
||||
return B_BAD_VALUE;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
TreeDirectory::SearchAndFillPath(uint32 offset, int type)
|
||||
{
|
||||
TRACE("SearchAndFillPath:\n");
|
||||
PathNode* path;
|
||||
if (type == DATA)
|
||||
path = fPathForData;
|
||||
else if (type == LEAF)
|
||||
path = fPathForLeaves;
|
||||
else
|
||||
return B_BAD_VALUE;
|
||||
|
||||
TreePointer* ptrToNode = NULL;
|
||||
TreeKey* offsetKey = NULL;
|
||||
// Go down the root of the tree first
|
||||
for (int i = fRoot->NumRecords() - 1; i >= 0; i--) {
|
||||
offsetKey = GetKeyFromRoot(i + 1);
|
||||
if (B_BENDIAN_TO_HOST_INT64(*offsetKey) <= offset) {
|
||||
ptrToNode = GetPtrFromRoot(i + 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (ptrToNode == NULL || offsetKey == NULL) {
|
||||
//Corrupt tree
|
||||
return B_BAD_VALUE;
|
||||
}
|
||||
|
||||
// We now have gone down the root and save path if not saved.
|
||||
int level = fRoot->Levels() - 1;
|
||||
Volume* volume = fInode->GetVolume();
|
||||
status_t status;
|
||||
for (int i = 0; i < MAX_TREE_DEPTH && level >= 0; i++, level--) {
|
||||
uint64 requiredBlock = B_BENDIAN_TO_HOST_INT64(*ptrToNode);
|
||||
TRACE("requiredBlock:(%d)\n", requiredBlock);
|
||||
if (path[i].blockNumber == requiredBlock) {
|
||||
// This block already has what we need
|
||||
if (path[i].type == 2)
|
||||
break;
|
||||
status = SearchOffsetInTreeNode(offset, &ptrToNode, i);
|
||||
if (status != B_OK)
|
||||
return status;
|
||||
continue;
|
||||
}
|
||||
// We do not have the block we need
|
||||
size_t len;
|
||||
if (level == 0) {
|
||||
// The size of buffer should be the directory block size
|
||||
len = fInode->DirBlockSize();
|
||||
TRACE("path node type:(%d)\n", path[i].type);
|
||||
if (path[i].type != 2) {
|
||||
// Size is not directory block size.
|
||||
delete path[i].blockData;
|
||||
path[i].type = 0;
|
||||
path[i].blockData = new(std::nothrow) char[len];
|
||||
if (path[i].blockData == NULL)
|
||||
return B_NO_MEMORY;
|
||||
path[i].type = 2;
|
||||
}
|
||||
uint64 readPos = fInode->FileSystemBlockToAddr(requiredBlock);
|
||||
if (read_pos(volume->Device(), readPos, path[i].blockData, len)
|
||||
!= len) {
|
||||
ERROR("FillPath::FillBlockBuffer(): IO Error");
|
||||
return B_IO_ERROR;
|
||||
}
|
||||
path[i].blockNumber = requiredBlock;
|
||||
break;
|
||||
}
|
||||
// The size of buffer should be the block size
|
||||
len = volume->BlockSize();
|
||||
if (path[i].type != 1) {
|
||||
delete path[i].blockData;
|
||||
path[i].type = 0;
|
||||
path[i].blockData = new(std::nothrow) char[len];
|
||||
if (path[i].blockData == NULL)
|
||||
return B_NO_MEMORY;
|
||||
path[i].type = 1;
|
||||
}
|
||||
uint64 readPos = fInode->FileSystemBlockToAddr(requiredBlock);
|
||||
if (read_pos(volume->Device(), readPos, path[i].blockData, len)
|
||||
!= len) {
|
||||
ERROR("FillPath::FillBlockBuffer(): IO Error");
|
||||
return B_IO_ERROR;
|
||||
}
|
||||
path[i].blockNumber = requiredBlock;
|
||||
status = SearchOffsetInTreeNode(offset, &ptrToNode, i);
|
||||
if (status != B_OK)
|
||||
return status;
|
||||
}
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
@ -99,26 +277,20 @@ status_t
|
||||
TreeDirectory::GetAllExtents()
|
||||
{
|
||||
xfs_extnum_t noOfExtents = fInode->DataExtentsCount();
|
||||
|
||||
ExtentMapUnwrap* extentsWrapped
|
||||
= new(std::nothrow) ExtentMapUnwrap[noOfExtents];
|
||||
if (extentsWrapped == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
ArrayDeleter<ExtentMapUnwrap> extentsWrappedDeleter(extentsWrapped);
|
||||
|
||||
Volume* volume = fInode->GetVolume();
|
||||
uint16 levelsInTree = fRoot->Levels();
|
||||
|
||||
size_t lengthOfDataFork;
|
||||
if (fInode->ForkOffset() != 0)
|
||||
lengthOfDataFork = fInode->ForkOffset() << 3;
|
||||
else
|
||||
lengthOfDataFork = volume->InodeSize() - INODE_CORE_UNLINKED_SIZE;
|
||||
|
||||
TRACE("Length Of Data Fork: (%d)\n", lengthOfDataFork);
|
||||
size_t maxRecords = MaxRecordsPossible(lengthOfDataFork);
|
||||
size_t maxRecords = MaxRecordsPossibleRoot();
|
||||
TRACE("Maxrecords: (%d)\n", maxRecords);
|
||||
TreePointer* ptrToNode = (TreePointer*)
|
||||
((char*)DIR_DFORK_PTR(fInode->Buffer())
|
||||
+ sizeof(BlockInDataFork) + maxRecords*KeySize());
|
||||
TreePointer* ptrToNode = GetPtrFromRoot(1);
|
||||
|
||||
size_t len = volume->BlockSize();
|
||||
char node[len];
|
||||
@ -136,14 +308,11 @@ TreeDirectory::GetAllExtents()
|
||||
readPos = fInode->FileSystemBlockToAddr(fileSystemBlockNo);
|
||||
if (read_pos(volume->Device(), readPos, node, len) != len) {
|
||||
ERROR("Extent::FillBlockBuffer(): IO Error");
|
||||
|
||||
delete[] extentsWrapped;
|
||||
extentsWrapped = NULL;
|
||||
return B_IO_ERROR;
|
||||
}
|
||||
LongBlock* curLongBlock = (LongBlock*)node;
|
||||
ASSERT(curLongBlock->Magic() == XFS_BMAP_MAGIC);
|
||||
ptrToNode = GetPtr(1, curLongBlock);
|
||||
ptrToNode = GetPtrFromNode(1, (void*)curLongBlock);
|
||||
// Get's the first pointer. This points to next node.
|
||||
levelsInTree--;
|
||||
}
|
||||
@ -153,9 +322,6 @@ TreeDirectory::GetAllExtents()
|
||||
if (read_pos(volume->Device(), readPos, fSingleDirBlock, len)
|
||||
!= len) {
|
||||
ERROR("Extent::FillBlockBuffer(): IO Error");
|
||||
|
||||
delete[] extentsWrapped;
|
||||
extentsWrapped = NULL;
|
||||
return B_IO_ERROR;
|
||||
}
|
||||
levelsInTree--;
|
||||
@ -187,9 +353,6 @@ TreeDirectory::GetAllExtents()
|
||||
if (read_pos(volume->Device(), readPos, fSingleDirBlock, len)
|
||||
!= len) {
|
||||
ERROR("Extent::FillBlockBuffer(): IO Error");
|
||||
|
||||
delete[] extentsWrapped;
|
||||
extentsWrapped = NULL;
|
||||
return B_IO_ERROR;
|
||||
}
|
||||
}
|
||||
@ -197,13 +360,200 @@ TreeDirectory::GetAllExtents()
|
||||
|
||||
status_t status = UnWrapExtents(extentsWrapped);
|
||||
|
||||
delete[] extentsWrapped;
|
||||
extentsWrapped = NULL;
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
TreeDirectory::FillBuffer(char* blockBuffer, int howManyBlocksFurther,
|
||||
ExtentMapEntry* targetMap)
|
||||
{
|
||||
TRACE("FILLBUFFER\n");
|
||||
ExtentMapEntry map;
|
||||
if (targetMap == NULL)
|
||||
map = fExtents[fCurMapIndex];
|
||||
if (targetMap != NULL)
|
||||
map = *targetMap;
|
||||
|
||||
if (map.br_state != 0)
|
||||
return B_BAD_VALUE;
|
||||
|
||||
size_t len = fInode->DirBlockSize();
|
||||
if (blockBuffer == NULL) {
|
||||
blockBuffer = new(std::nothrow) char[len];
|
||||
if (blockBuffer == NULL)
|
||||
return B_NO_MEMORY;
|
||||
}
|
||||
|
||||
xfs_daddr_t readPos = fInode->FileSystemBlockToAddr(
|
||||
map.br_startblock + howManyBlocksFurther);
|
||||
|
||||
if (read_pos(fInode->GetVolume()->Device(), readPos, blockBuffer, len)
|
||||
!= len) {
|
||||
ERROR("TreeDirectory::FillBlockBuffer(): IO Error");
|
||||
return B_IO_ERROR;
|
||||
}
|
||||
|
||||
if (targetMap == NULL) {
|
||||
fSingleDirBlock = blockBuffer;
|
||||
ExtentDataHeader* header = (ExtentDataHeader*) fSingleDirBlock;
|
||||
if (B_BENDIAN_TO_HOST_INT32(header->magic) == DATA_HEADER_MAGIC) {
|
||||
TRACE("DATA BLOCK VALID\n");
|
||||
} else {
|
||||
TRACE("DATA BLOCK INVALID\n");
|
||||
return B_BAD_VALUE;
|
||||
}
|
||||
}
|
||||
if (targetMap != NULL) {
|
||||
fSingleDirBlock = blockBuffer;
|
||||
ExtentLeafHeader* header = (ExtentLeafHeader*) fSingleDirBlock;
|
||||
if (B_BENDIAN_TO_HOST_INT16(header->info.magic) == XFS_DA_NODE_MAGIC
|
||||
|| B_BENDIAN_TO_HOST_INT16(header->info.magic)
|
||||
== XFS_DIR2_LEAFN_MAGIC) {
|
||||
TRACE("LEAF/NODE VALID\n");
|
||||
} else {
|
||||
TRACE("LEAF/NODE INVALID\n");
|
||||
return B_BAD_VALUE;
|
||||
}
|
||||
}
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
TreeDirectory::EntrySize(int len) const
|
||||
{
|
||||
int entrySize = sizeof(xfs_ino_t) + sizeof(uint8) + len + sizeof(uint16);
|
||||
// uint16 is for the tag
|
||||
if (fInode->HasFileTypeField())
|
||||
entrySize += sizeof(uint8);
|
||||
|
||||
return ROUNDUP(entrySize, 8);
|
||||
// rounding off to next greatest multiple of 8
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Throw in the desired block number and get the index of it
|
||||
*/
|
||||
status_t
|
||||
TreeDirectory::SearchMapInAllExtent(int blockNo, uint32& mapIndex)
|
||||
{
|
||||
ExtentMapEntry map;
|
||||
for (uint32 i = 0; i < fCountOfFilledExtents; i++) {
|
||||
map = fExtents[i];
|
||||
if (map.br_startoff <= blockNo
|
||||
&& (blockNo <= map.br_startoff + map.br_blockcount - 1)) {
|
||||
// Map found
|
||||
mapIndex = i;
|
||||
return B_OK;
|
||||
}
|
||||
}
|
||||
|
||||
return B_ENTRY_NOT_FOUND;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
TreeDirectory::GetNext(char* name, size_t* length, xfs_ino_t* ino)
|
||||
{
|
||||
TRACE("TreeDirectory::GetNext\n");
|
||||
status_t status;
|
||||
if (fExtents == NULL) {
|
||||
status = GetAllExtents();
|
||||
if (status != B_OK)
|
||||
return status;
|
||||
}
|
||||
status = FillBuffer(fSingleDirBlock, 0);
|
||||
if (status != B_OK)
|
||||
return status;
|
||||
|
||||
Volume* volume = fInode->GetVolume();
|
||||
void* entry = (void*)((ExtentDataHeader*)fSingleDirBlock + 1);
|
||||
// This could be an unused entry so we should check
|
||||
|
||||
uint32 blockNoFromAddress = BLOCKNO_FROM_ADDRESS(fOffset, volume);
|
||||
if (fOffset != 0 && blockNoFromAddress == fCurBlockNumber) {
|
||||
entry = (void*)(fSingleDirBlock
|
||||
+ BLOCKOFFSET_FROM_ADDRESS(fOffset, fInode));
|
||||
// This gets us a little faster to the next entry
|
||||
}
|
||||
|
||||
uint32 curDirectorySize = fInode->Size();
|
||||
ExtentMapEntry& map = fExtents[fCurMapIndex];
|
||||
while (fOffset != curDirectorySize) {
|
||||
blockNoFromAddress = BLOCKNO_FROM_ADDRESS(fOffset, volume);
|
||||
|
||||
TRACE("fOffset:(%d), blockNoFromAddress:(%d)\n",
|
||||
fOffset, blockNoFromAddress);
|
||||
if (fCurBlockNumber != blockNoFromAddress
|
||||
&& blockNoFromAddress > map.br_startoff
|
||||
&& blockNoFromAddress
|
||||
<= map.br_startoff + map.br_blockcount - 1) {
|
||||
// When the block is mapped in the same data
|
||||
// map entry but is not the first block
|
||||
status = FillBuffer(fSingleDirBlock,
|
||||
blockNoFromAddress - map.br_startoff);
|
||||
if (status != B_OK)
|
||||
return status;
|
||||
entry = (void*)((ExtentDataHeader*)fSingleDirBlock + 1);
|
||||
fOffset = fOffset + sizeof(ExtentDataHeader);
|
||||
fCurBlockNumber = blockNoFromAddress;
|
||||
} else if (fCurBlockNumber != blockNoFromAddress) {
|
||||
// When the block isn't mapped in the current data map entry
|
||||
uint32 curMapIndex;
|
||||
status = SearchMapInAllExtent(blockNoFromAddress, curMapIndex);
|
||||
if (status != B_OK)
|
||||
return status;
|
||||
fCurMapIndex = curMapIndex;
|
||||
map = fExtents[fCurMapIndex];
|
||||
status = FillBuffer(fSingleDirBlock,
|
||||
blockNoFromAddress - map.br_startoff);
|
||||
if (status != B_OK)
|
||||
return status;
|
||||
entry = (void*)((ExtentDataHeader*)fSingleDirBlock + 1);
|
||||
fOffset = fOffset + sizeof(ExtentDataHeader);
|
||||
fCurBlockNumber = blockNoFromAddress;
|
||||
}
|
||||
|
||||
ExtentUnusedEntry* unusedEntry = (ExtentUnusedEntry*)entry;
|
||||
|
||||
if (B_BENDIAN_TO_HOST_INT16(unusedEntry->freetag) == DIR2_FREE_TAG) {
|
||||
TRACE("Unused entry found\n");
|
||||
fOffset = fOffset + B_BENDIAN_TO_HOST_INT16(unusedEntry->length);
|
||||
entry = (void*)
|
||||
((char*)entry + B_BENDIAN_TO_HOST_INT16(unusedEntry->length));
|
||||
continue;
|
||||
}
|
||||
ExtentDataEntry* dataEntry = (ExtentDataEntry*) entry;
|
||||
|
||||
uint16 currentOffset = (char*)dataEntry - fSingleDirBlock;
|
||||
TRACE("GetNext: fOffset:(%d), currentOffset:(%d)\n",
|
||||
BLOCKOFFSET_FROM_ADDRESS(fOffset, fInode), currentOffset);
|
||||
|
||||
if (BLOCKOFFSET_FROM_ADDRESS(fOffset, fInode) > currentOffset) {
|
||||
entry = (void*)((char*)entry + EntrySize(dataEntry->namelen));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (dataEntry->namelen + 1 > *length)
|
||||
return B_BUFFER_OVERFLOW;
|
||||
|
||||
fOffset = fOffset + EntrySize(dataEntry->namelen);
|
||||
memcpy(name, dataEntry->name, dataEntry->namelen);
|
||||
name[dataEntry->namelen] = '\0';
|
||||
*length = dataEntry->namelen + 1;
|
||||
*ino = B_BENDIAN_TO_HOST_INT64(dataEntry->inumber);
|
||||
|
||||
TRACE("Entry found. Name: (%s), Length: (%ld), ino: (%ld)\n", name,
|
||||
*length, *ino);
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
return B_ENTRY_NOT_FOUND;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
TreeDirectory::UnWrapExtents(ExtentMapUnwrap* extentsWrapped)
|
||||
{
|
||||
@ -223,3 +573,253 @@ TreeDirectory::UnWrapExtents(ExtentMapUnwrap* extentsWrapped)
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
TreeDirectory::FillMapEntry(int num, ExtentMapEntry** fMap,
|
||||
int type, int pathIndex)
|
||||
{
|
||||
void* pointerToMap;
|
||||
if (type == DATA) {
|
||||
char* base = fPathForData[pathIndex].blockData + BlockLen();
|
||||
off_t offset = num * EXTENT_SIZE;
|
||||
pointerToMap = (void*)(base + offset);
|
||||
} else {
|
||||
char* base = fPathForLeaves[pathIndex].blockData + BlockLen();
|
||||
off_t offset = num * EXTENT_SIZE;
|
||||
pointerToMap = (void*)(base + offset);
|
||||
}
|
||||
uint64 firstHalf = *((uint64*)pointerToMap);
|
||||
uint64 secondHalf = *((uint64*)pointerToMap + 1);
|
||||
//dividing the 128 bits into 2 parts.
|
||||
|
||||
firstHalf = B_BENDIAN_TO_HOST_INT64(firstHalf);
|
||||
secondHalf = B_BENDIAN_TO_HOST_INT64(secondHalf);
|
||||
(*fMap)->br_state = firstHalf >> 63;
|
||||
(*fMap)->br_startoff = (firstHalf & MASK(63)) >> 9;
|
||||
(*fMap)->br_startblock = ((firstHalf & MASK(9)) << 43) | (secondHalf >> 21);
|
||||
(*fMap)->br_blockcount = secondHalf & MASK(21);
|
||||
TRACE("FillMapEntry: startoff:(%ld), startblock:(%ld), blockcount:(%ld),"
|
||||
"state:(%d)\n", (*fMap)->br_startoff, (*fMap)->br_startblock,
|
||||
(*fMap)->br_blockcount, (*fMap)->br_state);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
TreeDirectory::SearchForMapInDirectoryBlock(int blockNo,
|
||||
int entries, ExtentMapEntry** map, int type, int pathIndex)
|
||||
{
|
||||
TRACE("SearchForMapInDirectoryBlock: blockNo:(%d)\n", blockNo);
|
||||
for (int i = 0; i < entries; i++) {
|
||||
FillMapEntry(i, map, type, pathIndex);
|
||||
if ((*map)->br_startoff <= blockNo
|
||||
&& (blockNo <= (*map)->br_startoff + (*map)->br_blockcount - 1)) {
|
||||
// Map found
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Map wasn't found. Some kind of corruption. This is checked by caller.
|
||||
*map = NULL;
|
||||
}
|
||||
|
||||
|
||||
uint32
|
||||
TreeDirectory::SearchForHashInNodeBlock(uint32 hashVal)
|
||||
{
|
||||
NodeHeader* header = (NodeHeader*)(fSingleDirBlock);
|
||||
NodeEntry* entry = (NodeEntry*)(fSingleDirBlock + sizeof(NodeHeader));
|
||||
int count = B_BENDIAN_TO_HOST_INT16(header->count);
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
if (hashVal <= B_BENDIAN_TO_HOST_INT32(entry[i].hashval))
|
||||
return B_BENDIAN_TO_HOST_INT32(entry[i].before);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
TreeDirectory::Lookup(const char* name, size_t length, xfs_ino_t* ino)
|
||||
{
|
||||
TRACE("TreeDirectory: Lookup\n");
|
||||
TRACE("Name: %s\n", name);
|
||||
uint32 hashValueOfRequest = hashfunction(name, length);
|
||||
TRACE("Hashval:(%ld)\n", hashValueOfRequest);
|
||||
|
||||
Volume* volume = fInode->GetVolume();
|
||||
status_t status;
|
||||
|
||||
ExtentMapEntry* targetMap = new(std::nothrow) ExtentMapEntry;
|
||||
if (targetMap == NULL)
|
||||
return B_NO_MEMORY;
|
||||
int pathIndex = -1;
|
||||
uint32 rightOffset = LEAF_STARTOFFSET(volume->BlockLog());
|
||||
|
||||
// In node directories, the "node blocks" had a single level
|
||||
// Here we could have multiple levels. With each iteration of
|
||||
// the loop we go a level lower.
|
||||
while (rightOffset != fOffsetOfSingleDirBlock && 1) {
|
||||
status = SearchAndFillPath(rightOffset, LEAF);
|
||||
if (status != B_OK)
|
||||
return status;
|
||||
|
||||
// The path should now have the Tree Leaf at appropriate level
|
||||
// Find the directory block in the path
|
||||
for (int i = 0; i < MAX_TREE_DEPTH; i++) {
|
||||
if (fPathForLeaves[i].type == 2) {
|
||||
pathIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (pathIndex == -1) {
|
||||
// corrupt tree
|
||||
return B_BAD_VALUE;
|
||||
}
|
||||
|
||||
// Get the node block from directory block
|
||||
// If level is non-zero, reiterate with new "rightOffset"
|
||||
// Else, we are at leaf block, then break
|
||||
LongBlock* curDirBlock
|
||||
= (LongBlock*)fPathForLeaves[pathIndex].blockData;
|
||||
|
||||
if (curDirBlock->Magic() != XFS_BMAP_MAGIC)
|
||||
return B_BAD_VALUE;
|
||||
|
||||
SearchForMapInDirectoryBlock(rightOffset, curDirBlock->NumRecs(),
|
||||
&targetMap, LEAF, pathIndex);
|
||||
if (targetMap == NULL)
|
||||
return B_BAD_VALUE;
|
||||
|
||||
FillBuffer(fSingleDirBlock, rightOffset - targetMap->br_startoff,
|
||||
targetMap);
|
||||
fOffsetOfSingleDirBlock = rightOffset;
|
||||
ExtentLeafHeader* dirBlock = (ExtentLeafHeader*)fSingleDirBlock;
|
||||
if (B_BENDIAN_TO_HOST_INT16(dirBlock->info.magic)
|
||||
== XFS_DIR2_LEAFN_MAGIC) {
|
||||
// Got the potential leaf. Break.
|
||||
break;
|
||||
}
|
||||
if (B_BENDIAN_TO_HOST_INT16(dirBlock->info.magic)
|
||||
== XFS_DA_NODE_MAGIC) {
|
||||
rightOffset = SearchForHashInNodeBlock(hashValueOfRequest);
|
||||
if (rightOffset == 0)
|
||||
return B_ENTRY_NOT_FOUND;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// We now have the leaf block that might contain the entry we need.
|
||||
// Else go to the right subling if it might contain it. Else break.
|
||||
while (1) {
|
||||
ExtentLeafHeader* leafHeader
|
||||
= (ExtentLeafHeader*)fSingleDirBlock;
|
||||
ExtentLeafEntry* leafEntry
|
||||
= (ExtentLeafEntry*)(fSingleDirBlock + sizeof(ExtentLeafHeader));
|
||||
|
||||
int numberOfLeafEntries = B_BENDIAN_TO_HOST_INT16(leafHeader->count);
|
||||
TRACE("numberOfLeafEntries:(%d)\n", numberOfLeafEntries);
|
||||
int left = 0;
|
||||
int mid;
|
||||
int right = numberOfLeafEntries - 1;
|
||||
|
||||
// Trying to find the lowerbound of hashValueOfRequest
|
||||
// This is slightly different from bsearch(), as we want the first
|
||||
// instance of hashValueOfRequest and not any instance.
|
||||
while (left < right) {
|
||||
mid = (left + right) / 2;
|
||||
uint32 hashval = B_BENDIAN_TO_HOST_INT32(leafEntry[mid].hashval);
|
||||
if (hashval >= hashValueOfRequest) {
|
||||
right = mid;
|
||||
continue;
|
||||
}
|
||||
if (hashval < hashValueOfRequest) {
|
||||
left = mid + 1;
|
||||
}
|
||||
}
|
||||
TRACE("left:(%d), right:(%d)\n", left, right);
|
||||
uint32 nextLeaf = B_BENDIAN_TO_HOST_INT32(leafHeader->info.forw);
|
||||
uint32 lastHashVal = B_BENDIAN_TO_HOST_INT32(
|
||||
leafEntry[numberOfLeafEntries - 1].hashval);
|
||||
|
||||
while (B_BENDIAN_TO_HOST_INT32(leafEntry[left].hashval)
|
||||
== hashValueOfRequest) {
|
||||
uint32 address = B_BENDIAN_TO_HOST_INT32(leafEntry[left].address);
|
||||
if (address == 0) {
|
||||
left++;
|
||||
continue;
|
||||
}
|
||||
|
||||
uint32 dataBlockNumber = BLOCKNO_FROM_ADDRESS(address * 8, volume);
|
||||
uint32 offset = BLOCKOFFSET_FROM_ADDRESS(address * 8, fInode);
|
||||
|
||||
TRACE("BlockNumber:(%d), offset:(%d)\n", dataBlockNumber, offset);
|
||||
|
||||
status = SearchAndFillPath(dataBlockNumber, DATA);
|
||||
int pathIndex = -1;
|
||||
for (int i = 0; i < MAX_TREE_DEPTH; i++) {
|
||||
if (fPathForData[i].type == 2) {
|
||||
pathIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (pathIndex == -1)
|
||||
return B_BAD_VALUE;
|
||||
|
||||
LongBlock* curDirBlock
|
||||
= (LongBlock*)fPathForData[pathIndex].blockData;
|
||||
|
||||
SearchForMapInDirectoryBlock(dataBlockNumber,
|
||||
curDirBlock->NumRecs(), &targetMap, DATA, pathIndex);
|
||||
if (targetMap == NULL)
|
||||
return B_BAD_VALUE;
|
||||
|
||||
FillBuffer(fSingleDirBlock,
|
||||
dataBlockNumber - targetMap->br_startoff, targetMap);
|
||||
fOffsetOfSingleDirBlock = dataBlockNumber;
|
||||
|
||||
TRACE("offset:(%d)\n", offset);
|
||||
ExtentDataEntry* entry
|
||||
= (ExtentDataEntry*)(fSingleDirBlock + offset);
|
||||
|
||||
int retVal = strncmp(name, (char*)entry->name, entry->namelen);
|
||||
if (retVal == 0) {
|
||||
*ino = B_BENDIAN_TO_HOST_INT64(entry->inumber);
|
||||
TRACE("ino:(%d)\n", *ino);
|
||||
return B_OK;
|
||||
}
|
||||
left++;
|
||||
}
|
||||
if (lastHashVal == hashValueOfRequest && nextLeaf != -1) {
|
||||
// Go to forward neighbor. We might find an entry there.
|
||||
status = SearchAndFillPath(nextLeaf, LEAF);
|
||||
if (status != B_OK)
|
||||
return status;
|
||||
|
||||
pathIndex = -1;
|
||||
for (int i = 0; i < MAX_TREE_DEPTH; i++) {
|
||||
if (fPathForLeaves[i].type == 2) {
|
||||
pathIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (pathIndex == -1)
|
||||
return B_BAD_VALUE;
|
||||
|
||||
LongBlock* curDirBlock
|
||||
= (LongBlock*)fPathForLeaves[pathIndex].blockData;
|
||||
|
||||
SearchForMapInDirectoryBlock(nextLeaf, curDirBlock->NumRecs(),
|
||||
&targetMap, LEAF, pathIndex);
|
||||
if (targetMap == NULL)
|
||||
return B_BAD_VALUE;
|
||||
|
||||
FillBuffer(fSingleDirBlock,
|
||||
nextLeaf - targetMap->br_startoff, targetMap);
|
||||
|
||||
fOffsetOfSingleDirBlock = nextLeaf;
|
||||
continue;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return B_ENTRY_NOT_FOUND;
|
||||
}
|
||||
|
@ -20,6 +20,7 @@
|
||||
#define XFS_KEY_SIZE sizeof(xfs_fileoff_t)
|
||||
#define XFS_PTR_SIZE sizeof(xfs_fsblock_t)
|
||||
#define XFS_BMAP_MAGIC 0x424d4150
|
||||
#define MAX_TREE_DEPTH 5
|
||||
|
||||
|
||||
typedef xfs_fileoff_t TreeKey;
|
||||
@ -61,7 +62,7 @@ struct LongBlock {
|
||||
*/
|
||||
|
||||
|
||||
//xfs_bmdr_block
|
||||
// xfs_bmdr_block
|
||||
struct BlockInDataFork {
|
||||
uint16 Levels()
|
||||
{ return
|
||||
@ -80,6 +81,22 @@ struct ExtentMapUnwrap {
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Using the structure to prevent re-reading of already read blocks during
|
||||
* a traversal of tree.
|
||||
*
|
||||
* type:
|
||||
* 0, if its an unused node, 1 if blockData size is a single block,
|
||||
* 2 if blockData size is directory block size.
|
||||
*/
|
||||
struct PathNode {
|
||||
int type;
|
||||
char* blockData;
|
||||
uint32 blockNumber;
|
||||
// This is the file system block number
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* This class should handle B+Tree based directories
|
||||
*/
|
||||
@ -96,13 +113,29 @@ public:
|
||||
int BlockLen();
|
||||
size_t PtrSize();
|
||||
size_t KeySize();
|
||||
TreeKey GetKey(int pos);
|
||||
// get the pos'th key
|
||||
TreePointer* GetPtr(int pos, LongBlock* pointer);
|
||||
// get the pos'th pointer
|
||||
TreeKey* GetKeyFromNode(int pos, void* buffer);
|
||||
TreePointer* GetPtrFromNode(int pos, void* buffer);
|
||||
TreeKey* GetKeyFromRoot(int pos);
|
||||
TreePointer* GetPtrFromRoot(int pos);
|
||||
status_t SearchMapInAllExtent(int blockNo,
|
||||
uint32& mapIndex);
|
||||
status_t GetAllExtents();
|
||||
size_t MaxRecordsPossible(size_t len);
|
||||
|
||||
size_t MaxRecordsPossibleRoot();
|
||||
size_t MaxRecordsPossibleNode();
|
||||
void FillMapEntry(int num, ExtentMapEntry** map,
|
||||
int type, int pathIndex);
|
||||
status_t FillBuffer(char* blockBuffer,
|
||||
int howManyBlocksFurther,
|
||||
ExtentMapEntry* targetMap = NULL);
|
||||
size_t GetPtrOffsetIntoNode(int pos);
|
||||
size_t GetPtrOffsetIntoRoot(int pos);
|
||||
status_t SearchAndFillPath(uint32 offset, int type);
|
||||
status_t SearchOffsetInTreeNode (uint32 offset,
|
||||
TreePointer** pointer, int pathIndex);
|
||||
void SearchForMapInDirectoryBlock (int blockNo,
|
||||
int entries, ExtentMapEntry** map,
|
||||
int type, int pathIndex);
|
||||
uint32 SearchForHashInNodeBlock(uint32 hashVal);
|
||||
private:
|
||||
inline status_t UnWrapExtents(ExtentMapUnwrap* extentsWrapped);
|
||||
|
||||
@ -113,6 +146,12 @@ private:
|
||||
ExtentMapEntry* fExtents;
|
||||
uint32 fCountOfFilledExtents;
|
||||
char* fSingleDirBlock;
|
||||
uint32 fOffsetOfSingleDirBlock;
|
||||
uint32 fCurMapIndex;
|
||||
uint64 fOffset;
|
||||
uint32 fCurBlockNumber;
|
||||
PathNode fPathForLeaves[MAX_TREE_DEPTH];
|
||||
PathNode fPathForData[MAX_TREE_DEPTH];
|
||||
};
|
||||
|
||||
|
||||
|
@ -34,13 +34,14 @@ DirectoryIterator::Init()
|
||||
{
|
||||
if (fInode->Format() == XFS_DINODE_FMT_LOCAL)
|
||||
{
|
||||
TRACE("Iterator:Init: LOCAL");
|
||||
fShortDir = new(std::nothrow) ShortDirectory(fInode);
|
||||
if (fShortDir == NULL)
|
||||
return B_NO_MEMORY;
|
||||
return B_OK;
|
||||
}
|
||||
if (fInode->Format() == XFS_DINODE_FMT_EXTENTS) {
|
||||
// TODO: Only working with Block directories, not leaf.
|
||||
TRACE("Iterator:Init: EXTENTS");
|
||||
fExtentDir = new(std::nothrow) Extent(fInode);
|
||||
if (fExtentDir == NULL)
|
||||
return B_NO_MEMORY;
|
||||
@ -76,10 +77,8 @@ DirectoryIterator::Init()
|
||||
/* Return B_OK so even if the shortform directory has an extent directory
|
||||
* we can atleast still list the shortform directory
|
||||
*/
|
||||
|
||||
//TODO: Reading from B+Trees based directories
|
||||
if (fInode->Format() == XFS_DINODE_FMT_BTREE) {
|
||||
TRACE("Iterator:GetNext: B+TREE");
|
||||
TRACE("Iterator:Init(): B+TREE");
|
||||
fTreeDir = new(std::nothrow) TreeDirectory(fInode);
|
||||
if (fTreeDir == NULL)
|
||||
return B_NO_MEMORY;
|
||||
@ -95,11 +94,11 @@ DirectoryIterator::GetNext(char* name, size_t* length, xfs_ino_t* ino)
|
||||
{
|
||||
status_t status;
|
||||
if (fInode->Format() == XFS_DINODE_FMT_LOCAL) {
|
||||
TRACE("Iterator:GetNext: LOCAL");
|
||||
status = fShortDir->GetNext(name, length, ino);
|
||||
return status;
|
||||
}
|
||||
|
||||
//TODO: Reading from extent based directories
|
||||
if (fInode->Format() == XFS_DINODE_FMT_EXTENTS) {
|
||||
TRACE("Iterator:GetNext: EXTENTS");
|
||||
if (fExtentDir != NULL)
|
||||
@ -111,9 +110,10 @@ DirectoryIterator::GetNext(char* name, size_t* length, xfs_ino_t* ino)
|
||||
return status;
|
||||
}
|
||||
|
||||
//TODO: Reading from B+Trees based directories
|
||||
if (fInode->Format() == XFS_DINODE_FMT_BTREE) {
|
||||
TRACE("Iterator:GetNext: B+TREE");
|
||||
if (fTreeDir != NULL)
|
||||
return status = fTreeDir->GetNext(name, length, ino);
|
||||
return B_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
@ -127,13 +127,14 @@ DirectoryIterator::Lookup(const char* name, size_t length, xfs_ino_t* ino)
|
||||
{
|
||||
status_t status;
|
||||
if (fInode->Format() == XFS_DINODE_FMT_LOCAL) {
|
||||
TRACE("Iterator:Lookup: LOCAL\n");
|
||||
status = fShortDir->Lookup(name, length, ino);
|
||||
return status;
|
||||
}
|
||||
|
||||
//TODO: Reading from extent based dirs
|
||||
if (fInode->Format() == XFS_DINODE_FMT_EXTENTS) {
|
||||
TRACE("Iterator:Lookup: EXTENTS");
|
||||
TRACE("Iterator:Lookup: EXTENTS\n");
|
||||
if (fExtentDir != NULL)
|
||||
status = fExtentDir->Lookup(name, length, ino);
|
||||
else if (fLeafDir != NULL)
|
||||
@ -145,7 +146,9 @@ DirectoryIterator::Lookup(const char* name, size_t length, xfs_ino_t* ino)
|
||||
|
||||
//TODO: Reading from B+Tree based dirs
|
||||
if (fInode->Format() == XFS_DINODE_FMT_BTREE) {
|
||||
TRACE("Iterator:Lookup: B+TREE");
|
||||
TRACE("Iterator:Lookup: B+TREE\n");
|
||||
if (fTreeDir != NULL)
|
||||
return fTreeDir->Lookup(name, length, ino);
|
||||
return B_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
|
@ -149,6 +149,10 @@ Extent::GetNext(char* name, size_t* length, xfs_ino_t* ino)
|
||||
// This could be an unused entry so we should check
|
||||
|
||||
int numberOfEntries = B_BENDIAN_TO_HOST_INT32(BlockTail()->count);
|
||||
int numberOfStaleEntries = B_BENDIAN_TO_HOST_INT32(BlockTail()->stale);
|
||||
|
||||
// We don't read stale entries.
|
||||
numberOfEntries -= numberOfStaleEntries;
|
||||
TRACE("numberOfEntries:(%d)\n", numberOfEntries);
|
||||
uint16 currentOffset = (char*)entry - fBlockBuffer;
|
||||
|
||||
@ -161,6 +165,7 @@ Extent::GetNext(char* name, size_t* length, xfs_ino_t* ino)
|
||||
currentOffset += B_BENDIAN_TO_HOST_INT16(unusedEntry->length);
|
||||
entry = (void*)
|
||||
((char*)entry + B_BENDIAN_TO_HOST_INT16(unusedEntry->length));
|
||||
i--;
|
||||
continue;
|
||||
}
|
||||
ExtentDataEntry* dataEntry = (ExtentDataEntry*) entry;
|
||||
|
@ -120,14 +120,14 @@ LeafDirectory::FillBuffer(int type, char* blockBuffer, int howManyBlocksFurthur)
|
||||
|
||||
if (read_pos(fInode->GetVolume()->Device(), readPos, blockBuffer, len)
|
||||
!= len) {
|
||||
ERROR("Extent::FillBlockBuffer(): IO Error");
|
||||
ERROR("LeafDirectory::FillBlockBuffer(): IO Error");
|
||||
return B_IO_ERROR;
|
||||
}
|
||||
|
||||
if (type == DATA) {
|
||||
fDataBuffer = blockBuffer;
|
||||
ExtentDataHeader* header = (ExtentDataHeader*) fDataBuffer;
|
||||
if (B_BENDIAN_TO_HOST_INT32(header->magic) == HEADER_MAGIC)
|
||||
if (B_BENDIAN_TO_HOST_INT32(header->magic) == DATA_HEADER_MAGIC)
|
||||
TRACE("DATA BLOCK VALID\n");
|
||||
else {
|
||||
TRACE("DATA BLOCK INVALID\n");
|
||||
|
@ -11,7 +11,7 @@
|
||||
#include "system_dependencies.h"
|
||||
|
||||
|
||||
#define HEADER_MAGIC 0x58443244
|
||||
#define DATA_HEADER_MAGIC 0x58443244
|
||||
|
||||
|
||||
enum ContentType { DATA, LEAF };
|
||||
|
@ -106,14 +106,14 @@ NodeDirectory::FillBuffer(int type, char* blockBuffer, int howManyBlocksFurthur)
|
||||
|
||||
if (read_pos(fInode->GetVolume()->Device(), readPos, blockBuffer, len)
|
||||
!= len) {
|
||||
ERROR("Extent::FillBlockBuffer(): IO Error");
|
||||
ERROR("NodeDirectory::FillBlockBuffer(): IO Error");
|
||||
return B_IO_ERROR;
|
||||
}
|
||||
|
||||
if (type == DATA) {
|
||||
fDataBuffer = blockBuffer;
|
||||
ExtentDataHeader* header = (ExtentDataHeader*) fDataBuffer;
|
||||
if (B_BENDIAN_TO_HOST_INT32(header->magic) == HEADER_MAGIC) {
|
||||
if (B_BENDIAN_TO_HOST_INT32(header->magic) == DATA_HEADER_MAGIC) {
|
||||
TRACE("DATA BLOCK VALID\n");
|
||||
} else {
|
||||
TRACE("DATA BLOCK INVALID\n");
|
||||
|
@ -37,7 +37,7 @@ public:
|
||||
bool IsNodeType();
|
||||
void FillMapEntry(int num, ExtentMapEntry* map);
|
||||
status_t FillBuffer(int type, char* buffer,
|
||||
int howManyBlocksFurthur);
|
||||
int howManyBlocksFurther);
|
||||
void SearchAndFillDataMap(int blockNo);
|
||||
uint32 FindHashInNode(uint32 hashVal);
|
||||
uint32 GetOffsetFromAddress(uint32 address);
|
||||
|
@ -12,8 +12,8 @@
|
||||
|
||||
#include "fssh_api_wrapper.h"
|
||||
#include "fssh_auto_deleter.h"
|
||||
#include "fssh_kernel_priv.h"
|
||||
#include "Debug.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
@ -47,6 +47,7 @@ extern "C"
|
||||
#include <fs_volume.h>
|
||||
#include "Debug.h"
|
||||
#include <Drivers.h>
|
||||
#include <kernel.h>
|
||||
#include <KernelExport.h>
|
||||
#include <NodeMonitor.h>
|
||||
#include <SupportDefs.h>
|
||||
|
Loading…
Reference in New Issue
Block a user