1#![expect(missing_docs)]
5#![forbid(unsafe_code)]
6
7mod readwriteat;
8
9use self::readwriteat::ReadWriteAt;
10use blocking::unblock;
11use disk_backend::DiskError;
12use disk_backend::DiskIo;
13use disk_backend::resolve::ResolveDiskParameters;
14use disk_backend::resolve::ResolvedDisk;
15use disk_backend_resources::FileDiskHandle;
16use guestmem::MemoryRead;
17use guestmem::MemoryWrite;
18use inspect::Inspect;
19use scsi_buffers::RequestBuffers;
20use std::fs;
21use std::sync::Arc;
22use thiserror::Error;
23use vm_resource::ResolveResource;
24use vm_resource::declare_static_resolver;
25use vm_resource::kind::DiskHandleKind;
26
27pub struct FileDiskResolver;
28declare_static_resolver!(FileDiskResolver, (DiskHandleKind, FileDiskHandle));
29
30#[derive(Debug, Error)]
31pub enum ResolveFileDiskError {
32 #[error("i/o error")]
33 Io(#[source] std::io::Error),
34 #[error("invalid disk")]
35 InvalidDisk(#[source] disk_backend::InvalidDisk),
36}
37
38impl ResolveResource<DiskHandleKind, FileDiskHandle> for FileDiskResolver {
39 type Output = ResolvedDisk;
40 type Error = ResolveFileDiskError;
41
42 fn resolve(
43 &self,
44 rsrc: FileDiskHandle,
45 input: ResolveDiskParameters<'_>,
46 ) -> Result<Self::Output, Self::Error> {
47 ResolvedDisk::new(
48 FileDisk::open(rsrc.0, input.read_only).map_err(ResolveFileDiskError::Io)?,
49 )
50 .map_err(ResolveFileDiskError::InvalidDisk)
51 }
52}
53
54#[derive(Debug, Inspect)]
55pub struct FileDisk {
56 file: Arc<fs::File>,
57 metadata: Metadata,
58 sector_shift: u32,
59}
60
61#[derive(Debug, Inspect)]
62pub struct Metadata {
63 pub disk_size: u64,
64 pub sector_size: u32,
65 pub physical_sector_size: u32,
66 pub read_only: bool,
67}
68
69impl FileDisk {
70 pub fn open(file: fs::File, read_only: bool) -> Result<Self, std::io::Error> {
71 let metadata = Metadata {
72 disk_size: file.metadata()?.len(),
73 sector_size: 512,
74 physical_sector_size: 4096,
75 read_only,
76 };
77 Ok(Self::with_metadata(file, metadata))
78 }
79
80 pub fn with_metadata(file: fs::File, metadata: Metadata) -> Self {
86 assert!(metadata.sector_size.is_power_of_two());
87 assert!(metadata.sector_size >= 512);
88 let sector_shift = metadata.sector_size.trailing_zeros();
89 FileDisk {
90 file: Arc::new(file),
91 metadata,
92 sector_shift,
93 }
94 }
95
96 pub fn into_inner(self) -> fs::File {
97 Arc::try_unwrap(self.file).expect("no outstanding IOs")
98 }
99}
100
101impl FileDisk {
102 pub async fn read(&self, buffers: &RequestBuffers<'_>, sector: u64) -> Result<(), DiskError> {
103 if ((sector << self.sector_shift) + buffers.len() as u64) > self.metadata.disk_size {
104 return Err(DiskError::IllegalBlock);
105 }
106 let mut buffer = vec![0; buffers.len()];
107 let file = self.file.clone();
108 let offset = sector << self.sector_shift;
109 let buffer = unblock(move || -> Result<_, std::io::Error> {
110 file.read_at(&mut buffer, offset)?;
111 Ok(buffer)
112 })
113 .await
114 .map_err(DiskError::Io)?;
115 buffers.writer().write(&buffer)?;
116 Ok(())
117 }
118
119 pub async fn write(
120 &self,
121 buffers: &RequestBuffers<'_>,
122 sector: u64,
123 _fua: bool,
124 ) -> Result<(), DiskError> {
125 if ((sector << self.sector_shift) + buffers.len() as u64) > self.metadata.disk_size {
126 return Err(DiskError::IllegalBlock);
127 }
128 let mut buffer = vec![0; buffers.len()];
129 let file = self.file.clone();
130 buffers.reader().read(&mut buffer)?;
131 let offset = sector << self.sector_shift;
132 unblock(move || file.write_at(&buffer, offset))
133 .await
134 .map_err(DiskError::Io)?;
135 Ok(())
136 }
137
138 pub async fn flush(&self) -> Result<(), DiskError> {
139 let file = self.file.clone();
140 unblock(move || file.sync_all())
141 .await
142 .map_err(DiskError::Io)?;
143 Ok(())
144 }
145}
146
147impl DiskIo for FileDisk {
148 fn disk_type(&self) -> &str {
149 "file"
150 }
151
152 fn sector_count(&self) -> u64 {
153 self.metadata.disk_size >> self.sector_shift
154 }
155
156 fn sector_size(&self) -> u32 {
157 self.metadata.sector_size
158 }
159
160 fn is_read_only(&self) -> bool {
161 self.metadata.read_only
162 }
163
164 fn disk_id(&self) -> Option<[u8; 16]> {
165 None
166 }
167
168 fn physical_sector_size(&self) -> u32 {
169 self.metadata.physical_sector_size
170 }
171
172 fn is_fua_respected(&self) -> bool {
173 false
174 }
175
176 async fn read_vectored(
177 &self,
178 buffers: &RequestBuffers<'_>,
179 sector: u64,
180 ) -> Result<(), DiskError> {
181 self.read(buffers, sector).await
182 }
183
184 async fn write_vectored(
185 &self,
186 buffers: &RequestBuffers<'_>,
187 sector: u64,
188 fua: bool,
189 ) -> Result<(), DiskError> {
190 self.write(buffers, sector, fua).await
191 }
192
193 async fn sync_cache(&self) -> Result<(), DiskError> {
194 self.flush().await
195 }
196
197 async fn unmap(
198 &self,
199 _sector: u64,
200 _count: u64,
201 _block_level_only: bool,
202 ) -> Result<(), DiskError> {
203 Ok(())
204 }
205
206 fn unmap_behavior(&self) -> disk_backend::UnmapBehavior {
207 disk_backend::UnmapBehavior::Ignored
208 }
209}