1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
use byteorder::{BigEndian, ReadBytesExt};
use failure::Fail;
use serde::{Deserialize, Serialize};
use std::fmt;
use std::io::Read;

/// Minimal size of header.
const HEADER_SIZE: usize = 28;

#[derive(Fail, Debug, PartialEq)]
pub enum Error {
    #[fail(display = "Wrong magic number.")]
    MagicError,
    #[fail(display = "Wrong version: {}, expected 1.", _0)]
    Version(u16),
    #[fail(display = "Header too small: {} bytes, expected 28.", _0)]
    HeaderTooSmall(u16),
}

#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
pub enum ChecksumAlg {
    None,
    SHA1,
    MD5,
    SHA256,
    SHA512,
    Other(String),
    Unknown(u32),
}

impl From<u32> for ChecksumAlg {
    fn from(i: u32) -> ChecksumAlg {
        match i {
            0 => ChecksumAlg::None,
            1 => ChecksumAlg::SHA1,
            2 => ChecksumAlg::MD5,
            3 => ChecksumAlg::SHA256,
            4 => ChecksumAlg::SHA512,
            i => ChecksumAlg::Unknown(i),
        }
    }
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Header {
    pub magic: u32,
    pub size: u16,
    pub version: u16,
    pub toc_length_compressed: u64,
    pub toc_length_uncompressed: u64,
    pub checksum_alg: ChecksumAlg,
    pub data: Vec<u8>,
}

impl Header {
    pub fn check(&self) -> Result<(), Error> {
        // needs to start with magic sequence 'xar!'.
        if self.magic != 0x78617221 {
            return Err(Error::MagicError);
        }

        // header size has to be legal.
        if self.size < 28 {
            return Err(Error::HeaderTooSmall(self.size));
        }

        // we only accept version 1.
        if self.version != 1 {
            return Err(Error::Version(self.version));
        }

        Ok(())
    }

    pub fn to_json(&self) -> Result<String, serde_json::Error> {
        serde_json::to_string(self)
    }

    pub fn from_read<T: Read>(reader: &mut T) -> Result<Header, std::io::Error> {
        let magic = reader.read_u32::<BigEndian>()?;
        let size = reader.read_u16::<BigEndian>()?;
        let version = reader.read_u16::<BigEndian>()?;
        let toc_length_compressed = reader.read_u64::<BigEndian>()?;
        let toc_length_uncompressed = reader.read_u64::<BigEndian>()?;
        let checksum_alg = reader.read_u32::<BigEndian>()?;

        // Read extra data until we've read in the whole header.
        let data_size = (size as usize).saturating_sub(HEADER_SIZE);
        let mut data = Vec::with_capacity(data_size);
        data.resize(data_size, 0);
        reader.read_exact(&mut data)?;

        let checksum_alg = checksum_alg.into();

        Ok(Header {
            magic,
            size,
            version,
            toc_length_compressed,
            toc_length_uncompressed,
            checksum_alg,
            data,
        })
    }
}

impl fmt::Display for Header {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{:25}: {}\n", "magic", self.magic)?;
        write!(f, "{:25}: {}\n", "size (header)", self.size)?;
        write!(
            f,
            "{:25}: {}\n",
            "toc length (compressed)", self.toc_length_compressed
        )?;
        write!(f, "{:25}: {}\n", "toc length", self.toc_length_uncompressed)?;
        write!(f, "{:25}: {:?}\n", "checksum_alg", self.checksum_alg)?;
        write!(f, "{:25}: {:?}", "extra data", self.data)
    }
}