diff --git a/Cargo.lock b/Cargo.lock index 98625dc15b..b4c5c6a585 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -85,6 +85,7 @@ dependencies = [ "crosvm_plugin 0.17.0", "data_model 0.1.0", "devices 0.1.0", + "disk 0.1.0", "enumn 0.1.0", "gpu_buffer 0.1.0", "io_jail 0.1.0", @@ -137,6 +138,7 @@ dependencies = [ "audio_streams 0.1.0", "bit_field 0.1.0", "data_model 0.1.0", + "disk 0.1.0", "enumn 0.1.0", "gpu_buffer 0.1.0", "gpu_display 0.1.0", @@ -162,6 +164,16 @@ dependencies = [ "vm_control 0.1.0", ] +[[package]] +name = "disk" +version = "0.1.0" +dependencies = [ + "libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)", + "qcow 0.1.0", + "remain 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "sys_util 0.1.0", +] + [[package]] name = "enumn" version = "0.1.0" @@ -397,6 +409,7 @@ dependencies = [ name = "qcow_utils" version = "0.1.0" dependencies = [ + "disk 0.1.0", "getopts 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)", "qcow 0.1.0", diff --git a/Cargo.toml b/Cargo.toml index 75d3b136ca..ec0d12b9bc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -45,6 +45,7 @@ bit_field = { path = "bit_field" } crosvm_plugin = { path = "crosvm_plugin", optional = true } data_model = "*" devices = { path = "devices" } +disk = { path = "disk" } enumn = { path = "enumn" } gpu_buffer = { path = "gpu_buffer", optional = true } io_jail = { path = "io_jail" } diff --git a/devices/Cargo.toml b/devices/Cargo.toml index 47f55df63e..8a91a4ea18 100644 --- a/devices/Cargo.toml +++ b/devices/Cargo.toml @@ -15,6 +15,7 @@ x = ["gpu_display/x"] audio_streams = "*" bit_field = { path = "../bit_field" } data_model = { path = "../data_model" } +disk = { path = "../disk" } enumn = { path = "../enumn" } gpu_buffer = { path = "../gpu_buffer", optional = true } gpu_display = { path = "../gpu_display", optional = true } diff --git a/devices/src/virtio/block.rs b/devices/src/virtio/block.rs index a6d7a89aeb..7a728d73ef 100644 --- a/devices/src/virtio/block.rs +++ b/devices/src/virtio/block.rs @@ -13,16 +13,13 @@ use std::thread; use std::time::Duration; use std::u32; +use data_model::{DataInit, Le16, Le32, Le64}; +use disk::DiskFile; +use msg_socket::{MsgReceiver, MsgSender}; use sync::Mutex; use sys_util::Error as SysError; use sys_util::Result as SysResult; -use sys_util::{ - error, info, warn, EventFd, FileReadWriteVolatile, FileSetLen, FileSync, GuestMemory, - PollContext, PollToken, PunchHole, TimerFd, WriteZeroes, -}; - -use data_model::{DataInit, Le16, Le32, Le64}; -use msg_socket::{MsgReceiver, MsgSender}; +use sys_util::{error, info, warn, EventFd, GuestMemory, PollContext, PollToken, TimerFd}; use vm_control::{DiskControlCommand, DiskControlResponseSocket, DiskControlResult}; use super::{ @@ -129,15 +126,6 @@ const VIRTIO_BLK_DISCARD_WRITE_ZEROES_FLAG_UNMAP: u32 = 1 << 0; // Safe because it only has data and has no implicit padding. unsafe impl DataInit for virtio_blk_discard_write_zeroes {} -pub trait DiskFile: - FileSetLen + FileSync + FileReadWriteVolatile + PunchHole + Seek + WriteZeroes -{ -} -impl DiskFile - for D -{ -} - #[derive(Debug)] enum ExecuteError { Descriptor(DescriptorError), @@ -275,10 +263,10 @@ impl ExecuteError { } } -struct Worker { +struct Worker { queues: Vec, mem: GuestMemory, - disk_image: T, + disk_image: Box, disk_size: Arc>, read_only: bool, interrupt_status: Arc, @@ -286,7 +274,7 @@ struct Worker { interrupt_resample_evt: EventFd, } -impl Worker { +impl Worker { fn process_queue( &mut self, queue_index: usize, @@ -305,7 +293,7 @@ impl Worker { let status = match Block::execute_request( avail_desc, self.read_only, - &mut self.disk_image, + &mut *self.disk_image, *disk_size, flush_timer, flush_timer_armed, @@ -480,9 +468,9 @@ impl Worker { } /// Virtio device for exposing block level read/write operations on a host file. -pub struct Block { +pub struct Block { kill_evt: Option, - disk_image: Option, + disk_image: Option>, disk_size: Arc>, avail_features: u64, read_only: bool, @@ -504,15 +492,15 @@ fn build_config_space(disk_size: u64) -> virtio_blk_config { } } -impl Block { +impl Block { /// Create a new virtio block device that operates on the given file. /// /// The given file must be seekable and sizable. pub fn new( - mut disk_image: T, + mut disk_image: Box, read_only: bool, control_socket: Option, - ) -> SysResult> { + ) -> SysResult { let disk_size = disk_image.seek(SeekFrom::End(0))? as u64; if disk_size % SECTOR_SIZE != 0 { warn!( @@ -545,7 +533,7 @@ impl Block { fn execute_request( avail_desc: DescriptorChain, read_only: bool, - disk: &mut T, + disk: &mut DiskFile, disk_size: u64, flush_timer: &mut TimerFd, flush_timer_armed: &mut bool, @@ -705,7 +693,7 @@ impl Block { } } -impl Drop for Block { +impl Drop for Block { fn drop(&mut self) { if let Some(kill_evt) = self.kill_evt.take() { // Ignore the result because there is nothing we can do about it. @@ -714,7 +702,7 @@ impl Drop for Block { } } -impl VirtioDevice for Block { +impl VirtioDevice for Block { fn keep_fds(&self) -> Vec { let mut keep_fds = Vec::new(); @@ -820,7 +808,7 @@ mod tests { let f = File::create(&path).unwrap(); f.set_len(0x1000).unwrap(); - let b = Block::new(f, true, None).unwrap(); + let b = Block::new(Box::new(f), true, None).unwrap(); let mut num_sectors = [0u8; 4]; b.read_config(0, &mut num_sectors); // size is 0x1000, so num_sectors is 8 (4096/512). @@ -840,7 +828,7 @@ mod tests { // read-write block device { let f = File::create(&path).unwrap(); - let b = Block::new(f, false, None).unwrap(); + let b = Block::new(Box::new(f), false, None).unwrap(); // writable device should set VIRTIO_BLK_F_FLUSH + VIRTIO_BLK_F_DISCARD // + VIRTIO_BLK_F_WRITE_ZEROES + VIRTIO_F_VERSION_1 + VIRTIO_BLK_F_BLK_SIZE assert_eq!(0x100006240, b.features()); @@ -849,7 +837,7 @@ mod tests { // read-only block device { let f = File::create(&path).unwrap(); - let b = Block::new(f, true, None).unwrap(); + let b = Block::new(Box::new(f), true, None).unwrap(); // read-only device should set VIRTIO_BLK_F_FLUSH and VIRTIO_BLK_F_RO // + VIRTIO_F_VERSION_1 + VIRTIO_BLK_F_BLK_SIZE assert_eq!(0x100000260, b.features()); diff --git a/devices/src/virtio/descriptor_utils.rs b/devices/src/virtio/descriptor_utils.rs index fb2df16162..5142ecfafd 100644 --- a/devices/src/virtio/descriptor_utils.rs +++ b/devices/src/virtio/descriptor_utils.rs @@ -281,9 +281,9 @@ impl<'a> Reader<'a> { /// Returns the number of bytes read from the descriptor chain buffer. /// The number of bytes read can be less than `count` if there isn't /// enough data in the descriptor chain buffer. - pub fn read_to_volatile( + pub fn read_to_volatile( &mut self, - dst: &mut dyn FileReadWriteVolatile, + dst: &mut T, count: usize, ) -> Result { let mem = self.mem; @@ -399,9 +399,9 @@ impl<'a> Writer<'a> { /// Returns the number of bytes written to the descriptor chain buffer. /// The number of bytes written can be less than `count` if /// there isn't enough data in the descriptor chain buffer. - pub fn write_from_volatile( + pub fn write_from_volatile( &mut self, - src: &mut dyn FileReadWriteVolatile, + src: &mut T, count: usize, ) -> Result { let mem = self.mem; diff --git a/disk/Cargo.toml b/disk/Cargo.toml new file mode 100644 index 0000000000..c26cf4bad6 --- /dev/null +++ b/disk/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "disk" +version = "0.1.0" +authors = ["The Chromium OS Authors"] +edition = "2018" + +[lib] +path = "src/disk.rs" + +[dependencies] +libc = "*" +remain = "*" +qcow = { path = "../qcow" } +sys_util = { path = "../sys_util" } diff --git a/disk/src/disk.rs b/disk/src/disk.rs new file mode 100644 index 0000000000..c9ce1b3c1f --- /dev/null +++ b/disk/src/disk.rs @@ -0,0 +1,209 @@ +// Copyright 2019 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +use std::cmp::min; +use std::fmt::{self, Display}; +use std::fs::File; +use std::io::{self, Read, Seek, SeekFrom, Write}; +use std::os::unix::io::AsRawFd; + +use libc::EINVAL; +use qcow::{QcowFile, QCOW_MAGIC}; +use remain::sorted; +use sys_util::{FileReadWriteVolatile, FileSetLen, FileSync, PunchHole, SeekHole, WriteZeroes}; + +#[sorted] +#[derive(Debug)] +pub enum Error { + BlockDeviceNew(sys_util::Error), + QcowError(qcow::Error), + ReadingData(io::Error), + ReadingHeader(io::Error), + SeekingFile(io::Error), + SettingFileSize(io::Error), + UnknownType, + WritingData(io::Error), +} + +pub type Result = std::result::Result; + +/// The prerequisites necessary to support a block device. +pub trait DiskFile: + FileSetLen + FileSync + FileReadWriteVolatile + PunchHole + Seek + WriteZeroes + Send + AsRawFd +{ +} +impl< + D: FileSetLen + + FileSync + + PunchHole + + FileReadWriteVolatile + + Seek + + WriteZeroes + + Send + + AsRawFd, + > DiskFile for D +{ +} + +impl Display for Error { + #[remain::check] + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use self::Error::*; + + #[sorted] + match self { + BlockDeviceNew(e) => write!(f, "failed to create block device: {}", e), + QcowError(e) => write!(f, "failure in qcow: {}", e), + ReadingData(e) => write!(f, "failed to read data: {}", e), + ReadingHeader(e) => write!(f, "failed to read header: {}", e), + SeekingFile(e) => write!(f, "failed to seek file: {}", e), + SettingFileSize(e) => write!(f, "failed to set file size: {}", e), + UnknownType => write!(f, "unknown disk type"), + WritingData(e) => write!(f, "failed to write data: {}", e), + } + } +} + +/// The variants of image files on the host that can be used as virtual disks. +#[derive(Debug, PartialEq, Eq)] +pub enum ImageType { + Raw, + Qcow2, +} + +fn convert_copy(reader: &mut R, writer: &mut W, offset: u64, size: u64) -> Result<()> +where + R: Read + Seek, + W: Write + Seek, +{ + const CHUNK_SIZE: usize = 65536; + let mut buf = [0; CHUNK_SIZE]; + let mut read_count = 0; + reader + .seek(SeekFrom::Start(offset)) + .map_err(Error::SeekingFile)?; + writer + .seek(SeekFrom::Start(offset)) + .map_err(Error::SeekingFile)?; + loop { + let this_count = min(CHUNK_SIZE as u64, size - read_count) as usize; + let nread = reader + .read(&mut buf[..this_count]) + .map_err(Error::ReadingData)?; + writer.write(&buf[..nread]).map_err(Error::WritingData)?; + read_count += nread as u64; + if nread == 0 || read_count == size { + break; + } + } + + Ok(()) +} + +fn convert_reader_writer(reader: &mut R, writer: &mut W, size: u64) -> Result<()> +where + R: Read + Seek + SeekHole, + W: Write + Seek, +{ + let mut offset = 0; + while offset < size { + // Find the next range of data. + let next_data = match reader.seek_data(offset).map_err(Error::SeekingFile)? { + Some(o) => o, + None => { + // No more data in the file. + break; + } + }; + let next_hole = match reader.seek_hole(next_data).map_err(Error::SeekingFile)? { + Some(o) => o, + None => { + // This should not happen - there should always be at least one hole + // after any data. + return Err(Error::SeekingFile(io::Error::from_raw_os_error(EINVAL))); + } + }; + let count = next_hole - next_data; + convert_copy(reader, writer, next_data, count)?; + offset = next_hole; + } + + Ok(()) +} + +fn convert_reader(reader: &mut R, dst_file: File, dst_type: ImageType) -> Result<()> +where + R: Read + Seek + SeekHole, +{ + let src_size = reader.seek(SeekFrom::End(0)).map_err(Error::SeekingFile)?; + reader + .seek(SeekFrom::Start(0)) + .map_err(Error::SeekingFile)?; + + // Ensure the destination file is empty before writing to it. + dst_file.set_len(0).map_err(Error::SettingFileSize)?; + + match dst_type { + ImageType::Qcow2 => { + let mut dst_writer = QcowFile::new(dst_file, src_size).map_err(Error::QcowError)?; + convert_reader_writer(reader, &mut dst_writer, src_size) + } + ImageType::Raw => { + let mut dst_writer = dst_file; + // Set the length of the destination file to convert it into a sparse file + // of the desired size. + dst_writer + .set_len(src_size) + .map_err(Error::SettingFileSize)?; + convert_reader_writer(reader, &mut dst_writer, src_size) + } + } +} + +/// 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 +/// determined by `dst_type`. +pub fn convert(src_file: File, dst_file: File, dst_type: ImageType) -> Result<()> { + let src_type = detect_image_type(&src_file)?; + match src_type { + ImageType::Qcow2 => { + let mut src_reader = QcowFile::from(src_file).map_err(Error::QcowError)?; + convert_reader(&mut src_reader, dst_file, dst_type) + } + ImageType::Raw => { + // src_file is a raw file. + let mut src_reader = src_file; + convert_reader(&mut src_reader, dst_file, dst_type) + } + } +} + +/// Detect the type of an image file by checking for a valid qcow2 header. +pub fn detect_image_type(file: &File) -> Result { + let mut f = file; + let orig_seek = f.seek(SeekFrom::Current(0)).map_err(Error::SeekingFile)?; + f.seek(SeekFrom::Start(0)).map_err(Error::SeekingFile)?; + let mut magic = [0u8; 4]; + f.read_exact(&mut magic).map_err(Error::ReadingHeader)?; + let magic = u32::from_be_bytes(magic); + let image_type = if magic == QCOW_MAGIC { + ImageType::Qcow2 + } else { + ImageType::Raw + }; + f.seek(SeekFrom::Start(orig_seek)) + .map_err(Error::SeekingFile)?; + Ok(image_type) +} + +/// Inspect the image file type and create an appropriate disk file to match it. +pub fn create_disk_file(raw_image: File) -> Result> { + let image_type = detect_image_type(&raw_image)?; + Ok(match image_type { + ImageType::Raw => Box::new(raw_image) as Box, + ImageType::Qcow2 => { + Box::new(QcowFile::from(raw_image).map_err(Error::QcowError)?) as Box + } + }) +} diff --git a/qcow/src/qcow.rs b/qcow/src/qcow.rs index 57e42fcc6e..3153df1551 100644 --- a/qcow/src/qcow.rs +++ b/qcow/src/qcow.rs @@ -46,7 +46,6 @@ pub enum Error { NoRefcountClusters, NotEnoughSpaceForRefcounts, OpeningFile(io::Error), - ReadingData(io::Error), ReadingHeader(io::Error), ReadingPointers(io::Error), ReadingRefCountBlock(refcount::Error), @@ -55,14 +54,12 @@ pub enum Error { RefcountTableOffEnd, RefcountTableTooLarge, SeekingFile(io::Error), - SettingFileSize(io::Error), SettingRefcountRefcount(io::Error), SizeTooSmallForNumberOfClusters, TooManyL1Entries(u64), TooManyRefcounts(u64), UnsupportedRefcountOrder, UnsupportedVersion(u32), - WritingData(io::Error), WritingHeader(io::Error), } @@ -98,7 +95,6 @@ impl Display for Error { NoRefcountClusters => write!(f, "no refcount clusters"), NotEnoughSpaceForRefcounts => write!(f, "not enough space for refcounts"), OpeningFile(e) => write!(f, "failed to open file: {}", e), - ReadingData(e) => write!(f, "failed to read data: {}", e), ReadingHeader(e) => write!(f, "failed to read header: {}", e), ReadingPointers(e) => write!(f, "failed to read pointers: {}", e), ReadingRefCountBlock(e) => write!(f, "failed to read ref count block: {}", e), @@ -107,29 +103,22 @@ impl Display for Error { RefcountTableOffEnd => write!(f, "refcount table offset past file end"), RefcountTableTooLarge => write!(f, "too many clusters specified for refcount table"), SeekingFile(e) => write!(f, "failed to seek file: {}", e), - SettingFileSize(e) => write!(f, "failed to set file size: {}", e), SettingRefcountRefcount(e) => write!(f, "failed to set refcount refcount: {}", e), SizeTooSmallForNumberOfClusters => write!(f, "size too small for number of clusters"), TooManyL1Entries(count) => write!(f, "l1 entry table too large: {}", count), TooManyRefcounts(count) => write!(f, "ref count table too large: {}", count), UnsupportedRefcountOrder => write!(f, "unsupported refcount order"), UnsupportedVersion(v) => write!(f, "unsupported version: {}", v), - WritingData(e) => write!(f, "failed to write data: {}", e), WritingHeader(e) => write!(f, "failed to write header: {}", e), } } } -pub enum ImageType { - Raw, - Qcow2, -} - // Maximum data size supported. const MAX_QCOW_FILE_SIZE: u64 = 0x01 << 44; // 16 TB. // QCOW magic constant that starts the header. -const QCOW_MAGIC: u32 = 0x5146_49fb; +pub const QCOW_MAGIC: u32 = 0x5146_49fb; // Default to a cluster size of 2^DEFAULT_CLUSTER_BITS const DEFAULT_CLUSTER_BITS: u32 = 16; // Limit clusters to reasonable sizes. Choose the same limits as qemu. Making the clusters smaller @@ -1599,129 +1588,6 @@ fn div_round_up_u32(dividend: u32, divisor: u32) -> u32 { dividend / divisor + if dividend % divisor != 0 { 1 } else { 0 } } -fn convert_copy(reader: &mut R, writer: &mut W, offset: u64, size: u64) -> Result<()> -where - R: Read + Seek, - W: Write + Seek, -{ - const CHUNK_SIZE: usize = 65536; - let mut buf = [0; CHUNK_SIZE]; - let mut read_count = 0; - reader - .seek(SeekFrom::Start(offset)) - .map_err(Error::SeekingFile)?; - writer - .seek(SeekFrom::Start(offset)) - .map_err(Error::SeekingFile)?; - loop { - let this_count = min(CHUNK_SIZE as u64, size - read_count) as usize; - let nread = reader - .read(&mut buf[..this_count]) - .map_err(Error::ReadingData)?; - writer.write(&buf[..nread]).map_err(Error::WritingData)?; - read_count += nread as u64; - if nread == 0 || read_count == size { - break; - } - } - - Ok(()) -} - -fn convert_reader_writer(reader: &mut R, writer: &mut W, size: u64) -> Result<()> -where - R: Read + Seek + SeekHole, - W: Write + Seek, -{ - let mut offset = 0; - while offset < size { - // Find the next range of data. - let next_data = match reader.seek_data(offset).map_err(Error::SeekingFile)? { - Some(o) => o, - None => { - // No more data in the file. - break; - } - }; - let next_hole = match reader.seek_hole(next_data).map_err(Error::SeekingFile)? { - Some(o) => o, - None => { - // This should not happen - there should always be at least one hole - // after any data. - return Err(Error::SeekingFile(io::Error::from_raw_os_error(EINVAL))); - } - }; - let count = next_hole - next_data; - convert_copy(reader, writer, next_data, count)?; - offset = next_hole; - } - - Ok(()) -} - -fn convert_reader(reader: &mut R, dst_file: File, dst_type: ImageType) -> Result<()> -where - R: Read + Seek + SeekHole, -{ - let src_size = reader.seek(SeekFrom::End(0)).map_err(Error::SeekingFile)?; - reader - .seek(SeekFrom::Start(0)) - .map_err(Error::SeekingFile)?; - - // Ensure the destination file is empty before writing to it. - dst_file.set_len(0).map_err(Error::SettingFileSize)?; - - match dst_type { - ImageType::Qcow2 => { - let mut dst_writer = QcowFile::new(dst_file, src_size)?; - convert_reader_writer(reader, &mut dst_writer, src_size) - } - ImageType::Raw => { - let mut dst_writer = dst_file; - // Set the length of the destination file to convert it into a sparse file - // of the desired size. - dst_writer - .set_len(src_size) - .map_err(Error::SettingFileSize)?; - convert_reader_writer(reader, &mut dst_writer, src_size) - } - } -} - -/// 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 -/// determined by `dst_type`. -pub fn convert(src_file: File, dst_file: File, dst_type: ImageType) -> Result<()> { - let src_type = detect_image_type(&src_file)?; - match src_type { - ImageType::Qcow2 => { - let mut src_reader = QcowFile::from(src_file)?; - convert_reader(&mut src_reader, dst_file, dst_type) - } - ImageType::Raw => { - // src_file is a raw file. - let mut src_reader = src_file; - convert_reader(&mut src_reader, dst_file, dst_type) - } - } -} - -/// Detect the type of an image file by checking for a valid qcow2 header. -pub fn detect_image_type(file: &File) -> Result { - let mut f = file; - let orig_seek = f.seek(SeekFrom::Current(0)).map_err(Error::SeekingFile)?; - f.seek(SeekFrom::Start(0)).map_err(Error::SeekingFile)?; - let magic = read_u32_from_file(f)?; - let image_type = if magic == QCOW_MAGIC { - ImageType::Qcow2 - } else { - ImageType::Raw - }; - f.seek(SeekFrom::Start(orig_seek)) - .map_err(Error::SeekingFile)?; - Ok(image_type) -} - #[cfg(test)] mod tests { use super::*; diff --git a/qcow_utils/Cargo.toml b/qcow_utils/Cargo.toml index 67b9d28d38..88326db502 100644 --- a/qcow_utils/Cargo.toml +++ b/qcow_utils/Cargo.toml @@ -15,5 +15,6 @@ path = "src/qcow_img.rs" [dependencies] getopts = "*" libc = "*" +disk = { path = "../disk" } qcow = { path = "../qcow" } sys_util = { path = "../sys_util" } diff --git a/qcow_utils/src/qcow_img.rs b/qcow_utils/src/qcow_img.rs index 66230da0c7..08040a1650 100644 --- a/qcow_utils/src/qcow_img.rs +++ b/qcow_utils/src/qcow_img.rs @@ -304,12 +304,12 @@ fn convert(src_path: &str, dst_path: &str) -> std::result::Result<(), ()> { }; let dst_type = if dst_path.ends_with("qcow2") { - qcow::ImageType::Qcow2 + disk::ImageType::Qcow2 } else { - qcow::ImageType::Raw + disk::ImageType::Raw }; - match qcow::convert(src_file, dst_file, dst_type) { + match disk::convert(src_file, dst_file, dst_type) { Ok(_) => { println!("Converted {} to {}", src_path, dst_path); Ok(()) diff --git a/qcow_utils/src/qcow_utils.rs b/qcow_utils/src/qcow_utils.rs index 6c8b86f514..4c3ede7e9a 100644 --- a/qcow_utils/src/qcow_utils.rs +++ b/qcow_utils/src/qcow_utils.rs @@ -13,7 +13,8 @@ use std::os::raw::{c_char, c_int}; use std::os::unix::io::FromRawFd; use std::panic::catch_unwind; -use qcow::{ImageType, QcowFile}; +use disk::ImageType; +use qcow::QcowFile; use sys_util::{flock, FileSetLen, FlockOperation}; trait DiskFile: FileSetLen + Seek {} @@ -71,7 +72,7 @@ pub unsafe extern "C" fn expand_disk_image(path: *const c_char, virtual_size: u6 return -EIO; } - let image_type = match qcow::detect_image_type(&raw_image) { + let image_type = match disk::detect_image_type(&raw_image) { Ok(t) => t, Err(_) => return -EINVAL, }; @@ -120,7 +121,7 @@ pub unsafe extern "C" fn convert_to_qcow2(src_fd: c_int, dst_fd: c_int) -> c_int match (src_file_owned, dst_file_owned) { (Ok(src_file), Ok(dst_file)) => { catch_unwind( - || match qcow::convert(src_file, dst_file, ImageType::Qcow2) { + || match disk::convert(src_file, dst_file, ImageType::Qcow2) { Ok(_) => 0, Err(_) => -EIO, }, @@ -145,7 +146,7 @@ pub unsafe extern "C" fn convert_to_raw(src_fd: c_int, dst_fd: c_int) -> c_int { match (src_file_owned, dst_file_owned) { (Ok(src_file), Ok(dst_file)) => { - catch_unwind(|| match qcow::convert(src_file, dst_file, ImageType::Raw) { + catch_unwind(|| match disk::convert(src_file, dst_file, ImageType::Raw) { Ok(_) => 0, Err(_) => -EIO, }) diff --git a/src/linux.rs b/src/linux.rs index 75b09b5239..7ce7c82827 100644 --- a/src/linux.rs +++ b/src/linux.rs @@ -32,7 +32,6 @@ use kvm::*; use libcras::CrasClient; use msg_socket::{MsgError, MsgReceiver, MsgSender, MsgSocket}; use net_util::{Error as NetError, MacAddress, Tap}; -use qcow::{self, ImageType, QcowFile}; use rand_ish::SimpleRng; use remain::sorted; use resources::{Alloc, SystemAllocator}; @@ -82,6 +81,7 @@ pub enum Error { ChownTpmStorage(sys_util::Error), CloneEventFd(sys_util::Error), CreateCrasClient(libcras::Error), + CreateDiskError(disk::Error), CreateEventFd(sys_util::Error), CreatePollContext(sys_util::Error), CreateSignalFd(sys_util::SignalFdError), @@ -90,7 +90,6 @@ pub enum Error { CreateTimerFd(sys_util::Error), CreateTpmStorage(PathBuf, io::Error), CreateUsbProvider(devices::usb::host_backend::error::Error), - DetectImageType(qcow::Error), DeviceJail(io_jail::Error), DevicePivotRoot(io_jail::Error), Disk(io::Error), @@ -114,7 +113,6 @@ pub enum Error { PmemDeviceNew(sys_util::Error), PollContextAdd(sys_util::Error), PollContextDelete(sys_util::Error), - QcowDeviceCreate(qcow::Error), ReadLowmemAvailable(io::Error), ReadLowmemMargin(io::Error), RegisterBalloon(arch::DeviceRegistrationError), @@ -162,6 +160,7 @@ impl Display for Error { ChownTpmStorage(e) => write!(f, "failed to chown tpm storage: {}", e), CloneEventFd(e) => write!(f, "failed to clone eventfd: {}", e), CreateCrasClient(e) => write!(f, "failed to create cras client: {}", e), + CreateDiskError(e) => write!(f, "failed to create virtual disk: {}", e), CreateEventFd(e) => write!(f, "failed to create eventfd: {}", e), CreatePollContext(e) => write!(f, "failed to create poll context: {}", e), CreateSignalFd(e) => write!(f, "failed to create signalfd: {}", e), @@ -172,7 +171,6 @@ impl Display for Error { write!(f, "failed to create tpm storage dir {}: {}", p.display(), e) } CreateUsbProvider(e) => write!(f, "failed to create usb provider: {}", e), - DetectImageType(e) => write!(f, "failed to detect disk image type: {}", e), DeviceJail(e) => write!(f, "failed to jail device: {}", e), DevicePivotRoot(e) => write!(f, "failed to pivot root device: {}", e), Disk(e) => write!(f, "failed to load disk image: {}", e), @@ -203,7 +201,6 @@ impl Display for Error { PmemDeviceNew(e) => write!(f, "failed to create pmem device: {}", e), PollContextAdd(e) => write!(f, "failed to add fd to poll context: {}", e), PollContextDelete(e) => write!(f, "failed to remove fd from poll context: {}", e), - QcowDeviceCreate(e) => write!(f, "failed to read qcow formatted file {}", e), ReadLowmemAvailable(e) => write!( f, "failed to read /sys/kernel/mm/chromeos-low_mem/available: {}", @@ -351,25 +348,12 @@ fn create_block_device( }; flock(&raw_image, lock_op, true).map_err(Error::DiskImageLock)?; - let image_type = qcow::detect_image_type(&raw_image).map_err(Error::DetectImageType)?; - let dev = match image_type { - ImageType::Raw => { - // Access as a raw block device. - let dev = virtio::Block::new(raw_image, disk.read_only, Some(disk_device_socket)) - .map_err(Error::BlockDeviceNew)?; - Box::new(dev) as Box - } - ImageType::Qcow2 => { - // Valid qcow header present - let qcow_image = QcowFile::from(raw_image).map_err(Error::QcowDeviceCreate)?; - let dev = virtio::Block::new(qcow_image, disk.read_only, Some(disk_device_socket)) - .map_err(Error::BlockDeviceNew)?; - Box::new(dev) as Box - } - }; + let disk_file = disk::create_disk_file(raw_image).map_err(Error::CreateDiskError)?; + let dev = virtio::Block::new(disk_file, disk.read_only, Some(disk_device_socket)) + .map_err(Error::BlockDeviceNew)?; Ok(VirtioDeviceStub { - dev, + dev: Box::new(dev), jail: simple_jail(&cfg, "block_device.policy")?, }) }