diff --git a/block/src/qcow/mod.rs b/block/src/qcow/mod.rs index 4e6b744b2..b1109a33a 100644 --- a/block/src/qcow/mod.rs +++ b/block/src/qcow/mod.rs @@ -31,13 +31,16 @@ use vmm_sys_util::{ pub use crate::qcow::raw_file::RawFile; +/// Nesting depth limit for disk formats that can open other disk files. +const MAX_NESTING_DEPTH: u32 = 10; + #[sorted] #[derive(Debug, Error)] pub enum Error { #[error("Backing file io error: {0}")] BackingFileIo(io::Error), #[error("Backing file open error: {0}")] - BackingFileOpen(Box), + BackingFileOpen(Box), #[error("Backing file name is too long: {0} bytes over")] BackingFileTooLong(usize), #[error("Compressed blocks not supported")] @@ -70,6 +73,8 @@ pub enum Error { InvalidRefcountTableOffset, #[error("Invalid refcount table size: {0}")] InvalidRefcountTableSize(u64), + #[error("Maximum disk nesting depth exceeded")] + MaxNestingDepthExceeded, #[error("No free clusters")] NoFreeClusters, #[error("No refcount clusters")] @@ -434,12 +439,20 @@ pub struct QcowFile { // List of unreferenced clusters available to be used. unref clusters become available once the // removal of references to them have been synced to disk. avail_clusters: Vec, - backing_file: Option>, + backing_file: Option>, } impl QcowFile { /// Creates a QcowFile from `file`. File must be a valid qcow2 image. - pub fn from(mut file: RawFile) -> Result { + /// + /// Additionally, max nesting depth of this qcow2 image will be set to default value 10. + pub fn from(file: RawFile) -> Result { + Self::from_with_nesting_depth(file, MAX_NESTING_DEPTH) + } + + /// Creates a QcowFile from `file` and with a max nesting depth. File must be a valid qcow2 + /// image. + pub fn from_with_nesting_depth(mut file: RawFile, max_nesting_depth: u32) -> Result { let header = QcowHeader::new(&mut file)?; // Only v2 and v3 files are supported. @@ -466,14 +479,20 @@ impl QcowFile { let direct_io = file.is_direct(); let backing_file = if let Some(backing_file_path) = header.backing_file_path.as_ref() { + if max_nesting_depth == 0 { + return Err(Error::MaxNestingDepthExceeded); + } let path = backing_file_path.clone(); let backing_raw_file = OpenOptions::new() .read(true) .open(path) .map_err(Error::BackingFileIo)?; - let backing_file = crate::create_disk_file(backing_raw_file, direct_io) - .map_err(|e| Error::BackingFileOpen(Box::new(e)))?; - Some(backing_file) + let backing_file = Self::from_with_nesting_depth( + RawFile::new(backing_raw_file, direct_io), + max_nesting_depth - 1, + ) + .map_err(|e| Error::BackingFileOpen(Box::new(e)))?; + Some(Box::new(backing_file)) } else { None }; @@ -621,20 +640,22 @@ impl QcowFile { file: RawFile, version: u32, backing_file_name: &str, + backing_file_max_nesting_depth: u32, ) -> Result { let direct_io = file.is_direct(); let backing_raw_file = OpenOptions::new() .read(true) .open(backing_file_name) .map_err(Error::BackingFileIo)?; - let backing_file = crate::create_disk_file(backing_raw_file, direct_io) - .map_err(|e| Error::BackingFileOpen(Box::new(e)))?; - let size = backing_file - .size() - .map_err(|e| Error::BackingFileOpen(Box::new(e)))?; + let backing_file = Self::from_with_nesting_depth( + RawFile::new(backing_raw_file, direct_io), + backing_file_max_nesting_depth, + ) + .map_err(|e| Error::BackingFileOpen(Box::new(e)))?; + let size = backing_file.virtual_size(); let header = QcowHeader::create_for_size_and_path(version, size, Some(backing_file_name))?; let mut result = QcowFile::new_from_header(file, header)?; - result.backing_file = Some(backing_file); + result.backing_file = Some(Box::new(backing_file)); Ok(result) } @@ -662,7 +683,7 @@ impl QcowFile { Ok(qcow) } - pub fn set_backing_file(&mut self, backing: Option>) { + pub fn set_backing_file(&mut self, backing: Option>) { self.backing_file = backing; } @@ -1778,11 +1799,17 @@ where /// 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(mut src_file: RawFile, dst_file: RawFile, dst_type: ImageType) -> Result<()> { +pub fn convert( + mut src_file: RawFile, + dst_file: RawFile, + dst_type: ImageType, + src_max_nesting_depth: u32, +) -> Result<()> { let src_type = detect_image_type(&mut src_file)?; match src_type { ImageType::Qcow2 => { - let mut src_reader = QcowFile::from(src_file)?; + let mut src_reader = + QcowFile::from_with_nesting_depth(src_file, src_max_nesting_depth)?; convert_reader(&mut src_reader, dst_file, dst_type) } ImageType::Raw => {