// Tree.cpp // // Copyright (c) 2003, Ingo Weinhold (bonefish@cs.tu-berlin.de) // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // You can alternatively use *this file* under the terms of the the MIT // license included in this package. #include #include "Tree.h" #include "Block.h" #include "BlockCache.h" #include "Debug.h" #include "DirItem.h" #include "Iterators.h" #include "StatItem.h" #include "Volume.h" const uint32 kMaxTreeHeight = 5; /*! \class Tree \brief Represents the on-disk S+Tree. This class actually doesn't have that much functionality. GetBlock() and GetNode() are used to request a block/tree node from disk, FindDirEntry() searches an entry in a directory and FindStatItem() gets the stat data for an object. The mammoth part of the code is located in the iterator code (Iterators.{cpp,h}). */ // constructor Tree::Tree() : fVolume(NULL), fBlockCache(NULL), fRootNode(NULL), fTreeHeight(0) { } // destructor Tree::~Tree() { if (fRootNode) fRootNode->Put(); } // Init status_t Tree::Init(Volume *volume, Node *rootNode, uint32 treeHeight) { status_t error = (volume && volume->GetBlockCache() && rootNode ? B_OK : B_BAD_VALUE); if (error == B_OK) { if (treeHeight > kMaxTreeHeight) { // we don't need to fail, as we can deal with that gracefully INFORM(("WARNING: tree height greater maximal height: %" B_PRIu32 "\n", treeHeight)); } fVolume = volume; fBlockCache = fVolume->GetBlockCache(); fRootNode = rootNode; fRootNode->Get(); fTreeHeight = treeHeight; } return error; } // InitCheck status_t Tree::InitCheck() const { return (fBlockCache && fRootNode && fTreeHeight ? B_OK : B_NO_INIT); } // GetTreeHeight uint32 Tree::GetTreeHeight() const { return fTreeHeight; } // GetBlockSize uint32 Tree::GetBlockSize() const { if (fBlockCache) return fBlockCache->GetBlockSize(); return 0; } // GetBlockCache BlockCache * Tree::GetBlockCache() const { return fBlockCache; } // GetRootNode Node * Tree::GetRootNode() const { return fRootNode; } // GetBlock status_t Tree::GetBlock(uint64 blockNumber, Block **block) { status_t error = (block ? InitCheck() : B_BAD_VALUE); if (error == B_OK) error = fBlockCache->GetBlock(blockNumber, block); return error; } // GetNode status_t Tree::GetNode(uint64 blockNumber, Node **node) { status_t error = (node ? InitCheck() : B_BAD_VALUE); if (error == B_OK) { Block *block; error = fBlockCache->GetBlock(blockNumber, &block); if (error == B_OK) { if (block->GetKind() == Block::KIND_UNKNOWN) block->SetKind(Block::KIND_FORMATTED); if (block->GetKind() == Block::KIND_FORMATTED) { *node = block->ToNode(); // check the node if (!(*node)->IsChecked()) { if ((*node)->IsInternal()) error = (*node)->ToInternalNode()->Check(); else if ((*node)->IsLeaf()) error = (*node)->ToLeafNode()->Check(); else error = B_BAD_DATA; if (error == B_OK) (*node)->SetChecked(true); } } else { block->Put(); error = B_BAD_DATA; } } } return error; } // FindDirEntry status_t Tree::FindDirEntry(uint32 dirID, uint32 objectID, const char *name, DirItem *foundItem, int32 *entryIndex) { status_t error = (name && foundItem && entryIndex ? InitCheck() : B_BAD_VALUE); if (error == B_OK) { error = FindDirEntry(dirID, objectID, name, strlen(name), foundItem, entryIndex); } return error; } // FindDirEntry status_t Tree::FindDirEntry(uint32 dirID, uint32 objectID, const char *name, size_t nameLen, DirItem *foundItem, int32 *entryIndex) { status_t error = (name && foundItem && entryIndex ? InitCheck() : B_BAD_VALUE); if (error == B_OK) { uint64 offset = 0; if (fVolume->GetKeyOffsetForName(name, nameLen, &offset) == B_OK) { //PRINT(("Tree::FindDirEntry(): hash function: offset: %Lu (`%.*s', %lu)\n", //offset, (int)nameLen, name, nameLen)); // offset computed -- we can directly iterate only over entries // with that offset DirEntryIterator iterator(this, dirID, objectID, offset, true); DirItem dirItem; int32 index = 0; error = B_ENTRY_NOT_FOUND; while (iterator.GetPrevious(&dirItem, &index) == B_OK) { size_t itemNameLen = 0; if (const char *itemName = dirItem.EntryNameAt(index, &itemNameLen)) { if (itemNameLen == nameLen && !strncmp(name, itemName, nameLen)) { // item found *foundItem = dirItem; *entryIndex = index; error = B_OK; break; } } } } else { //PRINT(("Tree::FindDirEntry(): no hash function\n")); // no offset (no hash function) -- do it the slow way ObjectItemIterator iterator(this, dirID, objectID); DirItem dirItem; error = B_ENTRY_NOT_FOUND; while (iterator.GetNext(&dirItem, TYPE_DIRENTRY) == B_OK) { if (dirItem.Check() != B_OK) // bad data: skip item continue; int32 index = dirItem.IndexOfName(name); if (index >= 0) { // item found *foundItem = dirItem; *entryIndex = index; error = B_OK; //PRINT((" offset: %lu\n", dirItem.EntryAt(index)->GetOffset())); break; } } } } return error; } // FindStatItem status_t Tree::FindStatItem(uint32 dirID, uint32 objectID, StatItem *item) { status_t error = (item ? InitCheck() : B_BAD_VALUE); if (error == B_OK) { ItemIterator iterator(this); error = iterator.InitCheck(); VKey k(dirID, objectID, SD_OFFSET, V1_SD_UNIQUENESS, KEY_FORMAT_3_5); if (error == B_OK) error = iterator.FindRightMost(&k, item); } return error; }