/*
 * Copyright 2007, Ingo Weinhold, bonefish@users.sf.net.
 * Distributed under the terms of the MIT License.
 */
 
 
#include "ExtendedPartitionAddOn.h"
 
#include <new>
#include <stdio.h>
 
#include <DiskDeviceTypes.h>
#include <MutablePartition.h>
#include <PartitioningInfo.h>
 
#include <AutoDeleter.h>
 
#include "IntelDiskSystem.h"
 
 
//#define TRACE_EXTENDED_PARTITION_ADD_ON
#undef TRACE
#ifdef TRACE_EXTENDED_PARTITION_ADD_ON
#	define TRACE(x...) printf(x)
#else
#	define TRACE(x...) do {} while (false)
#endif
 
#define PTS_OFFSET (63 * Partition()->BlockSize())
 
 
using std::nothrow;
 
 
static const uint32 kDiskSystemFlags =
	0
//	| B_DISK_SYSTEM_SUPPORTS_CHECKING
//	| B_DISK_SYSTEM_SUPPORTS_REPAIRING
//	| B_DISK_SYSTEM_SUPPORTS_RESIZING
//	| B_DISK_SYSTEM_SUPPORTS_MOVING
//	| B_DISK_SYSTEM_SUPPORTS_SETTING_CONTENT_NAME
//	| B_DISK_SYSTEM_SUPPORTS_SETTING_CONTENT_PARAMETERS
//	| B_DISK_SYSTEM_SUPPORTS_INITIALIZING
//	| B_DISK_SYSTEM_SUPPORTS_CONTENT_NAME
 
//	| B_DISK_SYSTEM_SUPPORTS_RESIZING_CHILD
//	| B_DISK_SYSTEM_SUPPORTS_MOVING_CHILD
//	| B_DISK_SYSTEM_SUPPORTS_SETTING_NAME
	| B_DISK_SYSTEM_SUPPORTS_SETTING_TYPE
//	| B_DISK_SYSTEM_SUPPORTS_SETTING_PARAMETERS
	| B_DISK_SYSTEM_SUPPORTS_CREATING_CHILD
	| B_DISK_SYSTEM_SUPPORTS_DELETING_CHILD
