mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2026-08-05 02:19:16 +00:00
block: qcow: Move Qcow2MetadataBacking to qcow/backing
Move the QCOW2 metadata backed reader into qcow/backing alongside RawBacking. Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
This commit is contained in:
committed by
Rob Bradford
parent
e345299f4d
commit
5ec80d45ab
@@ -8,8 +8,9 @@
|
|||||||
|
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::os::fd::{AsRawFd, OwnedFd};
|
use std::os::fd::{AsRawFd, OwnedFd};
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
use crate::qcow::metadata::BackingRead;
|
use crate::qcow::metadata::{BackingRead, ClusterReadMapping, QcowMetadata};
|
||||||
use crate::qcow_common::pread_exact;
|
use crate::qcow_common::pread_exact;
|
||||||
|
|
||||||
/// Raw backing file using pread64 on a duplicated fd.
|
/// Raw backing file using pread64 on a duplicated fd.
|
||||||
@@ -38,3 +39,92 @@ impl BackingRead for RawBacking {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// QCOW2 image used as a backing file for another QCOW2 image.
|
||||||
|
///
|
||||||
|
/// Resolves guest offsets through the QCOW2 cluster mapping (L1/L2
|
||||||
|
/// tables, refcounts) before reading the underlying data. Read only
|
||||||
|
/// because backing files never receive writes. Nested backing chains
|
||||||
|
/// are handled recursively via the optional `backing_file` field.
|
||||||
|
pub(crate) struct Qcow2MetadataBacking {
|
||||||
|
pub(crate) metadata: Arc<QcowMetadata>,
|
||||||
|
pub(crate) data_fd: OwnedFd,
|
||||||
|
pub(crate) backing_file: Option<Arc<dyn BackingRead>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
// SAFETY: All reads go through QcowMetadata which uses RwLock
|
||||||
|
// and pread64 which is position independent and thread safe.
|
||||||
|
unsafe impl Sync for Qcow2MetadataBacking {}
|
||||||
|
|
||||||
|
impl BackingRead for Qcow2MetadataBacking {
|
||||||
|
fn read_at(&self, address: u64, buf: &mut [u8]) -> io::Result<()> {
|
||||||
|
let virtual_size = self.metadata.virtual_size();
|
||||||
|
if address >= virtual_size {
|
||||||
|
buf.fill(0);
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
let available = (virtual_size - address) as usize;
|
||||||
|
if available < buf.len() {
|
||||||
|
self.read_clusters(address, &mut buf[..available])?;
|
||||||
|
buf[available..].fill(0);
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
self.read_clusters(address, buf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Qcow2MetadataBacking {
|
||||||
|
/// Resolve cluster mappings via metadata then read allocated clusters
|
||||||
|
/// with pread64.
|
||||||
|
fn read_clusters(&self, address: u64, buf: &mut [u8]) -> io::Result<()> {
|
||||||
|
let total_len = buf.len();
|
||||||
|
let has_backing = self.backing_file.is_some();
|
||||||
|
|
||||||
|
let mappings = self
|
||||||
|
.metadata
|
||||||
|
.map_clusters_for_read(address, total_len, has_backing)?;
|
||||||
|
|
||||||
|
let mut buf_offset = 0usize;
|
||||||
|
for mapping in mappings {
|
||||||
|
match mapping {
|
||||||
|
ClusterReadMapping::Zero { length } => {
|
||||||
|
buf[buf_offset..buf_offset + length as usize].fill(0);
|
||||||
|
buf_offset += length as usize;
|
||||||
|
}
|
||||||
|
ClusterReadMapping::Allocated {
|
||||||
|
offset: host_offset,
|
||||||
|
length,
|
||||||
|
} => {
|
||||||
|
pread_exact(
|
||||||
|
self.data_fd.as_raw_fd(),
|
||||||
|
&mut buf[buf_offset..buf_offset + length as usize],
|
||||||
|
host_offset,
|
||||||
|
)?;
|
||||||
|
buf_offset += length as usize;
|
||||||
|
}
|
||||||
|
ClusterReadMapping::Compressed { data } => {
|
||||||
|
let len = data.len();
|
||||||
|
buf[buf_offset..buf_offset + len].copy_from_slice(&data);
|
||||||
|
buf_offset += len;
|
||||||
|
}
|
||||||
|
ClusterReadMapping::Backing {
|
||||||
|
offset: backing_offset,
|
||||||
|
length,
|
||||||
|
} => {
|
||||||
|
self.backing_file.as_ref().unwrap().read_at(
|
||||||
|
backing_offset,
|
||||||
|
&mut buf[buf_offset..buf_offset + length as usize],
|
||||||
|
)?;
|
||||||
|
buf_offset += length as usize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for Qcow2MetadataBacking {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
self.metadata.shutdown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+1
-88
@@ -15,7 +15,7 @@ use vmm_sys_util::write_zeroes::{PunchHole, WriteZeroesAt};
|
|||||||
use crate::async_io::{AsyncIo, AsyncIoError, AsyncIoResult, BorrowedDiskFd, DiskFileError};
|
use crate::async_io::{AsyncIo, AsyncIoError, AsyncIoResult, BorrowedDiskFd, DiskFileError};
|
||||||
use crate::disk_file;
|
use crate::disk_file;
|
||||||
use crate::error::{BlockError, BlockErrorKind, BlockResult, ErrorOp};
|
use crate::error::{BlockError, BlockErrorKind, BlockResult, ErrorOp};
|
||||||
use crate::qcow::backing::RawBacking;
|
use crate::qcow::backing::{Qcow2MetadataBacking, RawBacking};
|
||||||
use crate::qcow::metadata::{
|
use crate::qcow::metadata::{
|
||||||
BackingRead, ClusterReadMapping, ClusterWriteMapping, DeallocAction, QcowMetadata,
|
BackingRead, ClusterReadMapping, ClusterWriteMapping, DeallocAction, QcowMetadata,
|
||||||
};
|
};
|
||||||
@@ -27,93 +27,6 @@ use crate::qcow_common::{
|
|||||||
gather_from_iovecs, pread_exact, pwrite_all, scatter_to_iovecs, zero_fill_iovecs,
|
gather_from_iovecs, pread_exact, pwrite_all, scatter_to_iovecs, zero_fill_iovecs,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// QCOW2 backing file with RwLock metadata and pread64 data reads.
|
|
||||||
///
|
|
||||||
/// Read only because backing files never receive writes. Nested backing
|
|
||||||
/// files are handled recursively.
|
|
||||||
struct Qcow2MetadataBacking {
|
|
||||||
metadata: Arc<QcowMetadata>,
|
|
||||||
data_fd: OwnedFd,
|
|
||||||
backing_file: Option<Arc<dyn BackingRead>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
// SAFETY: All reads go through QcowMetadata which uses RwLock
|
|
||||||
// and pread64 which is position independent and thread safe.
|
|
||||||
unsafe impl Sync for Qcow2MetadataBacking {}
|
|
||||||
|
|
||||||
impl BackingRead for Qcow2MetadataBacking {
|
|
||||||
fn read_at(&self, address: u64, buf: &mut [u8]) -> io::Result<()> {
|
|
||||||
let virtual_size = self.metadata.virtual_size();
|
|
||||||
if address >= virtual_size {
|
|
||||||
buf.fill(0);
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
let available = (virtual_size - address) as usize;
|
|
||||||
if available < buf.len() {
|
|
||||||
self.read_clusters(address, &mut buf[..available])?;
|
|
||||||
buf[available..].fill(0);
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
self.read_clusters(address, buf)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Qcow2MetadataBacking {
|
|
||||||
/// Resolve cluster mappings via metadata then read allocated clusters
|
|
||||||
/// with pread64.
|
|
||||||
fn read_clusters(&self, address: u64, buf: &mut [u8]) -> io::Result<()> {
|
|
||||||
let total_len = buf.len();
|
|
||||||
let has_backing = self.backing_file.is_some();
|
|
||||||
|
|
||||||
let mappings = self
|
|
||||||
.metadata
|
|
||||||
.map_clusters_for_read(address, total_len, has_backing)?;
|
|
||||||
|
|
||||||
let mut buf_offset = 0usize;
|
|
||||||
for mapping in mappings {
|
|
||||||
match mapping {
|
|
||||||
ClusterReadMapping::Zero { length } => {
|
|
||||||
buf[buf_offset..buf_offset + length as usize].fill(0);
|
|
||||||
buf_offset += length as usize;
|
|
||||||
}
|
|
||||||
ClusterReadMapping::Allocated {
|
|
||||||
offset: host_offset,
|
|
||||||
length,
|
|
||||||
} => {
|
|
||||||
pread_exact(
|
|
||||||
self.data_fd.as_raw_fd(),
|
|
||||||
&mut buf[buf_offset..buf_offset + length as usize],
|
|
||||||
host_offset,
|
|
||||||
)?;
|
|
||||||
buf_offset += length as usize;
|
|
||||||
}
|
|
||||||
ClusterReadMapping::Compressed { data } => {
|
|
||||||
let len = data.len();
|
|
||||||
buf[buf_offset..buf_offset + len].copy_from_slice(&data);
|
|
||||||
buf_offset += len;
|
|
||||||
}
|
|
||||||
ClusterReadMapping::Backing {
|
|
||||||
offset: backing_offset,
|
|
||||||
length,
|
|
||||||
} => {
|
|
||||||
self.backing_file.as_ref().unwrap().read_at(
|
|
||||||
backing_offset,
|
|
||||||
&mut buf[buf_offset..buf_offset + length as usize],
|
|
||||||
)?;
|
|
||||||
buf_offset += length as usize;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for Qcow2MetadataBacking {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
self.metadata.shutdown();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Construct a thread safe backing file reader.
|
/// Construct a thread safe backing file reader.
|
||||||
fn shared_backing_from(bf: BackingFile) -> BlockResult<Arc<dyn BackingRead>> {
|
fn shared_backing_from(bf: BackingFile) -> BlockResult<Arc<dyn BackingRead>> {
|
||||||
let (kind, virtual_size) = bf.into_kind();
|
let (kind, virtual_size) = bf.into_kind();
|
||||||
|
|||||||
Reference in New Issue
Block a user