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
use byteorder::{ReadBytesExt, LE};
use std::io::{Read, Seek, SeekFrom};

mod tag;
pub use tag::{Tag, TagType};

// Defined by Multiboot2 spec
pub const HEADER_MAGIC: u32 = 0xE85250D6;
pub const SEARCH_END: u64 = 32768;
pub const ALIGNMENT: usize = 8;

#[derive(Debug, Copy, Clone)]
pub struct Header {
    pub magic: u32,
    pub architecture: u32,
    pub header_length: u32,
    pub checksum: u32,
}

impl Header {
    pub fn is_valid(&self) -> bool {
        if self.magic != HEADER_MAGIC {
            return false;
        }
        self.checksum
            .wrapping_add(self.magic)
            .wrapping_add(self.architecture)
            .wrapping_add(self.header_length)
            == 0
    }
}

pub fn find_header<R: Read + Seek>(mut image: R) -> std::io::Result<Option<(u64, Header)>> {
    for offset in (0..SEARCH_END).step_by(ALIGNMENT) {
        image.seek(SeekFrom::Start(offset))?;

        let magic = image.read_u32::<LE>()?;
        if magic == HEADER_MAGIC {
            let header = Header {
                magic,
                architecture: image.read_u32::<LE>()?,
                header_length: image.read_u32::<LE>()?,
                checksum: image.read_u32::<LE>()?,
            };
            if header.is_valid() {
                return Ok(Some((offset, header)));
            }
        }
    }
    Ok(None)
}

#[derive(Debug, Clone)]
pub struct TagIter<R> {
    done: bool,
    data: R,
}

impl<R: Read> TagIter<R> {
    pub fn new(data: R) -> Self {
        Self { done: false, data }
    }
}

impl<R: Read> Iterator for TagIter<R> {
    type Item = std::io::Result<Tag>;
    fn next(&mut self) -> Option<Self::Item> {
        if self.done {
            return None;
        }

        let tag = match Tag::from_reader(&mut self.data) {
            Ok(t) => t,
            Err(e) => return Some(Err(e)),
        };
        if tag == Tag::End {
            self.done = true;
        }

        Some(Ok(tag))
    }
}