diff options
Diffstat (limited to 'qcow/src/qcow.rs')
-rw-r--r-- | qcow/src/qcow.rs | 136 |
1 files changed, 1 insertions, 135 deletions
diff --git a/qcow/src/qcow.rs b/qcow/src/qcow.rs index 57e42fc..3153df1 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<R, W>(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<R, W>(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<R>(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<ImageType> { - 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::*; |