Skip to content

Commit

Permalink
Add support for OpenWrt compression options
Browse files Browse the repository at this point in the history
- Support OpenWrt XZ custom compression options
- Enable output of these options during FilesystemWriter

See #72
  • Loading branch information
wcampbell0x2a committed May 11, 2023
1 parent e79ccb6 commit d5908cc
Show file tree
Hide file tree
Showing 4 changed files with 69 additions and 30 deletions.
2 changes: 1 addition & 1 deletion src/bin/unsquashfs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ fn stat(args: Args, mut file: BufReader<File>, kind: Kind) {
println!("{superblock:#08x?}");

// show info about compression options
println!("Compression Options: {compression_options:#08x?}");
println!("Compression Options: {compression_options:#x?}");

// show info about flags
if superblock.inodes_uncompressed() {
Expand Down
73 changes: 56 additions & 17 deletions src/compressor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,19 +79,48 @@ pub struct Lzo {
pub struct Xz {
pub dictionary_size: u32,
pub filters: XzFilter,

// the rest of these fields are from OpenWRT. These are optional, as the kernel will ignore
// these fields when seen. We follow the same behaviour and don't attempt to parse if the bytes
// for these aren't found
// TODO: both are currently unused in this library
// TODO: in openwrt, git-hash:f97ad870e11ebe5f3dcf833dda6c83b9165b37cb shows that before
// offical squashfs-tools had xz support they had the dictionary_size field as the last field
// in this struct. If we get test images, I guess we can support this in the future.
#[deku(cond = "!deku::rest.is_empty()")]
pub bit_opts: Option<u16>,
#[deku(cond = "!deku::rest.is_empty()")]
pub fb: Option<u16>,
}

#[derive(Copy, Clone, Debug, PartialEq, Eq, DekuRead, DekuWrite)]
#[deku(endian = "endian", ctx = "endian: deku::ctx::Endian")]
#[deku(type = "u32")]
#[rustfmt::skip]
pub enum XzFilter {
X86 = 0x01,
PowerPC = 0x02,
IA64 = 0x04,
Arm = 0x08,
ArmThumb = 0x10,
Sparc = 0x20,
pub struct XzFilter(u32);

impl XzFilter {
fn x86(&self) -> bool {
self.0 & 0x0001 == 0x0001
}

fn powerpc(&self) -> bool {
self.0 & 0x0002 == 0x0002
}

fn ia64(&self) -> bool {
self.0 & 0x0004 == 0x0004
}

fn arm(&self) -> bool {
self.0 & 0x0008 == 0x0008
}

fn armthumb(&self) -> bool {
self.0 & 0x0010 == 0x0010
}

fn sparc(&self) -> bool {
self.0 & 0x0020 == 0x0020
}
}

#[derive(Debug, DekuRead, DekuWrite, PartialEq, Eq, Clone, Copy)]
Expand Down Expand Up @@ -227,14 +256,24 @@ impl CompressionAction for DefaultCompressor {

let mut filters = Filters::new();
if let Some(CompressionOptions::Xz(xz)) = option {
match xz.filters {
XzFilter::X86 => filters.x86(),
XzFilter::PowerPC => filters.powerpc(),
XzFilter::IA64 => filters.ia64(),
XzFilter::Arm => filters.arm(),
XzFilter::ArmThumb => filters.arm_thumb(),
XzFilter::Sparc => filters.sparc(),
};
if xz.filters.x86() {
filters.x86();
}
if xz.filters.powerpc() {
filters.powerpc();
}
if xz.filters.ia64() {
filters.ia64();
}
if xz.filters.arm() {
filters.arm();
}
if xz.filters.armthumb() {
filters.arm_thumb();
}
if xz.filters.sparc() {
filters.sparc();
}
}
filters.lzma2(&opts);

Expand Down
18 changes: 7 additions & 11 deletions src/squashfs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -306,22 +306,18 @@ impl Squashfs {
match superblock.compression_options_size() {
Some(size) => {
let bytes = metadata::read_block(reader, &superblock, kind)?;

// Some firmware (such as openwrt) that uses XZ compression has an extra 4 bytes.
// squashfs-tools/unsquashfs complains about this also
if bytes.len() != size {
tracing::warn!(
"Non standard compression options! CompressionOptions might be incorrect: {:02x?}",
bytes
);
}
// data -> compression options
let bv = BitVec::from_slice(&bytes);
match CompressionOptions::read(
&bv,
(deku::ctx::Endian::Little, superblock.compressor),
(kind.inner.type_endian, superblock.compressor),
) {
Ok(co) => Some(co.1),
Ok(co) => {
if !co.0.is_empty() {
error!("invalid compression options, bytes left over, using");
}
Some(co.1)
},
Err(e) => {
error!("invalid compression options: {e:?}[{bytes:02x?}], not using");
None
Expand Down
6 changes: 5 additions & 1 deletion tests/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ fn full_test(
let file = BufReader::new(File::open(&og_path).unwrap());
info!("calling from_reader");
let og_filesystem = FilesystemReader::from_reader_with_offset(file, offset).unwrap();
let og_comp_opts = og_filesystem.compression_options;
let mut new_filesystem = FilesystemWriter::from_fs_reader(&og_filesystem).unwrap();

// convert to bytes
Expand All @@ -51,7 +52,10 @@ fn full_test(
// assert that our library can atleast read the output, use unsquashfs to really assert this
info!("calling from_reader");
let created_file = BufReader::new(File::open(&new_path).unwrap());
let _new_filesystem = FilesystemReader::from_reader_with_offset(created_file, offset).unwrap();
let written_new_filesystem =
FilesystemReader::from_reader_with_offset(created_file, offset).unwrap();
let new_comp_opts = written_new_filesystem.compression_options;
assert_eq!(og_comp_opts, new_comp_opts);

match verify {
Verify::Extract => {
Expand Down

0 comments on commit d5908cc

Please sign in to comment.