Write support for the Intel partitioning system module. Courtesy of

Tomas Kucera.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@21697 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Ingo Weinhold
2007-07-24 22:27:57 +00:00
parent 96cf4bfb88
commit 92b8ea1d44
9 changed files with 3377 additions and 118 deletions
@@ -7,16 +7,20 @@ UsePrivateHeaders storage ;
KernelAddon intel :
intel.cpp
PartitionLocker.cpp
PartitionMap.cpp
PartitionMapParser.cpp
PartitionMapWriter.cpp
;
# Also build a userland version
# ToDo: it's probably not a good idea to build them into the same directory
#Addon <partitioning_system>intel :
# intel.cpp
# PartitionLocker.cpp
# PartitionMap.cpp
# PartitionMapParser.cpp
# PartitionMapWriter.cpp
#;
#LinkAgainst <partitioning_system>intel :
@@ -0,0 +1,73 @@
/*
* Copyright 2003-2007, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Tomas Kucera, [email protected]
*/
#include "PartitionLocker.h"
// #pragma mark - PartitionLocker
// constructor
PartitionLocker::PartitionLocker(partition_id partitionID)
: device_(NULL),
partitionID_(partitionID)
{
}
// destructor
PartitionLocker::~PartitionLocker()
{
}
// IsLocked
bool
PartitionLocker::IsLocked() const
{
return device_;
}
// PartitionId
partition_id
PartitionLocker::PartitionId() const
{
return partitionID_;
}
// #pragma mark - PartitionReadLocker
// constructor
PartitionReadLocker::PartitionReadLocker(partition_id partitionID)
: PartitionLocker(partitionID)
{
device_ = read_lock_disk_device(partitionID);
}
// destructor
PartitionReadLocker::~PartitionReadLocker()
{
if (IsLocked())
read_unlock_disk_device(PartitionId());
}
// #pragma mark - PartitionWriteLocker
// constructor
PartitionWriteLocker::PartitionWriteLocker(partition_id partitionID)
: PartitionLocker(partitionID)
{
device_ = write_lock_disk_device(partitionID);
}
// destructor
PartitionWriteLocker::~PartitionWriteLocker()
{
if (IsLocked())
write_unlock_disk_device(PartitionId());
}
@@ -0,0 +1,58 @@
/*
* Copyright 2003-2007, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Tomas Kucera, [email protected]
*/
/*!
\file PartitionLocker.h
\ingroup intel_module
\brief Structures for easy locking and automatic unlocking partitions.
*/
#ifndef _PARTITION_LOCKER_H
#define _PARTITION_LOCKER_H
#include <disk_device_manager.h>
class PartitionLocker {
public:
PartitionLocker(partition_id partitionID);
virtual ~PartitionLocker();
bool IsLocked() const;
partition_id PartitionId() const;
protected:
const disk_device_data *device_;
private:
partition_id partitionID_;
};
/*!
\brief Structure which locks given partition for reading.
When this structure is going to be destroyed, it automatically unlocks
that partition.
*/
class PartitionReadLocker : public PartitionLocker {
public:
PartitionReadLocker(partition_id partitionID);
virtual ~PartitionReadLocker();
};
/*!
\brief Structure which locks given partition for writing.
When this structure is going to be destroyed, it automatically unlocks
that partition.
*/
class PartitionWriteLocker : public PartitionLocker {
public:
PartitionWriteLocker(partition_id partitionID);
virtual ~PartitionWriteLocker();
};
#endif // _PARTITION_LOCKER_H
@@ -79,11 +79,27 @@ static const struct partition_type kPartitionTypes[] = {
{ 0xa8, "MacOS X" },
{ 0xa9, "NetBSD" },
{ 0xab, "MacOS X boot" },
{ 0xaf, "MacOS X HFS" },
{ 0xbe, "Solaris 8 boot" },
{ 0xeb, /*"BeOS"*/ BFS_NAME },
{ 0, NULL }
};
static const struct partition_type kPartitionContentTypes[] = {
#ifndef _USER_MODE
{ 0x01, kPartitionTypeFAT12 },
{ 0x0c, kPartitionTypeFAT32 },
{ 0x0f, kPartitionTypeIntelExtended },
{ 0x83, kPartitionTypeEXT2 },
{ 0x83, kPartitionTypeEXT3 },
{ 0x83, kPartitionTypeReiser },
{ 0xaf, kPartitionTypeHFS },
{ 0xaf, kPartitionTypeHFSPlus },
{ 0xeb, kPartitionTypeBFS },
#endif
{ 0, NULL }
};
// partition_type_string
static const char *
@@ -111,6 +127,7 @@ get_partition_type_string(uint8 type, char *buffer)
}
static int
cmp_partition_offset(const void *p1, const void *p2)
{
@@ -161,6 +178,67 @@ is_inside_partitions(off_t location, const Partition **partitions, int32 count)
}
// #pragma mark - PartitionType
// constructor
PartitionType::PartitionType()
: type_(0),
valid_(false)
{
}
// SetType
void
PartitionType::SetType(uint8 type)
{
type_ = type;
valid_ = partition_type_string(type);
}
// SetType
void
PartitionType::SetType(const char *type_name)
{
for (int32 i = 0; kPartitionTypes[i].name ; i++) {
if (!strcmp(type_name, kPartitionTypes[i].name)) {
type_ = kPartitionTypes[i].type;
valid_ = true;
return;
}
}
valid_ = false;
}
// SetContentType
void
PartitionType::SetContentType(const char *content_type)
{
for (int32 i = 0; kPartitionContentTypes[i].name ; i++) {
if (!strcmp(content_type, kPartitionContentTypes[i].name)) {
type_ = kPartitionContentTypes[i].type;
valid_ = true;
return;
}
}
valid_ = false;
}
// FindNext
void
PartitionType::FindNext()
{
for (int32 i = 0; kPartitionTypes[i].name; i++) {
if (type_ < kPartitionTypes[i].type) {
type_ = kPartitionTypes[i].type;
valid_ = true;
return;
}
}
valid_ = false;
}
// #pragma mark - Partition
@@ -212,6 +290,19 @@ Partition::Unset()
fActive = false;
}
// GetPartitionDescriptor
void
Partition::GetPartitionDescriptor(partition_descriptor *descriptor,
off_t baseOffset, int32 blockSize) const
{
descriptor->start = (fOffset - baseOffset) / blockSize;
descriptor->size = fSize / blockSize;
descriptor->type = fType;
descriptor->active = fActive ? 0x80 : 0x00;
descriptor->begin.Unset();
descriptor->end.Unset();
}
#ifdef _BOOT_MODE
void
@@ -303,6 +394,7 @@ PrimaryPartition::AddLogicalPartition(LogicalPartition *partition)
{
if (partition) {
partition->SetPrimaryPartition(this);
partition->SetPrevious(fTail);
if (fTail) {
fTail->SetNext(partition);
fTail = partition;
@@ -313,6 +405,30 @@ PrimaryPartition::AddLogicalPartition(LogicalPartition *partition)
}
}
// RemoveLogicalPartition
void
PrimaryPartition::RemoveLogicalPartition(LogicalPartition *partition)
{
if (!partition || partition->GetPrimaryPartition() != this)
return;
LogicalPartition *prev = partition->Previous();
LogicalPartition *next = partition->Next();
if (prev)
prev->SetNext(next);
else
fHead = next;
if (next)
next->SetPrevious(prev);
else
fTail = prev;
fLogicalPartitionCount--;
partition->SetNext(NULL);
partition->SetPrevious(NULL);
partition->SetPrimaryPartition(NULL);
}
// #pragma mark - LogicalPartition
@@ -321,7 +437,8 @@ PrimaryPartition::AddLogicalPartition(LogicalPartition *partition)
LogicalPartition::LogicalPartition()
: Partition(),
fPrimary(NULL),
fNext(NULL)
fNext(NULL),
fPrevious(NULL)
{
}
@@ -331,7 +448,8 @@ LogicalPartition::LogicalPartition(const partition_descriptor *descriptor,
PrimaryPartition *primary)
: Partition(),
fPrimary(NULL),
fNext(NULL)
fNext(NULL),
fPrevious(NULL)
{
SetTo(descriptor, ptsOffset, blockSize, primary);
}
@@ -357,6 +475,7 @@ LogicalPartition::Unset()
{
fPrimary = NULL;
fNext = NULL;
fPrevious = NULL;
Partition::Unset();
}
@@ -8,6 +8,7 @@
/*!
\file PartitionMap.h
\ingroup intel_module
\brief Definitions for "intel" style partitions and interface definitions
for related classes.
*/
@@ -45,12 +46,22 @@ is_extended_type(uint8 type)
return (type == 0x05 || type == 0x0f || type == 0x85);
}
// fill_buffer
static inline
void
fill_buffer(char *buffer, uint32 length, char ch)
{
for (uint32 i = 0; i < length; i++)
buffer[i] = ch;
}
void get_partition_type_string(uint8 type, char *buffer);
// chs
struct chs {
uint8 cylinder;
uint16 head_sector; // head[15:10], sector[9:0]
void Unset() { cylinder = 0; head_sector = 0; }
} _PACKED;
// partition_descriptor
@@ -71,6 +82,7 @@ struct partition_table_sector {
char pad1[446];
partition_descriptor table[4];
uint16 signature;
void clear_code_area() { fill_buffer(pad1, 446, '\0'); }
} _PACKED;
static const uint16 kPartitionTableSectorSignature = 0xaa55;
@@ -79,6 +91,68 @@ class Partition;
class PrimaryPartition;
class LogicalPartition;
// PartitionType
/*!
\brief Class for validating partition types.
To this class we can set partition type and then we can check whether
this type is valid, empty or if it represents extended partition.
We can also retrieve the name of that partition type or find next
supported type.
*/
class PartitionType {
public:
PartitionType();
/*!
\brief Sets the \a type via its ID.
\param type ID of the partition type, it is in the range [0..255].
*/
void SetType(uint8 type);
/*!
\brief Sets the type via its string name.
\param type_name Name of the partition type.
*/
void SetType(const char *type_name);
/*!
\brief Converts content type to the partition type that fits best.
\param content_type Name of the content type, it is standardized by system.
*/
void SetContentType(const char *content_type);
/*!
\brief Check whether the current type is valid.
*/
bool IsValid() const { return valid_; }
/*!
\brief Check whether the current type describes empty type.
*/
bool IsEmpty() const { return is_empty_type(type_); }
/*!
\brief Check whether the current type describes extended partition type.
*/
bool IsExtended() const { return is_extended_type(type_); }
/*!
\brief Returns ID of the current type.
*/
uint8 Type() const { return type_; }
/*!
\brief Finds next supported partition.
*/
void FindNext();
/*!
\brief Returns string name of the current type.
\param buffer Buffer where the name is stored, has to be allocated with
sufficient length.
*/
void GetTypeString(char *buffer) const
{ get_partition_type_string(type_, buffer); }
private:
uint8 type_;
bool valid_;
};
// Partition
class Partition {
public:
@@ -100,6 +174,8 @@ public:
bool Active() const { return fActive; }
void GetTypeString(char *buffer) const
{ get_partition_type_string(fType, buffer); }
void GetPartitionDescriptor(partition_descriptor *descriptor,
off_t baseOffset, int32 blockSize) const;
void SetPTSOffset(off_t offset) { fPTSOffset = offset; }
void SetOffset(off_t offset) { fOffset = offset; }
@@ -135,6 +211,7 @@ public:
int32 CountLogicalPartitions() const { return fLogicalPartitionCount; }
LogicalPartition *LogicalPartitionAt(int32 index) const;
void AddLogicalPartition(LogicalPartition *partition);
void RemoveLogicalPartition(LogicalPartition *partition);
private:
LogicalPartition *fHead;
@@ -159,9 +236,13 @@ public:
void SetNext(LogicalPartition *next) { fNext = next; }
LogicalPartition *Next() const { return fNext; }
void SetPrevious(LogicalPartition *previous) { fPrevious = previous; }
LogicalPartition *Previous() const { return fPrevious; }
private:
PrimaryPartition *fPrimary;
LogicalPartition *fNext;
LogicalPartition *fPrevious;
};
// PartitionMap
@@ -5,6 +5,15 @@
* Authors:
* Ingo Weinhold, [email protected]
*/
/*!
\file PartitionMapParser.h
\brief Implementation of disk parser for "intel" style partitions.
Parser reads primary and logical partitions from the disk (according to
Master Boot and Extended Boot Records) and fills \c PartitionMap structure
with partition representation.
*/
#ifndef PARTITION_MAP_PARSER_H
#define PARTITION_MAP_PARSER_H
@@ -0,0 +1,262 @@
/*
* Copyright 2003-2007, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Tomas Kucera, [email protected]
*/
#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 "PartitionMapWriter.h"
#define TRACE_ENABLED
#ifdef TRACE_ENABLED
# ifdef _USER_MODE
# define TRACE(x) printf x
# else
# define TRACE(x) dprintf x
# endif
#endif
using std::nothrow;
// constructor
PartitionMapWriter::PartitionMapWriter(int deviceFD, off_t sessionOffset,
off_t sessionSize, int32 blockSize)
: fDeviceFD(deviceFD),
fSessionOffset(sessionOffset),
fSessionSize(sessionSize),
fBlockSize(blockSize),
fPTS(NULL),
fMap(NULL)
{
}
// destructor
PartitionMapWriter::~PartitionMapWriter()
{
}
// WriteMBR
status_t
PartitionMapWriter::WriteMBR(uint8 *block, const PartitionMap *map)
{
status_t error = (map ? B_OK : B_BAD_VALUE);
if (error == B_OK) {
fMap = map;
if (block) {
partition_table_sector *pts
= (partition_table_sector*)block;
error = _WritePrimary(pts);
if (error == B_OK)
error = _WritePTS(0, pts);
} else {
partition_table_sector pts;
error = _ReadPTS(0, &pts);
if (error == B_OK) {
error = _WritePrimary(&pts);
if (error == B_OK)
error = _WritePTS(0, &pts);
}
}
fMap = NULL;
}
return error;
}
// WriteLogical
status_t
PartitionMapWriter::WriteLogical(uint8 *block, const LogicalPartition *partition)
{
status_t error = (partition ? B_OK : B_BAD_VALUE);
if (error == B_OK) {
if (block) {
partition_table_sector *pts
= (partition_table_sector*)block;
error = _WriteExtended(pts, partition, partition->Next());
if (error == B_OK)
error = _WritePTS(partition->PTSOffset(), pts);
} else {
partition_table_sector pts;
error = _ReadPTS(partition->PTSOffset(), &pts);
if (error == B_OK) {
error = _WriteExtended(&pts, partition, partition->Next());
if (error == B_OK)
error = _WritePTS(partition->PTSOffset(), &pts);
}
}
}
return error;
}
// WriteExtendedHead
status_t
PartitionMapWriter::WriteExtendedHead(uint8 *block,
const LogicalPartition *first_partition)
{
LogicalPartition partition;
if (first_partition)
partition.SetPrimaryPartition(first_partition->GetPrimaryPartition());
status_t error = B_OK;
if (block) {
partition_table_sector *pts
= (partition_table_sector*)block;
error = _WriteExtended(pts, &partition, first_partition);
if (error == B_OK)
error = _WritePTS(0, pts);
} else {
partition_table_sector pts;
error = _ReadPTS(0, &pts);
if (error == B_OK) {
error = _WriteExtended(&pts, &partition, first_partition);
if (error == B_OK)
error = _WritePTS(0, &pts);
}
}
return error;
}
// _WritePrimary
status_t
PartitionMapWriter::_WritePrimary(partition_table_sector *pts)
{
if (pts == NULL)
return B_BAD_VALUE;
// write the signature
pts->signature = kPartitionTableSectorSignature;
// write the table
for (int32 i = 0; i < 4; i++) {
partition_descriptor *descriptor = &pts->table[i];
const PrimaryPartition *partition = fMap->PrimaryPartitionAt(i);
// ignore, if location is bad
if (!partition->CheckLocation(fSessionSize, fBlockSize)) {
TRACE(("intel: _WritePrimary(): partition %ld: bad location, "
"ignoring\n", i));
return B_BAD_DATA;
}
partition->GetPartitionDescriptor(descriptor, 0, fBlockSize);
}
return B_OK;
}
// _WriteExtended
status_t
PartitionMapWriter::_WriteExtended(partition_table_sector *pts,
const LogicalPartition *partition,
const LogicalPartition *next)
{
if (!pts || !partition)
return B_BAD_VALUE;
// write the signature
pts->signature = kPartitionTableSectorSignature;
// check the partition's location
if (!partition->CheckLocation(fSessionSize, fBlockSize)) {
TRACE(("intel: _WriteExtended(): Invalid partition "
"location: pts: %lld, offset: %lld, size: %lld, fSessionSize: %lld\n",
partition->PTSOffset(), partition->Offset(),
partition->Size(), fSessionSize));
return B_BAD_DATA;
}
// write the table
partition_descriptor *descriptor = &(pts->table[0]);
partition->GetPartitionDescriptor(descriptor, partition->PTSOffset(), fBlockSize);
// setting offset and size of the next partition in the linked list
descriptor = &(pts->table[1]);
LogicalPartition extended;
if (next) {
extended.SetPTSOffset(partition->PTSOffset());
extended.SetOffset(next->PTSOffset());
extended.SetSize(next->Size() + next->Offset() - next->PTSOffset());
extended.SetType(partition->GetPrimaryPartition()->Type());
extended.GetPartitionDescriptor(descriptor, 0, fBlockSize);
extended.Unset();
} else
extended.GetPartitionDescriptor(descriptor, 0, fBlockSize);
// last two descriptors are empty
for (int32 i = 2; i < 4; i++) {
descriptor = &(pts->table[i]);
extended.GetPartitionDescriptor(descriptor, 0, fBlockSize);
}
return B_OK;
}
// _ReadPTS
status_t
PartitionMapWriter::_ReadPTS(off_t offset, partition_table_sector *pts)
// reads the sector from the disk
{
status_t error = B_OK;
if (!pts)
pts = fPTS;
int32 toRead = sizeof(partition_table_sector);
// check the offset
if (offset < 0 || offset + toRead > fSessionSize) {
error = B_BAD_VALUE;
TRACE(("intel: _ReadPTS(): bad offset: %Ld\n", offset));
// read
} else if (read_pos(fDeviceFD, fSessionOffset + offset, pts, toRead)
!= toRead) {
#ifndef _BOOT_MODE
error = errno;
if (error == B_OK)
error = B_IO_ERROR;
#else
error = B_IO_ERROR;
#endif
TRACE(("intel: _ReadPTS(): reading the PTS failed: %lx\n", error));
}
return error;
}
// _WritePTS
status_t
PartitionMapWriter::_WritePTS(off_t offset, const partition_table_sector *pts)
// writes the sector to the disk
{
status_t error = B_OK;
if (!pts)
pts = fPTS;
int32 toWrite = sizeof(partition_table_sector);
// check the offset
if (offset < 0 || offset + toWrite > fSessionSize) {
error = B_BAD_VALUE;
TRACE(("intel: _WritePTS(): bad offset: %Ld\n", offset));
// write
} else if (write_pos(fDeviceFD, fSessionOffset + offset, pts, toWrite)
!= toWrite) {
#ifndef _BOOT_MODE
error = errno;
if (error == B_OK)
error = B_IO_ERROR;
#else
error = B_IO_ERROR;
#endif
TRACE(("intel: _WritePTS(): writing the PTS failed: %lx\n", error));
}
return error;
}
@@ -0,0 +1,98 @@
/*
* Copyright 2003-2007, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Tomas Kucera, [email protected]
*/
/*!
\file PartitionMapWriter.h
\ingroup intel_module
\brief Implementation of disk writer for "intel" style partitions.
Writer can write \b Master \b Boot \b Record or \b Extended \b Boot \b Records
to the disk according to partitions defined in \c PartitionMap structure.
*/
#ifndef PARTITION_MAP_WRITER_H
#define PARTITION_MAP_WRITER_H
#include <SupportDefs.h>
class PartitionMap;
class LogicalPartition;
struct partition_table_sector;
/*!
\brief Writer for "Intel" style partitions.
This class serves for writing \a primary and \a logical \a partitions to disk.
*/
class PartitionMapWriter {
public:
/*!
\brief Creates the writer.
\param deviceFD File descriptor.
\param sessionOffset Disk offset of the partition with partitioning system.
\param sessionSize Size of the partition with partitioning system.
\param blockSize Size of the sector on given disk.
*/
PartitionMapWriter(int deviceFD, off_t sessionOffset, off_t sessionSize,
int32 blockSize);
~PartitionMapWriter();
/*!
\brief Writes Master Boot Record to the first sector of the disk.
If a \a block is not specified, the sector is firstly read from the disk
and after changing relevant items it is written back to the disk.
This allows to keep code area in MBR intact.
\param block Pointer to \c partition_table_sector.
\param map Pointer to the PartitionMap structure describing disk partitions.
*/
status_t WriteMBR(uint8 *block, const PartitionMap *map);
/*!
\brief Writes Partition Table Sector of the logical \a partition to the disk.
This function ensures that the connection of the following linked list of logical
partitions will be correct. It do nothing with the connection of previous logical
partitions (call this function on previous logical partition to ensure it).
\param block Pointer to \c partition_table_sector.
\param partition Pointer to the logical partition.
*/
status_t WriteLogical(uint8 *block, const LogicalPartition *partition);
/*!
\brief Writes Extended Boot Record to the first sector of Extended Partition.
Writes the head of linked list describing logical partitions.
If the \a first_partition is not specified, it only initializes EBR and the linked
list contains no logical partitions.
\param block Pointer to \c partition_table_sector.
\param first_partition Pointer to the first logical partition.
*/
status_t WriteExtendedHead(uint8 *block, const LogicalPartition *first_partition);
private:
status_t _WritePrimary(partition_table_sector *pts);
status_t _WriteExtended(partition_table_sector *pts,
const LogicalPartition *partition,
const LogicalPartition *next);
status_t _ReadPTS(off_t offset, partition_table_sector *pts = NULL);
status_t _WritePTS(off_t offset, const partition_table_sector *pts = NULL);
private:
int fDeviceFD;
off_t fSessionOffset;
off_t fSessionSize;
int32 fBlockSize;
partition_table_sector *fPTS; // while writing
const PartitionMap *fMap;
};
#endif // PARTITION_MAP_WRITER_H
File diff suppressed because it is too large Load Diff