squashfs_async/
superblock.rs1use serde::Deserialize;
2use serde_repr::Deserialize_repr;
3use tracing::*;
4
5use super::error::DecompressError;
6use super::inodes::InodeRef;
7use super::metadata::MetadataBlock;
8use super::Error;
9
10#[derive(Copy, Clone, Debug, PartialEq, Eq, Deserialize_repr)]
12#[repr(u16)]
13pub enum Compression {
14 Gzip = 1,
15 Lzma,
16 Lzd,
17 Xz,
18 Lz4,
19 Zstd,
20}
21
22bitflags::bitflags! {
23 #[derive(Deserialize)]
24 struct SuperBlockFlags: u16 {
25 const UNCOMPRESSED_INODES = 0x0001;
26 const UNCOMPRESSED_DATA = 0x0002;
27 const CHECK = 0x0004;
28 const UNCOMPRESSED_FRAGMENTS = 0x0008;
29 const NO_FRAGMENTS = 0x0010;
30 const ALWAYS_FRAGMENTS = 0x0020;
31 const DUPLICATES = 0x0040;
32 const EXPORTABLE = 0x0080;
33 const UNCOMPRESSED_XATTRS = 0x0100;
34 const NO_XATTRS = 0x0200;
35 const COMPRESSOR_OPTIONS = 0x0400;
36 const UNCOMPRESSED_IDS = 0x0800;
37 }
38}
39#[derive(Debug)]
40pub enum CompressionOptions {
41 Zstd,
42 Gzip,
43 Xz,
44}
45impl CompressionOptions {
46 fn from_metadata(compression: Compression, block: MetadataBlock) -> Result<Self, Error> {
47 match compression {
48 Compression::Zstd => {
49 if block.compressed_size != 4 {
50 return Err(Error::InvalidBufferSize);
51 }
52 Ok(Self::Zstd)
53 }
54 Compression::Gzip => {
55 if block.compressed_size != 8 {
56 return Err(Error::InvalidBufferSize);
57 }
58 Ok(Self::Gzip)
59 }
60 Compression::Xz => {
61 if block.compressed_size != 8 {
62 return Err(Error::InvalidBufferSize);
63 }
64 Ok(Self::Xz)
65 }
66 _ => Err(DecompressError::UnsupportedCompression(compression).into()),
68 }
69 }
70}
71#[derive(Debug, Deserialize)]
75pub struct SuperBlock {
76 magic: u32,
77 pub inode_count: u32,
78 _modification_time: u32,
79 pub block_size: u32,
80 pub fragment_entry_count: u32,
81 pub compression: Compression,
82 _block_log: u16,
83 flags: SuperBlockFlags,
84 _id_lookupcount: u16,
85 version_major: u16,
86 version_minor: u16,
87 pub root_inode: InodeRef,
88 pub bytes_used: u64,
90 _id_table_start: u64,
91 _xattr_id_table_start: u64,
92 pub inode_table_start: u64,
93 pub directory_table_start: u64,
94 pub fragment_table_start: u64,
95 _export_table_start: u64,
96 #[serde(skip)]
97 pub compression_options: Option<CompressionOptions>,
98}
99impl SuperBlock {
100 pub async fn from_reader(mut r: impl crate::AsyncSeekBufRead) -> Result<Self, Error> {
101 debug!("Reading superblock");
102 let mut superblock: Self = super::deser::bincode_deser_from(&mut r, 96)
103 .await
104 .map_err(|_| Error::InvalidSuperblock)?;
105
106 if superblock.magic != 0x73717368
107 || superblock.version_major != 4
108 || superblock.version_minor != 0
109 {
110 return Err(Error::InvalidSuperblock);
111 }
112 if superblock
113 .flags
114 .contains(SuperBlockFlags::COMPRESSOR_OPTIONS)
115 {
116 let block = MetadataBlock::from_reader(&mut r, superblock.compression).await?;
117 superblock.compression_options = Some(CompressionOptions::from_metadata(
118 superblock.compression,
119 block,
120 )?);
121 }
122 debug!("{:?}", superblock);
123 Ok(superblock)
124 }
125 pub fn tables_length(&self) -> u64 {
126 self.bytes_used - self.inode_table_start
127 }
128}