* Copyright 2003-2009, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Ingo Weinhold, bonefish@cs.tu-berlin.de
*/
#ifndef _USER_MODE
# include <KernelExport.h>
#endif
#include <errno.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <new>
#include "PartitionMap.h"
#include "PartitionMapParser.h"
#ifdef TRACE_ENABLED
# ifdef _USER_MODE
# define TRACE(x...) printf("intel: " x)
# else
# define TRACE(x...) dprintf("intel: " x)
# endif
#else
# define TRACE(x...) ;
#endif
#ifdef _USER_MODE
# define ERROR(x...) printf("intel: " x)
#else
# define ERROR(x...) dprintf("intel: " x)
#endif
using std::nothrow;
static const int32 kMaxLogicalPartitionCount = 128;
static const int32 kGPTSignatureSize = 8;
static const char kGPTSignature[8] = { 'E', 'F', 'I', ' ', 'P', 'A', 'R', 'T' };
PartitionMapParser::PartitionMapParser(int deviceFD, off_t sessionOffset,
off_t sessionSize, uint32 blockSize)
:
fDeviceFD(deviceFD),
fBlockSize(blockSize),
fSessionOffset(sessionOffset),
fSessionSize(sessionSize),
fPartitionTable(NULL),
fMap(NULL)
{
}
PartitionMapParser::~PartitionMapParser()
{
}
status_t
PartitionMapParser::Parse(const uint8* block, PartitionMap* map)
{
if (map == NULL)
return B_BAD_VALUE;
status_t error;
bool hadToReFitSize = false;
fMap = map;
fMap->Unset();
if (block) {
const partition_table* table = (const partition_table*)block;
error = _ParsePrimary(table, hadToReFitSize);
} else {
partition_table table;
error = _ReadPartitionTable(0, &table);
if (error == B_OK) {
error = _ParsePrimary(&table, hadToReFitSize);
if (fBlockSize != 512 && (hadToReFitSize
|| !fMap->Check(fSessionSize))) {
int32 previousPartitionCount = fMap->CountNonEmptyPartitions();
uint32 previousBlockSize = fBlockSize;
TRACE("Parse(): trying with a fixed 512 block size\n");
fBlockSize = 512;
fMap->Unset();
error = _ParsePrimary(&table, hadToReFitSize);
if (fMap->CountNonEmptyPartitions() < previousPartitionCount
|| error != B_OK || hadToReFitSize
|| !fMap->Check(fSessionSize)) {
TRACE("Parse(): try failed, reverting\n");
fBlockSize = previousBlockSize;
fMap->Unset();
error = _ParsePrimary(&table, hadToReFitSize);
}
}
}
}
if (error == B_OK && !fMap->Check(fSessionSize))
error = B_BAD_DATA;
fMap = NULL;
return error;
}
status_t
PartitionMapParser::_ParsePrimary(const partition_table* table,
bool& hadToReFitSize)
{
if (table == NULL)
return B_BAD_VALUE;
if (table->signature != kPartitionTableSectorSignature) {
TRACE("invalid PartitionTable signature: %" B_PRIx32 "\n",
(uint32)table->signature);
return B_BAD_DATA;
}
hadToReFitSize = false;
for (int32 i = 0; i < 4; i++) {
const partition_descriptor* descriptor = &table->table[i];
PrimaryPartition* partition = fMap->PrimaryPartitionAt(i);
partition->SetTo(descriptor, 0, fBlockSize);
hadToReFitSize |= partition->FitSizeToSession(fSessionSize);
if (!partition->CheckLocation(fSessionSize)) {
TRACE("partition %" B_PRId32 ": bad location, ignoring\n", i);
partition->Unset();
}
}
fPartitionTable = new(nothrow) partition_table;
if (fPartitionTable == NULL)
return B_NO_MEMORY;
status_t error = B_OK;
for (int32 i = 0; error == B_OK && i < 4; i++) {
PrimaryPartition* primary = fMap->PrimaryPartitionAt(i);
if (primary->IsExtended())
error = _ParseExtended(primary, primary->Offset());
}
delete fPartitionTable;
fPartitionTable = NULL;
return error;
}
status_t
PartitionMapParser::_ParseExtended(PrimaryPartition* primary, off_t offset)
{
status_t error = B_OK;
int32 partitionCount = 0;
while (error == B_OK) {
if (++partitionCount > kMaxLogicalPartitionCount) {
ERROR("ParseExtended: Maximal number of logical "
"partitions for extended partition reached. Cycle?\n");
error = B_BAD_DATA;
}
if (error == B_OK)
error = _ReadPartitionTable(offset);
if (error == B_OK
&& fPartitionTable->signature != kPartitionTableSectorSignature) {
ERROR("ParseExtended: invalid partition table signature: %" B_PRIx32 "\n",
(uint32)fPartitionTable->signature);
error = B_BAD_DATA;
}
if (error != B_OK) {
TRACE("ParseExtended: ignoring this partition table\n");
error = B_OK;
break;
}
LogicalPartition extended;
LogicalPartition nonExtended;
for (int32 i = 0; error == B_OK && i < 4; i++) {
const partition_descriptor* descriptor = &fPartitionTable->table[i];
if (descriptor->is_empty())
continue;
LogicalPartition* partition = NULL;
if (descriptor->is_extended()) {
if (extended.IsEmpty()) {
extended.SetTo(descriptor, offset, primary);
partition = &extended;
} else {
error = B_BAD_DATA;
ERROR("ParseExtended: only one extended partition allowed\n");
}
} else {
if (nonExtended.IsEmpty()) {
nonExtended.SetTo(descriptor, offset, primary);
partition = &nonExtended;
} else {
error = B_BAD_DATA;
ERROR("ParseExtended: only one non-extended partition allowed\n");
}
}
if (partition == NULL)
break;
partition->FitSizeToSession(fSessionSize);
if (!partition->CheckLocation(fSessionSize)) {
error = B_BAD_DATA;
ERROR("ParseExtended: invalid partition location: "
"pts: %" B_PRIdOFF ", offset: %" B_PRIdOFF ", size: %" B_PRIdOFF "\n",
partition->PartitionTableOffset(), partition->Offset(),
partition->Size());
}
}
if (error == B_OK && !nonExtended.IsEmpty()) {
LogicalPartition* partition
= new(nothrow) LogicalPartition(nonExtended);
if (partition)
primary->AddLogicalPartition(partition);
else
error = B_NO_MEMORY;
}
if (error == B_OK && !extended.IsEmpty())
offset = extended.Offset();
else
break;
}
return error;
}
status_t
PartitionMapParser::_ReadPartitionTable(off_t offset, partition_table* table)
{
int32 toRead = sizeof(partition_table);
if (offset < 0 || offset + toRead > fSessionSize) {
ERROR("ReadPartitionTable: bad offset: %" B_PRIdOFF "\n", offset);
return B_BAD_VALUE;
}
if (table == NULL)
table = fPartitionTable;
ssize_t bytesRead = read_pos(fDeviceFD, fSessionOffset + offset,
table, toRead);
if (bytesRead != (ssize_t)toRead) {
ERROR("reading the partition table failed: %" B_PRIx32 "\n", errno);
return bytesRead < 0 ? errno : B_IO_ERROR;
}
toRead = kGPTSignatureSize;
char gptSignature[8];
bytesRead = read_pos(fDeviceFD, fSessionOffset + offset
+ sizeof(partition_table), &gptSignature, toRead);
if (bytesRead != (ssize_t)toRead) {
ERROR("checking for GPT signature failed: %" B_PRIx32 "\n", errno);
return bytesRead < 0 ? errno : B_IO_ERROR;
}
if (memcmp(gptSignature, kGPTSignature, kGPTSignatureSize) == 0) {
ERROR("Found GPT signature, ignoring.\n");
return B_BAD_DATA;
}
return B_OK;
}