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::_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::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; }