From 05c0c7df8f6df29440d060c47b8a6499f04fe26c Mon Sep 17 00:00:00 2001 From: Janis Date: Wed, 12 Apr 2023 20:29:42 +0200 Subject: [PATCH] zstd decompression support - extracted common code out of get_inode_by_path functions --- btrfs/Cargo.toml | 1 + btrfs/src/v2/volume.rs | 138 ++++++++++++++++++++++++----------------- 2 files changed, 81 insertions(+), 58 deletions(-) diff --git a/btrfs/Cargo.toml b/btrfs/Cargo.toml index 09da630..51e2f86 100644 --- a/btrfs/Cargo.toml +++ b/btrfs/Cargo.toml @@ -24,6 +24,7 @@ num_enum = {version = "0.5.11", default-features = false} replace_with = "0.1.7" miniz_oxide = {version = "0.7.1"} +zstd-safe = "6.0.5+zstd.1.5.4" [dev-dependencies] diff --git a/btrfs/src/v2/volume.rs b/btrfs/src/v2/volume.rs index 4fc4c6d..f6033a2 100644 --- a/btrfs/src/v2/volume.rs +++ b/btrfs/src/v2/volume.rs @@ -6,7 +6,7 @@ use alloc::{collections::BTreeMap, rc::Rc, vec, vec::Vec}; use scroll::Pread; use crate::crc32c::calculate_crc32c; -use crate::path::Path; +use crate::path::{NormalizedPath, Path}; use crate::structs::{ Chunk, CompressionType, DirItemEntry, DirItemType, ExtentData, INodeItem, INodeRefEntry, Item, Key, KeyPtr, KnownObjectId, ObjectType, RootItem, Stripe, Superblock, TreeItem, @@ -430,33 +430,9 @@ impl Fs { P: Path, { if path.is_absolute() { - // stuff self.get_inode_by_path(path) } else { - let path = path.normalize().into_iter(); - let mut inode = inode; - - for segment in path { - match segment { - crate::path::Segment::ParentDir => { - inode = self.get_inode_parent(&inode)?; - } - crate::path::Segment::File(child_name) => { - let child = self - .get_inode_children_inodes(&inode)? - .find(|child| { - child.path.last().map(|bytes| bytes.as_slice()) == Some(child_name) - }) - .ok_or(Error::INodeNotFound)? - .clone(); - // silly borrow checker - inode = child; - } - _ => unreachable!(), - } - } - - Ok(inode) + self.get_inode_by_relative_normalized_path(inode, path.normalize()) } } @@ -472,21 +448,33 @@ impl Fs { _ = normalized.pop_segment(); } - let mut inode = self.get_root_dir(); + self.get_inode_by_relative_normalized_path(self.get_root_dir(), normalized) + } - while let Some(segment) = normalized.pop_segment() { + pub fn get_inode_by_relative_normalized_path( + &self, + inode: INode, + path: NormalizedPath, + ) -> Result { + let mut inode = inode; + + for segment in path.iter() { match segment { - crate::path::Segment::Root | crate::path::Segment::NoOp => {} // do nothing - crate::path::Segment::CurrentDir | crate::path::Segment::ParentDir => { - unimplemented!() - } // not normalized? - crate::path::Segment::File(child) => { - let dir_item = self - .find_inode_child(inode.id, child)? - .ok_or(Error::INodeNotFound)?; - - inode = inode.into_child(dir_item.item().location.id().into(), child.to_vec()); + crate::path::Segment::ParentDir => { + inode = self.get_inode_parent(&inode)?; } + crate::path::Segment::File(child_name) => { + let child = self + .get_inode_children_inodes(&inode)? + .find(|child| { + child.path.last().map(|bytes| bytes.as_slice()) == Some(child_name) + }) + .ok_or(Error::INodeNotFound)? + .clone(); + // silly borrow checker + inode = child; + } + _ => unreachable!(), } } @@ -565,6 +553,9 @@ impl Fs { core::ops::Bound::Unbounded => None, }; + // FIXME: offsets need to be calculated with the uncompressed length and offset + // currently are calculated with compressed length and offset afaik + log::info!("extents: {}", extents.len()); log::info!("{:?}", extents); for (offset, extent) in extents.into_iter().filter(|(offset, extent)| { @@ -608,7 +599,6 @@ impl Fs { start < extent_end } }) { - // let start = start.saturating_sub(offset); let end = end.map(|end| end - offset).unwrap_or(start + extent.len()); @@ -619,22 +609,20 @@ impl Fs { let data: alloc::borrow::Cow<[u8]> = match &extent { ExtentData::Inline { data, .. } => (&data[start as usize..end as usize]).into(), ExtentData::Other(extent) => { - let address = extent.address() + extent.offset() + start; + let address = extent.address() + extent.offset(); let address = self .volume .inner .offset_from_logical(address) .ok_or(Error::BadLogicalAddress)?; - let range = address - ..(address - + match extent.extent_data1().compression() { - // compressed size - CompressionType::Zlib - | CompressionType::Lzo - | CompressionType::ZStd => extent.size(), - _ => len, - }); + let range = match extent.extent_data1().compression() { + // compressed size + CompressionType::Zlib | CompressionType::Lzo | CompressionType::ZStd => { + address..address + extent.size() + } + _ => address + start..address + start + len, + }; let data = self.volume.inner.read_range(range).expect("bytes"); data.into() @@ -644,8 +632,10 @@ impl Fs { log::info!("reading {} bytes from file", data.len()); log::info!("compression: {:?}", extent.header().compression()); - let data = match extent.header().compression() { - CompressionType::None => data, + match extent.header().compression() { + CompressionType::None => { + contents.extend_from_slice(&data); + } CompressionType::Zlib => { let mut state = miniz_oxide::inflate::stream::InflateState::new( miniz_oxide::DataFormat::Zlib, @@ -682,22 +672,54 @@ impl Fs { .status .map_err(|_| Error::DecompressionError)?; - output_data.into() + // truncate inflated data if needed + contents + .extend_from_slice(&output_data[start as usize..(start + len) as usize]); } CompressionType::Lzo => { todo!() } CompressionType::ZStd => { - todo!() + let mut output_data = vec![0u8; extent.header().decoded_size() as usize]; + + let mut zstd = zstd_safe::DCtx::create(); + zstd.init().map_err(|e| { + log::error!("zstd init error: {}", zstd_safe::get_error_name(e)); + Error::DecompressionError + })?; + + let mut input = zstd_safe::InBuffer::around(&data); + let mut output = zstd_safe::OutBuffer::around(&mut output_data[..]); + + loop { + match zstd.decompress_stream(&mut output, &mut input) { + Ok(len) => { + if len == 0 { + break; + } + } + Err(e) => { + log::error!( + "zstd decompress stream error: {}", + zstd_safe::get_error_name(e) + ); + return Err(Error::DecompressionError); + } + } + + if output.pos() == extent.header().decoded_size() as usize { + break; + } + } + + contents + .extend_from_slice(&output_data[start as usize..(start + len) as usize]); } c => { log::error!("invalid compression type {:?}", c); - data + contents.extend_from_slice(&data); } - }; - - // truncate inflated data if needed - contents.extend_from_slice(&data[..len as usize]); + } } Ok(contents)