gpt: Fixed a number of remaining issues.

* The header and table is now correctly written; the backup is still
  missing, though.
* The Header class is now responsible for both, the primary, and the
  backup header.
* Changed the Header constructors: the block is no longer needed. Also,
  under GCC 4 the initialization code accidentally used the read
  Header constructor.
* Fixed incorrectly copied GUID - the static_guid cannot be copied
  into a guid_t directly.
* Fixed copy&paste bug that would overwrite the offset for the child
  partition to be created.
* With all of this in place I successfully created a BFS partition
  with a GUID partition table. However, I have not yet tested if other
  systems can still read this. Also, creating two partitions doesn't
  seem to work yet, either (luckily I only need a single one ;-)).
This commit is contained in:
Axel Dörfler
2013-01-26 21:13:00 +01:00
parent 959e02aa56
commit ef2558c0ae
6 changed files with 84 additions and 53 deletions
@@ -38,9 +38,8 @@
namespace EFI {
Header::Header(int fd, off_t block, uint32 blockSize)
Header::Header(int fd, uint64 lastBlock, uint32 blockSize)
:
fBlock(block),
fBlockSize(blockSize),
fStatus(B_NO_INIT),
fEntries(NULL)
@@ -49,8 +48,8 @@ Header::Header(int fd, off_t block, uint32 blockSize)
// read and check the partition table header
ssize_t bytesRead = read_pos(fd, block * blockSize, &fHeader,
sizeof(efi_table_header));
ssize_t bytesRead = read_pos(fd, (uint64)EFI_HEADER_LOCATION * blockSize,
&fHeader, sizeof(efi_table_header));
if (bytesRead != (ssize_t)sizeof(efi_table_header)) {
if (bytesRead < B_OK)
fStatus = bytesRead;
@@ -62,7 +61,7 @@ Header::Header(int fd, off_t block, uint32 blockSize)
if (memcmp(fHeader.header, EFI_PARTITION_HEADER, sizeof(fHeader.header))
|| !_ValidateHeaderCRC()
|| fHeader.AbsoluteBlock() != fBlock) {
|| fHeader.AbsoluteBlock() != EFI_HEADER_LOCATION) {
// TODO: check that partition counts are in valid bounds
fStatus = B_BAD_DATA;
return;
@@ -105,19 +104,21 @@ Header::Header(int fd, off_t block, uint32 blockSize)
#ifndef _BOOT_MODE
Header::Header(off_t block, off_t lastBlock, uint32 blockSize)
Header::Header(uint64 lastBlock, uint32 blockSize)
:
fBlock(block),
fBlockSize(blockSize),
fStatus(B_NO_INIT),
fEntries(NULL)
{
// initialize to an empty header
TRACE(("EFI::Header: Initialize GPT, block size %" B_PRIu32 "\n",
blockSize));
// Initialize to an empty header
memcpy(fHeader.header, EFI_PARTITION_HEADER, sizeof(fHeader.header));
fHeader.SetRevision(EFI_TABLE_REVISION);
fHeader.SetHeaderSize(sizeof(fHeader));
fHeader.SetHeaderCRC(0);
fHeader.SetAbsoluteBlock(fBlock);
fHeader.SetAbsoluteBlock(EFI_HEADER_LOCATION);
fHeader.SetAlternateBlock(0); // TODO
// TODO: set disk guid
fHeader.SetEntriesBlock(EFI_PARTITION_ENTRIES_BLOCK);
@@ -142,9 +143,6 @@ Header::Header(off_t block, off_t lastBlock, uint32 blockSize)
#ifdef TRACE_EFI_GPT
_Dump();
_DumpPartitions();
dprintf("GPT: HERE I AM!\n");
#else
dprintf("GPT: Nope!\n");
#endif
fStatus = B_OK;
@@ -182,12 +180,26 @@ Header::WriteEntry(int fd, uint32 entryIndex)
// TODO: write mirror at the end
// Update header, too -- the entries CRC changed
return Write(fd);
return _WriteHeader(fd);
}
status_t
Header::Write(int fd)
{
status_t status = _Write(fd, fHeader.EntriesBlock() * fBlockSize, fEntries,
_EntryArraySize());
if (status != B_OK)
return status;
// TODO: write mirror at the end
return _WriteHeader(fd);
}
status_t
Header::_WriteHeader(int fd)
{
_UpdateCRC();
@@ -200,7 +212,6 @@ Header::Write(int fd)
return B_OK;
}
#endif // !_BOOT_MODE
status_t
@@ -223,6 +234,7 @@ Header::_UpdateCRC()
fHeader.SetHeaderCRC(0);
fHeader.SetHeaderCRC(crc32((uint8*)&fHeader, sizeof(efi_table_header)));
}
#endif // !_BOOT_MODE
bool
@@ -233,7 +245,6 @@ Header::_ValidateHeaderCRC()
bool matches = originalCRC == crc32((const uint8*)&fHeader,
sizeof(efi_table_header));
dprintf("GPT: MATCHES %d!\n", matches);
fHeader.SetHeaderCRC(originalCRC);
return matches;
@@ -266,7 +277,7 @@ void
Header::_Dump()
{
dprintf("EFI header: %.8s\n", fHeader.header);
dprintf("EFI revision: %ld\n", fHeader.Revision());
dprintf("EFI revision: %" B_PRIx32 "\n", fHeader.Revision());
dprintf("header size: %ld\n", fHeader.HeaderSize());
dprintf("header CRC: %ld\n", fHeader.HeaderCRC());
dprintf("absolute block: %Ld\n", fHeader.AbsoluteBlock());
@@ -16,17 +16,15 @@ namespace EFI {
class Header {
public:
Header(int fd, off_t block, uint32 blockSize);
Header(int fd, uint64 lastBlock,
uint32 blockSize);
#ifndef _BOOT_MODE
// constructor for empty header
Header(off_t block, off_t lastBlock,
uint32 blockSize);
Header(uint64 lastBlock, uint32 blockSize);
#endif
~Header();
status_t InitCheck() const;
bool IsPrimary() const
{ return fBlock == EFI_HEADER_LOCATION; }
uint64 FirstUsableBlock() const
{ return fHeader.FirstUsableBlock(); }
@@ -49,9 +47,13 @@ private:
void _Dump();
void _DumpPartitions();
#ifndef _BOOT_MODE
status_t _WriteHeader(int fd);
status_t _Write(int fd, off_t offset, const void* data,
size_t size) const;
void _UpdateCRC();
#endif
bool _ValidateHeaderCRC();
bool _ValidateEntriesCRC() const;
size_t _EntryArraySize() const
@@ -59,7 +61,6 @@ private:
* fHeader.EntryCount(); }
private:
uint64 fBlock;
uint32 fBlockSize;
status_t fStatus;
efi_table_header fHeader;
@@ -71,10 +71,10 @@ efi_gpt_std_ops(int32 op, ...)
static float
efi_gpt_identify_partition(int fd, partition_data *partition, void **_cookie)
{
EFI::Header *header = new (std::nothrow) EFI::Header(fd,
EFI_HEADER_LOCATION, partition->block_size);
EFI::Header* header = new (std::nothrow) EFI::Header(fd,
partition->size / partition->block_size, partition->block_size);
status_t status = header->InitCheck();
if (status < B_OK) {
if (status != B_OK) {
delete header;
return -1;
}
@@ -323,7 +323,8 @@ efi_gpt_validate_set_content_name(partition_data *partition, char *name)
static bool
efi_gpt_validate_set_type(partition_data *partition, const char *type)
{
return guid_for_partition_type(type) != NULL;
guid_t typeGUID;
return get_guid_for_partition_type(type, typeGUID);
}
@@ -352,7 +353,7 @@ efi_gpt_validate_create_child(partition_data *partition, off_t *start,
& B_DISK_SYSTEM_SUPPORTS_CREATING_CHILD) == 0)
return false;
if (guid_for_partition_type(type) == NULL)
if (!efi_gpt_validate_set_type(partition, type))
return false;
EFI::Header *header = (EFI::Header *)partition->content_cookie;
@@ -396,7 +397,7 @@ efi_gpt_validate_create_child(partition_data *partition, off_t *start,
*size = other->offset - *start;
}
*start = block_align(partition, *size, true);
*start = block_align(partition, *start, true);
*size = block_align(partition, *size, false);
// TODO: support parameters
@@ -663,14 +664,14 @@ efi_gpt_set_type(int fd, partition_id partitionID, const char *type,
if (entryIndex >= header->EntryCount())
return B_BAD_VALUE;
const static_guid *newType = guid_for_partition_type(type);
if (newType == NULL)
guid_t typeGUID;
if (!get_guid_for_partition_type(type, typeGUID))
return B_BAD_VALUE;
update_disk_device_job_progress(job, 0.0);
efi_partition_entry &entry = header->EntryAt(entryIndex);
memcpy(&entry.partition_type, newType, sizeof(entry.partition_type));
entry.partition_type = typeGUID;
status_t result = header->WriteEntry(fd, entryIndex);
if (result != B_OK)
@@ -697,8 +698,8 @@ efi_gpt_initialize(int fd, partition_id partitionID, const char *name,
update_disk_device_job_progress(job, 0.0);
EFI::Header header(EFI_HEADER_LOCATION,
partitionSize / partition->block_size, partition->block_size);
EFI::Header header(partitionSize / partition->block_size,
partition->block_size);
status_t result = header.InitCheck();
if (result != B_OK)
return result;
@@ -745,8 +746,8 @@ efi_gpt_create_child(int fd, partition_id partitionID, off_t offset,
&validatedSize, type, name, parameters, (int32 *)&entryIndex))
return B_BAD_VALUE;
const static_guid *newType = guid_for_partition_type(type);
if (newType == NULL)
guid_t typeGUID;
if (!get_guid_for_partition_type(type, typeGUID))
return B_BAD_VALUE;
update_disk_device_job_progress(job, 0.0);
@@ -757,7 +758,8 @@ efi_gpt_create_child(int fd, partition_id partitionID, off_t offset,
return B_ERROR;
efi_partition_entry &entry = header->EntryAt(entryIndex);
memcpy(&entry.partition_type, newType, sizeof(entry.partition_type));
entry.partition_type = typeGUID;
// TODO: set unique partition ID
to_ucs2(name, strlen(name), entry.name, EFI_PARTITION_NAME_LENGTH);
entry.SetStartBlock((validatedOffset - partition->offset)
/ partition->block_size);
@@ -844,7 +846,7 @@ partition_module_info gEFIPartitionModule = {
0,
efi_gpt_std_ops
},
"efi", // short_name
"gpt", // short_name
EFI_PARTITION_NAME, // pretty_name
0 // flags
| B_DISK_SYSTEM_SUPPORTS_INITIALIZING
@@ -22,24 +22,39 @@ struct static_guid {
uint16 data3;
uint64 data4;
inline bool operator==(const guid &other) const;
inline bool operator==(const guid& other) const;
inline operator guid_t() const;
} _PACKED;
inline bool
static_guid::operator==(const guid_t &other) const
static_guid::operator==(const guid_t& other) const
{
return B_HOST_TO_LENDIAN_INT32(data1) == other.data1
&& B_HOST_TO_LENDIAN_INT16(data2) == other.data2
&& B_HOST_TO_LENDIAN_INT16(data3) == other.data3
&& B_HOST_TO_BENDIAN_INT64(*(uint64 *)&data4) == *(uint64 *)other.data4;
&& B_HOST_TO_BENDIAN_INT64(*(uint64*)&data4) == *(uint64*)other.data4;
// the last 8 bytes are in big-endian order
}
inline
static_guid::operator guid_t() const
{
guid_t guid;
guid.data1 = B_HOST_TO_LENDIAN_INT32(data1);
guid.data2 = B_HOST_TO_LENDIAN_INT16(data2);
guid.data3 = B_HOST_TO_LENDIAN_INT16(data3);
uint64 last = B_HOST_TO_BENDIAN_INT64(*(uint64*)&data4);
memcpy(guid.data4, &last, sizeof(uint64));
return guid;
}
const static struct type_map {
static_guid guid;
const char *type;
const char* type;
} kTypeMap[] = {
{{0xC12A7328, 0xF81F, 0x11D2, 0xBA4B00A0C93EC93BLL}, "EFI System Data"},
{{0x21686148, 0x6449, 0x6E6F, 0x744E656564454649LL}, "BIOS Boot Data"},
@@ -21,7 +21,7 @@ const guid_t kEmptyGUID = {0, 0, 0, {0, 0, 0, 0, 0, 0, 0, 0}};
static void
put_utf8_byte(char *&to, size_t &left, char c)
put_utf8_byte(char*& to, size_t& left, char c)
{
if (left <= 1)
return;
@@ -35,7 +35,7 @@ put_utf8_byte(char *&to, size_t &left, char c)
void
to_utf8(const uint16 *from, size_t maxFromLength, char *to, size_t toSize)
to_utf8(const uint16* from, size_t maxFromLength, char* to, size_t toSize)
{
for (uint32 i = 0; i < maxFromLength; i++) {
uint16 c = B_LENDIAN_TO_HOST_INT16(from[i]);
@@ -66,10 +66,10 @@ to_utf8(const uint16 *from, size_t maxFromLength, char *to, size_t toSize)
#ifndef _BOOT_MODE
void
to_ucs2(const char *from, size_t fromLength, uint16 *to, size_t maxToLength)
to_ucs2(const char* from, size_t fromLength, uint16* to, size_t maxToLength)
{
size_t index = 0;
while (from[0] && index < maxToLength) {
while (from[0] != '\0' && index < maxToLength) {
// TODO: handle characters that are not representable in UCS-2 better
uint32 code = UTF8ToCharCode(&from);
if (code < 0x10000)
@@ -82,8 +82,8 @@ to_ucs2(const char *from, size_t fromLength, uint16 *to, size_t maxToLength)
#endif // !_BOOT_MODE
const char *
get_partition_type(const guid_t &guid)
const char*
get_partition_type(const guid_t& guid)
{
for (uint32 i = 0; i < sizeof(kTypeMap) / sizeof(kTypeMap[0]); i++) {
if (kTypeMap[i].guid == guid)
@@ -95,14 +95,16 @@ get_partition_type(const guid_t &guid)
#ifndef _BOOT_MODE
const static_guid *
guid_for_partition_type(const char *type)
bool
get_guid_for_partition_type(const char* type, guid_t& guid)
{
for (uint32 i = 0; i < sizeof(kTypeMap) / sizeof(kTypeMap[0]); i++) {
if (strcmp(kTypeMap[i].type, type) == 0)
return &kTypeMap[i].guid;
if (strcmp(kTypeMap[i].type, type) == 0) {
guid = kTypeMap[i].guid;
return true;
}
}
return NULL;
return false;
}
#endif // !_BOOT_MODE
@@ -24,7 +24,7 @@ const char* get_partition_type(const guid_t& guid);
#ifndef _BOOT_MODE
void to_ucs2(const char* from, size_t fromLength, uint16* to,
size_t maxToLength);
const static_guid* guid_for_partition_type(const char* type);
bool get_guid_for_partition_type(const char* type, guid_t& guid);
#endif // !_BOOT_MODE