gpt: Added support for the backup header/entries.
This commit is contained in:
@@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user