cramfs 0.1.0

A Rust implementation of the CRAMFS filesystem (read-only)
Documentation
use binrw::BinRead;
use log::trace;
use size_of_no_padding::SizeOfNoPadding;

use crate::constant::{S_IFMT, S_ISBLK, S_ISCHR, major, minor};

#[derive(BinRead, SizeOfNoPadding)]
#[br(little)]
#[allow(dead_code)]
pub(crate) struct SuperBlock {
    pub(crate) magic: u32,          // 0x28cd3d45 - random number
    pub(crate) size: u32,           // length in bytes
    pub(crate) flags: u32,          // feature flags
    pub(crate) future: u32,         // reserved for future use
    pub(crate) signature: [u8; 16], // "Compressed ROMFS"
    pub(crate) fsid: Info,          // unique filesystem info
    pub(crate) name: [u8; 16],      // user-defined name
    pub(crate) root: INode,         // root inode
}

impl SuperBlock {
    pub(crate) const CRC_OFFSET: usize = 32;
    pub(crate) const CRC_SIZE: usize = 4;
}

#[derive(BinRead, Clone, Copy, SizeOfNoPadding)]
#[br(little)]
pub(crate) struct INode {
    pub(crate) uid_mode: u32,
    pub(crate) gid_size: u32,
    pub(crate) offset_namelen: u32,
}

impl INode {
    pub(crate) fn uid(&self) -> u32 {
        (self.uid_mode >> 16) & 0xffff
    }

    pub(crate) fn mode(&self) -> u32 {
        self.uid_mode & 0xffff
    }

    pub(crate) fn gid(&self) -> u32 {
        (self.gid_size >> 24) & 0xff
    }

    /// SIZE for device files is i_rdev
    pub(crate) fn size(&self) -> u32 {
        self.gid_size & 0xffffff
    }

    /// OFFSET: For symlinks and non-empty regular files, this
    /// contains the offset (dividedj by 4) of the file data in
    /// compressed form (starting with an array of block pointers;
    /// see README).  For non-empty directories it is the offset
    /// (divided by 4) of the inode of the first file in that
    /// directory.  For anything else, offset is zero.
    pub(crate) fn offset(&self) -> u32 {
        (self.offset_namelen >> 6) & 0x03ffffff
    }

    /// NAMELEN is the length of the file name, divided by 4 and
    /// rounded up.  (cramfs doesn't support hard links.)
    pub(crate) fn namelen(&self) -> u32 {
        self.offset_namelen & 0x3f
    }
}

#[derive(BinRead)]
#[allow(dead_code)]
pub(crate) struct Info {
    pub(crate) crc: u32,
    pub(crate) edition: u32,
    pub(crate) blocks: u32,
    pub(crate) files: u32,
}

pub(crate) fn print_node(r#type: char, node: &INode, name: impl AsRef<str>) {
    let name = name.as_ref();
    let info = if S_ISCHR(node.mode()) || S_ISBLK(node.mode()) {
        format!("{:4},{:4}", major(node.size()), minor(node.size()))
    } else {
        format!("{:9}", node.size())
    };

    trace!(
        "{} {:04o} {} {:5}:{:<3} {}",
        r#type,
        node.mode() & !S_IFMT,
        info,
        node.uid(),
        node.gid(),
        name
    );
}