///|
/// Return validated codec options, if stored in the image. Values not needed
/// by the caller's decoder remain in their original wire representation.
pub fn Image::compression_options(self : Image) -> Bytes? raise SquashError {
if (self.superblock.flags & 0x400) == 0 {
guard self.superblock.compression != 5 else {
raise Invalid(24, "LZ4 requires compressor options")
}
return None
}
let (options, next) = self.metadata_block(96)
guard next <= self.superblock.inode_table else {
raise Invalid(96, "compressor options cross data area")
}
let expected = match self.superblock.compression {
1 | 3 | 4 | 5 => 8
6 => 4
_ => raise Unsupported("legacy LZMA compressor options")
}
guard options.length() == expected else {
raise Invalid(96, "compressor option length")
}
if self.superblock.compression == 5 {
guard u32(options, 0) == 1U && (u32(options, 4) & 0xfffffffeU) == 0U else {
raise Invalid(96, "unsupported LZ4 version/flags")
}
}
Some(options)
}
///|
fn Image::validate_data_extents(
self : Image,
tree : Array[TreeEntry],
) -> Unit raise SquashError {
ignore(self.compression_options())
let lower = if (self.superblock.flags & 0x400) != 0 {
self.metadata_block(96).1
} else {
96
}
let spans : Map[Int, UInt] = Map([])
let add = fn(at : Int, descriptor : UInt) -> Unit raise SquashError {
let size = (descriptor & 0xffffffU).reinterpret_as_int()
guard (descriptor & 0xfe000000U) == 0U &&
size > 0 &&
at >= lower &&
at <= self.superblock.inode_table &&
size <= self.superblock.inode_table - at else {
raise Invalid(at, "data extent outside data area")
}
if spans.get(at) is Some(previous) {
guard previous == descriptor else {
raise Invalid(at, "inconsistent shared data descriptor")
}
} else {
spans[at] = descriptor
}
}
let seen : Map[Int, Bool] = Map([])
for entry in tree {
if seen.contains(entry.inode.number) {
continue
}
seen[entry.inode.number] = true
if entry.inode.content is File(start, _, _, _, blocks) {
let mut at = start
for descriptor in blocks {
if descriptor != 0U {
add(at, descriptor)
at += (descriptor & 0xffffffU).reinterpret_as_int()
}
}
}
}
for i in 0..= end else {
raise Invalid(at, "partially overlapping data extents")
}
end = at + (spans[at] & 0xffffffU).reinterpret_as_int()
}
}