1#![forbid(unsafe_code)]
63
64mod auto_cache;
65pub mod resolver;
66
67use anyhow::Context;
68use blocking::unblock;
69use disk_backend::DiskError;
70use disk_backend::UnmapBehavior;
71use disk_layered::LayerAttach;
72use disk_layered::LayerIo;
73use disk_layered::SectorMarker;
74use disk_layered::WriteNoOverwrite;
75use futures::lock::Mutex;
76use futures::lock::OwnedMutexGuard;
77use guestmem::MemoryRead;
78use guestmem::MemoryWrite;
79use inspect::Inspect;
80use rusqlite::Connection;
81use scsi_buffers::RequestBuffers;
82use std::path::Path;
83use std::path::PathBuf;
84use std::sync::Arc;
85
86#[derive(Inspect, Copy, Clone)]
91pub struct IncompleteFormatParams {
92 pub logically_read_only: bool,
94 pub len: Option<u64>,
96}
97
98#[derive(Inspect, Copy, Clone)]
100pub struct FormatParams {
101 pub logically_read_only: bool,
103 pub len: u64,
105 pub sector_size: u32,
107}
108
109pub struct FormatOnAttachSqliteDiskLayer {
112 dbhd_path: PathBuf,
113 read_only: bool,
114 format_dbhd: IncompleteFormatParams,
115}
116
117impl FormatOnAttachSqliteDiskLayer {
118 pub fn new(dbhd_path: PathBuf, read_only: bool, format_dbhd: IncompleteFormatParams) -> Self {
121 Self {
122 dbhd_path,
123 read_only,
124 format_dbhd,
125 }
126 }
127}
128
129#[derive(Inspect)]
131pub struct SqliteDiskLayer {
132 #[inspect(skip)]
133 conn: Arc<Mutex<Connection>>, meta: schema::DiskMeta,
135}
136
137impl SqliteDiskLayer {
138 pub fn new(
140 dbhd_path: &Path,
141 read_only: bool,
142 format_dbhd: Option<FormatParams>,
143 ) -> anyhow::Result<Self> {
144 let conn = Connection::open_with_flags(dbhd_path, {
155 use rusqlite::OpenFlags;
156
157 let mut flags = OpenFlags::SQLITE_OPEN_NO_MUTEX;
158
159 if read_only {
160 flags |= OpenFlags::SQLITE_OPEN_READ_ONLY;
161 } else {
162 flags |= OpenFlags::SQLITE_OPEN_READ_WRITE;
163 }
164
165 if format_dbhd.is_some() {
171 flags |= OpenFlags::SQLITE_OPEN_CREATE
172 }
173
174 flags
175 })?;
176
177 let meta = if let Some(FormatParams {
178 logically_read_only,
179 len,
180 sector_size,
181 }) = format_dbhd
182 {
183 use rusqlite::config::DbConfig;
184
185 conn.set_db_config(DbConfig::SQLITE_DBCONFIG_RESET_DATABASE, true)?;
189 conn.execute("VACUUM", ())?;
190 conn.set_db_config(DbConfig::SQLITE_DBCONFIG_RESET_DATABASE, false)?;
191
192 conn.pragma_update(None, "journal_mode", "WAL")?;
194 conn.execute(schema::DEFINE_TABLE_SECTORS, [])?;
195 conn.execute(schema::DEFINE_TABLE_METADATA, [])?;
196
197 if len % sector_size as u64 != 0 {
198 anyhow::bail!(
199 "failed to format: len={len} must be multiple of sector_size={sector_size}"
200 );
201 }
202 let sector_count = len / sector_size as u64;
203
204 let meta = schema::DiskMeta {
205 logically_read_only,
206 sector_count,
207 sector_size,
208 };
209
210 conn.execute(
211 "INSERT INTO meta VALUES (json(?))",
212 [serde_json::to_string(&meta).unwrap()],
213 )?;
214
215 meta
216 } else {
217 use rusqlite::OptionalExtension;
218 let data: String = conn
219 .query_row("SELECT json_extract(metadata, '$') FROM meta", [], |row| {
220 row.get(0)
221 })
222 .optional()?
223 .context("missing `meta` table")?;
224 serde_json::from_str(&data)?
225 };
226
227 Ok(SqliteDiskLayer {
228 conn: Arc::new(Mutex::new(conn)),
229 meta,
230 })
231 }
232
233 async fn write_maybe_overwrite(
234 &self,
235 buffers: &RequestBuffers<'_>,
236 sector: u64,
237 overwrite: bool,
238 ) -> Result<(), DiskError> {
239 assert!(!(overwrite && self.meta.logically_read_only));
240
241 let count = buffers.len() / self.meta.sector_size as usize;
242 tracing::trace!(sector, count, "write");
243
244 if sector + count as u64 > self.meta.sector_count {
247 return Err(DiskError::IllegalBlock);
248 }
249
250 let buf = buffers.reader().read_all()?;
251 unblock({
252 let conn = self.conn.clone().lock_owned().await;
253 let sector_size = self.meta.sector_size;
254 move || write_sectors(conn, sector_size, sector, buf, overwrite)
255 })
256 .await
257 .map_err(|e| DiskError::Io(std::io::Error::other(e)))?;
258
259 Ok(())
260 }
261}
262
263impl LayerAttach for FormatOnAttachSqliteDiskLayer {
264 type Error = anyhow::Error;
265 type Layer = SqliteDiskLayer;
266
267 async fn attach(
268 self,
269 lower_layer_metadata: Option<disk_layered::DiskLayerMetadata>,
270 ) -> Result<Self::Layer, Self::Error> {
271 let len = {
272 let lower_len = lower_layer_metadata
273 .as_ref()
274 .map(|m| m.sector_count * m.sector_size as u64);
275 self.format_dbhd
276 .len
277 .or(lower_len)
278 .context("no base layer to infer sector_count from")?
279 };
280 let sector_size = lower_layer_metadata.map(|x| x.sector_size).unwrap_or(512);
282
283 SqliteDiskLayer::new(
284 &self.dbhd_path,
285 self.read_only,
286 Some(FormatParams {
287 logically_read_only: self.format_dbhd.logically_read_only,
288 len,
289 sector_size,
290 }),
291 )
292 }
293}
294
295impl LayerIo for SqliteDiskLayer {
296 fn layer_type(&self) -> &str {
297 "sqlite"
298 }
299
300 fn sector_count(&self) -> u64 {
301 self.meta.sector_count
302 }
303
304 fn sector_size(&self) -> u32 {
305 self.meta.sector_size
306 }
307
308 fn is_logically_read_only(&self) -> bool {
309 self.meta.logically_read_only
310 }
311
312 fn disk_id(&self) -> Option<[u8; 16]> {
313 None
314 }
315
316 fn physical_sector_size(&self) -> u32 {
317 self.meta.sector_size
318 }
319
320 fn is_fua_respected(&self) -> bool {
321 false
322 }
323
324 async fn read(
325 &self,
326 buffers: &RequestBuffers<'_>,
327 sector: u64,
328 mut marker: SectorMarker<'_>,
329 ) -> Result<(), DiskError> {
330 let sector_count = (buffers.len() / self.meta.sector_size as usize) as u64;
331 let end_sector = sector + sector_count;
332 tracing::trace!(sector, sector_count, "read");
333 if end_sector > self.meta.sector_count {
334 return Err(DiskError::IllegalBlock);
335 }
336
337 let valid_sectors = unblock({
338 let conn = self.conn.clone().lock_owned().await;
339 let end_sector = sector + sector_count;
340 let sector_size = self.meta.sector_size;
341 move || read_sectors(conn, sector_size, sector, end_sector)
342 })
343 .await
344 .map_err(|e| DiskError::Io(std::io::Error::other(e)))?;
345
346 for (s, data) in valid_sectors {
347 let offset = (s - sector) as usize * self.meta.sector_size as usize;
348 let subrange = buffers.subrange(offset, self.meta.sector_size as usize);
349 let mut writer = subrange.writer();
350 match data {
351 SectorKind::AllZero => writer.zero(self.meta.sector_size as usize)?,
352 SectorKind::Data(data) => writer.write(&data)?,
353 };
354
355 marker.set(s);
356 }
357
358 Ok(())
359 }
360
361 async fn write(
362 &self,
363 buffers: &RequestBuffers<'_>,
364 sector: u64,
365 _fua: bool,
366 ) -> Result<(), DiskError> {
367 self.write_maybe_overwrite(buffers, sector, true).await
368 }
369
370 fn write_no_overwrite(&self) -> Option<impl WriteNoOverwrite> {
371 Some(self)
372 }
373
374 async fn sync_cache(&self) -> Result<(), DiskError> {
375 tracing::trace!("sync_cache");
376
377 unblock({
378 let mut conn = self.conn.clone().lock_owned().await;
379 move || -> rusqlite::Result<()> {
380 conn.pragma_update(None, "synchronous", "FULL")?;
382 {
383 let tx = conn.transaction()?;
384 tx.pragma_update(None, "user_version", "0")?;
385 }
386 conn.pragma_update(None, "synchronous", "NORMAL")?;
387 Ok(())
388 }
389 })
390 .await
391 .map_err(|e| DiskError::Io(std::io::Error::other(e)))
392 }
393
394 async fn unmap(
395 &self,
396 sector_offset: u64,
397 sector_count: u64,
398 _block_level_only: bool,
399 next_is_zero: bool,
400 ) -> Result<(), DiskError> {
401 tracing::trace!(sector_offset, sector_count, "unmap");
402 if sector_offset + sector_count > self.meta.sector_count {
403 return Err(DiskError::IllegalBlock);
404 }
405
406 unblock({
407 let conn = self.conn.clone().lock_owned().await;
408 move || unmap_sectors(conn, sector_offset, sector_count, next_is_zero)
409 })
410 .await
411 .map_err(|e| DiskError::Io(std::io::Error::other(e)))?;
412
413 Ok(())
414 }
415
416 fn unmap_behavior(&self) -> UnmapBehavior {
417 UnmapBehavior::Zeroes
418 }
419
420 fn optimal_unmap_sectors(&self) -> u32 {
421 1
422 }
423}
424
425impl WriteNoOverwrite for SqliteDiskLayer {
426 async fn write_no_overwrite(
427 &self,
428 buffers: &RequestBuffers<'_>,
429 sector: u64,
430 ) -> Result<(), DiskError> {
431 self.write_maybe_overwrite(buffers, sector, false).await
432 }
433}
434
435enum SectorKind {
436 AllZero,
437 Data(Vec<u8>),
438}
439
440fn read_sectors(
442 conn: OwnedMutexGuard<Connection>,
443 sector_size: u32,
444 start_sector: u64,
445 end_sector: u64,
446) -> anyhow::Result<Vec<(u64, SectorKind)>> {
447 let mut select_stmt = conn.prepare_cached(
448 "SELECT sector, data
449 FROM sectors
450 WHERE sector >= ? AND sector < ?
451 ORDER BY sector ASC",
452 )?;
453 let mut rows = select_stmt.query(rusqlite::params![start_sector, end_sector])?;
454
455 let mut res = Vec::new();
456 while let Some(row) = rows.next()? {
457 let sector: u64 = row.get(0)?;
458 let data: Option<&[u8]> = row.get_ref(1)?.as_blob_or_null()?;
459 let data = if let Some(data) = data {
460 if data.len() != sector_size as usize {
461 anyhow::bail!(
462 "db contained sector with unexpected size (expected={}, found={}, sector={:#x})",
463 sector_size,
464 data.len(),
465 sector
466 )
467 }
468 SectorKind::Data(data.into())
469 } else {
470 SectorKind::AllZero
471 };
472 res.push((sector, data));
473 }
474
475 Ok(res)
476}
477
478fn write_sectors(
480 mut conn: OwnedMutexGuard<Connection>,
481 sector_size: u32,
482 mut sector: u64,
483 buf: Vec<u8>,
484 overwrite: bool,
485) -> Result<(), rusqlite::Error> {
486 let tx = conn.transaction()?;
487 {
488 let mut stmt = if overwrite {
489 tx.prepare_cached("INSERT OR REPLACE INTO sectors (sector, data) VALUES (?, ?)")?
490 } else {
491 tx.prepare_cached("INSERT OR IGNORE INTO sectors (sector, data) VALUES (?, ?)")?
492 };
493
494 let chunks = buf.chunks_exact(sector_size as usize);
495 assert!(chunks.remainder().is_empty());
496 for chunk in chunks {
497 if chunk.iter().all(|x| *x == 0) {
498 stmt.execute(rusqlite::params![sector, rusqlite::types::Null])?;
499 } else {
500 stmt.execute(rusqlite::params![sector, chunk])?;
501 };
502
503 sector += 1;
504 }
505 }
506 tx.commit()?;
507
508 Ok(())
509}
510
511fn unmap_sectors(
512 mut conn: OwnedMutexGuard<Connection>,
513 sector_offset: u64,
514 sector_count: u64,
515 next_is_zero: bool,
516) -> Result<(), rusqlite::Error> {
517 if next_is_zero {
518 let mut clear_stmt =
519 conn.prepare_cached("DELETE FROM sectors WHERE sector BETWEEN ? AND ?")?;
520 clear_stmt.execute(rusqlite::params![
521 sector_offset,
522 sector_offset + sector_count - 1
523 ])?;
524 } else {
525 let tx = conn.transaction()?;
526 {
527 let mut stmt =
528 tx.prepare_cached("INSERT OR REPLACE INTO sectors (sector, data) VALUES (?, ?)")?;
529
530 for sector in sector_offset..(sector_offset + sector_count) {
531 stmt.execute(rusqlite::params![sector, rusqlite::types::Null])?;
532 }
533 }
534 tx.commit()?;
535 }
536
537 Ok(())
538}
539
540mod schema {
541 use inspect::Inspect;
542 use serde::Deserialize;
543 use serde::Serialize;
544
545 pub const DEFINE_TABLE_SECTORS: &str = r#"
553CREATE TABLE sectors (
554 -- if data is NULL, that indicates an all-zero sector.
555 -- otherwise, data has len == SECTOR_SIZE, containing the sector data.
556 sector INTEGER NOT NULL,
557 data BLOB,
558 PRIMARY KEY (sector)
559)
560"#; pub const DEFINE_TABLE_METADATA: &str = r#"
570CREATE TABLE meta (
571 metadata TEXT NOT NULL -- stored as JSON
572)
573"#;
574
575 #[derive(Debug, PartialEq, PartialOrd, Eq, Ord, Serialize, Deserialize, Inspect)]
576 pub struct DiskMeta {
577 pub logically_read_only: bool,
578 pub sector_count: u64,
579 pub sector_size: u32,
580 }
581}
582
583#[cfg(test)]
584mod tests {
585 use crate::FormatParams;
586 use crate::SqliteDiskLayer;
587 use disk_backend::Disk;
588 use disk_layered::DiskLayer;
589 use disk_layered::LayerConfiguration;
590 use disk_layered::LayeredDisk;
591 use pal_async::async_test;
592
593 const SECTOR_SIZE: u32 = 512;
594 const DISK_SIZE: u64 = 1024 * 1024;
595
596 fn new_layer(dir: &tempfile::TempDir) -> SqliteDiskLayer {
597 SqliteDiskLayer::new(
598 &dir.path().join("test.dbhd"),
599 false,
600 Some(FormatParams {
601 logically_read_only: false,
602 len: DISK_SIZE,
603 sector_size: SECTOR_SIZE,
604 }),
605 )
606 .unwrap()
607 }
608
609 #[async_test]
610 async fn sector_range_conformance() {
611 let dir = tempfile::tempdir().unwrap();
612 let disk = Disk::new(
613 LayeredDisk::new(
614 false,
615 vec![LayerConfiguration {
616 layer: DiskLayer::new(new_layer(&dir)),
617 write_through: false,
618 read_cache: false,
619 }],
620 )
621 .await
622 .unwrap(),
623 )
624 .unwrap();
625 storage_tests::sector_range::test_disk_sector_range_conformance(&disk).await;
626 }
627
628 #[async_test]
631 async fn layer_sector_range_conformance() {
632 let dir = tempfile::tempdir().unwrap();
633 storage_tests::sector_range::test_layer_sector_range(&new_layer(&dir)).await;
634 }
635}