diff --git a/block/src/formats/qcow/internal/header.rs b/block/src/formats/qcow/internal/header.rs index 97a7c45ba..d8c950bf2 100644 --- a/block/src/formats/qcow/internal/header.rs +++ b/block/src/formats/qcow/internal/header.rs @@ -9,7 +9,8 @@ //! QCOW2 header parsing, validation, and creation. use std::fmt::{Display, Formatter, Result as FmtResult}; -use std::io::{Read, Seek, SeekFrom, Write}; +use std::io::{Seek, SeekFrom, Write}; +use std::os::unix::fs::FileExt; use std::str::FromStr; use bitflags::bitflags; @@ -209,27 +210,34 @@ pub struct QcowHeader { impl QcowHeader { /// Read header extensions, optionally collecting feature names for error reporting. pub(super) fn read_header_extensions( - f: &mut AlignedFile, + f: &AlignedFile, header: &mut QcowHeader, mut feature_table: Option<&mut Vec<(u8, String)>>, ) -> Result<()> { - // Extensions start directly after the header - f.seek(SeekFrom::Start(header.header_size as u64)) - .map_err(Error::ReadingHeader)?; + // Extensions start directly after the header. + let mut offset = header.header_size as u64; loop { - let ext_type = u32::read_be(f).map_err(Error::ReadingHeader)?; + let mut field = [0u8; size_of::()]; + f.read_exact_at(&mut field, offset) + .map_err(Error::ReadingHeader)?; + offset += field.len() as u64; + let ext_type = u32::from_be_slice(&field) as u32; if ext_type == HEADER_EXT_END { break; } - let ext_length = u32::read_be(f).map_err(Error::ReadingHeader)?; + f.read_exact_at(&mut field, offset) + .map_err(Error::ReadingHeader)?; + offset += field.len() as u64; + let ext_length = u32::from_be_slice(&field) as u32; match ext_type { HEADER_EXT_BACKING_FORMAT => { let mut format_bytes = vec![0u8; ext_length as usize]; - f.read_exact(&mut format_bytes) + f.read_exact_at(&mut format_bytes, offset) .map_err(Error::ReadingHeader)?; + offset += format_bytes.len() as u64; let format_str = String::from_utf8(format_bytes) .map_err(|err| Error::InvalidBackingFileName(err.utf8_error()))?; if let Some(backing_file) = &mut header.backing_file { @@ -239,7 +247,9 @@ impl QcowHeader { HEADER_EXT_FEATURE_NAME_TABLE if feature_table.is_some() => { const FEATURE_NAME_ENTRY_SIZE: usize = 1 + 1 + 46; // type + bit + name let mut data = vec![0u8; ext_length as usize]; - f.read_exact(&mut data).map_err(Error::ReadingHeader)?; + f.read_exact_at(&mut data, offset) + .map_err(Error::ReadingHeader)?; + offset += data.len() as u64; let table = feature_table.as_mut().unwrap(); for entry in data.chunks_exact(FEATURE_NAME_ENTRY_SIZE) { if entry[0] == FEAT_TYPE_INCOMPATIBLE { @@ -253,72 +263,92 @@ impl QcowHeader { } _ => { // Skip unknown extension - f.seek(SeekFrom::Current(ext_length as i64)) - .map_err(Error::ReadingHeader)?; + offset += ext_length as u64; } } // Skip to the next 8 byte boundary let padding = (8 - (ext_length % 8)) % 8; - f.seek(SeekFrom::Current(padding as i64)) - .map_err(Error::ReadingHeader)?; + offset += padding as u64; } Ok(()) } /// Creates a QcowHeader from a reference to a file. - pub fn new(f: &mut AlignedFile) -> Result { - f.rewind().map_err(Error::ReadingHeader)?; - let magic = u32::read_be(f).map_err(Error::ReadingHeader)?; + pub fn new(f: &AlignedFile) -> Result { + // Decode the next big endian u32 from the buffer and advance `pos`. + fn next_u32(buf: &[u8], pos: &mut usize) -> u32 { + let value = u32::from_be_slice(&buf[*pos..]) as u32; + *pos += size_of::(); + value + } + + // Decode the next big endian u64 from the buffer and advance `pos`. + fn next_u64(buf: &[u8], pos: &mut usize) -> u64 { + let value = u64::from_be_slice(&buf[*pos..]); + *pos += size_of::(); + value + } + + // The bare header fits in V3_BARE_HEADER_SIZE plus the optional + // compression field. Read it once, then decode each field from the + // buffer at its running offset. + let mut buf = [0u8; V3_BARE_HEADER_SIZE as usize + size_of::()]; + f.read_exact_at(&mut buf, 0).map_err(Error::ReadingHeader)?; + + let mut pos = 0; + let magic = next_u32(&buf, &mut pos); if magic != QCOW_MAGIC { return Err(Error::InvalidMagic); } - // Reads the next u32 from the file. - fn read_u32_be(f: &mut AlignedFile) -> Result { - u32::read_be(f).map_err(Error::ReadingHeader) - } - - // Reads the next u64 from the file. - fn read_u64_be(f: &mut AlignedFile) -> Result { - u64::read_be(f).map_err(Error::ReadingHeader) - } - - let version = read_u32_be(f)?; + let version = next_u32(&buf, &mut pos); let mut header = QcowHeader { magic, version, - backing_file_offset: read_u64_be(f)?, - backing_file_size: read_u32_be(f)?, - cluster_bits: read_u32_be(f)?, - size: read_u64_be(f)?, - crypt_method: read_u32_be(f)?, - l1_size: read_u32_be(f)?, - l1_table_offset: read_u64_be(f)?, - refcount_table_offset: read_u64_be(f)?, - refcount_table_clusters: read_u32_be(f)?, - nb_snapshots: read_u32_be(f)?, - snapshots_offset: read_u64_be(f)?, - incompatible_features: if version == 2 { 0 } else { read_u64_be(f)? }, - compatible_features: if version == 2 { 0 } else { read_u64_be(f)? }, - autoclear_features: if version == 2 { 0 } else { read_u64_be(f)? }, + backing_file_offset: next_u64(&buf, &mut pos), + backing_file_size: next_u32(&buf, &mut pos), + cluster_bits: next_u32(&buf, &mut pos), + size: next_u64(&buf, &mut pos), + crypt_method: next_u32(&buf, &mut pos), + l1_size: next_u32(&buf, &mut pos), + l1_table_offset: next_u64(&buf, &mut pos), + refcount_table_offset: next_u64(&buf, &mut pos), + refcount_table_clusters: next_u32(&buf, &mut pos), + nb_snapshots: next_u32(&buf, &mut pos), + snapshots_offset: next_u64(&buf, &mut pos), + incompatible_features: if version == 2 { + 0 + } else { + next_u64(&buf, &mut pos) + }, + compatible_features: if version == 2 { + 0 + } else { + next_u64(&buf, &mut pos) + }, + autoclear_features: if version == 2 { + 0 + } else { + next_u64(&buf, &mut pos) + }, refcount_order: if version == 2 { DEFAULT_REFCOUNT_ORDER } else { - read_u32_be(f)? + next_u32(&buf, &mut pos) }, header_size: if version == 2 { V2_BARE_HEADER_SIZE } else { - read_u32_be(f)? + next_u32(&buf, &mut pos) }, compression_type: CompressionType::Zlib, backing_file: None, }; if version == 3 && header.header_size > V3_BARE_HEADER_SIZE { - let raw_compression_type = read_u64_be(f)? >> (64 - 8); + let raw_compression_type = next_u64(&buf, &mut pos) >> (64 - 8); header.compression_type = if raw_compression_type == COMPRESSION_TYPE_ZLIB { Ok(CompressionType::Zlib) } else if raw_compression_type == COMPRESSION_TYPE_ZSTD { @@ -360,10 +390,8 @@ impl QcowHeader { cluster_size, )); } - f.seek(SeekFrom::Start(header.backing_file_offset)) - .map_err(Error::ReadingHeader)?; let mut backing_file_name_bytes = vec![0u8; header.backing_file_size as usize]; - f.read_exact(&mut backing_file_name_bytes) + f.read_exact_at(&mut backing_file_name_bytes, header.backing_file_offset) .map_err(Error::ReadingHeader)?; let path = String::from_utf8(backing_file_name_bytes) .map_err(|err| Error::InvalidBackingFileName(err.utf8_error()))?; @@ -476,65 +504,63 @@ impl QcowHeader { }) } - /// Write the header to `file`. - pub fn write_to(&self, file: &mut F) -> Result<()> { - // Writes the next u32 to the file. - fn write_u32_be(f: &mut F, value: u32) -> Result<()> { - u32::write_be(f, value).map_err(Error::WritingHeader) - } - - // Writes the next u64 to the file. - fn write_u64_be(f: &mut F, value: u64) -> Result<()> { - u64::write_be(f, value).map_err(Error::WritingHeader) - } - - write_u32_be(file, self.magic)?; - write_u32_be(file, self.version)?; - write_u64_be(file, self.backing_file_offset)?; - write_u32_be(file, self.backing_file_size)?; - write_u32_be(file, self.cluster_bits)?; - write_u64_be(file, self.size)?; - write_u32_be(file, self.crypt_method)?; - write_u32_be(file, self.l1_size)?; - write_u64_be(file, self.l1_table_offset)?; - write_u64_be(file, self.refcount_table_offset)?; - write_u32_be(file, self.refcount_table_clusters)?; - write_u32_be(file, self.nb_snapshots)?; - write_u64_be(file, self.snapshots_offset)?; + /// Write the header to `f`. + pub fn write_to(&self, f: &AlignedFile) -> Result<()> { + // Build the header in memory, then write it in one positional write. + let mut buf = Vec::new(); + buf.extend_from_slice(&self.magic.to_be_bytes()); + buf.extend_from_slice(&self.version.to_be_bytes()); + buf.extend_from_slice(&self.backing_file_offset.to_be_bytes()); + buf.extend_from_slice(&self.backing_file_size.to_be_bytes()); + buf.extend_from_slice(&self.cluster_bits.to_be_bytes()); + buf.extend_from_slice(&self.size.to_be_bytes()); + buf.extend_from_slice(&self.crypt_method.to_be_bytes()); + buf.extend_from_slice(&self.l1_size.to_be_bytes()); + buf.extend_from_slice(&self.l1_table_offset.to_be_bytes()); + buf.extend_from_slice(&self.refcount_table_offset.to_be_bytes()); + buf.extend_from_slice(&self.refcount_table_clusters.to_be_bytes()); + buf.extend_from_slice(&self.nb_snapshots.to_be_bytes()); + buf.extend_from_slice(&self.snapshots_offset.to_be_bytes()); if self.version == 3 { - write_u64_be(file, self.incompatible_features)?; - write_u64_be(file, self.compatible_features)?; - write_u64_be(file, self.autoclear_features)?; - write_u32_be(file, self.refcount_order)?; - write_u32_be(file, self.header_size)?; + buf.extend_from_slice(&self.incompatible_features.to_be_bytes()); + buf.extend_from_slice(&self.compatible_features.to_be_bytes()); + buf.extend_from_slice(&self.autoclear_features.to_be_bytes()); + buf.extend_from_slice(&self.refcount_order.to_be_bytes()); + buf.extend_from_slice(&self.header_size.to_be_bytes()); if self.header_size > V3_BARE_HEADER_SIZE { - write_u64_be(file, 0)?; // no compression + // no compression + buf.extend_from_slice(&0u64.to_be_bytes()); } - write_u32_be(file, 0)?; // header extension type: end of header extension area - write_u32_be(file, 0)?; // length of header extension data: 0 + // header extension type: end of header extension area + buf.extend_from_slice(&0u32.to_be_bytes()); + // length of header extension data: 0 + buf.extend_from_slice(&0u32.to_be_bytes()); } + f.write_all_at(&buf, 0).map_err(Error::WritingHeader)?; + if let Some(backing_file_path) = self.backing_file.as_ref().map(|bf| &bf.path) { - if self.backing_file_offset > 0 { - file.seek(SeekFrom::Start(self.backing_file_offset)) - .map_err(Error::WritingHeader)?; - } - write!(file, "{backing_file_path}").map_err(Error::WritingHeader)?; + let offset = if self.backing_file_offset > 0 { + self.backing_file_offset + } else { + buf.len() as u64 + }; + f.write_all_at(backing_file_path.as_bytes(), offset) + .map_err(Error::WritingHeader)?; } - // Set the file length by seeking and writing a zero to the last byte. This avoids needing - // a `File` instead of anything that implements seek as the `file` argument. - // Zeros out the l1 and refcount table clusters. + // Set the file length by writing a zero to the last byte. This also + // zeros the l1 and refcount table clusters. let cluster_size = 0x01u64 << self.cluster_bits; let refcount_blocks_size = u64::from(self.refcount_table_clusters) * cluster_size; - file.seek(SeekFrom::Start( + f.write_all_at( + &[0u8], self.refcount_table_offset + refcount_blocks_size - 2, - )) + ) .map_err(Error::WritingHeader)?; - file.write(&[0u8]).map_err(Error::WritingHeader)?; Ok(()) } diff --git a/block/src/formats/qcow/internal/metadata.rs b/block/src/formats/qcow/internal/metadata.rs index d7a0aac3b..42f1b96b0 100644 --- a/block/src/formats/qcow/internal/metadata.rs +++ b/block/src/formats/qcow/internal/metadata.rs @@ -17,10 +17,9 @@ //! operations upgrade to a write lock. use std::cmp::min; -use std::io::{self, Seek}; -use std::mem; use std::os::unix::fs::FileExt; use std::sync::{Arc, RwLock}; +use std::{io, mem}; use libc::{EINVAL, EIO}; use vmm_sys_util::write_zeroes::WriteZeroesAt; @@ -756,7 +755,6 @@ impl QcowState { self.header.size = new_size; - self.raw_file.file_mut().rewind()?; self.header .write_to(self.raw_file.file_mut()) .map_err(|e| io::Error::other(format!("failed to write header during resize: {e}")))?; @@ -812,7 +810,6 @@ impl QcowState { self.header.l1_size = new_l1_size; self.header.l1_table_offset = new_l1_offset; - self.raw_file.file_mut().rewind()?; self.header .write_to(self.raw_file.file_mut()) .map_err(|e| io::Error::other(format!("failed to write header during resize: {e}")))?; diff --git a/block/src/formats/qcow/internal/mod.rs b/block/src/formats/qcow/internal/mod.rs index 1e443852f..16ea1c609 100644 --- a/block/src/formats/qcow/internal/mod.rs +++ b/block/src/formats/qcow/internal/mod.rs @@ -16,11 +16,10 @@ mod vec_cache; use std::cmp::{max, min}; use std::fmt::{Debug, Formatter, Result as FmtResult}; use std::fs::{OpenOptions, read_link}; -use std::io::{self, Seek}; use std::os::fd::AsRawFd; use std::os::unix::fs::FileExt; use std::path::Path; -use std::{result, str}; +use std::{io, result, str}; pub use header::{ BackingFileConfig, CompressionType, ImageType, IncompatFeatures, MissingFeatureError, @@ -127,8 +126,6 @@ pub enum Error { ResizeIo(#[source] io::Error), #[error("Resize not supported with backing file")] ResizeWithBackingFile, - #[error("Failed to seek file")] - SeekingFile(#[source] io::Error), #[error("Failed to set file size")] SettingFileSize(#[source] io::Error), #[error("Failed to set refcount refcount")] @@ -270,11 +267,11 @@ impl Debug for BackingFile { /// /// Used by [`crate::formats::qcow::QcowDisk`] when opening an image. pub(crate) fn parse_qcow( - mut file: AlignedFile, + file: AlignedFile, max_nesting_depth: u32, sparse: bool, ) -> BlockResult<(metadata::QcowState, Option, bool)> { - let mut header = QcowHeader::new(&mut file).map_err(|e| { + let mut header = QcowHeader::new(&file).map_err(|e| { let kind = match &e { Error::InvalidMagic | Error::BackingFileTooLong(_) @@ -730,7 +727,6 @@ fn rebuild_refcounts(raw_file: &mut QcowRawFile, header: QcowHeader) -> BlockRes ) -> Result<()> { // Rewrite the header with lazy refcounts enabled while we are rebuilding the tables. header.compatible_features |= COMPATIBLE_FEATURES_LAZY_REFCOUNTS; - raw_file.file_mut().rewind().map_err(Error::SeekingFile)?; header.write_to(raw_file.file_mut())?; for (i, refblock_addr) in ref_table.iter().enumerate() { @@ -763,7 +759,6 @@ fn rebuild_refcounts(raw_file: &mut QcowRawFile, header: QcowHeader) -> BlockRes // Rewrite the header again, now with lazy refcounts disabled. header.compatible_features &= !COMPATIBLE_FEATURES_LAZY_REFCOUNTS; - raw_file.file_mut().rewind().map_err(Error::SeekingFile)?; header.write_to(raw_file.file_mut())?; Ok(()) @@ -1000,8 +995,8 @@ mod unit_tests { fn try_open_qcow_header(header: &QcowHeader, backing_files: bool) -> BlockResult { let temp = TempFile::new().unwrap(); - let mut raw = AlignedFile::new(temp.as_file().try_clone().unwrap(), false); - header.write_to(&mut raw).expect("write header"); + let raw = AlignedFile::new(temp.as_file().try_clone().unwrap(), false); + header.write_to(&raw).expect("write header"); drop(raw); let file = temp.into_file(); QcowDisk::new(file, false, backing_files, true, false) @@ -1037,14 +1032,14 @@ mod unit_tests { #[test] fn header_read() { - with_basic_file(&valid_header_v2(), |mut disk_file: AlignedFile| { - let header = QcowHeader::new(&mut disk_file).expect("Failed to create Header."); + with_basic_file(&valid_header_v2(), |disk_file: AlignedFile| { + let header = QcowHeader::new(&disk_file).expect("Failed to create Header."); assert_eq!(header.version, 2); assert_eq!(header.refcount_order, DEFAULT_REFCOUNT_ORDER); assert_eq!(header.header_size, V2_BARE_HEADER_SIZE); }); - with_basic_file(&valid_header_v3(), |mut disk_file: AlignedFile| { - let header = QcowHeader::new(&mut disk_file).expect("Failed to create Header."); + with_basic_file(&valid_header_v3(), |disk_file: AlignedFile| { + let header = QcowHeader::new(&disk_file).expect("Failed to create Header."); assert_eq!(header.version, 3); assert_eq!(header.refcount_order, DEFAULT_REFCOUNT_ORDER); assert_eq!(header.header_size, V3_BARE_HEADER_SIZE); @@ -1055,13 +1050,11 @@ mod unit_tests { fn header_v2_with_backing() { let header = QcowHeader::create_for_size_and_path(2, 0x10_0000, Some("/my/path/to/a/file")) .expect("Failed to create header."); - let mut disk_file: AlignedFile = - AlignedFile::new(TempFile::new().unwrap().into_file(), false); + let disk_file: AlignedFile = AlignedFile::new(TempFile::new().unwrap().into_file(), false); header - .write_to(&mut disk_file) + .write_to(&disk_file) .expect("Failed to write header to shm."); - disk_file.rewind().unwrap(); - let read_header = QcowHeader::new(&mut disk_file).expect("Failed to create header."); + let read_header = QcowHeader::new(&disk_file).expect("Failed to create header."); assert_eq!( header.backing_file.as_ref().map(|bf| bf.path.clone()), Some(String::from("/my/path/to/a/file")) @@ -1076,13 +1069,11 @@ mod unit_tests { fn header_v3_with_backing() { let header = QcowHeader::create_for_size_and_path(3, 0x10_0000, Some("/my/path/to/a/file")) .expect("Failed to create header."); - let mut disk_file: AlignedFile = - AlignedFile::new(TempFile::new().unwrap().into_file(), false); + let disk_file: AlignedFile = AlignedFile::new(TempFile::new().unwrap().into_file(), false); header - .write_to(&mut disk_file) + .write_to(&disk_file) .expect("Failed to write header to shm."); - disk_file.rewind().unwrap(); - let read_header = QcowHeader::new(&mut disk_file).expect("Failed to create header."); + let read_header = QcowHeader::new(&disk_file).expect("Failed to create header."); assert_eq!( header.backing_file.as_ref().map(|bf| bf.path.clone()), Some(String::from("/my/path/to/a/file")) @@ -1101,17 +1092,16 @@ mod unit_tests { .expect("Failed to create header."); header.backing_file_offset = offset; header.backing_file_size = size; - let mut disk_file: AlignedFile = AlignedFile::new( + let disk_file: AlignedFile = AlignedFile::new( TempFile::new() .expect("Failed to create temp file.") .into_file(), false, ); header - .write_to(&mut disk_file) + .write_to(&disk_file) .expect("Failed to write header."); - disk_file.rewind().expect("Failed to rewind disk file."); - QcowHeader::new(&mut disk_file) + QcowHeader::new(&disk_file) } #[test] @@ -1178,7 +1168,7 @@ mod unit_tests { let mut disk_file: AlignedFile = AlignedFile::new(TempFile::new().unwrap().into_file(), false); - header.write_to(&mut disk_file).unwrap(); + header.write_to(&disk_file).unwrap(); // Write extension disk_file @@ -1203,7 +1193,7 @@ mod unit_tests { #[test] fn read_header_extensions_unknown_extension() { - let (mut disk_file, mut header) = create_header_with_extension( + let (disk_file, mut header) = create_header_with_extension( 0x12345678, // unknown type "test".as_bytes(), ); @@ -1214,13 +1204,13 @@ mod unit_tests { format: None, }); - QcowHeader::read_header_extensions(&mut disk_file, &mut header, None).unwrap(); + QcowHeader::read_header_extensions(&disk_file, &mut header, None).unwrap(); assert_eq!(header.backing_file.as_ref().and_then(|bf| bf.format), None); } #[test] fn read_header_extensions_raw_format() { - let (mut disk_file, mut header) = + let (disk_file, mut header) = create_header_with_extension(HEADER_EXT_BACKING_FORMAT, "raw".as_bytes()); header.backing_file = Some(BackingFileConfig { @@ -1228,7 +1218,7 @@ mod unit_tests { format: None, }); - QcowHeader::read_header_extensions(&mut disk_file, &mut header, None).unwrap(); + QcowHeader::read_header_extensions(&disk_file, &mut header, None).unwrap(); assert_eq!( header.backing_file.as_ref().and_then(|bf| bf.format), Some(ImageType::Raw) @@ -1237,7 +1227,7 @@ mod unit_tests { #[test] fn read_header_extensions_qcow2_format() { - let (mut disk_file, mut header) = + let (disk_file, mut header) = create_header_with_extension(HEADER_EXT_BACKING_FORMAT, "qcow2".as_bytes()); header.backing_file = Some(BackingFileConfig { @@ -1245,7 +1235,7 @@ mod unit_tests { format: None, }); - QcowHeader::read_header_extensions(&mut disk_file, &mut header, None).unwrap(); + QcowHeader::read_header_extensions(&disk_file, &mut header, None).unwrap(); assert_eq!( header.backing_file.as_ref().and_then(|bf| bf.format), Some(ImageType::Qcow2) @@ -1254,7 +1244,7 @@ mod unit_tests { #[test] fn read_header_extensions_invalid_format() { - let (mut disk_file, mut header) = + let (disk_file, mut header) = create_header_with_extension(HEADER_EXT_BACKING_FORMAT, "vmdk".as_bytes()); header.backing_file = Some(BackingFileConfig { @@ -1262,7 +1252,7 @@ mod unit_tests { format: None, }); - let result = QcowHeader::read_header_extensions(&mut disk_file, &mut header, None); + let result = QcowHeader::read_header_extensions(&disk_file, &mut header, None); assert!(matches!( result.unwrap_err(), Error::UnsupportedBackingFileFormat(_) @@ -1271,12 +1261,12 @@ mod unit_tests { #[test] fn read_header_extensions_invalid_utf8() { - let (mut disk_file, mut header) = create_header_with_extension( + let (disk_file, mut header) = create_header_with_extension( HEADER_EXT_BACKING_FORMAT, &[0xFF, 0xFE, 0xFD], // invalid UTF-8 ); - let result = QcowHeader::read_header_extensions(&mut disk_file, &mut header, None); + let result = QcowHeader::read_header_extensions(&disk_file, &mut header, None); // Should fail with InvalidBackingFileName error assert!(matches!( result.unwrap_err(), @@ -1312,11 +1302,11 @@ mod unit_tests { /// the file until stack overflow. fn new_self_referential_qcow(path: &Path) -> Result<()> { let header = QcowHeader::create_for_size_and_path(3, 0x10_0000, path.to_str())?; - let mut disk_file = AlignedFile::new( + let disk_file = AlignedFile::new( File::create(path).expect("Failed to create image file."), false, ); - header.write_to(&mut disk_file)?; + header.write_to(&disk_file)?; Ok(()) } diff --git a/block/src/formats/qcow/internal/qcow_raw_file.rs b/block/src/formats/qcow/internal/qcow_raw_file.rs index 0c55dbbe3..c7f58da3f 100644 --- a/block/src/formats/qcow/internal/qcow_raw_file.rs +++ b/block/src/formats/qcow/internal/qcow_raw_file.rs @@ -5,11 +5,11 @@ // SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause use std::fmt::Debug; -use std::io::{self, Read, Write}; +use std::io::{self, Write}; use std::os::fd::{AsFd, AsRawFd, BorrowedFd, RawFd}; use std::os::unix::fs::FileExt; -use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; +use byteorder::{BigEndian, WriteBytesExt}; use vmm_sys_util::write_zeroes::WriteZeroesAt; use crate::aligned_file::AlignedFile; @@ -21,7 +21,6 @@ type RefcountWriter = fn(&mut AlignedFile, u64, &[u64]) -> io::Result<()>; /// Big-endian file access trait. pub(super) trait BeUint: Sized + Copy { fn from_be_slice(bytes: &[u8]) -> u64; - fn read_be(r: &mut R) -> io::Result; fn write_be(w: &mut W, val: Self) -> io::Result<()>; } @@ -31,10 +30,6 @@ impl BeUint for u8 { bytes[0] as u64 } #[inline(always)] - fn read_be(r: &mut R) -> io::Result { - r.read_u8() - } - #[inline(always)] fn write_be(w: &mut W, val: Self) -> io::Result<()> { w.write_u8(val) } @@ -46,10 +41,6 @@ impl BeUint for u16 { u16::from_be_bytes([bytes[0], bytes[1]]) as u64 } #[inline(always)] - fn read_be(r: &mut R) -> io::Result { - r.read_u16::() - } - #[inline(always)] fn write_be(w: &mut W, val: Self) -> io::Result<()> { w.write_u16::(val) } @@ -61,10 +52,6 @@ impl BeUint for u32 { u32::from_be_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]) as u64 } #[inline(always)] - fn read_be(r: &mut R) -> io::Result { - r.read_u32::() - } - #[inline(always)] fn write_be(w: &mut W, val: Self) -> io::Result<()> { w.write_u32::(val) } @@ -78,10 +65,6 @@ impl BeUint for u64 { ]) } #[inline(always)] - fn read_be(r: &mut R) -> io::Result { - r.read_u64::() - } - #[inline(always)] fn write_be(w: &mut W, val: Self) -> io::Result<()> { w.write_u64::(val) } diff --git a/block/src/formats/qcow/mod.rs b/block/src/formats/qcow/mod.rs index 3837f3b3f..650411cca 100644 --- a/block/src/formats/qcow/mod.rs +++ b/block/src/formats/qcow/mod.rs @@ -12,8 +12,6 @@ pub mod internal; pub mod worker; use std::fs::File; -#[cfg(any(test, feature = "test-utils"))] -use std::io::Seek; use std::os::unix::io::AsRawFd; #[cfg(any(test, feature = "test-utils"))] use std::path::Path; @@ -29,7 +27,7 @@ use self::internal::backing::shared_backing_from; use self::internal::metadata::{BackingRead, QcowMetadata}; use self::internal::qcow_raw_file::QcowRawFile; #[cfg(any(test, feature = "test-utils"))] -use self::internal::{BackingFileConfig, Error as QcowError, QcowHeader}; +use self::internal::{BackingFileConfig, QcowHeader}; use self::internal::{MAX_NESTING_DEPTH, parse_qcow}; #[cfg(feature = "io_uring")] use self::worker::async_uring::QcowAsync; @@ -164,15 +162,13 @@ pub(crate) fn create_image( { backing_file.format = cfg.format; } - let mut raw = AlignedFile::new( + let raw = AlignedFile::new( file.try_clone() .map_err(|e| BlockError::new(BlockErrorKind::Io, DiskFileError::Clone(e)))?, false, ); - raw.rewind() - .map_err(|e| BlockError::new(BlockErrorKind::Io, QcowError::SeekingFile(e)))?; header - .write_to(&mut raw) + .write_to(&raw) .map_err(|e| BlockError::new(BlockErrorKind::Io, e))?; let (inner, _backing, _sparse) = parse_qcow(raw, MAX_NESTING_DEPTH, true)?; // Flush dirty caches and clear the dirty bit diff --git a/block/src/formats/qcow/worker/sync.rs b/block/src/formats/qcow/worker/sync.rs index 56732b058..9f1bc662b 100644 --- a/block/src/formats/qcow/worker/sync.rs +++ b/block/src/formats/qcow/worker/sync.rs @@ -373,9 +373,8 @@ mod unit_tests { } fn qcow_header_is_corrupt(file: &File) -> bool { - let mut raw = AlignedFile::new(file.try_clone().unwrap(), false); - raw.seek(SeekFrom::Start(0)).unwrap(); - QcowHeader::new(&mut raw).unwrap().is_corrupt() + let raw = AlignedFile::new(file.try_clone().unwrap(), false); + QcowHeader::new(&raw).unwrap().is_corrupt() } fn create_disk_with_data( @@ -866,7 +865,7 @@ mod unit_tests { } fn create_qcow2_overlay_header(overlay_path: &Path, backing_path: &str, file_size: u64) { - let mut file = OpenOptions::new() + let file = OpenOptions::new() .read(true) .write(true) .create(true) @@ -875,8 +874,9 @@ mod unit_tests { .unwrap(); let header = QcowHeader::create_for_size_and_path(3, file_size, Some(backing_path)).unwrap(); - header.write_to(&mut file).unwrap(); - file.sync_all().unwrap(); + let raw = AlignedFile::new(file, false); + header.write_to(&raw).unwrap(); + raw.sync_all().unwrap(); } #[test] @@ -989,11 +989,12 @@ mod unit_tests { let file_size = cluster_size * 2; { - let mut file = overlay_temp.as_file().try_clone().unwrap(); + let file = overlay_temp.as_file().try_clone().unwrap(); let header = QcowHeader::create_for_size_and_path(3, file_size, Some("missing.raw")).unwrap(); - header.write_to(&mut file).unwrap(); - file.sync_all().unwrap(); + let raw = AlignedFile::new(file, false); + header.write_to(&raw).unwrap(); + raw.sync_all().unwrap(); } let mut overlay_file = overlay_temp.into_file();