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), fStatus(B_NO_INIT),
fEntries(NULL) 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)); &fHeader, sizeof(efi_table_header));
if (bytesRead != (ssize_t)sizeof(efi_table_header)) { if (fStatus == B_OK) {
if (bytesRead < B_OK) if (!_IsHeaderValid(fHeader, EFI_HEADER_LOCATION))
fStatus = bytesRead; fStatus = B_BAD_DATA;
else
fStatus = B_IO_ERROR;
return;
} }
if (memcmp(fHeader.header, EFI_PARTITION_HEADER, sizeof(fHeader.header)) // Read backup header, too
|| !_ValidateHeaderCRC() status_t status = _Read(fd, lastBlock * blockSize, &fBackupHeader,
|| fHeader.AbsoluteBlock() != EFI_HEADER_LOCATION) { sizeof(efi_table_header));
// TODO: check that partition counts are in valid bounds if (status == B_OK) {
fStatus = B_BAD_DATA; 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; 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 // allocate, read, and check partition entry array
@@ -77,22 +87,22 @@ Header::Header(int fd, uint64 lastBlock, uint32 blockSize)
return; return;
} }
bytesRead = read_pos(fd, fHeader.EntriesBlock() * blockSize, fStatus = _Read(fd, fHeader.EntriesBlock() * blockSize,
fEntries, _EntryArraySize()); fEntries, _EntryArraySize());
if (bytesRead != (ssize_t)_EntryArraySize()) { if (fStatus != B_OK || !_ValidateEntriesCRC()) {
if (bytesRead < B_OK) // Read backup entries instead
fStatus = bytesRead; fStatus = _Read(fd, fBackupHeader.EntriesBlock() * blockSize,
else fEntries, _EntryArraySize());
fStatus = B_IO_ERROR; if (fStatus != B_OK)
return; return;
}
if (!_ValidateEntriesCRC()) { if (!_ValidateEntriesCRC()) {
// TODO: check overlapping or out of range partitions
fStatus = B_BAD_DATA; fStatus = B_BAD_DATA;
return; return;
} }
}
// TODO: check overlapping or out of range partitions
#ifdef TRACE_EFI_GPT #ifdef TRACE_EFI_GPT
_Dump(); _Dump();
@@ -140,6 +150,8 @@ Header::Header(uint64 lastBlock, uint32 blockSize)
fHeader.SetFirstUsableBlock(EFI_PARTITION_ENTRIES_BLOCK + entryBlocks); fHeader.SetFirstUsableBlock(EFI_PARTITION_ENTRIES_BLOCK + entryBlocks);
fHeader.SetLastUsableBlock(lastBlock - 1 - entryBlocks); fHeader.SetLastUsableBlock(lastBlock - 1 - entryBlocks);
_SetBackupHeaderFromPrimary(lastBlock);
#ifdef TRACE_EFI_GPT #ifdef TRACE_EFI_GPT
_Dump(); _Dump();
_DumpPartitions(); _DumpPartitions();
@@ -168,19 +180,25 @@ status_t
Header::WriteEntry(int fd, uint32 entryIndex) Header::WriteEntry(int fd, uint32 entryIndex)
{ {
// Determine block to write // Determine block to write
off_t block = fHeader.EntriesBlock() off_t blockOffset =
+ entryIndex * fHeader.EntrySize() / fBlockSize; + entryIndex * fHeader.EntrySize() / fBlockSize;
uint32 entryOffset = entryIndex * fHeader.EntrySize() % fBlockSize; uint32 entryOffset = entryIndex * fHeader.EntrySize() % fBlockSize;
status_t status = _Write(fd, block * fBlockSize, fEntries + entryOffset, status_t status = _Write(fd,
fBlockSize); (fHeader.EntriesBlock() + blockOffset) * fBlockSize,
fEntries + entryOffset, fBlockSize);
if (status != B_OK) if (status != B_OK)
return status; return status;
// TODO: write mirror at the end
// Update header, too -- the entries CRC changed // 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) if (status != B_OK)
return status; 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) if (status != B_OK)
return status; return status;
// TODO: write mirror at the end return _Write(fd, fBackupHeader.AbsoluteBlock() * fBlockSize,
&fBackupHeader, sizeof(efi_table_header));
return B_OK;
} }
@@ -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 void
Header::_UpdateCRC() Header::_UpdateCRC()
{ {
@@ -237,6 +273,15 @@ Header::_UpdateCRC()
#endif // !_BOOT_MODE #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 bool
Header::_ValidateHeaderCRC() 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 #ifdef TRACE_EFI_GPT
const char * const char *
Header::_PrintGUID(const guid_t &id) Header::_PrintGUID(const guid_t &id)
@@ -43,27 +43,33 @@ public:
#endif #endif
private: private:
const char* _PrintGUID(const guid_t& id);
void _Dump();
void _DumpPartitions();
#ifndef _BOOT_MODE #ifndef _BOOT_MODE
status_t _WriteHeader(int fd); status_t _WriteHeader(int fd);
status_t _Write(int fd, off_t offset, const void* data, status_t _Write(int fd, off_t offset, const void* data,
size_t size) const; size_t size) const;
status_t _Read(int fd, off_t offset, void* data,
size_t size) const;
void _UpdateCRC(); void _UpdateCRC();
#endif #endif
bool _IsHeaderValid(const efi_table_header& header,
uint64 block);
bool _ValidateHeaderCRC(); bool _ValidateHeaderCRC();
bool _ValidateEntriesCRC() const; bool _ValidateEntriesCRC() const;
void _SetBackupHeaderFromPrimary(uint64 lastBlock);
size_t _EntryArraySize() const size_t _EntryArraySize() const
{ return fHeader.EntrySize() { return fHeader.EntrySize()
* fHeader.EntryCount(); } * fHeader.EntryCount(); }
const char* _PrintGUID(const guid_t& id);
void _Dump();
void _DumpPartitions();
private: private:
uint32 fBlockSize; uint32 fBlockSize;
status_t fStatus; status_t fStatus;
efi_table_header fHeader; efi_table_header fHeader;
efi_table_header fBackupHeader;
uint8* fEntries; uint8* fEntries;
}; };