Exemplo n.º 1
0
static status_t
checksumfs_lookup(fs_volume* fsVolume, fs_vnode* fsDir, const char* name,
	ino_t* _id)
{
	Volume* volume = (Volume*)fsVolume->private_volume;
	Node* node = (Node*)fsDir->private_node;

	Directory* directory = dynamic_cast<Directory*>(node);
	if (directory == NULL)
		return B_NOT_A_DIRECTORY;

	uint64 blockIndex;

	if (strcmp(name, ".") == 0) {
		blockIndex = directory->BlockIndex();
	} else if (strcmp(name, "..") == 0) {
		blockIndex = directory->ParentDirectory();
	} else {
		// TODO: Implement!
		return B_ENTRY_NOT_FOUND;
	}

	// get the node
	Node* childNode;
	status_t error = volume->GetNode(blockIndex, childNode);
	if (error != B_OK)
		return error;

	*_id = blockIndex;
	return B_OK;
}
Exemplo n.º 2
0
status_t
DirEntryTree::_InitWritable(Transaction& transaction)
{
	if (!fRootBlock.GetWritable(fDirectory->GetVolume(),
			fDirectory->BlockIndex(), transaction)) {
		RETURN_ERROR(B_ERROR);
	}

	return _InitCommon();
}
Exemplo n.º 3
0
status_t
DirEntryTree::_InitReadOnly()
{
	if (!fRootBlock.GetReadable(fDirectory->GetVolume(),
			fDirectory->BlockIndex())) {
		RETURN_ERROR(B_ERROR);
	}

	return _InitCommon();
}
Exemplo n.º 4
0
bool
DirEntryTree::Check()
{
	if (_InitReadOnly() != B_OK) {
		ERROR("DirEntryTree::Check(): Init failed!\n");
		return false;
	}

	DirEntryBlock entryBlock(fRootEntryBlock, fRootEntryBlockSize);
	return _Check(0, fDirectory->BlockIndex(), NULL, 0, entryBlock);
}
Exemplo n.º 5
0
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;
}
Exemplo n.º 6
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.º 7
0
	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;
	}
Exemplo n.º 8
0
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;
}
Exemplo n.º 9
0
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);
}