⛏️ index : haiku.git

/*
 * Copyright 2017, ChαΊΏ VΕ© Gia Hy, cvghy116@gmail.com.
 * Copyright 2011-2013, JΓ©rΓ΄me Duval, korli@users.berlios.de.
 * This file may be used under the terms of the MIT License.
 */


#include "DirectoryIterator.h"
#include "CRCTable.h"


//#define TRACE_BTRFS
#ifdef TRACE_BTRFS
#	define TRACE(x...) dprintf("\33[34mbtrfs:\33[0m " x)
#else
#	define TRACE(x...) ;
#endif
#	define ERROR(x...) dprintf("\33[34mbtrfs:\33[0m " x)


DirectoryIterator::DirectoryIterator(Inode* inode)
	:
	fOffset(0),
	fInode(inode),
	fIterator(NULL)
{
	btrfs_key key;
	key.SetType(BTRFS_KEY_TYPE_DIR_INDEX);
	key.SetObjectID(inode->ID());
	key.SetOffset(BTREE_BEGIN);
	fIterator = new(std::nothrow) TreeIterator(inode->GetVolume()->FSTree(),
		key);
}


DirectoryIterator::~DirectoryIterator()
{
	delete fIterator;
	fIterator = NULL;
}


status_t
DirectoryIterator::InitCheck()
{
	return fIterator != NULL ? B_OK : B_NO_MEMORY;
}


status_t
DirectoryIterator::GetNext(char* name, size_t* _nameLength, ino_t* _id)
{
	if (fOffset == 0) {
		if (*_nameLength < 3)
			return B_BUFFER_OVERFLOW;
		*_nameLength = 2;
		strlcpy(name, "..", *_nameLength + 1);
		*_id = fInode->ID();
		fOffset = 1;
		return B_OK;
	} else if (fOffset == 1) {
		if (*_nameLength < 2)
			return B_BUFFER_OVERFLOW;
		*_nameLength = 1;
		strlcpy(name, ".", *_nameLength + 1);
		fOffset = 2;
		if (fInode->ID() == BTRFS_FIRST_SUBVOLUME) {
			*_id = fInode->ID();
			return B_OK;
		}
		return fInode->FindParent(_id);
	}

	btrfs_dir_entry* entries;
	uint32 entries_length;
	status_t status = fIterator->GetNextEntry((void**)&entries, &entries_length);
	if (status != B_OK)
		return status;

	btrfs_dir_entry* entry = entries;
	uint16 current = 0;
	while (current < entries_length) {
		current += entry->Length();
		break;
		// TODO there could be several entries with the same name hash
		entry = (btrfs_dir_entry*)((uint8*)entry + entry->Length());
	}

	size_t length = entry->NameLength();

	TRACE("DirectoryIterator::GetNext() entries_length %ld name_length %d\n",
		entries_length, entry->NameLength());

	if (length + 1 > *_nameLength) {
		free(entries);
		return B_BUFFER_OVERFLOW;
	}

	memcpy(name, entry + 1, length);
	name[length] = '\0';
	*_nameLength = length;
	*_id = entry->InodeID();
	free(entries);

	return B_OK;
}


status_t
DirectoryIterator::Lookup(const char* name, size_t nameLength, ino_t* _id)
{
	if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0) {
		if (strcmp(name, ".") == 0
			|| fInode->ID() == BTRFS_FIRST_SUBVOLUME) {
			*_id = fInode->ID();
			return B_OK;
		}
		return fInode->FindParent(_id);
	}

	uint32 hash = calculate_crc((uint32)~1, (uint8*)name, nameLength);
	btrfs_key key;
	key.SetType(BTRFS_KEY_TYPE_DIR_ITEM);
	key.SetObjectID(fInode->ID());
	key.SetOffset(hash);
	BTree::Path path(fInode->GetVolume()->FSTree());

	btrfs_dir_entry* entries;
	uint32 length;
	status_t status = fInode->GetVolume()->FSTree()->FindExact(&path, key,
		(void**)&entries, &length);
	if (status != B_OK) {
		TRACE("DirectoryIterator::Lookup(): Couldn't find entry with hash %" B_PRIu32
			" \"%s\"\n", hash, name);
		return status;
	}

	btrfs_dir_entry* entry = entries;
	uint16 current = 0;
	while (current < length) {
		current += entry->Length();
		break;
		// TODO there could be several entries with the same name hash
		entry = (btrfs_dir_entry*)((uint8*)entry + entry->Length());
	}

	TRACE("DirectoryIterator::Lookup() entries_length %ld name_length %d\n",
		length, entry->NameLength());

	*_id = entry->InodeID();
	free(entries);

	return B_OK;
}


status_t
DirectoryIterator::Rewind()
{
	fIterator->Rewind();
	fOffset = BTREE_BEGIN;
	return B_OK;
}