diff --git a/docs/spec/blocks/erasure_coding.adoc b/docs/spec/blocks/erasure_coding.adoc index 8334907..f8dec5a 100644 --- a/docs/spec/blocks/erasure_coding.adoc +++ b/docs/spec/blocks/erasure_coding.adoc @@ -194,7 +194,7 @@ CAUTION: Reading a non-DataBlock header as a `BlockHeader` and accessing the `cm ==== Recovery Footer -The last 160 bytes of the file contain the recovery footer, enabling the ECMB to be found even when the header or index is destroyed: +The last 179 bytes of the file contain the recovery footer, enabling the ECMB to be found even when the header or index is destroyed: [source,c] ---- @@ -203,7 +203,7 @@ typedef struct AaruRecoveryFooter uint64_t ecmbOffset; // Absolute file offset of the primary ECMB uint64_t ecmbLength; // Total on-disk size of the ECMB (header + payload) uint64_t headerCrc64; // CRC64-ECMA of the original AaruHeaderV2 - AaruHeaderV2 backupHeader; // Complete copy of AaruHeaderV2 (128 bytes) + AaruHeaderV2 backupHeader; // Complete copy of AaruHeaderV2 (147 bytes) uint64_t footerMagic; // Must be 0x52464D4345525641 ("AVRECMFR") } AaruRecoveryFooter; ---- @@ -214,7 +214,7 @@ typedef struct AaruRecoveryFooter |uint64_t |8 bytes |ecmbOffset |Absolute file offset of the primary ECMB. |uint64_t |8 bytes |ecmbLength |Total on-disk size of the ECMB (header + payload). |uint64_t |8 bytes |headerCrc64 |CRC64-ECMA of the original `AaruHeaderV2` at file offset 0. -|AaruHeaderV2 |128 bytes |backupHeader |Complete backup copy of the file header. +|AaruHeaderV2 |147 bytes |backupHeader |Complete backup copy of the file header. |uint64_t |8 bytes |footerMagic |Must be `0x52464D4345525641` ("AVRECMFR" in ASCII little-endian). |=== @@ -241,7 +241,7 @@ The recovery chain defines the fallback sequence when structures are damaged: The erasure coding structures are written at the end of the file: ---- -[AaruHeaderV2] offset 0, 128 bytes +[AaruHeaderV2] offset 0, 147 bytes [Data blocks with consecutive parity] variable size [DDT blocks (all levels)] variable size [DDT parity blocks] M blocks (uncompressed) @@ -251,7 +251,7 @@ The erasure coding structures are written at the end of the file: [Index parity blocks] M blocks (uncompressed) [ECMB (primary)] at footer.ecmbOffset [ECMB (duplicate)] at ecmbOffset + ecmbLength (aligned) -[Recovery Footer] last 160 bytes of file +[Recovery Footer] last 179 bytes of file ---- ==== Automatic K,M Selection @@ -442,7 +442,7 @@ With K=16 and M=2, you need 33+ contiguously corrupted blocks before any stripe ==== Recovery Footer -The last 160 bytes of the file contain the recovery footer, enabling the ECMB to be found even when the header or index is destroyed: +The last 179 bytes of the file contain the recovery footer, enabling the ECMB to be found even when the header or index is destroyed: [source,c] ---- @@ -451,7 +451,7 @@ typedef struct AaruRecoveryFooter uint64_t ecmbOffset; // Absolute file offset of the primary ECMB uint64_t ecmbLength; // Total on-disk size of the ECMB (header + payload) uint64_t headerCrc64; // CRC64-ECMA of the original AaruHeaderV2 - AaruHeaderV2 backupHeader; // Complete copy of AaruHeaderV2 (128 bytes) + AaruHeaderV2 backupHeader; // Complete copy of AaruHeaderV2 (147 bytes) uint64_t footerMagic; // Must be 0x52464D4345525641 ("AVRECMFR") } AaruRecoveryFooter; ---- @@ -462,7 +462,7 @@ typedef struct AaruRecoveryFooter |uint64_t |8 bytes |ecmbOffset |Absolute file offset of the primary ECMB. |uint64_t |8 bytes |ecmbLength |Total on-disk size of the ECMB (header + payload). |uint64_t |8 bytes |headerCrc64 |CRC64-ECMA of the original `AaruHeaderV2` at file offset 0. -|AaruHeaderV2 |128 bytes |backupHeader |Complete backup copy of the file header. +|AaruHeaderV2 |147 bytes |backupHeader |Complete backup copy of the file header. |uint64_t |8 bytes |footerMagic |Must be `0x52464D4345525641` ("AVRECMFR" in ASCII little-endian). |=== @@ -489,7 +489,7 @@ The recovery chain defines the fallback sequence when structures are damaged: The erasure coding structures are written after the index block, at the end of the file: ---- -[AaruHeaderV2] offset 0, 128 bytes +[AaruHeaderV2] offset 0, 147 bytes [Data blocks + interleaved parity] variable size [DDT blocks (all levels)] variable size [DDT parity blocks] M blocks @@ -499,5 +499,5 @@ The erasure coding structures are written after the index block, at the end of t [Index parity blocks] M blocks [ECMB (primary)] at footer.ecmbOffset [ECMB (duplicate)] at ecmbOffset + ecmbLength (aligned) -[Recovery Footer] last 160 bytes of file +[Recovery Footer] last 179 bytes of file ---- diff --git a/include/aaruformat/structs/erasure.h b/include/aaruformat/structs/erasure.h index 8623805..5e47526 100644 --- a/include/aaruformat/structs/erasure.h +++ b/include/aaruformat/structs/erasure.h @@ -117,7 +117,7 @@ typedef struct AaruRecoveryFooter { uint64_t ecmbOffset; ///< Absolute file offset of the primary ECMB. uint64_t ecmbLength; ///< Total on-disk size of the ECMB (header + payload). - uint64_t headerCrc64; ///< CRC64-ECMA of the original AaruHeaderV2 (128 bytes at offset 0). + uint64_t headerCrc64; ///< CRC64-ECMA of the original AaruHeaderV2 (147 bytes at offset 0). AaruHeaderV2 backupHeader; ///< Complete copy of AaruHeaderV2 from file offset 0. uint64_t footerMagic; ///< Must be AARU_RECOVERY_FOOTER_MAGIC (0x52464D4345525641). } AaruRecoveryFooter; diff --git a/src/erasure.c b/src/erasure.c index 819de3a..d9cd840 100644 --- a/src/erasure.c +++ b/src/erasure.c @@ -893,7 +893,7 @@ void ec_load_ecmb(aaruformat_context *ctx) { TRACE("Entering ec_load_ecmb(%p)", (void *)ctx); - /* Read recovery footer from last 160 bytes of file */ + /* Read recovery footer from last sizeof(AaruRecoveryFooter) bytes of file */ aaruf_fseek(ctx->imageStream, 0, SEEK_END); int64_t file_size = aaruf_ftell(ctx->imageStream); if(file_size < (int64_t)sizeof(AaruRecoveryFooter)) diff --git a/templates/aaruformat.hexpat b/templates/aaruformat.hexpat index 787885d..cefd957 100644 --- a/templates/aaruformat.hexpat +++ b/templates/aaruformat.hexpat @@ -1260,7 +1260,7 @@ struct AaruRecoveryFooter u64 ecmbOffset; u64 ecmbLength; u64 headerCrc64; - AaruHeader backupHeader; + u8 backupHeader[147]; // Raw AaruHeaderV2 bytes (NOT the parsed struct) u64 footerMagic; }; @@ -1316,19 +1316,19 @@ if(header.identifier != DIC_MAGIC && header.identifier != AARU_MAGIC) std::error("Incorrect signature!"); // Parse recovery footer at EOF (if present) -// The footer is the last 160 bytes of the file +// The footer is the last 179 bytes of the file u64 fileSize = std::mem::size(); -if(fileSize >= 160) +if(fileSize >= 179) { u64 footerMagicPos = fileSize - 8; u64 footerMagic = std::mem::read_unsigned(footerMagicPos, 8); if(footerMagic == 0x52464D4345525641) // "AVRECMFR" { - AaruRecoveryFooter recoveryFooter @ (fileSize - 160); + AaruRecoveryFooter recoveryFooter @ (fileSize - 179); // Parse the ECMB at the offset recorded in the recovery footer // (ECMB is NOT in the index due to chicken-egg: index itself needs recovery) - u64 ecmbOff = std::mem::read_unsigned(fileSize - 160, 8); + u64 ecmbOff = std::mem::read_unsigned(fileSize - 179, 8); if(ecmbOff > 0 && ecmbOff < fileSize) { ErasureCodingMapHeader ecmb @ ecmbOff;