On this page· 3
Open and verify a pack
- file format 6
- Rust SDK 1.0 preview
Goal: open a pack, read its verify report, then change one letter and see what is reported.
- 1. Open the bytes with
Pack::from_bytesandOpenOptions::default(). The default verifies steps 1 to 8. - 2. Call
pack.verify()for a report. On a good pack,report.is_ok()istrue. - 3. Change one byte of the record text:
Personbecomesperson. Opening the changed bytes with the default options fails withchecksum_mismatch. - 4. Open the changed bytes with
OpenOptions::no_verify()and callverify(). The report hassha256_ok: false.
Needs the Rust SDK
verify.rsrust
// Verify a pack, and see what a damaged pack reports.
use plxi_sdk::{write, ErrorKind, OpenOptions, Pack, Record};
fn main() -> Result<(), plxi_sdk::Error> {
let records = vec![
Record::from_json_line(r#"{"k":"ent","id":"e1","t":"Person"}"#)?,
Record::from_json_line(r#"{"k":"ent","id":"e2","t":"Person"}"#)?,
];
let good = write(&records, None)?;
// A good pack: every member of the report is fine.
let pack = Pack::from_bytes(good.clone(), OpenOptions::default())?;
let report = pack.verify()?;
assert!(report.is_ok());
let count = &report.record_count;
assert_eq!((count.declared, count.actual), (2, 2));
println!("{}", report.to_json());
// Change one letter in the record text after the pack was sealed:
// "Person" becomes "person". The JSON is still valid; the digest
// no longer matches.
let mut damaged = good;
let at = damaged
.windows(6)
.position(|w| w == b"Person")
.expect("the record text contains \"Person\"");
damaged[at] = b'p';
// The default open refuses it and names the reason.
let err = Pack::from_bytes(damaged.clone(), OpenOptions::default())
.unwrap_err();
assert_eq!(err.kind(), ErrorKind::ChecksumMismatch);
assert_eq!(err.kind().as_str(), "checksum_mismatch");
println!("refused: {err}");
// With verification off the pack opens, and the report says what
// is wrong with it.
let pack = Pack::from_bytes(damaged, OpenOptions::no_verify())?;
let report = pack.verify()?;
assert!(!report.sha256_ok);
assert!(!report.is_ok());
assert_eq!(report.record_count.actual, 2); // the records still parse
println!("{}", report.to_json());
Ok(())
}
Output
{"sha256_ok":true,"header_footer_agree":true,"record_count":{"declared":2,"actual":2},"appendix":{"present":false,"file_checksum_ok":true,"sections":[]},"embrefs":{"total":0,"resolved":0,"dangling":0,"type_mismatch":0}}
refused: checksum_mismatch: the file's SHA-256 digest does not match the one recorded in its footer
{"sha256_ok":false,"header_footer_agree":true,"record_count":{"declared":2,"actual":2},"appendix":{"present":false,"file_checksum_ok":true,"sections":[]},"embrefs":{"total":0,"resolved":0,"dangling":0,"type_mismatch":0}}Ran with cargo run --example 02_verify · exit status 0
The first line is the report for the good pack. The second is the refusal: the error kind, a colon, then a message for people. The third is the report for the changed pack, opened without verification.
| Member | Meaning |
|---|---|
sha256_ok | the computed digest equals the footer's |
header_footer_agree | a final header line equals the footer; a placeholder header counts as agreeing |
record_count | declared is the footer's count; actual is the number of record lines read |
appendix | present; file_checksum_ok; and sections, one {index, type, crc_ok} per section |
embrefs | counts of bindings: total, resolved, dangling, type_mismatch |
- Opening verifies by default.
no_verify()exists for the case shown: reading a report from a pack already known to be damaged. Records read from a pack opened that way may come from a damaged body. - The message after the kind is for people and can change between releases. Code branches on the kind.
- If the changed byte breaks a record's JSON,
verify()returns an error,jsonorinvalid_record, instead of a report. This guide changes a letter inside a string so the line still parses. Pack::opendoes the same for a file path, and addsiowhen the file cannot be read.