#[non_exhaustive]pub enum Fidelity {
Show 15 variants
TrailingIndexUnread {
format: FormatId,
},
SizeFromDataDescriptor {
entry: String,
},
EntryCountUnknown,
EntryCountMismatch {
format: FormatId,
declared: u64,
enumerated: u64,
reason: String,
},
MetadataIncomplete {
entry: String,
fields: MetaFields,
},
SolidBlockFullyDecoded {
wasted_bytes: u64,
},
EncryptedEntrySkipped {
entry: String,
},
EntrySkipped {
entry: String,
reason: String,
},
EntrySizeChanged {
entry: String,
declared: u64,
fixup: String,
},
TruncatedStream {
at: u64,
},
DirectoryRecordChecksum {
format: FormatId,
offset: u64,
computed: u16,
recorded: u16,
},
HeadersDisagree {
format: FormatId,
entry: String,
fields: Vec<&'static str>,
},
HeadersDisagreeUnlisted {
format: FormatId,
entries: u64,
},
TrailingDataIgnored {
format: FormatId,
bytes: u64,
},
LocalHeadersUnchecked {
format: FormatId,
entries: u64,
},
}Expand description
One specific, structured thing that was lost or approximated.
Variants (Non-exhaustive)§
This enum is marked as non-exhaustive
TrailingIndexUnread
SizeFromDataDescriptor
EntryCountUnknown
EntryCountMismatch
The archive’s own index declares a different number of records than the reader enumerated. Usually FEWER were enumerated, so entries exist in the file that no caller can reach; zip can also enumerate MORE, when its end-of-central-directory record’s two counts contradict each other (the v0.7.0 deep-fuzz finding), so the message names both figures without saying which way round they are.
Distinct from Self::EntryCountUnknown, which is the forward reader
admitting it never saw a count at all. Here the count WAS read, from
the format’s authoritative structure, and it disagrees with what came
back — the strongest evidence available that a read is incomplete, and
the one a seekable Rung::Exact read would otherwise have no way to
express. Without it a partial enumeration is indistinguishable from a
complete one, which is the failure this variant exists to prevent:
listing six of eight records and calling it exact fidelity.
Raised, not recovered. Reaching the shadowed records would mean
re-parsing the index by hand; naming the shortfall is what the caller
needs in order to know not to trust the result, and
--strict-fidelity turns it into exit 4.
reason is an owned String for the same reason
Self::EntrySkipped’s is: the cause is specific to the container
and the reader, and a shortfall that cannot say WHY is one the user
cannot act on.
Fields
MetadataIncomplete
SolidBlockFullyDecoded
EncryptedEntrySkipped
EntrySkipped
An entry this build cannot materialise, named along with why.
Distinct from Self::MetadataIncomplete, which is about an entry
that WAS written and lost some of its metadata: here the entry itself
is absent from the result. Extraction raises it for a device node,
fifo or socket — crate::EntryKind::Other, the honest answer a
container gives for an entry shape EntryKind has no variant for yet
— and for a crate::EntryKind::Hardlink whose target it did not
write, or whose target is a directory. Writing one out as a regular
file carrying its “contents” would be a silent lie about what the
archive held. The write side
raises it too, for something met on disk that no container here can
store — a socket, an undecodable name, a directory being packed into
ar.
reason is an owned String rather than a &'static str because the
write side’s reasons carry runtime detail — the failing errno, the
container’s own name, the undecodable bytes — and a reason that cannot
name the particular thing that went wrong is a reason the user cannot
act on.
EntrySizeChanged
A file that changed length between the moment its size was recorded and the moment its bytes were read, stored at the length its header already declares.
Distinct from Self::EntrySkipped: the entry IS in the archive, and
the archive is structurally valid. What was lost is the tail of a file
that grew, or the tail of a file that shrank — replaced by zeros,
because the entry header was written before the payload was read and a
container cannot un-write it. Refusing instead would throw away the
whole archive over one file that a live system happened to touch,
which is what pack ~ -o backup.tar over a running home directory does
routinely; GNU tar prints file changed as we read it and continues,
and this is the same bargain, with the loss named rather than printed
and forgotten.
--strict-fidelity still refuses on it, which is the half of the
bargain that keeps it honest.
Fields
fixup: StringWhat the file actually supplied and what was done about it.
Prose rather than a second u64 because the two directions know
different things: a file that shrank supplies an exact count, while
a file that grew is only known to have had more — measuring how
much more would mean reading a tail that is deliberately not being
stored. A field that could name the figure in one case and not the
other would have to lie in one of them.
TruncatedStream
DirectoryRecordChecksum
A directory record carries a checksum over ITSELF, and it does not match the bytes the record is made of.
Distinct from every other integrity signal in this enum: those are
about an entry’s CONTENT, and a content checksum that fails is
crate::Error::Corrupt, not a warning, because there is nothing
honest to hand back. This is about the METADATA record, and the
verdict is deliberately softer for one measured reason — the format
that carries such a field may itself treat it as advisory. ZOO is the
first: zoo 2.10’s own lister prints a * beside an entry whose
dir_crc fails and carries on listing it, so refusing would be
stricter than the reference implementation, which is how a guard comes
to fire on input a real tool accepts.
What it must NOT do is stay silent. A record whose own checksum fails
has been altered since it was written, and every field a caller is
about to trust — the entry’s name, size, offset, and the link to the
next record — comes out of it. --strict-fidelity turns this into
exit 4, which is the whole point: a verb that says “exact fidelity”
over an altered record is making a claim the archive itself disputes.
offset is the record’s own position in the file, because a record
that fails its checksum may have no usable name to identify it by —
the reproducer that motivated this variant is a record whose name
field is intact and whose link field is not.
HeadersDisagree
A zip entry’s local file header and its central-directory record disagree on a field both carry — the “cloaking” shape (GHSA-m665-v7pm- 7grj), where one archive shows different names or contents depending on which of its two descriptions a reader trusts.
A warning, not crate::Error::Corrupt: the bytes handed back are
still a coherent reading (the central directory’s on a seekable
source, the local headers’ on a pipe), and the archive is not
self-contradictory within either description. What cannot be claimed
is that the reading is the only one, so --strict-fidelity turns this
into exit 4.
fields lists, in a fixed order and never empty, every field the
two records disagree on for this one entry — one warning per entry,
not per field (it was per field up to 0.22.0, as field). Each is
one of "order", "name", "compression method", "CRC-32",
"compressed size", "uncompressed size", "encryption flag",
"data-descriptor flag", "UTF-8 flag" or "entry count". entry
is the name as the authoritative description (the central directory,
when it was read) spells it, decoded as the reader decodes names.
An archive lists at most 64 entries this way; the rest are counted
in one Fidelity::HeadersDisagreeUnlisted.
HeadersDisagreeUnlisted
More entries whose local header and central record disagree, past
the 64 an archive lists as Fidelity::HeadersDisagree: entries
counts them. Each listed warning copies its entry’s name, so an
archive of a hundred thousand cloaked records would otherwise make a
report as large as its index; the count keeps the verdict
(--strict-fidelity exits 4 on either) without the copies.
TrailingDataIgnored
A codec stream whose last complete, verified member (or stream) is followed by bytes that are neither another member nor padding, and were ignored.
gzip and bzip2 files are legitimately concatenations, and the tools
that write them sometimes pad the end with NUL or 'X' bytes, which
stay silent. Anything else after a verified member is ambiguous: it
may be harmless junk, or it may be the next member with its first
magic byte flipped — one byte that drops every later member while the
members before it still verify. The data handed back is the members
that did verify, so this is a warning rather than
crate::Error::Corrupt (the reference tools likewise say “trailing
garbage ignored” and recover the data), and --strict-fidelity turns
it into exit 4. A tail that starts like a damaged member header — the
magic intact and a field behind it impossible — is Corrupt instead,
and never reaches this variant.
bytes is how many bytes were ignored, counted while the tail was
read through (never buffered).
LocalHeadersUnchecked
The local headers of entries entries were not compared with their
central-directory records (Fidelity::HeadersDisagree could not be
ruled out for them), because the comparison ran out of its work
budget.
Each comparison reads a local header — up to 128 KiB of name and extra
field — and an index whose records keep pointing at the same few fat
headers (or at headers overlapping one another) makes every record pay
that again. The reads are bounded by a budget proportional to the
archive’s own length, generous enough that a well-formed archive,
which reads each local header once, never reaches it. Past it, the
remaining entries are delivered as the central directory describes
them, unchecked, and this says so; --strict-fidelity turns it into
exit 4.
Trait Implementations§
impl Eq for Fidelity
Source§impl Error for Fidelity
impl Error for Fidelity
1.30.0 · Source§fn source(&self) -> Option<&(dyn Error + 'static)>
fn source(&self) -> Option<&(dyn Error + 'static)>
1.0.0 · Source§fn description(&self) -> &str
fn description(&self) -> &str
use the Display impl or to_string()