Skip to main content

x86/
image.rs

1use crate::error::{Result, X86Error, io_error};
2use serde::{Deserialize, Serialize};
3use sha2::{Digest, Sha256};
4use std::fs;
5use std::path::{Path, PathBuf};
6
7#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
8pub enum ImageKind {
9    RawDisk,
10    Iso9660,
11    Bios,
12    VgaBios,
13    Kernel,
14    Initrd,
15    Bootloader,
16    SavedState,
17    Other,
18}
19
20#[derive(Debug, Clone, Serialize, Deserialize)]
21pub struct Image {
22    kind: ImageKind,
23    name: String,
24    bytes: Vec<u8>,
25    source: Option<PathBuf>,
26}
27
28impl Image {
29    pub fn from_bytes(kind: ImageKind, name: impl Into<String>, bytes: impl Into<Vec<u8>>) -> Self {
30        Self {
31            kind,
32            name: name.into(),
33            bytes: bytes.into(),
34            source: None,
35        }
36    }
37
38    pub fn from_file(kind: ImageKind, path: impl AsRef<Path>) -> Result<Self> {
39        let path = path.as_ref();
40        let bytes = fs::read(path).map_err(|source| io_error(path, source))?;
41        Ok(Self {
42            kind,
43            name: path
44                .file_name()
45                .and_then(|x| x.to_str())
46                .unwrap_or("image")
47                .to_owned(),
48            bytes,
49            source: Some(path.to_path_buf()),
50        })
51    }
52
53    pub fn kind(&self) -> ImageKind {
54        self.kind
55    }
56
57    pub fn name(&self) -> &str {
58        &self.name
59    }
60
61    pub fn bytes(&self) -> &[u8] {
62        &self.bytes
63    }
64
65    pub fn len(&self) -> usize {
66        self.bytes.len()
67    }
68
69    pub fn is_empty(&self) -> bool {
70        self.bytes.is_empty()
71    }
72
73    pub fn source(&self) -> Option<&Path> {
74        self.source.as_deref()
75    }
76
77    pub fn sha256(&self) -> String {
78        let mut hasher = Sha256::new();
79        hasher.update(&self.bytes);
80        hex::encode(hasher.finalize())
81    }
82
83    pub fn verify_sha256(&self, expected: &str) -> Result<()> {
84        let expected = expected.trim().to_ascii_lowercase();
85        let actual = self.sha256();
86        if actual != expected {
87            return Err(X86Error::InvalidImage(format!(
88                "SHA-256 mismatch for {}: expected {}, got {}",
89                self.name, expected, actual
90            )));
91        }
92        Ok(())
93    }
94
95    pub fn write_to(&self, path: impl AsRef<Path>) -> Result<()> {
96        let path = path.as_ref();
97        fs::write(path, &self.bytes).map_err(|source| io_error(path, source))
98    }
99}