Exemplo n.º 1
0
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;
}
Exemplo n.º 2
0
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;
}