qcow: Use RawFile as backend instead of File

Use RawFile as backend instead of File. This allows us to abstract
the access to the actual image with a specialized layer, so we have a
place where we can deal with the low-level peculiarities.

Signed-off-by: Sergio Lopez <slp@redhat.com>
This commit is contained in:
Sergio Lopez 2020-01-15 13:20:02 -05:00 committed by Rob Bradford
parent c5a656c9dc
commit a14aee9213
6 changed files with 93 additions and 73 deletions

1
Cargo.lock generated
View File

@ -588,6 +588,7 @@ dependencies = [
"log 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)",
"remain 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "remain 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"vm-virtio 0.1.0",
"vmm-sys-util 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "vmm-sys-util 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
] ]

View File

@ -14,6 +14,7 @@ libc = "0.2.60"
log = "0.4.8" log = "0.4.8"
remain = "0.2.0" remain = "0.2.0"
vmm-sys-util = ">=0.3.1" vmm-sys-util = ">=0.3.1"
vm-virtio = { path = "../vm-virtio" }
[dev-dependencies] [dev-dependencies]
tempfile = "3.1.0" tempfile = "3.1.0"

View File

@ -12,6 +12,7 @@ mod vec_cache;
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
use libc::{EINVAL, ENOSPC, ENOTSUP}; use libc::{EINVAL, ENOSPC, ENOTSUP};
use remain::sorted; use remain::sorted;
use vm_virtio::RawFile;
use vmm_sys_util::{ use vmm_sys_util::{
file_traits::FileSetLen, file_traits::FileSync, seek_hole::SeekHole, write_zeroes::PunchHole, file_traits::FileSetLen, file_traits::FileSync, seek_hole::SeekHole, write_zeroes::PunchHole,
write_zeroes::WriteZeroes, write_zeroes::WriteZeroes,
@ -19,10 +20,8 @@ use vmm_sys_util::{
use std::cmp::{max, min}; use std::cmp::{max, min};
use std::fmt::{self, Display}; use std::fmt::{self, Display};
use std::fs::File;
use std::io::{self, Read, Seek, SeekFrom, Write}; use std::io::{self, Read, Seek, SeekFrom, Write};
use std::mem::size_of; use std::mem::size_of;
use std::os::unix::io::{AsRawFd, RawFd};
use crate::qcow_raw_file::QcowRawFile; use crate::qcow_raw_file::QcowRawFile;
use crate::refcount::RefCount; use crate::refcount::RefCount;
@ -190,7 +189,7 @@ pub struct QcowHeader {
impl QcowHeader { impl QcowHeader {
/// Creates a QcowHeader from a reference to a file. /// Creates a QcowHeader from a reference to a file.
pub fn new(f: &mut File) -> Result<QcowHeader> { pub fn new(f: &mut RawFile) -> Result<QcowHeader> {
f.seek(SeekFrom::Start(0)).map_err(Error::ReadingHeader)?; f.seek(SeekFrom::Start(0)).map_err(Error::ReadingHeader)?;
let magic = f.read_u32::<BigEndian>().map_err(Error::ReadingHeader)?; let magic = f.read_u32::<BigEndian>().map_err(Error::ReadingHeader)?;
if magic != QCOW_MAGIC { if magic != QCOW_MAGIC {
@ -198,12 +197,12 @@ impl QcowHeader {
} }
// Reads the next u32 from the file. // Reads the next u32 from the file.
fn read_u32_from_file(f: &mut File) -> Result<u32> { fn read_u32_from_file(f: &mut RawFile) -> Result<u32> {
f.read_u32::<BigEndian>().map_err(Error::ReadingHeader) f.read_u32::<BigEndian>().map_err(Error::ReadingHeader)
} }
// Reads the next u64 from the file. // Reads the next u64 from the file.
fn read_u64_from_file(f: &mut File) -> Result<u64> { fn read_u64_from_file(f: &mut RawFile) -> Result<u64> {
f.read_u64::<BigEndian>().map_err(Error::ReadingHeader) f.read_u64::<BigEndian>().map_err(Error::ReadingHeader)
} }
@ -368,9 +367,11 @@ fn max_refcount_clusters(refcount_order: u32, cluster_size: u32, num_clusters: u
/// ///
/// ``` /// ```
/// # use std::io::{Read, Seek, SeekFrom}; /// # use std::io::{Read, Seek, SeekFrom};
/// # use vm_virtio::RawFile;
/// # use qcow::{self, QcowFile}; /// # use qcow::{self, QcowFile};
/// # fn test(file: std::fs::File) -> std::io::Result<()> { /// # fn test(file: std::fs::File) -> std::io::Result<()> {
/// let mut q = QcowFile::from(file).expect("Can't open qcow file"); /// let mut raw_img = RawFile::new(file, false);
/// let mut q = QcowFile::from(raw_img).expect("Can't open qcow file");
/// let mut buf = [0u8; 12]; /// let mut buf = [0u8; 12];
/// q.seek(SeekFrom::Start(10 as u64))?; /// q.seek(SeekFrom::Start(10 as u64))?;
/// q.read(&mut buf[..])?; /// q.read(&mut buf[..])?;
@ -395,7 +396,7 @@ pub struct QcowFile {
impl QcowFile { impl QcowFile {
/// Creates a QcowFile from `file`. File must be a valid qcow2 image. /// Creates a QcowFile from `file`. File must be a valid qcow2 image.
pub fn from(mut file: File) -> Result<QcowFile> { pub fn from(mut file: RawFile) -> Result<QcowFile> {
let header = QcowHeader::new(&mut file)?; let header = QcowHeader::new(&mut file)?;
// Only v2 and v3 files are supported. // Only v2 and v3 files are supported.
@ -544,7 +545,7 @@ impl QcowFile {
} }
/// Creates a new QcowFile at the given path. /// Creates a new QcowFile at the given path.
pub fn new(mut file: File, version: u32, virtual_size: u64) -> Result<QcowFile> { pub fn new(mut file: RawFile, version: u32, virtual_size: u64) -> Result<QcowFile> {
let header = QcowHeader::create_for_size(version, virtual_size); let header = QcowHeader::create_for_size(version, virtual_size);
file.seek(SeekFrom::Start(0)).map_err(Error::SeekingFile)?; file.seek(SeekFrom::Start(0)).map_err(Error::SeekingFile)?;
header.write_to(&mut file)?; header.write_to(&mut file)?;
@ -1394,12 +1395,6 @@ impl Drop for QcowFile {
} }
} }
impl AsRawFd for QcowFile {
fn as_raw_fd(&self) -> RawFd {
self.raw_file.file().as_raw_fd()
}
}
impl Read for QcowFile { impl Read for QcowFile {
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> { fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
let address: u64 = self.current_offset as u64; let address: u64 = self.current_offset as u64;
@ -1634,7 +1629,7 @@ where
Ok(()) Ok(())
} }
fn convert_reader<R>(reader: &mut R, dst_file: File, dst_type: ImageType) -> Result<()> fn convert_reader<R>(reader: &mut R, dst_file: RawFile, dst_type: ImageType) -> Result<()>
where where
R: Read + Seek + SeekHole, R: Read + Seek + SeekHole,
{ {
@ -1666,8 +1661,8 @@ where
/// Copy the contents of a disk image in `src_file` into `dst_file`. /// Copy the contents of a disk image in `src_file` into `dst_file`.
/// The type of `src_file` is automatically detected, and the output file type is /// The type of `src_file` is automatically detected, and the output file type is
/// determined by `dst_type`. /// determined by `dst_type`.
pub fn convert(src_file: File, dst_file: File, dst_type: ImageType) -> Result<()> { pub fn convert(mut src_file: RawFile, dst_file: RawFile, dst_type: ImageType) -> Result<()> {
let src_type = detect_image_type(&src_file)?; let src_type = detect_image_type(&mut src_file)?;
match src_type { match src_type {
ImageType::Qcow2 => { ImageType::Qcow2 => {
let mut src_reader = QcowFile::from(src_file)?; let mut src_reader = QcowFile::from(src_file)?;
@ -1682,17 +1677,18 @@ pub fn convert(src_file: File, dst_file: File, dst_type: ImageType) -> Result<()
} }
/// Detect the type of an image file by checking for a valid qcow2 header. /// Detect the type of an image file by checking for a valid qcow2 header.
pub fn detect_image_type(file: &File) -> Result<ImageType> { pub fn detect_image_type(file: &mut RawFile) -> Result<ImageType> {
let mut f = file; let orig_seek = file
let orig_seek = f.seek(SeekFrom::Current(0)).map_err(Error::SeekingFile)?; .seek(SeekFrom::Current(0))
f.seek(SeekFrom::Start(0)).map_err(Error::SeekingFile)?; .map_err(Error::SeekingFile)?;
let magic = f.read_u32::<BigEndian>().map_err(Error::ReadingHeader)?; file.seek(SeekFrom::Start(0)).map_err(Error::SeekingFile)?;
let magic = file.read_u32::<BigEndian>().map_err(Error::ReadingHeader)?;
let image_type = if magic == QCOW_MAGIC { let image_type = if magic == QCOW_MAGIC {
ImageType::Qcow2 ImageType::Qcow2
} else { } else {
ImageType::Raw ImageType::Raw
}; };
f.seek(SeekFrom::Start(orig_seek)) file.seek(SeekFrom::Start(orig_seek))
.map_err(Error::SeekingFile)?; .map_err(Error::SeekingFile)?;
Ok(image_type) Ok(image_type)
} }
@ -1700,9 +1696,9 @@ pub fn detect_image_type(file: &File) -> Result<ImageType> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use std::fs::File;
use std::io::{Read, Seek, SeekFrom, Write}; use std::io::{Read, Seek, SeekFrom, Write};
use tempfile::tempfile; use tempfile::tempfile;
use vm_virtio::RawFile;
fn valid_header_v3() -> Vec<u8> { fn valid_header_v3() -> Vec<u8> {
vec![ vec![
@ -1771,9 +1767,9 @@ mod tests {
fn with_basic_file<F>(header: &[u8], mut testfn: F) fn with_basic_file<F>(header: &[u8], mut testfn: F)
where where
F: FnMut(File), F: FnMut(RawFile),
{ {
let mut disk_file: File = tempfile().unwrap(); let mut disk_file: RawFile = RawFile::new(tempfile().unwrap(), false);
disk_file.write_all(&header).unwrap(); disk_file.write_all(&header).unwrap();
disk_file.set_len(0x1_0000_0000).unwrap(); disk_file.set_len(0x1_0000_0000).unwrap();
disk_file.seek(SeekFrom::Start(0)).unwrap(); disk_file.seek(SeekFrom::Start(0)).unwrap();
@ -1781,11 +1777,11 @@ mod tests {
testfn(disk_file); // File closed when the function exits. testfn(disk_file); // File closed when the function exits.
} }
fn with_default_file<F>(file_size: u64, mut testfn: F) fn with_default_file<F>(file_size: u64, direct: bool, mut testfn: F)
where where
F: FnMut(QcowFile), F: FnMut(QcowFile),
{ {
let tmp = tempfile().unwrap(); let tmp: RawFile = RawFile::new(tempfile().unwrap(), direct);
let qcow_file = QcowFile::new(tmp, 3, file_size).unwrap(); let qcow_file = QcowFile::new(tmp, 3, file_size).unwrap();
testfn(qcow_file); // File closed when the function exits. testfn(qcow_file); // File closed when the function exits.
@ -1794,7 +1790,7 @@ mod tests {
#[test] #[test]
fn default_header_v2() { fn default_header_v2() {
let header = QcowHeader::create_for_size(2, 0x10_0000); let header = QcowHeader::create_for_size(2, 0x10_0000);
let mut disk_file: File = tempfile().unwrap(); let mut disk_file: RawFile = RawFile::new(tempfile().unwrap(), false);
header header
.write_to(&mut disk_file) .write_to(&mut disk_file)
.expect("Failed to write header to temporary file."); .expect("Failed to write header to temporary file.");
@ -1805,7 +1801,7 @@ mod tests {
#[test] #[test]
fn default_header_v3() { fn default_header_v3() {
let header = QcowHeader::create_for_size(3, 0x10_0000); let header = QcowHeader::create_for_size(3, 0x10_0000);
let mut disk_file: File = tempfile().unwrap(); let mut disk_file: RawFile = RawFile::new(tempfile().unwrap(), false);
header header
.write_to(&mut disk_file) .write_to(&mut disk_file)
.expect("Failed to write header to temporary file."); .expect("Failed to write header to temporary file.");
@ -1815,13 +1811,13 @@ mod tests {
#[test] #[test]
fn header_read() { fn header_read() {
with_basic_file(&valid_header_v2(), |mut disk_file: File| { with_basic_file(&valid_header_v2(), |mut disk_file: RawFile| {
let header = QcowHeader::new(&mut disk_file).expect("Failed to create Header."); let header = QcowHeader::new(&mut disk_file).expect("Failed to create Header.");
assert_eq!(header.version, 2); assert_eq!(header.version, 2);
assert_eq!(header.refcount_order, DEFAULT_REFCOUNT_ORDER); assert_eq!(header.refcount_order, DEFAULT_REFCOUNT_ORDER);
assert_eq!(header.header_size, V2_BARE_HEADER_SIZE); assert_eq!(header.header_size, V2_BARE_HEADER_SIZE);
}); });
with_basic_file(&valid_header_v3(), |mut disk_file: File| { with_basic_file(&valid_header_v3(), |mut disk_file: RawFile| {
let header = QcowHeader::new(&mut disk_file).expect("Failed to create Header."); let header = QcowHeader::new(&mut disk_file).expect("Failed to create Header.");
assert_eq!(header.version, 3); assert_eq!(header.version, 3);
assert_eq!(header.refcount_order, DEFAULT_REFCOUNT_ORDER); assert_eq!(header.refcount_order, DEFAULT_REFCOUNT_ORDER);
@ -1832,7 +1828,7 @@ mod tests {
#[test] #[test]
fn invalid_magic() { fn invalid_magic() {
let invalid_header = vec![0x51u8, 0x46, 0x4a, 0xfb]; let invalid_header = vec![0x51u8, 0x46, 0x4a, 0xfb];
with_basic_file(&invalid_header, |mut disk_file: File| { with_basic_file(&invalid_header, |mut disk_file: RawFile| {
QcowHeader::new(&mut disk_file).expect_err("Invalid header worked."); QcowHeader::new(&mut disk_file).expect_err("Invalid header worked.");
}); });
} }
@ -1841,7 +1837,7 @@ mod tests {
fn invalid_refcount_order() { fn invalid_refcount_order() {
let mut header = valid_header_v3(); let mut header = valid_header_v3();
header[99] = 2; header[99] = 2;
with_basic_file(&header, |disk_file: File| { with_basic_file(&header, |disk_file: RawFile| {
QcowFile::from(disk_file).expect_err("Invalid refcount order worked."); QcowFile::from(disk_file).expect_err("Invalid refcount order worked.");
}); });
} }
@ -1850,7 +1846,7 @@ mod tests {
fn invalid_cluster_bits() { fn invalid_cluster_bits() {
let mut header = valid_header_v3(); let mut header = valid_header_v3();
header[23] = 3; header[23] = 3;
with_basic_file(&header, |disk_file: File| { with_basic_file(&header, |disk_file: RawFile| {
QcowFile::from(disk_file).expect_err("Failed to create file."); QcowFile::from(disk_file).expect_err("Failed to create file.");
}); });
} }
@ -1858,7 +1854,7 @@ mod tests {
#[test] #[test]
fn test_header_huge_file() { fn test_header_huge_file() {
let header = test_huge_header(); let header = test_huge_header();
with_basic_file(&header, |disk_file: File| { with_basic_file(&header, |disk_file: RawFile| {
QcowFile::from(disk_file).expect_err("Failed to create file."); QcowFile::from(disk_file).expect_err("Failed to create file.");
}); });
} }
@ -1867,7 +1863,7 @@ mod tests {
fn test_header_crazy_file_size_rejected() { fn test_header_crazy_file_size_rejected() {
let mut header = valid_header_v3(); let mut header = valid_header_v3();
&mut header[24..32].copy_from_slice(&[0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1e]); &mut header[24..32].copy_from_slice(&[0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1e]);
with_basic_file(&header, |disk_file: File| { with_basic_file(&header, |disk_file: RawFile| {
QcowFile::from(disk_file).expect_err("Failed to create file."); QcowFile::from(disk_file).expect_err("Failed to create file.");
}); });
} }
@ -1876,7 +1872,7 @@ mod tests {
fn test_huge_l1_table() { fn test_huge_l1_table() {
let mut header = valid_header_v3(); let mut header = valid_header_v3();
header[36] = 0x12; header[36] = 0x12;
with_basic_file(&header, |disk_file: File| { with_basic_file(&header, |disk_file: RawFile| {
QcowFile::from(disk_file).expect_err("Failed to create file."); QcowFile::from(disk_file).expect_err("Failed to create file.");
}); });
} }
@ -1888,7 +1884,7 @@ mod tests {
header[26] = 1; header[26] = 1;
header[31] = 0; header[31] = 0;
// 1 TB with the min cluster size makes the arrays too big, it should fail. // 1 TB with the min cluster size makes the arrays too big, it should fail.
with_basic_file(&header, |disk_file: File| { with_basic_file(&header, |disk_file: RawFile| {
QcowFile::from(disk_file).expect_err("Failed to create file."); QcowFile::from(disk_file).expect_err("Failed to create file.");
}); });
} }
@ -1902,7 +1898,7 @@ mod tests {
header[31] = 0; header[31] = 0;
// set cluster_bits // set cluster_bits
header[23] = 16; header[23] = 16;
with_basic_file(&header, |disk_file: File| { with_basic_file(&header, |disk_file: RawFile| {
let mut qcow = QcowFile::from(disk_file).expect("Failed to create file."); let mut qcow = QcowFile::from(disk_file).expect("Failed to create file.");
qcow.seek(SeekFrom::Start(0x100_0000_0000 - 8)) qcow.seek(SeekFrom::Start(0x100_0000_0000 - 8))
.expect("Failed to seek."); .expect("Failed to seek.");
@ -1916,7 +1912,7 @@ mod tests {
fn test_header_huge_num_refcounts() { fn test_header_huge_num_refcounts() {
let mut header = valid_header_v3(); let mut header = valid_header_v3();
&mut header[56..60].copy_from_slice(&[0x02, 0x00, 0xe8, 0xff]); &mut header[56..60].copy_from_slice(&[0x02, 0x00, 0xe8, 0xff]);
with_basic_file(&header, |disk_file: File| { with_basic_file(&header, |disk_file: RawFile| {
QcowFile::from(disk_file).expect_err("Created disk with crazy refcount clusters"); QcowFile::from(disk_file).expect_err("Created disk with crazy refcount clusters");
}); });
} }
@ -1925,14 +1921,14 @@ mod tests {
fn test_header_huge_refcount_offset() { fn test_header_huge_refcount_offset() {
let mut header = valid_header_v3(); let mut header = valid_header_v3();
&mut header[48..56].copy_from_slice(&[0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x02, 0x00]); &mut header[48..56].copy_from_slice(&[0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x02, 0x00]);
with_basic_file(&header, |disk_file: File| { with_basic_file(&header, |disk_file: RawFile| {
QcowFile::from(disk_file).expect_err("Created disk with crazy refcount offset"); QcowFile::from(disk_file).expect_err("Created disk with crazy refcount offset");
}); });
} }
#[test] #[test]
fn write_read_start() { fn write_read_start() {
with_basic_file(&valid_header_v3(), |disk_file: File| { with_basic_file(&valid_header_v3(), |disk_file: RawFile| {
let mut q = QcowFile::from(disk_file).unwrap(); let mut q = QcowFile::from(disk_file).unwrap();
q.write(b"test first bytes") q.write(b"test first bytes")
.expect("Failed to write test string."); .expect("Failed to write test string.");
@ -1945,7 +1941,7 @@ mod tests {
#[test] #[test]
fn offset_write_read() { fn offset_write_read() {
with_basic_file(&valid_header_v3(), |disk_file: File| { with_basic_file(&valid_header_v3(), |disk_file: RawFile| {
let mut q = QcowFile::from(disk_file).unwrap(); let mut q = QcowFile::from(disk_file).unwrap();
let b = [0x55u8; 0x1000]; let b = [0x55u8; 0x1000];
q.seek(SeekFrom::Start(0xfff2000)).expect("Failed to seek."); q.seek(SeekFrom::Start(0xfff2000)).expect("Failed to seek.");
@ -1959,7 +1955,7 @@ mod tests {
#[test] #[test]
fn write_zeroes_read() { fn write_zeroes_read() {
with_basic_file(&valid_header_v3(), |disk_file: File| { with_basic_file(&valid_header_v3(), |disk_file: RawFile| {
let mut q = QcowFile::from(disk_file).unwrap(); let mut q = QcowFile::from(disk_file).unwrap();
// Write some test data. // Write some test data.
let b = [0x55u8; 0x1000]; let b = [0x55u8; 0x1000];
@ -1985,7 +1981,7 @@ mod tests {
// Choose a size that is larger than a cluster. // Choose a size that is larger than a cluster.
// valid_header uses cluster_bits = 12, which corresponds to a cluster size of 4096. // valid_header uses cluster_bits = 12, which corresponds to a cluster size of 4096.
const CHUNK_SIZE: usize = 4096 * 2 + 512; const CHUNK_SIZE: usize = 4096 * 2 + 512;
with_basic_file(&valid_header_v3(), |disk_file: File| { with_basic_file(&valid_header_v3(), |disk_file: RawFile| {
let mut q = QcowFile::from(disk_file).unwrap(); let mut q = QcowFile::from(disk_file).unwrap();
// Write some test data. // Write some test data.
let b = [0x55u8; CHUNK_SIZE]; let b = [0x55u8; CHUNK_SIZE];
@ -2006,11 +2002,11 @@ mod tests {
#[test] #[test]
fn test_header() { fn test_header() {
with_basic_file(&valid_header_v2(), |disk_file: File| { with_basic_file(&valid_header_v2(), |disk_file: RawFile| {
let q = QcowFile::from(disk_file).unwrap(); let q = QcowFile::from(disk_file).unwrap();
assert_eq!(q.virtual_size(), 0x20_0000_0000); assert_eq!(q.virtual_size(), 0x20_0000_0000);
}); });
with_basic_file(&valid_header_v3(), |disk_file: File| { with_basic_file(&valid_header_v3(), |disk_file: RawFile| {
let q = QcowFile::from(disk_file).unwrap(); let q = QcowFile::from(disk_file).unwrap();
assert_eq!(q.virtual_size(), 0x20_0000_0000); assert_eq!(q.virtual_size(), 0x20_0000_0000);
}); });
@ -2018,7 +2014,7 @@ mod tests {
#[test] #[test]
fn read_small_buffer() { fn read_small_buffer() {
with_basic_file(&valid_header_v3(), |disk_file: File| { with_basic_file(&valid_header_v3(), |disk_file: RawFile| {
let mut q = QcowFile::from(disk_file).unwrap(); let mut q = QcowFile::from(disk_file).unwrap();
let mut b = [5u8; 16]; let mut b = [5u8; 16];
q.seek(SeekFrom::Start(1000)).expect("Failed to seek."); q.seek(SeekFrom::Start(1000)).expect("Failed to seek.");
@ -2030,7 +2026,7 @@ mod tests {
#[test] #[test]
fn replay_ext4() { fn replay_ext4() {
with_basic_file(&valid_header_v3(), |disk_file: File| { with_basic_file(&valid_header_v3(), |disk_file: RawFile| {
let mut q = QcowFile::from(disk_file).unwrap(); let mut q = QcowFile::from(disk_file).unwrap();
const BUF_SIZE: usize = 0x1000; const BUF_SIZE: usize = 0x1000;
let mut b = [0u8; BUF_SIZE]; let mut b = [0u8; BUF_SIZE];
@ -2406,7 +2402,16 @@ mod tests {
#[test] #[test]
fn combo_write_read() { fn combo_write_read() {
with_default_file(1024 * 1024 * 1024 * 256, |mut qcow_file| { combo_write_read_common(false);
}
#[test]
fn combo_write_read_direct() {
combo_write_read_common(true);
}
fn combo_write_read_common(direct: bool) {
with_default_file(1024 * 1024 * 1024 * 256, direct, |mut qcow_file| {
const NUM_BLOCKS: usize = 555; const NUM_BLOCKS: usize = 555;
const BLOCK_SIZE: usize = 0x1_0000; const BLOCK_SIZE: usize = 0x1_0000;
const OFFSET: usize = 0x1_0000_0020; const OFFSET: usize = 0x1_0000_0020;
@ -2459,7 +2464,16 @@ mod tests {
#[test] #[test]
fn seek_data() { fn seek_data() {
with_default_file(0x30000, |mut file| { seek_data_common(false);
}
#[test]
fn seek_data_direct() {
seek_data_common(true);
}
fn seek_data_common(direct: bool) {
with_default_file(0x30000, direct, |mut file| {
// seek_data at or after the end of the file should return None // seek_data at or after the end of the file should return None
assert_eq!(file.seek_data(0x10000).unwrap(), None); assert_eq!(file.seek_data(0x10000).unwrap(), None);
assert_eq!(seek_cur(&mut file), 0); assert_eq!(seek_cur(&mut file), 0);
@ -2492,7 +2506,16 @@ mod tests {
#[test] #[test]
fn seek_hole() { fn seek_hole() {
with_default_file(0x30000, |mut file| { seek_hole_common(false);
}
#[test]
fn seek_hole_direct() {
seek_hole_common(true);
}
fn seek_hole_common(direct: bool) {
with_default_file(0x30000, direct, |mut file| {
// File consisting entirely of a hole // File consisting entirely of a hole
assert_eq!(file.seek_hole(0).unwrap(), Some(0)); assert_eq!(file.seek_hole(0).unwrap(), Some(0));
assert_eq!(seek_cur(&mut file), 0); assert_eq!(seek_cur(&mut file), 0);
@ -2566,7 +2589,7 @@ mod tests {
#[test] #[test]
fn rebuild_refcounts() { fn rebuild_refcounts() {
with_basic_file(&valid_header_v3(), |mut disk_file: File| { with_basic_file(&valid_header_v3(), |mut disk_file: RawFile| {
let header = QcowHeader::new(&mut disk_file).expect("Failed to create Header."); let header = QcowHeader::new(&mut disk_file).expect("Failed to create Header.");
let cluster_size = 65536; let cluster_size = 65536;
let mut raw_file = let mut raw_file =

View File

@ -2,17 +2,17 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE-BSD-3-Clause file. // found in the LICENSE-BSD-3-Clause file.
use std::fs::File;
use std::io::{self, BufWriter, Seek, SeekFrom}; use std::io::{self, BufWriter, Seek, SeekFrom};
use std::mem::size_of; use std::mem::size_of;
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
use vm_virtio::RawFile;
use vmm_sys_util::write_zeroes::WriteZeroes; use vmm_sys_util::write_zeroes::WriteZeroes;
/// A qcow file. Allows reading/writing clusters and appending clusters. /// A qcow file. Allows reading/writing clusters and appending clusters.
#[derive(Debug)] #[derive(Debug)]
pub struct QcowRawFile { pub struct QcowRawFile {
file: File, file: RawFile,
cluster_size: u64, cluster_size: u64,
cluster_mask: u64, cluster_mask: u64,
} }
@ -20,7 +20,7 @@ pub struct QcowRawFile {
impl QcowRawFile { impl QcowRawFile {
/// Creates a `QcowRawFile` from the given `File`, `None` is returned if `cluster_size` is not /// Creates a `QcowRawFile` from the given `File`, `None` is returned if `cluster_size` is not
/// a power of two. /// a power of two.
pub fn from(file: File, cluster_size: u64) -> Option<Self> { pub fn from(file: RawFile, cluster_size: u64) -> Option<Self> {
if cluster_size.count_ones() != 1 { if cluster_size.count_ones() != 1 {
return None; return None;
} }
@ -67,7 +67,7 @@ impl QcowRawFile {
non_zero_flags: u64, non_zero_flags: u64,
) -> io::Result<()> { ) -> io::Result<()> {
self.file.seek(SeekFrom::Start(offset))?; self.file.seek(SeekFrom::Start(offset))?;
let mut buffer = BufWriter::with_capacity(table.len() * size_of::<u64>(), &self.file); let mut buffer = BufWriter::with_capacity(table.len() * size_of::<u64>(), &mut self.file);
for addr in table { for addr in table {
let val = if *addr == 0 { let val = if *addr == 0 {
0 0
@ -92,7 +92,7 @@ impl QcowRawFile {
/// Writes a refcount block to the file. /// Writes a refcount block to the file.
pub fn write_refcount_block(&mut self, offset: u64, table: &[u16]) -> io::Result<()> { pub fn write_refcount_block(&mut self, offset: u64, table: &[u16]) -> io::Result<()> {
self.file.seek(SeekFrom::Start(offset))?; self.file.seek(SeekFrom::Start(offset))?;
let mut buffer = BufWriter::with_capacity(table.len() * size_of::<u16>(), &self.file); let mut buffer = BufWriter::with_capacity(table.len() * size_of::<u16>(), &mut self.file);
for count in table { for count in table {
buffer.write_u16::<BigEndian>(*count)?; buffer.write_u16::<BigEndian>(*count)?;
} }
@ -115,13 +115,8 @@ impl QcowRawFile {
Ok(Some(new_cluster_address)) Ok(Some(new_cluster_address))
} }
/// Returns a reference to the underlying file.
pub fn file(&self) -> &File {
&self.file
}
/// Returns a mutable reference to the underlying file. /// Returns a mutable reference to the underlying file.
pub fn file_mut(&mut self) -> &mut File { pub fn file_mut(&mut self) -> &mut RawFile {
&mut self.file &mut self.file
} }

View File

@ -78,18 +78,17 @@ struct VhostUserBlkBackend {
impl VhostUserBlkBackend { impl VhostUserBlkBackend {
pub fn new(image_path: String, rdonly: bool) -> Result<Self> { pub fn new(image_path: String, rdonly: bool) -> Result<Self> {
let raw_img: File = OpenOptions::new() let image: File = OpenOptions::new()
.read(true) .read(true)
.write(!rdonly) .write(!rdonly)
.open(&image_path) .open(&image_path)
.unwrap(); .unwrap();
let mut raw_img = vm_virtio::RawFile::new(image, false);
let image_id = build_disk_image_id(&PathBuf::from(&image_path)); let image_id = build_disk_image_id(&PathBuf::from(&image_path));
let image_type = qcow::detect_image_type(&raw_img).unwrap(); let image_type = qcow::detect_image_type(&mut raw_img).unwrap();
let mut image = match image_type { let mut image = match image_type {
ImageType::Raw => { ImageType::Raw => Box::new(raw_img) as Box<dyn DiskFile>,
Box::new(vm_virtio::RawFile::new(raw_img, false)) as Box<dyn DiskFile>
}
ImageType::Qcow2 => Box::new(QcowFile::from(raw_img).unwrap()) as Box<dyn DiskFile>, ImageType::Qcow2 => Box::new(QcowFile::from(raw_img).unwrap()) as Box<dyn DiskFile>,
}; };

View File

@ -927,17 +927,18 @@ impl DeviceManager {
if let Some(disk_list_cfg) = &vm_info.vm_cfg.lock().unwrap().disks { if let Some(disk_list_cfg) = &vm_info.vm_cfg.lock().unwrap().disks {
for disk_cfg in disk_list_cfg.iter() { for disk_cfg in disk_list_cfg.iter() {
// Open block device path // Open block device path
let raw_img: File = OpenOptions::new() let image: File = OpenOptions::new()
.read(true) .read(true)
.write(true) .write(true)
.open(&disk_cfg.path) .open(&disk_cfg.path)
.map_err(DeviceManagerError::Disk)?; .map_err(DeviceManagerError::Disk)?;
let image_type = qcow::detect_image_type(&raw_img) let mut raw_img = vm_virtio::RawFile::new(image, false);
let image_type = qcow::detect_image_type(&mut raw_img)
.map_err(DeviceManagerError::DetectImageType)?; .map_err(DeviceManagerError::DetectImageType)?;
match image_type { match image_type {
ImageType::Raw => { ImageType::Raw => {
let raw_img = vm_virtio::RawFile::new(raw_img, false);
let dev = vm_virtio::Block::new( let dev = vm_virtio::Block::new(
raw_img, raw_img,
disk_cfg.path.clone(), disk_cfg.path.clone(),