1#![forbid(unsafe_code)]
8
9pub mod blob;
10pub mod resolver;
11
12use blob::Blob;
13use disk_backend::DiskError;
14use disk_backend::DiskIo;
15use disk_backend::UnmapBehavior;
16use guestmem::MemoryWrite;
17use inspect::Inspect;
18use scsi_buffers::RequestBuffers;
19use std::sync::Arc;
20use thiserror::Error;
21use vhd1_defs::VhdFooter;
22use zerocopy::FromZeros;
23use zerocopy::IntoBytes;
24
25const DEFAULT_SECTOR_SIZE: u32 = 512;
26
27#[derive(Inspect)]
29pub struct BlobDisk {
30 blob: Arc<dyn Blob + Send + Sync>,
31 sector_count: u64,
32 sector_size: u32,
33 sector_shift: u32,
34 disk_id: Option<[u8; 16]>,
35}
36
37#[derive(Debug, Error)]
38enum ErrorInner {
39 #[error("blob is too small")]
40 BlobTooSmall,
41 #[error("failed to read the vhd footer")]
42 VhdFooter(#[source] std::io::Error),
43 #[error("invalid vhd1 footer cookie")]
44 VhdFooterCookie,
45 #[error("invalid vhd1 footer checksum")]
46 VhdFooterChecksum,
47 #[error("unsupported vhd version: {0:#x}")]
48 UnsupportedVhdVersion(u32),
49 #[error("not a fixed vhd")]
50 NotFixedVhd,
51 #[error("invalid disk size: {0}")]
52 InvalidDiskSize(u64),
53}
54
55#[derive(Debug, Error)]
57#[error(transparent)]
58pub struct Vhd1Error(#[from] ErrorInner);
59
60impl BlobDisk {
61 pub fn new(blob: impl 'static + Blob + Send + Sync) -> Self {
63 let blob = Arc::new(blob);
64 let sector_count = blob.len() / DEFAULT_SECTOR_SIZE as u64;
65 Self::new_inner(blob, sector_count, None)
66 }
67
68 pub async fn new_fixed_vhd1(blob: impl 'static + Blob + Send + Sync) -> anyhow::Result<Self> {
70 let blob = Arc::new(blob);
71 let blob_len = blob.len();
72 let footer_offset = blob_len
73 .checked_sub(VhdFooter::LEN)
74 .ok_or(ErrorInner::BlobTooSmall)?;
75
76 let mut footer = VhdFooter::new_zeroed();
77 blob.read(footer.as_mut_bytes(), footer_offset)
78 .await
79 .map_err(ErrorInner::VhdFooter)?;
80
81 if footer.cookie != VhdFooter::COOKIE_MAGIC {
82 return Err(ErrorInner::VhdFooterCookie.into());
83 }
84 if footer.checksum.get() != footer.compute_checksum() {
85 return Err(ErrorInner::VhdFooterChecksum.into());
86 }
87 if footer.file_format_version.get() != VhdFooter::FILE_FORMAT_VERSION_MAGIC {
88 return Err(ErrorInner::UnsupportedVhdVersion(footer.file_format_version.get()).into());
89 }
90 if footer.disk_type.get() != VhdFooter::DISK_TYPE_FIXED {
91 return Err(ErrorInner::NotFixedVhd.into());
92 }
93 let disk_size = footer.current_size.get();
94 if disk_size > footer_offset || disk_size % (DEFAULT_SECTOR_SIZE as u64) != 0 {
95 return Err(ErrorInner::InvalidDiskSize(disk_size).into());
96 }
97
98 Ok(Self::new_inner(
99 blob,
100 disk_size / DEFAULT_SECTOR_SIZE as u64,
101 Some(footer.unique_id.into()),
102 ))
103 }
104
105 fn new_inner(
106 blob: Arc<dyn Blob + Send + Sync>,
107 sector_count: u64,
108 disk_id: Option<[u8; 16]>,
109 ) -> Self {
110 Self {
111 blob,
112 sector_count,
113 sector_size: DEFAULT_SECTOR_SIZE,
114 sector_shift: DEFAULT_SECTOR_SIZE.trailing_zeros(),
115 disk_id,
116 }
117 }
118}
119
120impl DiskIo for BlobDisk {
121 fn disk_type(&self) -> &str {
122 "blob"
123 }
124
125 fn sector_count(&self) -> u64 {
126 self.sector_count
127 }
128
129 fn sector_size(&self) -> u32 {
130 self.sector_size
131 }
132
133 fn disk_id(&self) -> Option<[u8; 16]> {
134 self.disk_id
135 }
136
137 fn physical_sector_size(&self) -> u32 {
138 4096
139 }
140
141 fn is_fua_respected(&self) -> bool {
142 false
143 }
144
145 fn is_read_only(&self) -> bool {
146 true
147 }
148
149 async fn read_vectored(
150 &self,
151 buffers: &RequestBuffers<'_>,
152 sector: u64,
153 ) -> Result<(), DiskError> {
154 let mut buf = vec![0; buffers.len()];
155 self.blob
156 .read(&mut buf, sector << self.sector_shift)
157 .await
158 .map_err(DiskError::Io)?;
159
160 buffers.writer().write(&buf)?;
161 Ok(())
162 }
163
164 async fn write_vectored(
165 &self,
166 _buffers: &RequestBuffers<'_>,
167 _sector: u64,
168 _fua: bool,
169 ) -> Result<(), DiskError> {
170 Err(DiskError::ReadOnly)
171 }
172
173 async fn sync_cache(&self) -> Result<(), DiskError> {
174 Err(DiskError::ReadOnly)
175 }
176
177 async fn unmap(
178 &self,
179 _sector: u64,
180 _count: u64,
181 _block_level_only: bool,
182 ) -> Result<(), DiskError> {
183 Err(DiskError::ReadOnly)
184 }
185
186 fn unmap_behavior(&self) -> UnmapBehavior {
187 UnmapBehavior::Ignored
188 }
189}