/*
 * Copyright 2003-2009, Haiku, Inc. All Rights Reserved.
 * Distributed under the terms of the MIT License.
 *
 * Authors:
 *		Bryce Groff, brycegroff@gmail.com
 */
 
#include "PartitionMapWriter.h"
 
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
 
#include <new>
 
#ifndef _USER_MODE
#include <debug.h>
#endif
 
#ifndef _USER_MODE
#	include <KernelExport.h>
#endif
 
#include "PartitionMap.h"
 
using std::nothrow;
 
 
#define TRACE_ENABLED
#ifdef TRACE_ENABLED
#	ifdef _USER_MODE
#		define TRACE(x) printf x
#	else
#		define TRACE(x) dprintf x
#	endif
#endif
 
 
#if defined(__i386__) || defined(__x86_64__)
#	ifndef _USER_MODE
#		define MBR_HEADER "MBR.h"
#		include MBR_HEADER
#	endif
#endif
 
 
bool
check_logical_location(const LogicalPartition* child,
	const PrimaryPartition* parent)
{
	if (child->PartitionTableOffset() % child->BlockSize() != 0) {
		TRACE(("check_logical_location() - PartitionTableOffset: %" B_PRId64 " "
			"not a multiple of media's block size: %" B_PRId32 "\n",
			child->PartitionTableOffset(), child->BlockSize()));
		return false;
	}
	if (child->Offset() % child->BlockSize() != 0) {
		TRACE(("check_logical_location() - Parition offset: %" B_PRId64 " "
			"is not a multiple of block size: %" B_PRId32 "\n", child->Offset(),
			child->BlockSize()));
		return false;
	}
	if (child->Size() % child->BlockSize() != 0) {
		TRACE(("check_logical_location() - Size: (%" B_PRId64 ") is not a "
			"multiple of block size: (%" B_PRId32 ")\n", child->Size(),
			child->BlockSize()));
		return false;
	}
	if (child->PartitionTableOffset() < parent->Offset()
		|| child->PartitionTableOffset() >= parent->Offset()
		+ parent->Size()) {
		TRACE(("check_logical_location() - Partition table: (%" B_PRId64 ") not"
			" within extended partition (start: %" B_PRId64 "), (end: "
			"%" B_PRId64 ")\n", child->PartitionTableOffset(), parent->Offset(),
			parent->Offset() + parent->Size()));
		return false;
	}
	if (child->Offset() + child->Size() > parent->Offset() + parent->Size()) {
		TRACE(("check_logical_location() - logical paritition does not lie "
			"within extended partition\n"));
		return false;
	}
	return true;
}
 
 
PartitionMapWriter::PartitionMapWriter(int deviceFD, uint32 blockSize)
	:
	fDeviceFD(deviceFD),
	fBlockSize(blockSize)
{
}
 
 
PartitionMapWriter::~PartitionMapWriter()
{
}
 
 
status_t
PartitionMapWriter::WriteMBR(const PartitionMap* map, bool writeBootCode)
{
	if (map == NULL)
		return B_BAD_VALUE;
 
	partition_table partitionTable;
	status_t error = _ReadBlock(0, partitionTable);
	if (error != B_OK)
		return error;
#ifdef MBR_HEADER
	if (writeBootCode) {
		// the boot code must be small enough to fit in the code area
		STATIC_ASSERT(kMBRSize <= sizeof(partitionTable.code_area));
		partitionTable.clear_code_area();
		partitionTable.fill_code_area(kMBR, kMBRSize);
	}
#endif
 
	partitionTable.signature = kPartitionTableSectorSignature;
 
	for (int i = 0; i < 4; i++) {
		partition_descriptor* descriptor = &partitionTable.table[i];
		const PrimaryPartition* partition = map->PrimaryPartitionAt(i);
 
		partition->GetPartitionDescriptor(descriptor);
	}
 
	error = _WriteBlock(0, partitionTable);
	return error;
}
 
 
status_t
PartitionMapWriter::WriteLogical(const LogicalPartition* logical,
	const PrimaryPartition* primary, bool clearCode)
{
	if (logical == NULL || primary == NULL)
		return B_BAD_VALUE;
 
	if (!check_logical_location(logical, primary))
		return B_BAD_DATA;
 
	partition_table partitionTable;
	if (clearCode) {
		partitionTable.clear_code_area();
	} else {
		status_t error = _ReadBlock(logical->PartitionTableOffset(),
			partitionTable);
		if (error != B_OK)
			return error;
	}
 
	partitionTable.signature = kPartitionTableSectorSignature;
 
	partition_descriptor* descriptor = &partitionTable.table[0];
	logical->GetPartitionDescriptor(descriptor);
 
	descriptor = &partitionTable.table[1];
	if (logical->Next() != NULL)
		logical->Next()->GetPartitionDescriptor(descriptor, true);
	else
		memset(descriptor, 0, sizeof(partition_descriptor));
 
	// last two descriptors are empty
	for (int32 i = 2; i < 4; i++) {
		descriptor = &partitionTable.table[i];
		memset(descriptor, 0, sizeof(partition_descriptor));
	}
 
	status_t error = _WriteBlock(logical->PartitionTableOffset(),
		partitionTable);
	return error;
}
 
 
status_t
PartitionMapWriter::WriteExtendedHead(const LogicalPartition* logical,
	const PrimaryPartition* primary, bool clearCode)
{
	if (primary == NULL)
		return B_BAD_VALUE;
 
	partition_table partitionTable;
	if (clearCode) {
		partitionTable.clear_code_area();
	} else {
		status_t error = _ReadBlock(primary->Offset(), partitionTable);
		if (error != B_OK)
			return error;
	}
 
	partitionTable.signature = kPartitionTableSectorSignature;
	partition_descriptor* descriptor;
	if (logical == NULL) {
		for (int32 i = 0; i < 4; i++) {
			descriptor = &partitionTable.table[i];
			memset(descriptor, 0, sizeof(partition_descriptor));
		}
	} else {
		LogicalPartition partition;
		partition.SetPartitionTableOffset(primary->Offset());
		partition.SetBlockSize(logical->BlockSize());
		partition.SetOffset(logical->Offset());
		partition.SetSize(logical->Size());
		partition.SetType(logical->Type());
 
		// set the logicals partition table to the correct location
		descriptor = &partitionTable.table[0];
		partition.GetPartitionDescriptor(descriptor);
 
		descriptor = &partitionTable.table[1];
		LogicalPartition* next = logical->Next();
		if (next != NULL)
			next->GetPartitionDescriptor(descriptor, true);
		else
			memset(descriptor, 0, sizeof(partition_descriptor));
 
		// last two descriptors are empty
		for (int32 i = 2; i < 4; i++) {
			descriptor = &partitionTable.table[i];
			memset(descriptor, 0, sizeof(partition_descriptor));
		}
	}
 
	status_t error = _WriteBlock(primary->Offset(), partitionTable);
	if (error != B_OK)
		return error;
 
	return B_OK;
}
 
 
 
