A module for accessing disk image files (with virtio-blk)
This commit is contained in:
parent
9cca10a2c0
commit
e79502c271
67
rust/src/disk/memory.rs
Normal file
67
rust/src/disk/memory.rs
Normal file
@ -0,0 +1,67 @@
|
||||
use crate::system::{MemoryFd, BitVec};
|
||||
use crate::disk::{Result, Error, SECTOR_SIZE, DiskImage};
|
||||
use std::io::SeekFrom;
|
||||
|
||||
pub struct MemoryOverlay {
|
||||
memory: MemoryFd,
|
||||
written_sectors: BitVec,
|
||||
}
|
||||
|
||||
impl MemoryOverlay {
|
||||
pub fn new() -> Result<Self> {
|
||||
let memory = MemoryFd::new_memfd(0, false)
|
||||
.map_err(Error::MemoryOverlayCreate)?;
|
||||
let written_sectors = BitVec::new();
|
||||
Ok(MemoryOverlay { memory, written_sectors })
|
||||
}
|
||||
|
||||
pub fn write_sectors(&mut self, start: u64, buffer: &[u8]) -> Result<()> {
|
||||
let sector_count = buffer.len() / SECTOR_SIZE;
|
||||
let len = sector_count * SECTOR_SIZE;
|
||||
let seek_offset = SeekFrom::Start(start * SECTOR_SIZE as u64);
|
||||
|
||||
self.memory.fd_mut()
|
||||
.seek(seek_offset)
|
||||
.map_err(Error::DiskSeek)?;
|
||||
|
||||
self.memory.fd_mut()
|
||||
.write_all(&buffer[..len])
|
||||
.map_err(Error::DiskWrite)?;
|
||||
|
||||
for n in 0..sector_count {
|
||||
let idx = start as usize + n;
|
||||
self.written_sectors.set_bit(idx);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn read_sectors<D: DiskImage>(&mut self, disk: &mut D, start: u64, buffer: &mut [u8]) -> Result<()> {
|
||||
let sector_count = buffer.len() / SECTOR_SIZE;
|
||||
if (0..sector_count).all(|i| !self.written_sectors.get_bit(i)) {
|
||||
return disk.read_sectors(start, buffer);
|
||||
}
|
||||
|
||||
for n in 0..sector_count {
|
||||
let sector = start + n as u64;
|
||||
let offset = n * SECTOR_SIZE;
|
||||
let sector_buffer = &mut buffer[offset..offset+SECTOR_SIZE];
|
||||
if self.written_sectors.get_bit(sector as usize) {
|
||||
self.read_single_sector(sector, sector_buffer)?;
|
||||
} else {
|
||||
disk.read_sectors(sector, sector_buffer)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn read_single_sector(&mut self, sector: u64, buffer: &mut [u8]) -> Result<()> {
|
||||
assert_eq!(buffer.len(), SECTOR_SIZE);
|
||||
let offset = SeekFrom::Start(sector * SECTOR_SIZE as u64);
|
||||
self.memory.fd_mut().seek(offset)
|
||||
.map_err(Error::DiskSeek)?;
|
||||
self.memory.fd_mut().read_exact(buffer)
|
||||
.map_err(Error::DiskRead)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
}
|
88
rust/src/disk/mod.rs
Normal file
88
rust/src/disk/mod.rs
Normal file
@ -0,0 +1,88 @@
|
||||
use std::{io, error, fmt, result, cmp};
|
||||
use std::fs::File;
|
||||
use std::os::linux::fs::MetadataExt;
|
||||
use std::io::{SeekFrom, Seek};
|
||||
|
||||
use crate::system;
|
||||
|
||||
mod realmfs;
|
||||
mod raw;
|
||||
mod memory;
|
||||
|
||||
pub use raw::RawDiskImage;
|
||||
pub use realmfs::RealmFSImage;
|
||||
use std::path::PathBuf;
|
||||
|
||||
const SECTOR_SIZE: usize = 512;
|
||||
|
||||
#[derive(Debug,PartialEq)]
|
||||
pub enum OpenType {
|
||||
ReadOnly,
|
||||
ReadWrite,
|
||||
MemoryOverlay,
|
||||
}
|
||||
|
||||
pub trait DiskImage: Sync+Send {
|
||||
fn read_only(&self) -> bool;
|
||||
fn sector_count(&self) -> u64;
|
||||
fn disk_file(&self) -> &File;
|
||||
|
||||
fn seek_to_sector(&self, sector: u64) -> Result<()> {
|
||||
if sector > self.sector_count() {
|
||||
return Err(Error::BadSectorOffset(sector));
|
||||
}
|
||||
let offset = SeekFrom::Start(sector * SECTOR_SIZE as u64);
|
||||
self.disk_file().seek(offset)
|
||||
.map_err(Error::DiskSeek)?;
|
||||
Ok(())
|
||||
}
|
||||
fn write_sectors(&mut self, start_sector: u64, buffer: &[u8]) -> Result<()>;
|
||||
fn read_sectors(&mut self, start_sector: u64, buffer: &mut [u8]) -> Result<()>;
|
||||
fn flush(&mut self) -> Result<()> { Ok(()) }
|
||||
|
||||
fn disk_image_id(&self) -> &[u8];
|
||||
}
|
||||
|
||||
fn generate_disk_image_id(disk_file: &File) -> Vec<u8> {
|
||||
const VIRTIO_BLK_ID_BYTES: usize = 20;
|
||||
let meta = match disk_file.metadata() {
|
||||
Ok(meta) => meta,
|
||||
Err(_) => return vec![0u8; VIRTIO_BLK_ID_BYTES]
|
||||
};
|
||||
let dev_id = format!("{}{}{}", meta.st_dev(), meta.st_rdev(), meta.st_ino());
|
||||
let bytes = dev_id.as_bytes();
|
||||
let len = cmp::min(bytes.len(), VIRTIO_BLK_ID_BYTES);
|
||||
Vec::from(&bytes[..len])
|
||||
}
|
||||
|
||||
pub type Result<T> = result::Result<T, Error>;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
ReadOnly,
|
||||
DiskOpen(PathBuf,io::Error),
|
||||
DiskOpenTooShort(PathBuf),
|
||||
DiskRead(io::Error),
|
||||
DiskWrite(io::Error),
|
||||
DiskSeek(io::Error),
|
||||
BadSectorOffset(u64),
|
||||
MemoryOverlayCreate(system::Error),
|
||||
}
|
||||
|
||||
impl error::Error for Error {}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
use Error::*;
|
||||
match self {
|
||||
ReadOnly => write!(f, "attempted write to read-only device"),
|
||||
DiskOpen(path, err) => write!(f, "failed to open disk image {}: {}", path.display(), err),
|
||||
DiskOpenTooShort(path) => write!(f, "failed to open disk image {} because file is too short", path.display()),
|
||||
DiskRead(err) => write!(f, "error reading from disk image: {}", err),
|
||||
DiskWrite(err) => write!(f, "error writing to disk image: {}", err),
|
||||
DiskSeek(err) => write!(f, "error seeking to offset on disk image: {}", err),
|
||||
BadSectorOffset(sector) => write!(f, "attempt to access invalid sector offset {}", sector),
|
||||
MemoryOverlayCreate(err) => write!(f, "failed to create memory overlay: {}", err),
|
||||
}
|
||||
}
|
||||
}
|
116
rust/src/disk/raw.rs
Normal file
116
rust/src/disk/raw.rs
Normal file
@ -0,0 +1,116 @@
|
||||
use crate::disk::{Result, Error, DiskImage, SECTOR_SIZE, generate_disk_image_id, OpenType};
|
||||
use std::fs::{File, OpenOptions};
|
||||
use std::io::{Write, Read, SeekFrom, Seek};
|
||||
use crate::disk::Error::DiskRead;
|
||||
use crate::disk::memory::MemoryOverlay;
|
||||
use std::path::Path;
|
||||
|
||||
pub struct RawDiskImage {
|
||||
file: File,
|
||||
offset: usize,
|
||||
nsectors: u64,
|
||||
read_only: bool,
|
||||
disk_image_id: Vec<u8>,
|
||||
overlay: Option<MemoryOverlay>,
|
||||
}
|
||||
|
||||
impl RawDiskImage {
|
||||
#[allow(dead_code)]
|
||||
pub fn open<P: AsRef<Path>>(path: P, open_type: OpenType) -> Result<Self> {
|
||||
Self::open_with_offset(path, open_type, 0)
|
||||
}
|
||||
|
||||
pub fn open_with_offset<P: AsRef<Path>>(path: P, open_type: OpenType, offset: usize) -> Result<Self> {
|
||||
let path = path.as_ref();
|
||||
let meta = path.metadata()
|
||||
.map_err(|e| Error::DiskOpen(path.into(), e))?;
|
||||
|
||||
if meta.len() < offset as u64 {
|
||||
return Err(Error::DiskOpenTooShort(path.into()))
|
||||
}
|
||||
|
||||
let nsectors = (meta.len() - offset as u64) / SECTOR_SIZE as u64;
|
||||
|
||||
let file = OpenOptions::new()
|
||||
.read(true)
|
||||
.write(open_type == OpenType::ReadWrite)
|
||||
.open(path)
|
||||
.map_err(|e| Error::DiskOpen(path.into(), e))?;
|
||||
|
||||
|
||||
let disk = match open_type {
|
||||
OpenType::MemoryOverlay => {
|
||||
let overlay = MemoryOverlay::new()?;
|
||||
Self::new(file, nsectors, offset, false, Some(overlay))
|
||||
}
|
||||
OpenType::ReadOnly => {
|
||||
Self::new(file, nsectors, offset, true, None)
|
||||
}
|
||||
OpenType::ReadWrite => {
|
||||
Self::new(file, nsectors, offset, false, None)
|
||||
}
|
||||
};
|
||||
Ok(disk)
|
||||
}
|
||||
|
||||
pub fn new(file: File, nsectors: u64, offset: usize, read_only: bool, overlay: Option<MemoryOverlay>) -> Self {
|
||||
let disk_image_id = generate_disk_image_id(&file);
|
||||
RawDiskImage { file, nsectors, read_only, offset, disk_image_id, overlay }
|
||||
}
|
||||
}
|
||||
|
||||
impl DiskImage for RawDiskImage {
|
||||
fn read_only(&self) -> bool {
|
||||
self.read_only
|
||||
}
|
||||
|
||||
fn sector_count(&self) -> u64 {
|
||||
self.nsectors
|
||||
}
|
||||
|
||||
fn disk_file(&self) -> &File {
|
||||
&self.file
|
||||
}
|
||||
|
||||
fn seek_to_sector(&self, sector: u64) -> Result<()> {
|
||||
if sector > self.sector_count() {
|
||||
return Err(Error::BadSectorOffset(sector));
|
||||
}
|
||||
let offset = SeekFrom::Start(sector * SECTOR_SIZE as u64 + self.offset as u64);
|
||||
self.disk_file().seek(offset)
|
||||
.map_err(Error::DiskSeek)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_sectors(&mut self, start_sector: u64, buffer: &[u8]) -> Result<()> {
|
||||
if let Some(ref mut overlay) = self.overlay {
|
||||
return overlay.write_sectors(start_sector, buffer);
|
||||
}
|
||||
if self.read_only {
|
||||
return Err(Error::ReadOnly)
|
||||
}
|
||||
self.seek_to_sector(start_sector)?;
|
||||
let len = (buffer.len() / SECTOR_SIZE) * SECTOR_SIZE;
|
||||
self.file.write_all(&buffer[..len])
|
||||
.map_err(Error::DiskWrite)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn read_sectors(&mut self, start_sector: u64, buffer: &mut [u8]) -> Result<()> {
|
||||
if let Some(mut overlay) = self.overlay.take() {
|
||||
let ret = overlay.read_sectors(self, start_sector, buffer);
|
||||
self.overlay.replace(overlay);
|
||||
return ret;
|
||||
}
|
||||
|
||||
self.seek_to_sector(start_sector)?;
|
||||
let len = (buffer.len() / SECTOR_SIZE) * SECTOR_SIZE;
|
||||
self.file.read_exact(&mut buffer[..len])
|
||||
.map_err(DiskRead)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn disk_image_id(&self) -> &[u8] {
|
||||
&self.disk_image_id
|
||||
}
|
||||
}
|
45
rust/src/disk/realmfs.rs
Normal file
45
rust/src/disk/realmfs.rs
Normal file
@ -0,0 +1,45 @@
|
||||
use crate::disk::{Result, DiskImage, SECTOR_SIZE, RawDiskImage, OpenType};
|
||||
use std::fs::File;
|
||||
use std::path::Path;
|
||||
|
||||
// skip 4096 byte realmfs header
|
||||
const HEADER_SECTOR_COUNT: usize = 8;
|
||||
|
||||
pub struct RealmFSImage {
|
||||
raw: RawDiskImage,
|
||||
}
|
||||
|
||||
// Just pass everything through to raw image for now
|
||||
impl RealmFSImage {
|
||||
pub fn open<P: AsRef<Path>>(path: P, read_only: bool) -> Result<Self> {
|
||||
let open_type = if read_only { OpenType::ReadOnly } else { OpenType::MemoryOverlay };
|
||||
let raw = RawDiskImage::open_with_offset(path, open_type, HEADER_SECTOR_COUNT * SECTOR_SIZE)?;
|
||||
Ok(RealmFSImage { raw })
|
||||
}
|
||||
}
|
||||
|
||||
impl DiskImage for RealmFSImage {
|
||||
fn read_only(&self) -> bool {
|
||||
self.raw.read_only()
|
||||
}
|
||||
|
||||
fn sector_count(&self) -> u64 {
|
||||
self.raw.sector_count()
|
||||
}
|
||||
|
||||
fn disk_file(&self) -> &File {
|
||||
self.raw.disk_file()
|
||||
}
|
||||
|
||||
fn write_sectors(&mut self, start_sector: u64, buffer: &[u8]) -> Result<()> {
|
||||
self.raw.write_sectors(start_sector, buffer)
|
||||
}
|
||||
|
||||
fn read_sectors(&mut self, start_sector: u64, buffer: &mut [u8]) -> Result<()> {
|
||||
self.raw.read_sectors(start_sector, buffer)
|
||||
}
|
||||
|
||||
fn disk_image_id(&self) -> &[u8] {
|
||||
self.raw.disk_image_id()
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user