use crate::compress::Algorithm;
use crate::SqsIoReader;
use prettytable::Table;
use std::fmt;
use std::io::Result;
pub const MAGIC_NUMBER: u32 = 0x7371_7368;
pub const VERSION_MAJOR: u16 = 4;
pub const VERSION_MINOR: u16 = 0;
#[macro_export]
macro_rules! impl_converter {
($T: ty) => {
impl AsRef<[u8]> for $T {
#[inline]
fn as_ref(&self) -> &[u8] {
use std::mem::size_of;
let ptr = self as *const $T as *const u8;
unsafe { &*std::slice::from_raw_parts(ptr, size_of::<$T>()) }
}
}
impl AsMut<[u8]> for $T {
#[inline]
fn as_mut(&mut self) -> &mut [u8] {
use std::mem::size_of;
let ptr = self as *mut $T as *mut u8;
unsafe { &mut *std::slice::from_raw_parts_mut(ptr, size_of::<$T>()) }
}
}
};
}
macro_rules! has_flag {
($H: ident, $F: ident) => {
#[inline]
pub fn $H(&self) -> bool {
self.contains(Flags::$F)
}
};
}
#[repr(C)]
#[derive(Debug, Default)]
pub struct Superblock {
pub magic: u32,
pub inode_count: u32,
pub modification_time: u32,
pub block_size: u32,
pub fragment_entry_count: u32,
pub compressor: Algorithm,
pub block_log: u16,
pub flags: Flags,
pub id_count: u16,
pub version_major: u16,
pub version_minor: u16,
pub root_inode_ref: InodeRef,
pub bytes_used: u64,
pub id_table_start: u64,
pub xattr_id_table_start: u64,
pub inode_table_start: u64,
pub directory_table_start: u64,
pub fragment_table_start: u64,
pub export_table_start: u64,
}
impl_converter!(Superblock);
impl Superblock {
pub fn new() -> Self {
Self::default()
}
pub fn load(&mut self, r: &mut SqsIoReader) -> Result<()> {
r.read_exact(self.as_mut())?;
Ok(())
}
pub fn to_table(&self) -> Table {
table!(
["Field", "Value"],
["magic", self.magic],
["inode_count", self.inode_count],
["modification_time", self.modification_time],
["block_size", self.block_size],
["fragment_entry_count", self.fragment_entry_count],
["compressor", self.compressor],
["block_log", self.block_log],
["flags", self.flags.to_table()],
["id_count", self.id_count],
["version_major", self.version_major],
["version_minor", self.version_minor],
["root_inode_ref", self.root_inode_ref],
["bytes_used", self.bytes_used],
["id_table_start", self.id_table_start],
["xattr_id_table_start", self.xattr_id_table_start],
["inode_table_start", self.inode_table_start],
["directory_table_start", self.directory_table_start],
["fragment_table_start", self.fragment_table_start],
["export_table_start", self.export_table_start]
)
}
}
bitflags! {
pub struct Flags: u16 {
const UNCOMPRESSED_INODES = 0x0001;
const UNCOMPRESSED_DATA = 0x0002;
const CHECK = 0x0004;
const UNCOMPRESSED_FRAGMENTS = 0x0008;
const NO_FRAGMENTS = 0x0010;
const ALWAYS_FRAGMENTS = 0x0020;
const DUPLICATES = 0x0040;
const EXPORTABLE = 0x0080;
const UNCOMPRESSED_XATTRS = 0x0100;
const NO_XATTRS = 0x0200;
const COMPRESSOR_OPTIONS = 0x0400;
const UNCOMPRESSED_IDS = 0x0800;
}
}
impl Flags {
has_flag!(uncompressed_inodes, UNCOMPRESSED_INODES);
has_flag!(uncompressed_data, UNCOMPRESSED_DATA);
has_flag!(check, CHECK);
has_flag!(uncompressed_fragments, UNCOMPRESSED_FRAGMENTS);
has_flag!(no_fragments, NO_FRAGMENTS);
has_flag!(always_fragments, ALWAYS_FRAGMENTS);
has_flag!(duplicates, DUPLICATES);
has_flag!(exportable, EXPORTABLE);
has_flag!(uncompressed_xattrs, UNCOMPRESSED_XATTRS);
has_flag!(no_xattrs, NO_XATTRS);
has_flag!(compressor_options, COMPRESSOR_OPTIONS);
has_flag!(uncompressed_ids, UNCOMPRESSED_IDS);
pub fn to_table(&self) -> Table {
table!(
["Flag", "Exist"],
[Flags::UNCOMPRESSED_INODES, self.uncompressed_inodes()],
[Flags::UNCOMPRESSED_DATA, self.uncompressed_data()],
[Flags::CHECK, self.check()],
[Flags::UNCOMPRESSED_FRAGMENTS, self.uncompressed_fragments()],
[Flags::NO_FRAGMENTS, self.no_fragments()],
[Flags::ALWAYS_FRAGMENTS, self.always_fragments()],
[Flags::DUPLICATES, self.duplicates()],
[Flags::EXPORTABLE, self.exportable()],
[Flags::UNCOMPRESSED_XATTRS, self.uncompressed_xattrs()],
[Flags::NO_XATTRS, self.no_xattrs()],
[Flags::COMPRESSOR_OPTIONS, self.compressor_options()],
[Flags::UNCOMPRESSED_IDS, self.uncompressed_ids()]
)
}
}
impl Default for Flags {
fn default() -> Self {
Flags::empty()
}
}
impl fmt::Display for Flags {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", format!("{:?}", self))?;
Ok(())
}
}
#[repr(C)]
#[derive(Debug, Default)]
pub struct InodeRef {
pub offset: u16,
pub block: u16,
padding: u32,
}
impl fmt::Display for InodeRef {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", format!("{:?}", self))?;
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::tests::*;
use std::io::Result;
use std::mem::size_of;
#[test]
fn read_superblock() -> Result<()> {
debug!("Superblock size: {}", size_of::<Superblock>());
let (_, sb) = prepare_tests()?;
sb.to_table().printstd();
assert_eq!(sb.magic, MAGIC_NUMBER);
assert_eq!(sb.version_major, VERSION_MAJOR);
assert_eq!(sb.version_minor, VERSION_MINOR);
Ok(())
}
}