Repository: wcampbell0x2a/backhand (language: rust)

Issue:
Could not read image: deku error: Assertion("ExtendedDirectory.index_count field failed assertion: * index_count < 256")
Run `unsquashfs-backhand` got error:

```
root@MagMilk [ test@master ] # ../target/release/unsquashfs-backhand ../aosc-os_desktop_20240916_amd64.squashfs
  Could not read image: deku error: Assertion("ExtendedDirectory.index_count field failed assertion: * index_count < 256") 
```

image: https://releases.aosc.io/os-amd64/desktop/aosc-os_desktop_20240916_amd64.squashfs

info with `file`:

```
saki@MagMilk [ backhand@master ] $ file aosc-os_desktop_20240916_amd64.squashfs
aosc-os_desktop_20240916_amd64.squashfs: Squashfs filesystem, little endian, version 4.0, xz compressed, 6455197643 bytes, 549597 inodes, blocksize: 131072 bytes, created: Mon Sep 16 11:46:29 2024
```


Discussion / hints:

Fix patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index ca82402d..fc4674ad 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,6 +6,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
 and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
 
 ## [Unreleased]
+### `backhand`
+- Remove incorrect check for `ExtendedDirectory` index count ([#691](https://github.com/wcampbell0x2a/backhand/pull/691))
+- Correctly support `ExtendedFile` ([#691](https://github.com/wcampbell0x2a/backhand/pull/691))
 
 ## [v0.19.0] - 2024-11-12
 ### `backhand`
diff --git a/backhand-cli/src/bin/unsquashfs.rs b/backhand-cli/src/bin/unsquashfs.rs
index 84a6aa5f..a6572447 100644
--- a/backhand-cli/src/bin/unsquashfs.rs
+++ b/backhand-cli/src/bin/unsquashfs.rs
@@ -468,8 +468,8 @@ fn extract_all<'a, S: ParallelIterator<Item = &'a Node<SquashfsFileReader>>>(
 
                 // write to file
                 let fd = File::create(&filepath).unwrap();
-                let mut writer = BufWriter::with_capacity(file.basic.file_size as usize, &fd);
-                let file = filesystem.file(&file.basic);
+                let mut writer = BufWriter::with_capacity(file.file_len(), &fd);
+                let file = filesystem.file(file);
                 let mut reader = file.reader();
 
                 match io::copy(&mut reader, &mut writer) {
diff --git a/backhand/src/data.rs b/backhand/src/data.rs
index 452a1e9b..306501f7 100644
--- a/backhand/src/data.rs
+++ b/backhand/src/data.rs
@@ -137,7 +137,7 @@ impl<'a> DataWriter<'a> {
         mut writer: W,
     ) -> Result<(usize, Added), BackhandError> {
         //just clone it, because block sizes where never modified, just copy it
-        let mut block_sizes = reader.file.basic.block_sizes.clone();
+        let mut block_sizes = reader.file.file.block_sizes().to_vec();
         let mut read_buf = vec![];
         let mut decompress_buf = vec![];
 
@@ -190,7 +190,7 @@ impl<'a> DataWriter<'a> {
                 writer.write_all(&read_buf)?;
             }
         }
-        let file_size = reader.file.basic.file_size as usize;
+        let file_size = reader.file.file.file_len();
         Ok((file_size, Added::Data { blocks_start, block_sizes }))
     }
 
diff --git a/backhand/src/filesystem/node.rs b/backhand/src/filesystem/node.rs
index 40d7fd7c..a974c3bd 100644
--- a/backhand/src/filesystem/node.rs
+++ b/backhand/src/filesystem/node.rs
@@ -6,8 +6,8 @@ use std::sync::{Arc, Mutex};
 
 use super::normalize_squashfs_path;
 use crate::data::Added;
-use crate::inode::{BasicFile, InodeHeader};
-use crate::{BackhandError, FilesystemReaderFile, Id};
+use crate::inode::{BasicFile, ExtendedFile, InodeHeader};
+use crate::{BackhandError, DataSize, FilesystemReaderFile, Id};
 
 /// File information for Node
 #[derive(Debug, PartialEq, Eq, Default, Clone, Copy)]
@@ -91,8 +91,46 @@ pub enum InnerNode<T> {
 
 /// Unread file for filesystem
 #[derive(Debug, PartialEq, Eq, Clone)]
-pub struct SquashfsFileReader {
-    pub basic: BasicFile,
+pub enum SquashfsFileReader {
+    Basic(BasicFile),
+    Extended(ExtendedFile),
+}
+
+impl SquashfsFileReader {
+    pub fn file_len(&self) -> usize {
+        match self {
+            SquashfsFileReader::Basic(basic) => basic.file_size as usize,
+            SquashfsFileReader::Extended(extended) => extended.file_size as usize,
+        }
+    }
+
+    pub fn frag_index(&self) -> usize {
+        match self {
+            SquashfsFileReader::Basic(basic) => basic.frag_index as usize,
+            SquashfsFileReader::Extended(extended) => extended.frag_index as usize,
+        }
+    }
+
+    pub fn block_sizes(&self) -> &[DataSize] {
+        match self {
+            SquashfsFileReader::Basic(basic) => &basic.block_sizes,
+            SquashfsFileReader::Extended(extended) => &extended.block_sizes,
+        }
+    }
+
+    pub fn blocks_start(&self) -> u64 {
+        match self {
+            SquashfsFileReader::Basic(basic) => basic.blocks_start as u64,
+            SquashfsFileReader::Extended(extended) => extended.blocks_start,
+        }
+    }
+
+    pub fn block_offset(&self) -> u32 {
+        match self {
+            SquashfsFileReader::Basic(basic) => basic.block_offset,
+            SquashfsFileReader::Extended(extended) => extended.block_offset,
+        }
+    }
 }
 
 /// Read file from other SquashfsFile or an user file
diff --git a/backhand/src/filesystem/reader.rs b/backhand/src/filesystem/reader.rs
index c76835b1..eddcf2fe 100644
--- a/backhand/src/filesystem/reader.rs
+++ b/backhand/src/filesystem/reader.rs
@@ -7,7 +7,6 @@ use crate::data::DataSize;
 use crate::error::BackhandError;
 use crate::fragment::Fragment;
 use crate::id::Id;
-use crate::inode::BasicFile;
 use crate::kinds::Kind;
 use crate::reader::BufReadSeek;
 use crate::squashfs::Cache;
@@ -130,8 +129,8 @@ impl<'b> FilesystemReader<'b> {
     }
 
     /// Return a file handler for this file
-    pub fn file<'a>(&'a self, basic_file: &'a BasicFile) -> FilesystemReaderFile<'a, 'b> {
-        FilesystemReaderFile::new(self, basic_file)
+    pub fn file<'a>(&'a self, file: &'a SquashfsFileReader) -> FilesystemReaderFile<'a, 'b> {
+        FilesystemReaderFile::new(self, file)
     }
 
     /// Iterator of all files, including the root
@@ -154,7 +153,7 @@ impl<'b> FilesystemReader<'b> {
     ///     match &node.inner {
     ///         InnerNode::File(file) => {
     ///             let mut reader = filesystem
-    ///                 .file(&file.basic)
+    ///                 .file(&file)
     ///                 .reader();
     ///             // Then, do something with the reader
     ///         },
@@ -171,12 +170,12 @@ impl<'b> FilesystemReader<'b> {
 #[derive(Copy, Clone)]
 pub struct FilesystemReaderFile<'a, 'b> {
     pub(crate) system: &'a FilesystemReader<'b>,
-    pub(crate) basic: &'a BasicFile,
+    pub(crate) file: &'a SquashfsFileReader,
 }
 
 impl<'a, 'b> FilesystemReaderFile<'a, 'b> {
-    pub fn new(system: &'a FilesystemReader<'b>, basic: &'a BasicFile) -> Self {
-        Self { system, basic }
+    pub fn new(system: &'a FilesystemReader<'b>, file: &'a SquashfsFileReader) -> Self {
+        Self { system, file }
     }
 
     /// Create [`SquashfsReadFile`] that impls [`std::io::Read`] from [`FilesystemReaderFile`].
@@ -190,18 +189,15 @@ impl<'a, 'b> FilesystemReaderFile<'a, 'b> {
     }
 
     pub fn fragment(&self) -> Option<&'a Fragment> {
-        if self.basic.frag_index == 0xffffffff {
+        if self.file.frag_index() == 0xffffffff {
             None
         } else {
-            self.system
-                .fragments
-                .as_ref()
-                .map(|fragments| &fragments[self.basic.frag_index as usize])
+            self.system.fragments.as_ref().map(|fragments| &fragments[self.file.frag_index()])
         }
     }
 
     pub(crate) fn raw_data_reader(&self) -> SquashfsRawData<'a, 'b> {
-        SquashfsRawData::new(Self { system: self.system, basic: self.basic })
+        SquashfsRawData::new(Self { system: self.system, file: self.file })
     }
 }
 
@@ -210,7 +206,7 @@ impl<'a> IntoIterator for FilesystemReaderFile<'a, '_> {
     type Item = <BlockIterator<'a> as Iterator>::Item;
 
     fn into_iter(self) -> Self::IntoIter {
-        BlockIterator { blocks: &self.basic.block_sizes, fragment: self.fragment() }
+        BlockIterator { blocks: self.file.block_sizes(), fragment: self.fragment() }
     }
 }
 
@@ -252,7 +248,7 @@ pub(crate) struct SquashfsRawData<'a, 'b> {
 
 impl<'a, 'b> SquashfsRawData<'a, 'b> {
     pub fn new(file: FilesystemReaderFile<'a, 'b>) -> Self {
-        let pos = file.basic.blocks_start.into();
+        let pos = file.file.blocks_start();
         let current_block = file.into_iter();
         Self { file, current_block, pos }
     }
@@ -337,10 +333,10 @@ impl<'a, 'b> SquashfsRawData<'a, 'b> {
     #[inline]
     fn fragment_range(&self) -> std::ops::Range<usize> {
         let block_len = self.file.system.block_size as usize;
-        let block_num = self.file.basic.block_sizes.len();
-        let file_size = self.file.basic.file_size as usize;
+        let block_num = self.file.file.block_sizes().len();
+        let file_size = self.file.file.file_len();
         let frag_len = file_size - (block_num * block_len);
-        let frag_start = self.file.basic.block_offset as usize;
+        let frag_start = self.file.file.block_offset() as usize;
         let frag_end = frag_start + frag_len;
         frag_start..frag_end
     }
@@ -386,7 +382,7 @@ impl<'a, 'b> SquashfsRawData<'a, 'b> {
     #[inline]
     pub fn into_reader(self) -> SquashfsReadFile<'a, 'b> {
         let block_size = self.file.system.block_size as usize;
-        let bytes_available = self.file.basic.file_size as usize;
+        let bytes_available = self.file.file.file_len();
         SquashfsReadFile::new(block_size, self, 0, bytes_available)
     }
 }
diff --git a/backhand/src/filesystem/writer.rs b/backhand/src/filesystem/writer.rs
index e3f05b4a..03f1ee32 100644
--- a/backhand/src/filesystem/writer.rs
+++ b/backhand/src/filesystem/writer.rs
@@ -217,7 +217,7 @@ impl<'a, 'b, 'c> FilesystemWriter<'a, 'b, 'c> {
             .map(|node| {
                 let inner = match &node.inner {
                     InnerNode::File(file) => {
-                        let reader = reader.file(&file.basic);
+                        let reader = reader.file(file);
                         InnerNode::File(SquashfsFileWriter::SquashfsFile(reader))
                     }
                     InnerNode::Symlink(x) => InnerNode::Symlink(x.clone()),
diff --git a/backhand/src/inode.rs b/backhand/src/inode.rs
index d8bb3ab3..ea40c097 100644
--- a/backhand/src/inode.rs
+++ b/backhand/src/inode.rs
@@ -158,7 +158,6 @@ pub struct ExtendedDirectory {
     pub file_size: u32,
     pub block_index: u32,
     pub parent_inode: u32,
-    #[deku(assert = "*index_count < 256")]
     pub index_count: u16,
     pub block_offset: u16,
     pub xattr_index: u32,
@@ -181,18 +180,6 @@ pub struct BasicFile {
     pub block_sizes: Vec<DataSize>,
 }
 
-impl From<&ExtendedFile> for BasicFile {
-    fn from(ex_file: &ExtendedFile) -> Self {
-        Self {
-            blocks_start: ex_file.blocks_start as u32,
-            frag_index: ex_file.frag_index,
-            block_offset: ex_file.block_offset,
-            file_size: ex_file.file_size as u32,
-            block_sizes: ex_file.block_sizes.clone(),
-        }
-    }
-}
-
 #[derive(Debug, DekuRead, DekuWrite, Clone, PartialEq, Eq)]
 #[deku(
     endian = "endian",
diff --git a/backhand/src/squashfs.rs b/backhand/src/squashfs.rs
index 382d6e00..b4420c51 100644
--- a/backhand/src/squashfs.rs
+++ b/backhand/src/squashfs.rs
@@ -544,17 +544,20 @@ impl<'b> Squashfs<'b> {
                         }
                         // BasicFile
                         InodeId::BasicFile => {
-                            trace!("before_file: {:#02x?}", entry);
-                            let basic = match &found_inode.inner {
-                                InodeInner::BasicFile(file) => file.clone(),
-                                InodeInner::ExtendedFile(file) => file.into(),
+                            let inner = match &found_inode.inner {
+                                InodeInner::BasicFile(file) => {
+                                    SquashfsFileReader::Basic(file.clone())
+                                }
+                                InodeInner::ExtendedFile(file) => {
+                                    SquashfsFileReader::Extended(file.clone())
+                                }
                                 _ => {
                                     return Err(BackhandError::UnexpectedInode(
                                         found_inode.inner.clone(),
                                     ))
                                 }
                             };
-                            InnerNode::File(SquashfsFileReader { basic })
+                            InnerNode::File(inner)
                         }
                         // Basic Symlink
                         InodeId::BasicSymlink => {


Test patch:
diff --git a/backhand-test/tests/non_standard.rs b/backhand-test/tests/non_standard.rs
index d173b7f4..3e16a916 100644
--- a/backhand-test/tests/non_standard.rs
+++ b/backhand-test/tests/non_standard.rs
@@ -30,12 +30,9 @@ fn full_test(
     {
         let file = BufReader::new(File::open(og_path).unwrap());
         info!("calling from_reader");
-        let og_filesystem = FilesystemReader::from_reader_with_offset_and_kind(
-            file,
-            offset,
-            Kind::from_kind(&kind),
-        )
-        .unwrap();
+        let og_filesystem =
+            FilesystemReader::from_reader_with_offset_and_kind(file, offset, Kind::from_kind(kind))
+                .unwrap();
         let mut new_filesystem = FilesystemWriter::from_fs_reader(&og_filesystem).unwrap();
         if let Some(pad) = pad {
             new_filesystem.set_kib_padding(pad);
@@ -57,7 +54,7 @@ fn full_test(
         let _new_filesystem = FilesystemReader::from_reader_with_offset_and_kind(
             created_file,
             offset,
-            Kind::from_kind(&kind),
+            Kind::from_kind(kind),
         )
         .unwrap();
     }
@@ -140,7 +137,7 @@ fn test_custom_compressor() {
             if let Compressor::Gzip = compressor {
                 out.resize(out.capacity(), 0);
                 let mut decompressor = libdeflater::Decompressor::new();
-                let amt = decompressor.zlib_decompress(&bytes, out).unwrap();
+                let amt = decompressor.zlib_decompress(bytes, out).unwrap();
                 out.truncate(amt);
             } else {
                 unimplemented!();