git-svn-id: file:///srv/svn/repos/haiku/trunk/current@4552 a95241bf-73f2-0310-859d-f6bbb57e9c96
483 lines
11 KiB
C++
483 lines
11 KiB
C++
//----------------------------------------------------------------------
|
|
// This software is part of the OpenBeOS distribution and is covered
|
|
// by the OpenBeOS license.
|
|
//---------------------------------------------------------------------
|
|
/*!
|
|
\file PartitionMap.cpp
|
|
\brief Definitions for "intel" style partitions and implementation
|
|
of related classes.
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include <util/kernel_cpp.h>
|
|
#ifndef _BOOT_MODE
|
|
# include <DiskDeviceTypes.h>
|
|
#else
|
|
# include <boot/partitions.h>
|
|
#endif
|
|
#include <KernelExport.h>
|
|
|
|
#include "PartitionMap.h"
|
|
|
|
#define TRACE(x) ;
|
|
//#define TRACE(x) dprintf x
|
|
|
|
|
|
// partition_type
|
|
struct partition_type {
|
|
uint8 type;
|
|
const char *name;
|
|
};
|
|
|
|
static const struct partition_type kPartitionTypes[] = {
|
|
// these entries must be sorted by type (currently not)
|
|
// TODO: Standardize naming.
|
|
{ 0x00, "empty" },
|
|
{ 0x01, "FAT 12-bit" },
|
|
{ 0x02, "Xenix root" },
|
|
{ 0x03, "Xenix user" },
|
|
{ 0x04, "FAT 16-bit (dos 3.0)" },
|
|
{ 0x05, /*"Extended Partition"*/kPartitionTypeIntelExtended },
|
|
{ 0x06, "FAT 16-bit (dos 3.31)" },
|
|
{ 0x07, "OS/2 IFS, Windows NT, Advanced Unix" },
|
|
{ 0x0b, "FAT 32-bit" },
|
|
{ 0x0c, "FAT 32-bit, LBA-mapped" },
|
|
{ 0x0d, "FAT 16-bit, LBA-mapped" },
|
|
{ 0x0f, /*"Extended Partition, LBA-mapped"*/kPartitionTypeIntelExtended },
|
|
{ 0x42, "Windows 2000 marker (switches to a proprietary partition table)" },
|
|
{ 0x4d, "QNX 4" },
|
|
{ 0x4e, "QNX 4 2nd part" },
|
|
{ 0x4f, "QNX 4 3rd part" },
|
|
{ 0x78, "XOSL boot loader" },
|
|
{ 0x82, "Linux swapfile" },
|
|
{ 0x83, "Linux native" },
|
|
{ 0x85, /*"Linux extendend partition"*/kPartitionTypeIntelExtended },
|
|
{ 0xa5, "FreeBSD" },
|
|
{ 0xa6, "OpenBSD" },
|
|
{ 0xa7, "NextSTEP" },
|
|
{ 0xa8, "MacOS X" },
|
|
{ 0xa9, "NetBSD" },
|
|
{ 0xab, "MacOS X boot" },
|
|
{ 0xbe, "Solaris 8 boot" },
|
|
{ 0xeb, /*"BeOS"*/ kPartitionTypeBFS },
|
|
{ 0, NULL }
|
|
};
|
|
|
|
// partition_type_string
|
|
static
|
|
const char *
|
|
partition_type_string(uint8 type)
|
|
{
|
|
int32 i;
|
|
for (i = 0; kPartitionTypes[i].name ; i++)
|
|
{
|
|
if (type == kPartitionTypes[i].type)
|
|
return kPartitionTypes[i].name;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
// get_partition_type_string
|
|
void
|
|
get_partition_type_string(uint8 type, char *buffer)
|
|
{
|
|
if (buffer) {
|
|
if (const char *str = partition_type_string(type))
|
|
strcpy(buffer, str);
|
|
else
|
|
sprintf(buffer, "Unrecognized Type 0x%x", type);
|
|
}
|
|
}
|
|
|
|
|
|
// Partition
|
|
|
|
// constructor
|
|
Partition::Partition()
|
|
: fPTSOffset(0),
|
|
fOffset(0),
|
|
fSize(0),
|
|
fType(0),
|
|
fActive(false)
|
|
{
|
|
}
|
|
|
|
// constructor
|
|
Partition::Partition(const partition_descriptor *descriptor,off_t ptsOffset,
|
|
off_t baseOffset, int32 blockSize)
|
|
: fPTSOffset(0),
|
|
fOffset(0),
|
|
fSize(0),
|
|
fType(0),
|
|
fActive(false)
|
|
{
|
|
SetTo(descriptor, ptsOffset, baseOffset, blockSize);
|
|
}
|
|
|
|
// SetTo
|
|
void
|
|
Partition::SetTo(const partition_descriptor *descriptor, off_t ptsOffset,
|
|
off_t baseOffset, int32 blockSize)
|
|
{
|
|
TRACE(("Partition::SetTo(): active: %x\n", descriptor->active));
|
|
fPTSOffset = ptsOffset;
|
|
fOffset = baseOffset + (off_t)descriptor->start * blockSize;
|
|
fSize = (off_t)descriptor->size * blockSize;
|
|
fType = descriptor->type;
|
|
fActive = descriptor->active;
|
|
if (fSize == 0)
|
|
Unset();
|
|
}
|
|
|
|
// Unset
|
|
void
|
|
Partition::Unset()
|
|
{
|
|
fPTSOffset = 0;
|
|
fOffset = 0;
|
|
fSize = 0;
|
|
fType = 0;
|
|
fActive = false;
|
|
}
|
|
|
|
// CheckLocation
|
|
bool
|
|
Partition::CheckLocation(off_t sessionSize, int32 blockSize) const
|
|
{
|
|
// offsets and size must be block aligned, PTS and partition must lie
|
|
// within the session
|
|
return (fPTSOffset % blockSize == 0
|
|
&& fOffset % blockSize == 0
|
|
&& fSize % blockSize == 0
|
|
&& fPTSOffset >= 0 && fPTSOffset < sessionSize
|
|
&& fOffset >= 0 && fOffset + fSize <= sessionSize);
|
|
}
|
|
|
|
|
|
// PrimaryPartition
|
|
|
|
// constructor
|
|
PrimaryPartition::PrimaryPartition()
|
|
: Partition(),
|
|
fHead(NULL),
|
|
fTail(NULL),
|
|
fLogicalPartitionCount(0)
|
|
{
|
|
}
|
|
|
|
// constructor
|
|
PrimaryPartition::PrimaryPartition(const partition_descriptor *descriptor,
|
|
off_t ptsOffset, int32 blockSize)
|
|
: Partition(),
|
|
fHead(NULL),
|
|
fTail(NULL),
|
|
fLogicalPartitionCount(0)
|
|
{
|
|
SetTo(descriptor, ptsOffset, blockSize);
|
|
}
|
|
|
|
// SetTo
|
|
void
|
|
PrimaryPartition::SetTo(const partition_descriptor *descriptor,
|
|
off_t ptsOffset, int32 blockSize)
|
|
{
|
|
Unset();
|
|
Partition::SetTo(descriptor, ptsOffset, 0, blockSize);
|
|
}
|
|
|
|
// Unset
|
|
void
|
|
PrimaryPartition::Unset()
|
|
{
|
|
while (LogicalPartition *partition = fHead) {
|
|
fHead = partition->Next();
|
|
delete partition;
|
|
}
|
|
fHead = NULL;
|
|
fTail = NULL;
|
|
fLogicalPartitionCount = 0;
|
|
Partition::Unset();
|
|
}
|
|
|
|
// LogicalPartitionAt
|
|
LogicalPartition *
|
|
PrimaryPartition::LogicalPartitionAt(int32 index) const
|
|
{
|
|
LogicalPartition *partition = NULL;
|
|
if (index >= 0 && index < fLogicalPartitionCount) {
|
|
for (partition = fHead; index > 0; index--)
|
|
partition = partition->Next();
|
|
}
|
|
return partition;
|
|
}
|
|
|
|
// AddLogicalPartition
|
|
void
|
|
PrimaryPartition::AddLogicalPartition(LogicalPartition *partition)
|
|
{
|
|
if (partition) {
|
|
partition->SetPrimaryPartition(this);
|
|
if (fTail) {
|
|
fTail->SetNext(partition);
|
|
fTail = partition;
|
|
} else
|
|
fHead = fTail = partition;
|
|
partition->SetNext(NULL);
|
|
fLogicalPartitionCount++;
|
|
}
|
|
}
|
|
|
|
|
|
// LogicalPartition
|
|
|
|
// constructor
|
|
LogicalPartition::LogicalPartition()
|
|
: Partition(),
|
|
fPrimary(NULL),
|
|
fNext(NULL)
|
|
{
|
|
}
|
|
|
|
// constructor
|
|
LogicalPartition::LogicalPartition(const partition_descriptor *descriptor,
|
|
off_t ptsOffset, int32 blockSize,
|
|
PrimaryPartition *primary)
|
|
: Partition(),
|
|
fPrimary(NULL),
|
|
fNext(NULL)
|
|
{
|
|
SetTo(descriptor, ptsOffset, blockSize, primary);
|
|
}
|
|
|
|
// SetTo
|
|
void
|
|
LogicalPartition::SetTo(const partition_descriptor *descriptor,
|
|
off_t ptsOffset, int32 blockSize,
|
|
PrimaryPartition *primary)
|
|
{
|
|
Unset();
|
|
if (descriptor && primary) {
|
|
off_t baseOffset = (descriptor->is_extended() ? primary->Offset()
|
|
: ptsOffset);
|
|
Partition::SetTo(descriptor, ptsOffset, baseOffset, blockSize);
|
|
fPrimary = primary;
|
|
}
|
|
}
|
|
|
|
// Unset
|
|
void
|
|
LogicalPartition::Unset()
|
|
{
|
|
fPrimary = NULL;
|
|
fNext = NULL;
|
|
Partition::Unset();
|
|
}
|
|
|
|
|
|
// PartitionMap
|
|
|
|
// constructor
|
|
PartitionMap::PartitionMap()
|
|
{
|
|
}
|
|
|
|
// destructor
|
|
PartitionMap::~PartitionMap()
|
|
{
|
|
}
|
|
|
|
// Unset
|
|
void
|
|
PartitionMap::Unset()
|
|
{
|
|
for (int32 i = 0; i < 4; i++)
|
|
fPrimaries[i].Unset();
|
|
}
|
|
|
|
// PrimaryPartitionAt
|
|
PrimaryPartition *
|
|
PartitionMap::PrimaryPartitionAt(int32 index)
|
|
{
|
|
PrimaryPartition *partition = NULL;
|
|
if (index >= 0 && index < 4)
|
|
partition = fPrimaries + index;
|
|
return partition;
|
|
}
|
|
|
|
// PrimaryPartitionAt
|
|
const PrimaryPartition *
|
|
PartitionMap::PrimaryPartitionAt(int32 index) const
|
|
{
|
|
const PrimaryPartition *partition = NULL;
|
|
if (index >= 0 && index < 4)
|
|
partition = fPrimaries + index;
|
|
return partition;
|
|
}
|
|
|
|
// CountPartitions
|
|
int32
|
|
PartitionMap::CountPartitions() const
|
|
{
|
|
int32 count = 4;
|
|
for (int32 i = 0; i < 4; i++)
|
|
count += fPrimaries[i].CountLogicalPartitions();
|
|
return count;
|
|
}
|
|
|
|
// PartitionAt
|
|
Partition *
|
|
PartitionMap::PartitionAt(int32 index)
|
|
{
|
|
Partition *partition = NULL;
|
|
int32 count = CountPartitions();
|
|
if (index >= 0 && index < count) {
|
|
if (index < 4)
|
|
partition = fPrimaries + index;
|
|
else {
|
|
index -= 4;
|
|
int32 primary = 0;
|
|
while (index >= fPrimaries[primary].CountLogicalPartitions()) {
|
|
index -= fPrimaries[primary].CountLogicalPartitions();
|
|
primary++;
|
|
}
|
|
partition = fPrimaries[primary].LogicalPartitionAt(index);
|
|
}
|
|
}
|
|
return partition;
|
|
}
|
|
|
|
// PartitionAt
|
|
const Partition *
|
|
PartitionMap::PartitionAt(int32 index) const
|
|
{
|
|
return const_cast<PartitionMap*>(this)->PartitionAt(index);
|
|
}
|
|
|
|
// cmp_partition_offset
|
|
static
|
|
int
|
|
cmp_partition_offset(const void *p1, const void *p2)
|
|
{
|
|
const Partition *partition1 = *(const Partition**)p1;
|
|
const Partition *partition2 = *(const Partition**)p2;
|
|
if (partition1->Offset() < partition2->Offset())
|
|
return -1;
|
|
else if (partition1->Offset() > partition2->Offset())
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
// cmp_offset
|
|
static
|
|
int
|
|
cmp_offset(const void *o1, const void *o2)
|
|
{
|
|
off_t offset1 = *static_cast<const off_t*>(o1);
|
|
off_t offset2 = *static_cast<const off_t*>(o2);
|
|
if (offset1 < offset2)
|
|
return -1;
|
|
else if (offset1 > offset2)
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
// is_inside_partitions
|
|
static
|
|
bool
|
|
is_inside_partitions(off_t location, const Partition **partitions, int32 count)
|
|
{
|
|
bool result = false;
|
|
if (count > 0) {
|
|
// binary search
|
|
int32 lower = 0;
|
|
int32 upper = count - 1;
|
|
while (lower < upper) {
|
|
int32 mid = (lower + upper) / 2;
|
|
const Partition *midPartition = partitions[mid];
|
|
if (location >= midPartition->Offset() + midPartition->Size())
|
|
lower = mid + 1;
|
|
else
|
|
upper = mid;
|
|
}
|
|
const Partition *partition = partitions[lower];
|
|
result = (location >= partition->Offset() &&
|
|
location < partition->Offset() + partition->Size());
|
|
}
|
|
return result;
|
|
}
|
|
|
|
// Check
|
|
bool
|
|
PartitionMap::Check(off_t sessionSize, int32 blockSize) const
|
|
{
|
|
int32 partitionCount = CountPartitions();
|
|
// 1. check partition locations
|
|
for (int32 i = 0; i < partitionCount; i++) {
|
|
if (!PartitionAt(i)->CheckLocation(sessionSize, blockSize))
|
|
return false;
|
|
}
|
|
// 2. check overlapping of partitions and location of PTSs
|
|
bool result = true;
|
|
const Partition **byOffset = new(nothrow) const Partition*[partitionCount];
|
|
off_t *ptsOffsets = new(nothrow) off_t[partitionCount - 3];
|
|
if (byOffset && ptsOffsets) {
|
|
// fill the arrays
|
|
int32 byOffsetCount = 0;
|
|
int32 ptsOffsetCount = 1; // primary PTS
|
|
ptsOffsets[0] = 0; //
|
|
for (int32 i = 0; i < partitionCount; i++) {
|
|
const Partition *partition = PartitionAt(i);
|
|
if (!partition->IsExtended())
|
|
byOffset[byOffsetCount++] = partition;
|
|
// add only logical partition PTS locations
|
|
if (i >= 4)
|
|
ptsOffsets[ptsOffsetCount++] = partition->PTSOffset();
|
|
}
|
|
// sort the arrays
|
|
qsort(byOffset, byOffsetCount, sizeof(const Partition*),
|
|
cmp_partition_offset);
|
|
qsort(ptsOffsets, ptsOffsetCount, sizeof(off_t), cmp_offset);
|
|
// check for overlappings
|
|
off_t nextOffset = 0;
|
|
for (int32 i = 0; i < byOffsetCount; i++) {
|
|
const Partition *partition = byOffset[i];
|
|
if (partition->Offset() < nextOffset) {
|
|
TRACE(("intel: PartitionMap::Check(): overlapping partitions!"
|
|
"\n"));
|
|
result = false;
|
|
break;
|
|
}
|
|
nextOffset = partition->Offset() + partition->Size();
|
|
}
|
|
// check uniqueness of PTS offsets and whether they lie outside of the
|
|
// non-extended partitions
|
|
if (result) {
|
|
for (int32 i = 0; i < ptsOffsetCount; i++) {
|
|
if (i > 0 && ptsOffsets[i] == ptsOffsets[i - 1]) {
|
|
TRACE(("intel: PartitionMap::Check(): same PTS for "
|
|
"different extended partitions!\n"));
|
|
result = false;
|
|
break;
|
|
} else if (is_inside_partitions(ptsOffsets[i], byOffset,
|
|
byOffsetCount)) {
|
|
TRACE(("intel: PartitionMap::Check(): a PTS lies "
|
|
"inside a non-extended partition!\n"));
|
|
result = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
} else
|
|
result = false; // no memory: assume failure
|
|
// cleanup
|
|
if (byOffset)
|
|
delete[] byOffset;
|
|
if (ptsOffsets)
|
|
delete[] ptsOffsets;
|
|
return result;
|
|
}
|
|
|