gpt: Added support for the backup header/entries.

This commit is contained in:
Axel Dörfler
2013-01-27 14:16:01 +01:00
parent 90308ec327
commit 953d310a47
2 changed files with 103 additions and 42 deletions
@@ -44,27 +44,37 @@ Header::Header(int fd, uint64 lastBlock, uint32 blockSize)
fStatus(B_NO_INIT),
fEntries(NULL)
{
// TODO: check the correctness of the protective MBR
// TODO: check the correctness of the protective MBR and warn if invalid
// read and check the partition table header
// Read and check the partition table header
ssize_t bytesRead = read_pos(fd, (uint64)EFI_HEADER_LOCATION * blockSize,
fStatus = _Read(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;
else
fStatus = B_IO_ERROR;
return;
if (fStatus == B_OK) {
if (!_IsHeaderValid(fHeader, EFI_HEADER_LOCATION))
fStatus = B_BAD_DATA;
}
if (memcmp(fHeader.header, EFI_PARTITION_HEADER, sizeof(fHeader.header))
|| !_ValidateHeaderCRC()
|| fHeader.AbsoluteBlock() != EFI_HEADER_LOCATION) {
// TODO: check that partition counts are in valid bounds
fStatus = B_BAD_DATA;
// Read backup header, too
status_t status = _Read(fd, lastBlock * blockSize, &fBackupHeader,
sizeof(efi_table_header));
if (status == B_OK) {
if (!_IsHeaderValid(fBackupHeader, lastBlock))
status = B_BAD_DATA;
}
// If both headers are invalid, bail out -- this is probably not a GPT disk
if (status != B_OK && fStatus != B_OK)
return;
if (fStatus != B_OK) {
// Recreate primary header from the backup
fHeader = fBackupHeader;
fHeader.SetAbsoluteBlock(EFI_HEADER_LOCATION);
fHeader.SetEntriesBlock(EFI_PARTITION_ENTRIES_BLOCK);
} else if (status != B_OK) {
// Recreate backup header from primary
_SetBackupHeaderFromPrimary(lastBlock);
}
// allocate, read, and check partition entry array
@@ -77,22 +87,22 @@ Header::Header(int fd, uint64 lastBlock, uint32 blockSize)
return;
}
bytesRead = read_pos(fd, fHeader.EntriesBlock() * blockSize,
fStatus = _Read(fd, fHeader.EntriesBlock() * blockSize,
fEntries, _EntryArraySize());
if (bytesRead != (ssize_t)_EntryArraySize()) {
if (bytesRead < B_OK)
fStatus = bytesRead;
else
fStatus = B_IO_ERROR;
if (fStatus != B_OK || !_ValidateEntriesCRC()) {
// Read backup entries instead
fStatus = _Read(fd, fBackupHeader.EntriesBlock() * blockSize,
fEntries, _EntryArraySize());
if (fStatus != B_OK)
return;
return;
if (!_ValidateEntriesCRC()) {
fStatus = B_BAD_DATA;
return;
}
}
if (!_ValidateEntriesCRC()) {
// TODO: check overlapping or out of range partitions
fStatus = B_BAD_DATA;
return;
}
// TODO: check overlapping or out of range partitions
#ifdef TRACE_EFI_GPT
_Dump();
@@ -140,6 +150,8 @@ Header::Header(uint64 lastBlock, uint32 blockSize)
fHeader.SetFirstUsableBlock(EFI_PARTITION_ENTRIES_BLOCK + entryBlocks);
fHeader.SetLastUsableBlock(lastBlock - 1 - entryBlocks);
_SetBackupHeaderFromPrimary(lastBlock);
#ifdef TRACE_EFI_GPT
_Dump();
_DumpPartitions();
@@ -168,19 +180,25 @@ status_t
Header::WriteEntry(int fd, uint32 entryIndex)
{
// Determine block to write
off_t block = fHeader.EntriesBlock()
off_t blockOffset =
+ entryIndex * fHeader.EntrySize() / fBlockSize;
uint32 entryOffset = entryIndex * fHeader.EntrySize() % fBlockSize;
status_t status = _Write(fd, block * fBlockSize, fEntries + entryOffset,
fBlockSize);
status_t status = _Write(fd,
(fHeader.EntriesBlock() + blockOffset) * fBlockSize,
fEntries + entryOffset, fBlockSize);
if (status != B_OK)
return status;
// TODO: write mirror at the end
// Update header, too -- the entries CRC changed
return _WriteHeader(fd);
status = _WriteHeader(fd);
// Write backup
status_t backupStatus = _Write(fd,
(fBackupHeader.EntriesBlock() + blockOffset) * fBlockSize,
fEntries + entryOffset, fBlockSize);
return status == B_OK ? backupStatus : status;
}
@@ -192,9 +210,15 @@ Header::Write(int fd)
if (status != B_OK)
return status;
// TODO: write mirror at the end
// First write the header, so that we have at least one completely correct
// data set
status = _WriteHeader(fd);
return _WriteHeader(fd);
// Write backup entries
status_t backupStatus = _Write(fd,
fBackupHeader.EntriesBlock() * fBlockSize, fEntries, _EntryArraySize());
return status == B_OK ? backupStatus : status;
}
@@ -208,9 +232,8 @@ Header::_WriteHeader(int fd)
if (status != B_OK)
return status;
// TODO: write mirror at the end
return B_OK;
return _Write(fd, fBackupHeader.AbsoluteBlock() * fBlockSize,
&fBackupHeader, sizeof(efi_table_header));
}
@@ -227,6 +250,19 @@ Header::_Write(int fd, off_t offset, const void* data, size_t size) const
}
status_t
Header::_Read(int fd, off_t offset, void* data, size_t size) const
{
ssize_t bytesRead = read_pos(fd, offset, data, size);
if (bytesRead < 0)
return bytesRead;
if (bytesRead != (ssize_t)size)
return B_IO_ERROR;
return B_OK;
}
void
Header::_UpdateCRC()
{
@@ -237,6 +273,15 @@ Header::_UpdateCRC()
#endif // !_BOOT_MODE
bool
Header::_IsHeaderValid(const efi_table_header& header, uint64 block)
{
return !memcmp(fHeader.header, EFI_PARTITION_HEADER, sizeof(fHeader.header))
&& _ValidateHeaderCRC()
&& fHeader.AbsoluteBlock() == block;
}
bool
Header::_ValidateHeaderCRC()
{
@@ -258,6 +303,16 @@ Header::_ValidateEntriesCRC() const
}
void
Header::_SetBackupHeaderFromPrimary(uint64 lastBlock)
{
fBackupHeader = fHeader;
fBackupHeader.SetAbsoluteBlock(lastBlock);
fBackupHeader.SetEntriesBlock(
lastBlock - _EntryArraySize() / fBlockSize);
}
#ifdef TRACE_EFI_GPT
const char *
Header::_PrintGUID(const guid_t &id)
@@ -43,27 +43,33 @@ public:
#endif
private:
const char* _PrintGUID(const guid_t& id);
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;
status_t _Read(int fd, off_t offset, void* data,
size_t size) const;
void _UpdateCRC();
#endif
bool _IsHeaderValid(const efi_table_header& header,
uint64 block);
bool _ValidateHeaderCRC();
bool _ValidateEntriesCRC() const;
void _SetBackupHeaderFromPrimary(uint64 lastBlock);
size_t _EntryArraySize() const
{ return fHeader.EntrySize()
* fHeader.EntryCount(); }
const char* _PrintGUID(const guid_t& id);
void _Dump();
void _DumpPartitions();
private:
uint32 fBlockSize;
status_t fStatus;
efi_table_header fHeader;
efi_table_header fBackupHeader;
uint8* fEntries;
};