status_t
PartitionMapWriter::ClearExtendedHead(const PrimaryPartition* primary)
{
	if (primary == NULL)
		return B_BAD_VALUE;
 
	partition_table partitionTable;
	partitionTable.clear_code_area();
	partitionTable.signature = kPartitionTableSectorSignature;
 
	partition_descriptor* descriptor;
	for (int32 i = 0; i < 4; i++) {
		descriptor = &partitionTable.table[i];
		memset(descriptor, 0, sizeof(partition_descriptor));
	}
 
	status_t error = _WriteBlock(primary->Offset(), partitionTable);
	if (error != B_OK)
		return error;
 
	return B_OK;
}
 
 
status_t
PartitionMapWriter::_ReadBlock(off_t partitionOffset,
	partition_table& partitionTable)
{
	if (partitionOffset < 0)
		return B_BAD_VALUE;
	// TODO: If fBlockSize > sizeof(partition_table) then stop/read NULL after
	if (read_pos(fDeviceFD, partitionOffset, &partitionTable,
		sizeof(partitionTable)) != sizeof(partitionTable)) {
		status_t error = errno;
		if (error == B_OK)
			error = B_IO_ERROR;
 
		return error;
	}
 
	return B_OK;
}
 
 
status_t
PartitionMapWriter::_WriteBlock(off_t partitionOffset,
	const partition_table& partitionTable)
{
	if (partitionOffset < 0)
		return B_BAD_VALUE;
	// TODO: maybe clear the rest of the block if
	// fBlockSize > sizeof(partition_table)?
	if (write_pos(fDeviceFD, partitionOffset, &partitionTable,
		sizeof(partitionTable)) != sizeof(partitionTable)) {
		status_t error = errno;
		if (error == B_OK)
			error = B_IO_ERROR;
 
		return error;
	}
 
	return B_OK;
}
 

V512 A call of the 'memset' function will lead to underflow of the buffer 'descriptor'.

V512 A call of the 'memset' function will lead to underflow of the buffer 'descriptor'.