//	| B_DISK_SYSTEM_SUPPORTS_NAME
;
 
 
// #pragma mark - ExtendedPartitionAddOn
 
 
ExtendedPartitionAddOn::ExtendedPartitionAddOn()
	:
	BDiskSystemAddOn(kPartitionTypeIntelExtended, kDiskSystemFlags)
{
}
 
 
ExtendedPartitionAddOn::~ExtendedPartitionAddOn()
{
}
 
 
status_t
ExtendedPartitionAddOn::CreatePartitionHandle(BMutablePartition* partition,
	BPartitionHandle** _handle)
{
	ExtendedPartitionHandle* handle
		= new(nothrow) ExtendedPartitionHandle(partition);
	if (!handle)
		return B_NO_MEMORY;
 
	status_t error = handle->Init();
	if (error != B_OK) {
		delete handle;
		return error;
	}
 
	*_handle = handle;
	return B_OK;
}
 
 
bool
ExtendedPartitionAddOn::CanInitialize(const BMutablePartition* partition)
{
	// If it's big enough, we can initialize it.
	return false;
}
 
 
status_t
ExtendedPartitionAddOn::ValidateInitialize(const BMutablePartition* partition,
	BString* name, const char* parameters)
{
	if (!CanInitialize(partition)
		|| (parameters != NULL && parameters[0] != '\0')) {
		return B_BAD_VALUE;
	}
 
	// we don't support a content name
	if (name != NULL)
		name->Truncate(0);
 
	return B_OK;
}
 
 
status_t
ExtendedPartitionAddOn::Initialize(BMutablePartition* partition,
	const char* name, const char* parameters, BPartitionHandle** _handle)
{
	if (!CanInitialize(partition)
		|| (name != NULL && name[0] != '\0')
		|| (parameters != NULL && parameters[0] != '\0')) {
		return B_BAD_VALUE;
	}
 
	// create the handle
	ExtendedPartitionHandle* handle
		= new(nothrow) ExtendedPartitionHandle(partition);
	if (!handle)
		return B_NO_MEMORY;
	ObjectDeleter<ExtendedPartitionHandle> handleDeleter(handle);
 
	// init the partition
	status_t error = partition->SetContentType(Name());
	if (error != B_OK)
		return error;
	// TODO: The content type could as well be set by the caller.
 
	partition->SetContentName(NULL);
	partition->SetContentParameters(NULL);
	partition->SetContentSize(
		sector_align(partition->Size(), partition->BlockSize()));
 
	*_handle = handleDeleter.Detach();
 
	return B_OK;
}
 
 
// #pragma mark - ExtendedPartitionHandle
 
 
ExtendedPartitionHandle::ExtendedPartitionHandle(BMutablePartition* partition)
	:
	BPartitionHandle(partition)
{
}
 
 
ExtendedPartitionHandle::~ExtendedPartitionHandle()
{
}
 
 
status_t
ExtendedPartitionHandle::Init()
{
	// initialize the extended partition from the mutable partition
 
	BMutablePartition* partition = Partition();
 
	// our parent has already set the child cookie to the primary partition.
	fPrimaryPartition = (PrimaryPartition*)partition->ChildCookie();
	if (!fPrimaryPartition)
		return B_BAD_VALUE;
 
	if (!fPrimaryPartition->IsExtended())
		return B_BAD_VALUE;
 
	// init the child partitions
	int32 count = partition->CountChildren();
	for (int32 i = 0; i < count; i++) {
		BMutablePartition* child = partition->ChildAt(i);
 
		PartitionType type;
		if (!type.SetType(child->Type()))
			return B_BAD_VALUE;
 
		void* handle = parse_driver_settings_string(child->Parameters());
		if (handle == NULL)
			return B_ERROR;
 
		bool active = get_driver_boolean_parameter(
			handle, "active", false, true);
 
		off_t ptsOffset = 0;
		const char* buffer = get_driver_parameter(handle,
			"partition_table_offset", NULL, NULL);
		if (buffer != NULL)
			ptsOffset = strtoull(buffer, NULL, 10);
		else {
			delete_driver_settings(handle);
			return B_BAD_VALUE;
		}
		delete_driver_settings(handle);
 
		LogicalPartition* logical = new(nothrow) LogicalPartition;
		if (!logical)
			return B_NO_MEMORY;
 
		logical->SetTo(child->Offset(), child->Size(), type.Type(), active,
			ptsOffset, fPrimaryPartition);
 
		child->SetChildCookie(logical);
	}
 
	return B_OK;
}
 
 
uint32
ExtendedPartitionHandle::SupportedOperations(uint32 mask)
{
	uint32 flags = 0;
 
	// creating child
	if ((mask & B_DISK_SYSTEM_SUPPORTS_CREATING_CHILD) != 0) {
		BPartitioningInfo info;
		if (GetPartitioningInfo(&info) == B_OK
			&& info.CountPartitionableSpaces() > 1) {
			flags |= B_DISK_SYSTEM_SUPPORTS_CREATING_CHILD;
		}
	}
 
	return flags;
}
 
 
uint32
ExtendedPartitionHandle::SupportedChildOperations(
	const BMutablePartition* child, uint32 mask)
{
	return B_DISK_SYSTEM_SUPPORTS_DELETING_CHILD;
}
 
 
status_t
ExtendedPartitionHandle::GetNextSupportedType(const BMutablePartition* child,
	int32* cookie, BString* type)
{
	int32 index = *cookie;
	const partition_type* nextType;
	PartitionMap partitionMap;
	while (true) {
		nextType = partitionMap.GetNextSupportedPartitionType(index);
		if (nextType == NULL)
			return B_ENTRY_NOT_FOUND;
		index++;
		if (nextType->used
			&& strcmp(nextType->name, kPartitionTypeIntelExtended) != 0)
			break;
	}
 
	if (!nextType)
		return B_ENTRY_NOT_FOUND;
 
	type->SetTo(nextType->name);
	*cookie = index;
 
	return B_OK;
}
 
 
status_t
ExtendedPartitionHandle::GetPartitioningInfo(BPartitioningInfo* info)
{
	// init to the full size (minus the first PTS_OFFSET)
	BMutablePartition* partition = Partition();
	off_t offset = partition->Offset() + PTS_OFFSET;
	off_t size = partition->Size() - PTS_OFFSET;
	status_t error = info->SetTo(offset, size);
	if (error != B_OK)
		return error;
 
	// exclude the space of the existing logical partitions
	int32 count = partition->CountChildren();
	for (int32 i = 0; i < count; i++) {
		BMutablePartition* child = partition->ChildAt(i);
		error = info->ExcludeOccupiedSpace(child->Offset(),
			child->Size() + PTS_OFFSET + Partition()->BlockSize());
		if (error != B_OK)
			return error;
 
		LogicalPartition* logical = (LogicalPartition*)child->ChildCookie();
		if (logical == NULL)
			return B_BAD_VALUE;
		error = info->ExcludeOccupiedSpace(
			logical->PartitionTableOffset(),
				PTS_OFFSET + Partition()->BlockSize());
		if (error != B_OK)
			return error;
	}
 
	return B_OK;
}
 
 
status_t
ExtendedPartitionHandle::GetParameterEditor(B_PARAMETER_EDITOR_TYPE type,
	BPartitionParameterEditor** editor)
{
	*editor = NULL;
	return B_NOT_SUPPORTED;
}
 
 
status_t
ExtendedPartitionHandle::ValidateCreateChild(off_t* _offset, off_t* _size,
	const char* typeString, BString* name, const char* parameters)
{
	// check type
	if (!typeString)
		return B_BAD_VALUE;
 
	// check name
	if (name)
		name->Truncate(0);
 
	// check the free space situation
	BPartitioningInfo info;
	status_t error = GetPartitioningInfo(&info);
	if (error != B_OK)
		return error;
 
	// any space in the partition at all?
	int32 spacesCount = info.CountPartitionableSpaces();
	if (spacesCount == 0)
		return B_BAD_VALUE;
 
	// check offset and size
	off_t offset = sector_align(*_offset, Partition()->BlockSize());
	off_t size = sector_align(*_size, Partition()->BlockSize());
		// TODO: Rather round size up?
	off_t end = offset + size;
 
	// get the first partitionable space the requested interval intersects with
	int32 spaceIndex = -1;
	int32 closestSpaceIndex = -1;
	off_t closestSpaceDistance = 0;
	for (int32 i = 0; i < spacesCount; i++) {
		off_t spaceOffset, spaceSize;
		info.GetPartitionableSpaceAt(i, &spaceOffset, &spaceSize);
		off_t spaceEnd = spaceOffset + spaceSize;
 
		if ((spaceOffset >= offset && spaceOffset < end)
			|| (offset >= spaceOffset && offset < spaceEnd)) {
			spaceIndex = i;
			break;
		}
 
		off_t distance;
		if (offset < spaceOffset)
			distance = spaceOffset - end;
		else
			distance = spaceEnd - offset;
 
		if (closestSpaceIndex == -1 || distance < closestSpaceDistance) {
			closestSpaceIndex = i;
			closestSpaceDistance = distance;
		}
	}
 
	// get the space we found
	off_t spaceOffset, spaceSize;
	info.GetPartitionableSpaceAt(
		spaceIndex >= 0 ? spaceIndex : closestSpaceIndex, &spaceOffset,
		&spaceSize);
	off_t spaceEnd = spaceOffset + spaceSize;
 
	// If the requested intervald doesn't intersect with any space yet, move
	// it, so that it does.
	if (spaceIndex < 0) {
		spaceIndex = closestSpaceIndex;
		if (offset < spaceOffset) {
			offset = spaceOffset;
			end = offset + size;
		} else {
			end = spaceEnd;
			offset = end - size;
		}
	}
 
	// move/shrink the interval, so that it fully lies within the space
	if (offset < spaceOffset) {
		offset = spaceOffset;
		end = offset + size;
		if (end > spaceEnd) {
			end = spaceEnd;
			size = end - offset;
		}
	} else if (end > spaceEnd) {
		end = spaceEnd;
		offset = end - size;
		if (offset < spaceOffset) {
			offset = spaceOffset;
			size = end - offset;
		}
	}
 
	*_offset = offset;
	*_size = size;
 
	return B_OK;
}
 
 
status_t
ExtendedPartitionHandle::CreateChild(off_t offset, off_t size,
	const char* typeString, const char* name, const char* _parameters,
	BMutablePartition** _child)
{
	// check type
	PartitionType type;
	if (!type.SetType(typeString) || type.IsEmpty())
		return B_BAD_VALUE;
 
	// check name
	if (name != NULL && name[0] != '\0')
		return B_BAD_VALUE;
 
	// offset properly aligned?
	if (offset != sector_align(offset, Partition()->BlockSize())
		|| size != sector_align(size, Partition()->BlockSize()))
		return B_BAD_VALUE;
 
	// check the free space situation
	BPartitioningInfo info;
	status_t error = GetPartitioningInfo(&info);
	if (error != B_OK)
		return error;
 
	bool foundSpace = false;
	off_t end = offset + size;
	int32 spacesCount = info.CountPartitionableSpaces();
	for (int32 i = 0; i < spacesCount; i++) {
		off_t spaceOffset, spaceSize;
		info.GetPartitionableSpaceAt(i, &spaceOffset, &spaceSize);
		off_t spaceEnd = spaceOffset + spaceSize;
 
		if (offset >= spaceOffset && end <= spaceEnd) {
			foundSpace = true;
			break;
		}
	}
 
	if (!foundSpace)
		return B_BAD_VALUE;
 
	BString parameters(_parameters);
	parameters << "partition_table_offset " << offset - PTS_OFFSET << " ;\n";
	// everything looks good, create the child
	BMutablePartition* child;
	error = Partition()->CreateChild(-1, typeString,
		NULL, parameters.String(), &child);
	if (error != B_OK)
		return error;
 
	// init the child
	child->SetOffset(offset);
	child->SetSize(size);
	child->SetBlockSize(Partition()->BlockSize());
	//child->SetFlags(0);
	child->SetChildCookie(Partition());
 
	*_child = child;
	return B_OK;
}
 
 
status_t
ExtendedPartitionHandle::DeleteChild(BMutablePartition* child)
{
	BMutablePartition* parent = child->Parent();
	status_t error = parent->DeleteChild(child);
 
	return error;
}

V730 Not all members of a class are initialized inside the constructor. Consider inspecting: fPrimaryPartition.