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}