hrev45208 adds 1 changeset to branch 'master' old head: 90308ec327dce4e1894b4507f9197ba92512926c new head: 953d310a47372bb97f2b1154d032aa344dbbcb69 overview: http://cgit.haiku-os.org/haiku/log/?qt=range&q=953d310+%5E90308ec ---------------------------------------------------------------------------- 953d310: gpt: Added support for the backup header/entries. [ Axel Dörfler <axeld@xxxxxxxxxxxxxxxx> ] ---------------------------------------------------------------------------- Revision: hrev45208 Commit: 953d310a47372bb97f2b1154d032aa344dbbcb69 URL: http://cgit.haiku-os.org/haiku/commit/?id=953d310 Author: Axel Dörfler <axeld@xxxxxxxxxxxxxxxx> Date: Sun Jan 27 13:16:01 2013 UTC ---------------------------------------------------------------------------- 2 files changed, 103 insertions(+), 42 deletions(-) .../kernel/partitioning_systems/gpt/Header.cpp | 131 +++++++++++++------ .../kernel/partitioning_systems/gpt/Header.h | 14 +- ---------------------------------------------------------------------------- diff --git a/src/add-ons/kernel/partitioning_systems/gpt/Header.cpp b/src/add-ons/kernel/partitioning_systems/gpt/Header.cpp index c47b64c..55ccb82 100644 --- a/src/add-ons/kernel/partitioning_systems/gpt/Header.cpp +++ b/src/add-ons/kernel/partitioning_systems/gpt/Header.cpp @@ -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; + if (fStatus == B_OK) { + if (!_IsHeaderValid(fHeader, EFI_HEADER_LOCATION)) + fStatus = B_BAD_DATA; + } - return; + // 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 (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; + // 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; - - return; + if (fStatus != B_OK || !_ValidateEntriesCRC()) { + // Read backup entries instead + fStatus = _Read(fd, fBackupHeader.EntriesBlock() * blockSize, + fEntries, _EntryArraySize()); + if (fStatus != B_OK) + 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); + + // Write backup entries + status_t backupStatus = _Write(fd, + fBackupHeader.EntriesBlock() * fBlockSize, fEntries, _EntryArraySize()); - return _WriteHeader(fd); + 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() { @@ -238,6 +274,15 @@ Header::_UpdateCRC() 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() { uint32 originalCRC = fHeader.HeaderCRC(); @@ -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) diff --git a/src/add-ons/kernel/partitioning_systems/gpt/Header.h b/src/add-ons/kernel/partitioning_systems/gpt/Header.h index 486a5e2..f3681df 100644 --- a/src/add-ons/kernel/partitioning_systems/gpt/Header.h +++ b/src/add-ons/kernel/partitioning_systems/gpt/Header.h @@ -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; };