status_t DirEntryTree::FreeTree(Transaction& transaction) { status_t error = _InitReadOnly(); if (error != B_OK) RETURN_ERROR(error); int32 depth = _Depth(); if (depth == 0) return B_OK; LevelInfo* infos = new(std::nothrow) LevelInfo[ kCheckSumFSMaxDirEntryTreeDepth + 1]; if (infos == NULL) RETURN_ERROR(B_NO_MEMORY); ArrayDeleter<LevelInfo> infosDeleter(infos); infos[0].entryBlock.SetTo(fRootEntryBlock, fRootEntryBlockSize); infos[0].index = 0; // Iterate through the tree in post order. We don't touch the content of // any block, we only free the blocks. int32 level = 0; while (true) { LevelInfo& info = infos[level]; if (level == depth || info.index >= info.entryBlock.EntryCount()) { // we're through with the block if (level == 0) break; // free it error = fDirectory->GetVolume()->GetBlockAllocator()->Free( info.block.Index(), 1, transaction); // continue with the next sibling branch infos[--level].index++; } // descend to next level uint64 nextBlockIndex = info.entryBlock.BlockIndexAt(info.index); LevelInfo& nextInfo = infos[++level]; if (!nextInfo.block.GetReadable(fDirectory->GetVolume(), nextBlockIndex)) { RETURN_ERROR(B_ERROR); } nextInfo.entryBlock.SetTo( (checksumfs_dir_entry_block*)nextInfo.block.Data(), B_PAGE_SIZE); } return B_OK; }
status_t DirEntryTree::_InsertEntryIncrementDepth(LevelInfo* infos, Transaction& transaction) { FUNCTION("depth: %u -> %u\n", _Depth(), _Depth() + 1); if (_Depth() >= kCheckSumFSMaxDirEntryTreeDepth) RETURN_ERROR(B_DEVICE_FULL); // allocate a new block AllocatedBlock allocatedBlock( fDirectory->GetVolume()->GetBlockAllocator(), transaction); status_t error = allocatedBlock.Allocate(fDirectory->BlockIndex()); if (error != B_OK) RETURN_ERROR(error); fDirectory->SetSize(fDirectory->Size() + B_PAGE_SIZE); LevelInfo& newInfo = infos[1]; if (!newInfo.block.GetZero(fDirectory->GetVolume(), allocatedBlock.Index(), transaction)) { RETURN_ERROR(B_ERROR); } allocatedBlock.Detach(); newInfo.entryBlock.SetTo( (checksumfs_dir_entry_block*)newInfo.block.Data(), B_PAGE_SIZE); ASSERT(newInfo.entryBlock.Check()); // move the old root block contents to the new block LevelInfo& rootInfo = infos[0]; rootInfo.entryBlock.SplitBlock(0, newInfo.entryBlock); // add an entry for the new block to the root block size_t nameLength; const char* name = newInfo.entryBlock.NameAt(0, nameLength); rootInfo.entryBlock.InsertEntry(0, name, nameLength, newInfo.block.Index()); PRINT(" -> new block: %" B_PRIu64 "\n", newInfo.block.Index()); newInfo.index = rootInfo.index; rootInfo.index = 0; fTree->depth++; return B_OK; }
status_t DirEntryTree::_InitWritable(Transaction& transaction) { if (!fRootBlock.GetWritable(fDirectory->GetVolume(), fDirectory->BlockIndex(), transaction)) { RETURN_ERROR(B_ERROR); } return _InitCommon(); }
status_t DirEntryTree::_InitReadOnly() { if (!fRootBlock.GetReadable(fDirectory->GetVolume(), fDirectory->BlockIndex())) { RETURN_ERROR(B_ERROR); } return _InitCommon(); }
status_t DirEntryTree::_InsertEntrySplitBlock(int32 level, LevelInfo& info, size_t needed, Transaction& transaction, Block& newBlock, int32& _splitIndex) { int32 splitIndex = info.entryBlock.FindSplitIndex(info.index, needed); FUNCTION("level: %" B_PRId32 ", size needed: %" B_PRIuSIZE ", split index: " "%" B_PRId32 "/%" B_PRId32 "\n", level, needed, splitIndex, info.entryBlock.EntryCount()); // allocate a new block AllocatedBlock allocatedBlock( fDirectory->GetVolume()->GetBlockAllocator(), transaction); status_t error = allocatedBlock.Allocate(fDirectory->BlockIndex()); if (error != B_OK) RETURN_ERROR(error); fDirectory->SetSize(fDirectory->Size() + B_PAGE_SIZE); if (!newBlock.GetZero(fDirectory->GetVolume(), allocatedBlock.Index(), transaction)) { RETURN_ERROR(B_ERROR); } allocatedBlock.Detach(); // split the old block DirEntryBlock newEntryBlock( (checksumfs_dir_entry_block*)newBlock.Data(), B_PAGE_SIZE); ASSERT(newEntryBlock.Check()); info.entryBlock.SplitBlock(splitIndex, newEntryBlock); PRINT(" -> new block: %" B_PRIu64 "\n", newBlock.Index()); _splitIndex = splitIndex; return B_OK; }
status_t ReadNextEntry(struct dirent* buffer, size_t size, uint32& _countRead) { const char* name; uint64 blockIndex; int nextIterationState = OTHERS; switch (iterationState) { case DOT: name = "."; blockIndex = directory->BlockIndex(); nextIterationState = DOT_DOT; break; case DOT_DOT: name = ".."; blockIndex = directory->ParentDirectory(); break; default: // TODO: Implement! _countRead = 0; return B_OK; } size_t entrySize = sizeof(dirent) + strlen(name); if (entrySize > size) return B_BUFFER_OVERFLOW; buffer->d_dev = directory->GetVolume()->ID(); buffer->d_ino = blockIndex; buffer->d_reclen = entrySize; strcpy(buffer->d_name, name); iterationState = nextIterationState; _countRead = 1; return B_OK; }
status_t DirEntryTree::RemoveEntry(const char* name, Transaction& transaction) { FUNCTION("name: \"%s\"\n", name); status_t error = _InitWritable(transaction); if (error != B_OK) RETURN_ERROR(error); size_t nameLength = strlen(name); if (nameLength == 0) RETURN_ERROR(B_BAD_VALUE); if (nameLength > kCheckSumFSNameLength) RETURN_ERROR(B_ENTRY_NOT_FOUND); int32 depth = _Depth(); LevelInfo* infos = new(std::nothrow) LevelInfo[ kCheckSumFSMaxDirEntryTreeDepth + 1]; if (infos == NULL) RETURN_ERROR(B_NO_MEMORY); ArrayDeleter<LevelInfo> infosDeleter(infos); infos[0].entryBlock.SetTo(fRootEntryBlock, fRootEntryBlockSize); for (int32 level = 0; level <= depth; level++) { LevelInfo& info = infos[level]; if (info.entryBlock.EntryCount() == 0) { if (level == 0) { // directory is empty PRINT(" directory is empty\n"); RETURN_ERROR(B_ENTRY_NOT_FOUND); } RETURN_ERROR(B_BAD_DATA); } info.index = info.entryBlock.FindInsertionIndex(name, nameLength, info.exactMatch); PRINT(" level %" B_PRId32 ", block %" B_PRIu64 " -> index: %" B_PRId32 " %sexact\n", level, level == 0 ? fDirectory->BlockIndex() : info.block.Index(), info.index, info.exactMatch ? "" : " not "); if (level == depth) { // final level -- here the entry should be found if (!info.exactMatch) RETURN_ERROR(B_ENTRY_NOT_FOUND); break; } // If we haven't found an exact match, the index points to the first // entry that is greater or after the last entry. if (!info.exactMatch) { if (info.index == 0) { // The first entry is already greater, so the branch doesn't // contain the entry we're looking for. RETURN_ERROR(B_ENTRY_NOT_FOUND); } info.index--; } uint64 nextBlockIndex = info.entryBlock.BlockIndexAt(info.index); // not the final level -- load the block and descend to the next // level LevelInfo& nextInfo = infos[level + 1]; if (!nextInfo.block.GetReadable(fDirectory->GetVolume(), nextBlockIndex)) { RETURN_ERROR(B_ERROR); } nextInfo.entryBlock.SetTo( (checksumfs_dir_entry_block*)nextInfo.block.Data(), B_PAGE_SIZE); ASSERT(nextInfo.entryBlock.Check()); } // We've found the entry. Insert the key and iterate backwards to perform // the potentially necessary updates. Removal at index 0 of the block // changes the block's key, requiring an update in the parent block. // Removal of the last entry will require removal of the block from its // parent. Key update can cause the block to be split (if there's not // enough space left in it), requiring an insertion in the parent block. // We start with a pending removal in the leaf block and work our way // upwards as long as the blocks become empty. As soon as a key update is // required, we delegate the remaining to the update/insert backwards loop. for (int32 level = depth; level >= 0; level--) { LevelInfo& info = infos[level]; // make the block writable if (level > 0) { error = info.block.MakeWritable(transaction); if (error != B_OK) RETURN_ERROR(error); } PRINT(" level: %" B_PRId32 ", index: %" B_PRId32 ": removing key " "\"%.*s\" (%" B_PRIuSIZE ")\n", level, info.index, (int)nameLength, name, nameLength); if (info.entryBlock.EntryCount() == 1) { // That's the last key in the block. Unless that's the root level, // we remove the block completely. PRINT(" -> block is empty\n"); if (level == 0) { info.entryBlock.RemoveEntry(info.index); return B_OK; } error = fDirectory->GetVolume()->GetBlockAllocator()->Free( info.block.Index(), 1, transaction); if (error != B_OK) RETURN_ERROR(error); fDirectory->SetSize(fDirectory->Size() - B_PAGE_SIZE); // remove the key (the same one) from the parent block continue; } // There are more entries, so just remove the entry in question. If it // is not the first one, we're done, otherwise we have to update the // block's key in the parent block. info.entryBlock.RemoveEntry(info.index); if (info.index > 0 || level == 0) return B_OK; name = info.entryBlock.NameAt(0, nameLength); return _UpdateOrInsertKey(infos, level - 1, name, nameLength, 0, false, transaction); } return B_OK; }
status_t DirEntryTree::InsertEntry(const char* name, uint64 blockIndex, Transaction& transaction) { FUNCTION("name: \"%s\", blockIndex: %" B_PRIu64 "\n", name, blockIndex); status_t error = _InitWritable(transaction); if (error != B_OK) RETURN_ERROR(error); size_t nameLength = strlen(name); if (nameLength == 0) RETURN_ERROR(B_BAD_VALUE); if (nameLength > kCheckSumFSNameLength) RETURN_ERROR(B_NAME_TOO_LONG); int32 depth = _Depth(); LevelInfo* infos = new(std::nothrow) LevelInfo[ kCheckSumFSMaxDirEntryTreeDepth + 1]; if (infos == NULL) RETURN_ERROR(B_NO_MEMORY); ArrayDeleter<LevelInfo> infosDeleter(infos); infos[0].entryBlock.SetTo(fRootEntryBlock, fRootEntryBlockSize); for (int32 level = 0; level <= depth; level++) { LevelInfo& info = infos[level]; if (info.entryBlock.EntryCount() == 0) { if (level == 0) { PRINT(" directory is empty\n"); // directory is empty info.index = 0; break; } RETURN_ERROR(B_BAD_DATA); } info.index = info.entryBlock.FindInsertionIndex(name, nameLength, info.exactMatch); PRINT(" level %" B_PRId32 ", block %" B_PRIu64 " -> index: %" B_PRId32 " %sexact\n", level, level == 0 ? fDirectory->BlockIndex() : info.block.Index(), info.index, info.exactMatch ? "" : " not "); // Finding an exact match -- even in the non-final level -- means // that there's an entry with that name. if (info.exactMatch) RETURN_ERROR(B_FILE_EXISTS); if (level == depth) { // final level -- here we need to insert the entry break; } // Since we haven't found an exact match, the index points to the // first entry that is greater or after the last entry. info.index--; uint64 nextBlockIndex = info.entryBlock.BlockIndexAt(info.index); // not the final level -- load the block and descend to the next // level LevelInfo& nextInfo = infos[level + 1]; if (!nextInfo.block.GetReadable(fDirectory->GetVolume(), nextBlockIndex)) { RETURN_ERROR(B_ERROR); } nextInfo.entryBlock.SetTo( (checksumfs_dir_entry_block*)nextInfo.block.Data(), B_PAGE_SIZE); ASSERT(nextInfo.entryBlock.Check()); } // We've found the insertion point. Insert the key and iterate backwards // to perform the potentially necessary updates. Insertion at index 0 of // the block changes the block's key, requiring an update in the parent // block. Insertion or key update can cause the block to be split (if // there's not enough space left in it), requiring an insertion in the // parent block. So we start with a pending insertion in the leaf block // and work our way upwards, performing key updates and insertions as // necessary. return _UpdateOrInsertKey(infos, depth, name, nameLength, blockIndex, true, transaction); }
status_t DirEntryTree::LookupNextEntry(const char* name, char* foundName, size_t& _foundNameLength, uint64& _blockIndex) { FUNCTION("name: \"%s\"\n", name); status_t error = _InitReadOnly(); if (error != B_OK) RETURN_ERROR(error); size_t nameLength = strlen(name); if (nameLength > kCheckSumFSNameLength) RETURN_ERROR(B_ENTRY_NOT_FOUND); int32 depth = _Depth(); LevelInfo* infos = new(std::nothrow) LevelInfo[ kCheckSumFSMaxDirEntryTreeDepth + 1]; if (infos == NULL) RETURN_ERROR(B_NO_MEMORY); ArrayDeleter<LevelInfo> infosDeleter(infos); infos[0].entryBlock.SetTo(fRootEntryBlock, fRootEntryBlockSize); ASSERT(infos[0].entryBlock.Check()); // descend the tree for (int32 level = 0; level <= depth; level++) { LevelInfo& info = infos[level]; if (info.entryBlock.EntryCount() == 0) { if (level == 0) { // directory is empty return B_ENTRY_NOT_FOUND; } RETURN_ERROR(B_BAD_DATA); } info.index = info.entryBlock.FindInsertionIndex(name, nameLength, info.exactMatch); PRINT(" level %" B_PRId32 " -> index: %" B_PRId32 " %sexact\n", level, info.index, info.exactMatch ? "" : " not "); if (level == depth) break; // If we haven't found an exact match, the index points to the first // entry that is greater or after the last entry. if (!info.exactMatch && info.index > 0) info.index--; uint64 nextBlockIndex = info.entryBlock.BlockIndexAt(info.index); // not the final level -- load the block and descend to the next // level LevelInfo& nextInfo = infos[level + 1]; if (!nextInfo.block.GetReadable(fDirectory->GetVolume(), nextBlockIndex)) { RETURN_ERROR(B_ERROR); } nextInfo.entryBlock.SetTo( (checksumfs_dir_entry_block*)nextInfo.block.Data(), B_PAGE_SIZE); ASSERT(nextInfo.entryBlock.Check()); } if (infos[depth].exactMatch) infos[depth].index++; if (infos[depth].index >= infos[depth].entryBlock.EntryCount()) { // We're at the end of the last block -- we need to track back to find a // greater branch. PRINT(" searching for greater branch\n"); int32 level; for (level = depth - 1; level >= 0; level--) { LevelInfo& info = infos[level]; if (++info.index < info.entryBlock.EntryCount()) { PRINT(" found greater branch: level: %" B_PRId32 " -> index: %" B_PRId32 "\n", level, info.index); break; } } if (level < 0) return B_ENTRY_NOT_FOUND; // We've found a greater branch -- get the first entry in that branch. for (level++; level <= depth; level++) { LevelInfo& previousInfo = infos[level - 1]; LevelInfo& info = infos[level]; uint64 nextBlockIndex = previousInfo.entryBlock.BlockIndexAt( previousInfo.index); // load the block if (!info.block.GetReadable(fDirectory->GetVolume(), nextBlockIndex)) { RETURN_ERROR(B_ERROR); } info.entryBlock.SetTo( (checksumfs_dir_entry_block*)info.block.Data(), B_PAGE_SIZE); ASSERT(info.entryBlock.Check()); info.index = 0; if (info.entryBlock.EntryCount() == 0) RETURN_ERROR(B_BAD_DATA); } } // get and check the name LevelInfo& info = infos[depth]; name = info.entryBlock.NameAt(info.index, nameLength); if (nameLength > kCheckSumFSNameLength || strnlen(name, nameLength) != nameLength) { RETURN_ERROR(B_BAD_DATA); } // set the return values memcpy(foundName, name, nameLength); foundName[nameLength] = '\0'; _foundNameLength = nameLength; _blockIndex = info.entryBlock.BlockIndexAt(info.index); PRINT(" found entry: \"%s\" -> %" B_PRIu64 "\n", foundName, _blockIndex); return B_OK; }
status_t DirEntryTree::LookupEntry(const char* name, uint64& _blockIndex) { FUNCTION("name: \"%s\"\n", name); status_t error = _InitReadOnly(); if (error != B_OK) RETURN_ERROR(error); size_t nameLength = strlen(name); if (nameLength > kCheckSumFSNameLength) RETURN_ERROR(B_ENTRY_NOT_FOUND); uint32 depth = _Depth(); DirEntryBlock entryBlock(fRootEntryBlock, fRootEntryBlockSize); ASSERT(entryBlock.Check()); Block block; for (uint32 level = 0; level <= depth; level++) { if (entryBlock.EntryCount() == 0) RETURN_ERROR(level == 0 ? B_ENTRY_NOT_FOUND : B_BAD_DATA); bool exactMatch; int32 index = entryBlock.FindInsertionIndex(name, nameLength, exactMatch); // If we haven't found an exact match, the index points to the first // entry that is greater or after the last entry. if (!exactMatch) { if (index == 0) { // The first entry is already greater, so the branch doesn't // contain the entry we're looking for. RETURN_ERROR(B_ENTRY_NOT_FOUND); } index--; } PRINT(" level %" B_PRId32 " -> index: %" B_PRId32 " %sexact\n", level, index, exactMatch ? "" : " not "); uint64 blockIndex = entryBlock.BlockIndexAt(index); if (level == depth) { // final level -- here we should have an exact match if (!exactMatch) RETURN_ERROR(B_ENTRY_NOT_FOUND); _blockIndex = blockIndex; return B_OK; } // not the final level -- load the block and descend to the next // level if (!block.GetReadable(fDirectory->GetVolume(), blockIndex)) RETURN_ERROR(B_ERROR); entryBlock.SetTo((checksumfs_dir_entry_block*)block.Data(), B_PAGE_SIZE); ASSERT(entryBlock.Check()); } // cannot get here, but keep the compiler happy RETURN_ERROR(B_ENTRY_NOT_FOUND); }
bool DirEntryTree::_Check(int32 level, uint64 blockIndex, const char* key, size_t keyLength, DirEntryBlock& entryBlock) { // check block for validity if (!entryBlock.Check()) { ERROR("DirEntryTree::Check(): level %" B_PRIu32 ": block %" B_PRIu64 " not valid!\n", level, blockIndex); return false; } // The root block is allowed to be empty. For all other blocks that is an // error. uint32 entryCount = entryBlock.EntryCount(); if (entryCount == 0) { if (level == 0) return true; ERROR("DirEntryTree::Check(): level %" B_PRIu32 ": block %" B_PRIu64 " empty!\n", level, blockIndex); return false; } // Verify that the block's first entry matches the key with which the // parent block refers to it. if (level > 0) { size_t nameLength; const char* name = entryBlock.NameAt(0, nameLength); if (nameLength != keyLength || strncmp(name, key, keyLength) != 0) { ERROR("DirEntryTree::Check(): level %" B_PRIu32 ": block %" B_PRIu64 " key mismatch: is \"%.*s\", should be \"%.*s\"\n", level, blockIndex, (int)keyLength, key, (int)nameLength, name); return false; } } if (level == _Depth()) return true; // not the final level -- recurse for (uint32 i = 0; i < entryCount; i++) { size_t nameLength; const char* name = entryBlock.NameAt(i, nameLength); uint64 childBlockIndex = entryBlock.BlockIndexAt(i); Block childBlock; if (!childBlock.GetReadable(fDirectory->GetVolume(), childBlockIndex)) { ERROR("DirEntryTree::Check(): level %" B_PRIu32 ": block %" B_PRIu64 " failed to get child block %" B_PRIu64 " (entry %" B_PRIu32 ")\n", level, blockIndex, childBlockIndex, i); } DirEntryBlock childEntryBlock( (checksumfs_dir_entry_block*)childBlock.Data(), B_PAGE_SIZE); if (!_Check(level + 1, childBlockIndex, name, nameLength, childEntryBlock)) { return false; } } return true; }