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 DirEntryTree::_UpdateOrInsertKey(LevelInfo* infos, int32 level, const char* name, size_t nameLength, uint64 blockIndex, bool insertKey, Transaction& transaction) { FUNCTION("level: %" B_PRId32 ": %s name: \"%.*s\" (%" B_PRIuSIZE "), " "blockIndex: %" B_PRIu64 "\n", level, insertKey ? "insert" : "update", (int)nameLength, name, nameLength, blockIndex); // Some temporary blocks: newBlock is used when a block is split. The // other three are used when a key update respectively insertion in the // parent block becomes necessary. We only need them, since the name // we update/insert is potentially from a block and instead of cloning // the name, we simple postpone putting the block until we don't need // the name anymore. Block newBlock; Block tempBlockUpdate; Block tempBlockUpdateInsert; Block tempBlockInsert; int32 depth = _Depth(); status_t error; bool updateNextKey = !insertKey; bool insertNextKey = insertKey; const char* nameToUpdate = name; size_t nameToUpdateLength = nameLength; const char* nextNameToInsert = name; size_t nextNameToInsertLength = nameLength; uint64 nextBlockIndexToInsert = blockIndex; for (; level >= 0; level--) { LevelInfo& info = infos[level]; bool updateThisKey = updateNextKey; bool insertThisKey = insertNextKey; if (!updateThisKey && !insertThisKey) return B_OK; updateNextKey = false; insertNextKey = false; blockIndex = nextBlockIndexToInsert; name = nextNameToInsert; nameLength = nextNameToInsertLength; // make the block writable if (level > 0) { error = info.block.MakeWritable(transaction); if (error != B_OK) RETURN_ERROR(error); } if (updateThisKey) { PRINT(" level: %" B_PRId32 ", index: %" B_PRId32 ": updating key " "to \"%.*s\" (%" B_PRIuSIZE ")\n", level, info.index, (int)nameToUpdateLength, nameToUpdate, nameToUpdateLength); size_t oldNameLength; info.entryBlock.NameAt(info.index, oldNameLength); size_t spaceNeeded = oldNameLength < nameToUpdateLength ? nameToUpdateLength - oldNameLength : 0; if (spaceNeeded <= info.entryBlock.FreeSpace()) { info.entryBlock.ReplaceEntryName(info.index, nameToUpdate, nameToUpdateLength); if (info.index == 0) { // we updated at index 0, so we need to update this // block's key in the parent block updateNextKey = true; nameToUpdate = info.entryBlock.NameAt(0, nameToUpdateLength); // make sure the new block is kept until we no longer // use the name in the next iteration tempBlockUpdate.TransferFrom(info.block); } } else if (level == 0) { // We need to split the root block -- clone it first. error = _InsertEntryIncrementDepth(infos, transaction); if (error != B_OK) RETURN_ERROR(error); level = 2; // _InsertEntryIncrementDepth() moved the root block // content to level 1, where we want to continue. updateNextKey = true; insertNextKey = insertThisKey; continue; } else { // We need to split this non-root block. int32 splitIndex; error = _InsertEntrySplitBlock(level, info, spaceNeeded, transaction, newBlock, splitIndex); if (error != B_OK) RETURN_ERROR(error); nextBlockIndexToInsert = newBlock.Index(); DirEntryBlock newEntryBlock( (checksumfs_dir_entry_block*)newBlock.Data(), B_PAGE_SIZE); ASSERT(newEntryBlock.Check()); if (info.index < splitIndex) { ASSERT(info.entryBlock.FreeSpace() >= spaceNeeded); info.entryBlock.ReplaceEntryName(info.index, nameToUpdate, nameToUpdateLength); if (info.index == 0) { // we updated at index 0, so we need to update this // block's key in the parent block updateNextKey = true; nameToUpdate = info.entryBlock.NameAt(0, nameToUpdateLength); // make sure the new block is kept until we no // longer use the name in the next iteration tempBlockUpdate.TransferFrom(info.block); } } else { ASSERT(newEntryBlock.FreeSpace() >= spaceNeeded); // we need to transfer the block to the info, in case we // also need to insert a key below info.block.TransferFrom(newBlock); info.entryBlock.SetTo( (checksumfs_dir_entry_block*)info.block.Data(), B_PAGE_SIZE); ASSERT(info.entryBlock.Check()); info.index -= splitIndex; info.entryBlock.ReplaceEntryName(info.index, nameToUpdate, nameToUpdateLength); } // the newly created block needs to be inserted in the // parent insertNextKey = true; nextNameToInsert = newEntryBlock.NameAt(0, nextNameToInsertLength); // make sure the new block is kept until we no longer use // the name in the next iteration (might already have been // transferred to entry.block) tempBlockUpdateInsert.TransferFrom(newBlock); } } if (insertThisKey) { // insert after the block we descended if (level < depth) info.index++; PRINT(" level: %" B_PRId32 ", index: %" B_PRId32 ": inserting key " "\"%.*s\" (%" B_PRIuSIZE "), blockIndex: %" B_PRIu64 "\n", level, info.index, (int)nameLength, name, nameLength, blockIndex); if (info.entryBlock.FreeSpace() >= nameLength + 10) { info.entryBlock.InsertEntry(info.index, name, nameLength, blockIndex); if (info.index == 0) { // we inserted at index 0, so we need to update this // block's key in the parent block updateNextKey = true; nameToUpdate = info.entryBlock.NameAt(0, nameToUpdateLength); // make sure the new block is kept until we no longer // use the name in the next iteration tempBlockUpdate.TransferFrom(info.block); } continue; } // Not enough space left in the block -- we need to split it. ASSERT(!insertNextKey); // for level == 0 we need to clone the block first if (level == 0) { error = _InsertEntryIncrementDepth(infos, transaction); if (error != B_OK) RETURN_ERROR(error); level = 2; // _InsertEntryIncrementDepth() moved the root block // content to level 1, where we want to continue. updateNextKey = false; insertNextKey = true; continue; } int32 splitIndex; error = _InsertEntrySplitBlock(level, info, nameLength + 10, transaction, newBlock, splitIndex); if (error != B_OK) RETURN_ERROR(error); DirEntryBlock newEntryBlock( (checksumfs_dir_entry_block*)newBlock.Data(), B_PAGE_SIZE); ASSERT(newEntryBlock.Check()); if (info.index < splitIndex) { ASSERT(info.entryBlock.FreeSpace() >= nameLength + 10); info.entryBlock.InsertEntry(info.index, name, nameLength, blockIndex); if (info.index == 0) { // we inserted at index 0, so we need to update this // block's key in the parent block updateNextKey = true; nameToUpdate = info.entryBlock.NameAt(0, nameToUpdateLength); // make sure the new block is kept until we no longer // use the name in the next iteration tempBlockUpdate.TransferFrom(info.block); } } else { ASSERT(newEntryBlock.FreeSpace() >= nameLength + 10); info.index -= splitIndex; newEntryBlock.InsertEntry(info.index, name, nameLength, blockIndex); } // the newly created block needs to be inserted in the parent insertNextKey = true; nextNameToInsert = newEntryBlock.NameAt(0, nextNameToInsertLength); nextBlockIndexToInsert = newBlock.Index(); // make sure the new block is kept until we no longer use // the name in the next iteration tempBlockInsert.TransferFrom(newBlock); } } return B_OK; }