compression support for zlib
This commit is contained in:
parent
3aa8ecbd77
commit
8ba04a0b94
|
@ -23,6 +23,14 @@ thiserror = { version = "1.0", package = "thiserror-core", default-features = fa
|
||||||
num_enum = {version = "0.5.11", default-features = false}
|
num_enum = {version = "0.5.11", default-features = false}
|
||||||
replace_with = "0.1.7"
|
replace_with = "0.1.7"
|
||||||
|
|
||||||
|
miniz_oxide = {version = "0.7.1"}
|
||||||
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
env_logger = "*"
|
env_logger = "*"
|
||||||
test-log = "*"
|
test-log = "*"
|
||||||
|
|
||||||
|
include-blob = "0.1.2"
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
include-blob = "0.1.2"
|
|
@ -360,6 +360,17 @@ impl Parseable for ExtentData {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[repr(u8)]
|
||||||
|
#[derive(Debug, Clone, Copy, FromPrimitive, IntoPrimitive)]
|
||||||
|
pub enum CompressionType {
|
||||||
|
None = 0,
|
||||||
|
Zlib,
|
||||||
|
Lzo,
|
||||||
|
ZStd,
|
||||||
|
#[num_enum(catch_all)]
|
||||||
|
Invalid(u8),
|
||||||
|
}
|
||||||
|
|
||||||
#[repr(C, packed(1))]
|
#[repr(C, packed(1))]
|
||||||
#[derive(Debug, Clone, Copy, FromBytes, AsBytes)]
|
#[derive(Debug, Clone, Copy, FromBytes, AsBytes)]
|
||||||
pub struct ExtentData1 {
|
pub struct ExtentData1 {
|
||||||
|
@ -372,6 +383,14 @@ pub struct ExtentData1 {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ExtentData1 {
|
impl ExtentData1 {
|
||||||
|
pub fn decoded_size(&self) -> u64 {
|
||||||
|
self.decoded_size.get()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn compression(&self) -> CompressionType {
|
||||||
|
self.compression.into()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn ty(&self) -> ExtentDataType {
|
pub fn ty(&self) -> ExtentDataType {
|
||||||
match self.ty {
|
match self.ty {
|
||||||
0 => ExtentDataType::Inline,
|
0 => ExtentDataType::Inline,
|
||||||
|
|
|
@ -21,6 +21,8 @@ pub mod error {
|
||||||
NoDefaultSubvolFsRoot,
|
NoDefaultSubvolFsRoot,
|
||||||
#[error("INode could not be found in FsTree")]
|
#[error("INode could not be found in FsTree")]
|
||||||
INodeNotFound,
|
INodeNotFound,
|
||||||
|
#[error("decompression error")]
|
||||||
|
DecompressionError,
|
||||||
#[error("attempted to access {index}th item out of bounds {range:?}")]
|
#[error("attempted to access {index}th item out of bounds {range:?}")]
|
||||||
OutOfBounds {
|
OutOfBounds {
|
||||||
range: core::ops::Range<usize>,
|
range: core::ops::Range<usize>,
|
||||||
|
|
|
@ -566,6 +566,8 @@ impl<R: super::Read> Fs<R> {
|
||||||
core::ops::Bound::Unbounded => None,
|
core::ops::Bound::Unbounded => None,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
log::info!("extents: {}", extents.len());
|
||||||
|
log::info!("{:?}", extents);
|
||||||
for (offset, extent) in extents.into_iter().filter(|(offset, extent)| {
|
for (offset, extent) in extents.into_iter().filter(|(offset, extent)| {
|
||||||
let extent_start = *offset;
|
let extent_start = *offset;
|
||||||
let extent_end = extent_start + extent.len();
|
let extent_end = extent_start + extent.len();
|
||||||
|
@ -602,6 +604,18 @@ impl<R: super::Read> Fs<R> {
|
||||||
}
|
}
|
||||||
ExtentData::Other(extent) => {
|
ExtentData::Other(extent) => {
|
||||||
let address = extent.address() + extent.offset() + start;
|
let address = extent.address() + extent.offset() + start;
|
||||||
|
log::info!(
|
||||||
|
"address: {} = {} + {} + {}",
|
||||||
|
address,
|
||||||
|
extent.address(),
|
||||||
|
extent.offset(),
|
||||||
|
start
|
||||||
|
);
|
||||||
|
let address = self
|
||||||
|
.volume
|
||||||
|
.inner
|
||||||
|
.offset_from_logical(address)
|
||||||
|
.ok_or(Error::BadLogicalAddress)?;
|
||||||
let data = self
|
let data = self
|
||||||
.volume
|
.volume
|
||||||
.inner
|
.inner
|
||||||
|
@ -612,6 +626,63 @@ impl<R: super::Read> Fs<R> {
|
||||||
};
|
};
|
||||||
|
|
||||||
log::info!("reading {} bytes from file", data.len());
|
log::info!("reading {} bytes from file", data.len());
|
||||||
|
log::info!("extent type: {:?}", extent.header().ty());
|
||||||
|
log::info!("compression: {:?}", extent.header().compression());
|
||||||
|
log::info!("raw bytes: {:?}", data);
|
||||||
|
let data = match extent.header().compression() {
|
||||||
|
crate::structs::CompressionType::None => data,
|
||||||
|
crate::structs::CompressionType::Zlib => {
|
||||||
|
let mut state = miniz_oxide::inflate::stream::InflateState::new(
|
||||||
|
miniz_oxide::DataFormat::Zlib,
|
||||||
|
);
|
||||||
|
let mut output_data = vec![0u8; extent.header().decoded_size() as usize];
|
||||||
|
let mut output = &mut output_data[..];
|
||||||
|
let mut data = &data[..];
|
||||||
|
loop {
|
||||||
|
let result = miniz_oxide::inflate::stream::inflate(
|
||||||
|
&mut state,
|
||||||
|
&data,
|
||||||
|
&mut output,
|
||||||
|
miniz_oxide::MZFlush::None,
|
||||||
|
);
|
||||||
|
|
||||||
|
log::info!("inflated: {:?}", result);
|
||||||
|
|
||||||
|
match result.status.map_err(|_| Error::DecompressionError)? {
|
||||||
|
miniz_oxide::MZStatus::Ok => {}
|
||||||
|
miniz_oxide::MZStatus::StreamEnd => break,
|
||||||
|
_ => {
|
||||||
|
log::error!("need dict ?!");
|
||||||
|
return Err(Error::DecompressionError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data = &data[result.bytes_consumed..];
|
||||||
|
output = &mut output[result.bytes_written..];
|
||||||
|
}
|
||||||
|
_ = miniz_oxide::inflate::stream::inflate(
|
||||||
|
&mut state,
|
||||||
|
&data,
|
||||||
|
&mut output,
|
||||||
|
miniz_oxide::MZFlush::Finish,
|
||||||
|
)
|
||||||
|
.status
|
||||||
|
.map_err(|_| Error::DecompressionError)?;
|
||||||
|
|
||||||
|
output_data.into()
|
||||||
|
}
|
||||||
|
crate::structs::CompressionType::Lzo => {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
crate::structs::CompressionType::ZStd => {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
c => {
|
||||||
|
log::error!("invalid compression type {:?}", c);
|
||||||
|
data
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
contents.extend_from_slice(&data);
|
contents.extend_from_slice(&data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,31 @@ use btrfs::v2::volume::*;
|
||||||
use include_blob::include_blob;
|
use include_blob::include_blob;
|
||||||
|
|
||||||
fn open_filesystem() -> Result<std::rc::Rc<Volume2<&'static [u8]>>> {
|
fn open_filesystem() -> Result<std::rc::Rc<Volume2<&'static [u8]>>> {
|
||||||
let filesystem_data = include_bytes!("../simple.img").as_slice();
|
let filesystem_data = include_blob!("simple.img").as_slice();
|
||||||
|
|
||||||
|
let volume = Volume::new(filesystem_data)?.into_volume2()?;
|
||||||
|
|
||||||
|
Ok(volume)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn open_filesystem_lzo() -> Result<std::rc::Rc<Volume2<&'static [u8]>>> {
|
||||||
|
let filesystem_data = include_blob!("compressed-lzo.img").as_slice();
|
||||||
|
|
||||||
|
let volume = Volume::new(filesystem_data)?.into_volume2()?;
|
||||||
|
|
||||||
|
Ok(volume)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn open_filesystem_zlib() -> Result<std::rc::Rc<Volume2<&'static [u8]>>> {
|
||||||
|
let filesystem_data = include_blob!("compressed-zlib.img").as_slice();
|
||||||
|
|
||||||
|
let volume = Volume::new(filesystem_data)?.into_volume2()?;
|
||||||
|
|
||||||
|
Ok(volume)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn open_filesystem_zstd() -> Result<std::rc::Rc<Volume2<&'static [u8]>>> {
|
||||||
|
let filesystem_data = include_blob!("compressed-zstd.img").as_slice();
|
||||||
|
|
||||||
let volume = Volume::new(filesystem_data)?.into_volume2()?;
|
let volume = Volume::new(filesystem_data)?.into_volume2()?;
|
||||||
|
|
||||||
|
@ -14,9 +38,7 @@ fn open_filesystem() -> Result<std::rc::Rc<Volume2<&'static [u8]>>> {
|
||||||
|
|
||||||
#[test_log::test]
|
#[test_log::test]
|
||||||
fn asdf() -> Result<()> {
|
fn asdf() -> Result<()> {
|
||||||
let filesystem_data = include_bytes!("../simple.img").as_slice();
|
let a = open_filesystem()?;
|
||||||
let volume = Volume::new(filesystem_data)?;
|
|
||||||
//.into_volume2();
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -121,9 +143,75 @@ fn find_file() -> Result<()> {
|
||||||
log::info!("chidlren: {:?}", children);
|
log::info!("chidlren: {:?}", children);
|
||||||
|
|
||||||
let cmake_list = fs.get_inode_by_path(b"/quibble/LICENCE")?;
|
let cmake_list = fs.get_inode_by_path(b"/quibble/LICENCE")?;
|
||||||
let file_contents = fs
|
let file_contents = fs.read_inode_raw(&cmake_list, ..).expect("file contents");
|
||||||
.read_inode_raw(&cmake_list, ..100)
|
assert_eq!(
|
||||||
.expect("file contents");
|
&file_contents[..52],
|
||||||
|
b" GNU LESSER GENERAL PUBLIC LICENSE"
|
||||||
|
);
|
||||||
|
log::info!("license file:");
|
||||||
|
log::info!("{}", String::from_utf8_lossy(&file_contents));
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test_log::test]
|
||||||
|
fn find_file_zlib() -> Result<()> {
|
||||||
|
let v2 = open_filesystem_zlib()?;
|
||||||
|
let fs = v2.default_subvolume().expect("default subvol");
|
||||||
|
|
||||||
|
let root_dir = fs.get_root_dir();
|
||||||
|
let children = fs.get_inode_children(&root_dir)?.collect::<Vec<_>>();
|
||||||
|
log::info!("chidlren: {:?}", children);
|
||||||
|
|
||||||
|
let cmake_list = fs.get_inode_by_path(b"/quibble/LICENCE")?;
|
||||||
|
let file_contents = fs.read_inode_raw(&cmake_list, ..).expect("file contents");
|
||||||
|
//assert_eq!(&file_contents[..11], b"hello world");
|
||||||
|
log::info!("license file:");
|
||||||
|
log::info!("{}", String::from_utf8_lossy(&file_contents));
|
||||||
|
assert_eq!(
|
||||||
|
&file_contents[..52],
|
||||||
|
b" GNU LESSER GENERAL PUBLIC LICENSE"
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test_log::test]
|
||||||
|
fn find_file_lzo() -> Result<()> {
|
||||||
|
let v2 = open_filesystem_lzo()?;
|
||||||
|
let fs = v2.default_subvolume().expect("default subvol");
|
||||||
|
|
||||||
|
let root_dir = fs.get_root_dir();
|
||||||
|
let children = fs.get_inode_children(&root_dir)?.collect::<Vec<_>>();
|
||||||
|
log::info!("chidlren: {:?}", children);
|
||||||
|
|
||||||
|
let cmake_list = fs.get_inode_by_path(b"/quibble/LICENCE")?;
|
||||||
|
let file_contents = fs.read_inode_raw(&cmake_list, ..).expect("file contents");
|
||||||
|
assert_eq!(
|
||||||
|
&file_contents[..52],
|
||||||
|
b" GNU LESSER GENERAL PUBLIC LICENSE"
|
||||||
|
);
|
||||||
|
log::info!("license file:");
|
||||||
|
log::info!("{}", String::from_utf8_lossy(&file_contents));
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test_log::test]
|
||||||
|
fn find_file_zstd() -> Result<()> {
|
||||||
|
let v2 = open_filesystem_zstd()?;
|
||||||
|
let fs = v2.default_subvolume().expect("default subvol");
|
||||||
|
|
||||||
|
let root_dir = fs.get_root_dir();
|
||||||
|
let children = fs.get_inode_children(&root_dir)?.collect::<Vec<_>>();
|
||||||
|
log::info!("chidlren: {:?}", children);
|
||||||
|
|
||||||
|
let cmake_list = fs.get_inode_by_path(b"/quibble/LICENCE")?;
|
||||||
|
let file_contents = fs.read_inode_raw(&cmake_list, ..).expect("file contents");
|
||||||
|
assert_eq!(
|
||||||
|
&file_contents[..52],
|
||||||
|
b" GNU LESSER GENERAL PUBLIC LICENSE"
|
||||||
|
);
|
||||||
log::info!("license file:");
|
log::info!("license file:");
|
||||||
log::info!("{}", String::from_utf8_lossy(&file_contents));
|
log::info!("{}", String::from_utf8_lossy(&file_contents));
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue