block: qcow: Use BeUint for header field I/O

Update QcowHeader and other related places to use BeUint methods
internally for reading/writing header fields.

This removes the byteorder dependency from mod.rs and consolidates
all big-endian file I/O through the shared BeUint trait.

Suggested-by: Rob Bradford <rbradford@rivosinc.com>
Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
This commit is contained in:
Anatol Belski
2026-01-26 15:34:20 +01:00
committed by Rob Bradford
parent 8cdc3b53c0
commit 87e8ac3f1f

View File

@@ -19,7 +19,6 @@ use std::os::fd::{AsRawFd, RawFd};
use std::str::{self, FromStr}; use std::str::{self, FromStr};
use bitflags::bitflags; use bitflags::bitflags;
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
use libc::{EINVAL, EIO, ENOSPC}; use libc::{EINVAL, EIO, ENOSPC};
use log::error; use log::error;
use remain::sorted; use remain::sorted;
@@ -30,7 +29,7 @@ use vmm_sys_util::write_zeroes::{PunchHole, WriteZeroesAt};
use crate::BlockBackend; use crate::BlockBackend;
use crate::qcow::decoder::{Decoder, ZlibDecoder, ZstdDecoder}; use crate::qcow::decoder::{Decoder, ZlibDecoder, ZstdDecoder};
use crate::qcow::qcow_raw_file::QcowRawFile; use crate::qcow::qcow_raw_file::{BeUint, QcowRawFile};
pub use crate::qcow::raw_file::RawFile; pub use crate::qcow::raw_file::RawFile;
use crate::qcow::refcount::RefCount; use crate::qcow::refcount::RefCount;
use crate::qcow::vec_cache::{CacheMap, Cacheable, VecCache}; use crate::qcow::vec_cache::{CacheMap, Cacheable, VecCache};
@@ -376,12 +375,12 @@ impl QcowHeader {
.map_err(Error::ReadingHeader)?; .map_err(Error::ReadingHeader)?;
loop { loop {
let ext_type = f.read_u32::<BigEndian>().map_err(Error::ReadingHeader)?; let ext_type = u32::read_be(f).map_err(Error::ReadingHeader)?;
if ext_type == HEADER_EXT_END { if ext_type == HEADER_EXT_END {
break; break;
} }
let ext_length = f.read_u32::<BigEndian>().map_err(Error::ReadingHeader)?; let ext_length = u32::read_be(f).map_err(Error::ReadingHeader)?;
match ext_type { match ext_type {
HEADER_EXT_BACKING_FORMAT => { HEADER_EXT_BACKING_FORMAT => {
@@ -428,67 +427,55 @@ 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 RawFile) -> Result<QcowHeader> { pub fn new(f: &mut RawFile) -> Result<QcowHeader> {
f.rewind().map_err(Error::ReadingHeader)?; f.rewind().map_err(Error::ReadingHeader)?;
let magic = f.read_u32::<BigEndian>().map_err(Error::ReadingHeader)?; let magic = u32::read_be(f).map_err(Error::ReadingHeader)?;
if magic != QCOW_MAGIC { if magic != QCOW_MAGIC {
return Err(Error::InvalidMagic); return Err(Error::InvalidMagic);
} }
// Reads the next u32 from the file. // Reads the next u32 from the file.
fn read_u32_from_file(f: &mut RawFile) -> Result<u32> { fn read_u32_be(f: &mut RawFile) -> Result<u32> {
f.read_u32::<BigEndian>().map_err(Error::ReadingHeader) u32::read_be(f).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 RawFile) -> Result<u64> { fn read_u64_be(f: &mut RawFile) -> Result<u64> {
f.read_u64::<BigEndian>().map_err(Error::ReadingHeader) u64::read_be(f).map_err(Error::ReadingHeader)
} }
let version = read_u32_from_file(f)?; let version = read_u32_be(f)?;
let mut header = QcowHeader { let mut header = QcowHeader {
magic, magic,
version, version,
backing_file_offset: read_u64_from_file(f)?, backing_file_offset: read_u64_be(f)?,
backing_file_size: read_u32_from_file(f)?, backing_file_size: read_u32_be(f)?,
cluster_bits: read_u32_from_file(f)?, cluster_bits: read_u32_be(f)?,
size: read_u64_from_file(f)?, size: read_u64_be(f)?,
crypt_method: read_u32_from_file(f)?, crypt_method: read_u32_be(f)?,
l1_size: read_u32_from_file(f)?, l1_size: read_u32_be(f)?,
l1_table_offset: read_u64_from_file(f)?, l1_table_offset: read_u64_be(f)?,
refcount_table_offset: read_u64_from_file(f)?, refcount_table_offset: read_u64_be(f)?,
refcount_table_clusters: read_u32_from_file(f)?, refcount_table_clusters: read_u32_be(f)?,
nb_snapshots: read_u32_from_file(f)?, nb_snapshots: read_u32_be(f)?,
snapshots_offset: read_u64_from_file(f)?, snapshots_offset: read_u64_be(f)?,
incompatible_features: if version == 2 { incompatible_features: if version == 2 { 0 } else { read_u64_be(f)? },
0 compatible_features: if version == 2 { 0 } else { read_u64_be(f)? },
} else { autoclear_features: if version == 2 { 0 } else { read_u64_be(f)? },
read_u64_from_file(f)?
},
compatible_features: if version == 2 {
0
} else {
read_u64_from_file(f)?
},
autoclear_features: if version == 2 {
0
} else {
read_u64_from_file(f)?
},
refcount_order: if version == 2 { refcount_order: if version == 2 {
DEFAULT_REFCOUNT_ORDER DEFAULT_REFCOUNT_ORDER
} else { } else {
read_u32_from_file(f)? read_u32_be(f)?
}, },
header_size: if version == 2 { header_size: if version == 2 {
V2_BARE_HEADER_SIZE V2_BARE_HEADER_SIZE
} else { } else {
read_u32_from_file(f)? read_u32_be(f)?
}, },
compression_type: CompressionType::Zlib, compression_type: CompressionType::Zlib,
backing_file: None, backing_file: None,
}; };
if version == 3 && header.header_size > V3_BARE_HEADER_SIZE { if version == 3 && header.header_size > V3_BARE_HEADER_SIZE {
let raw_compression_type = read_u64_from_file(f)? >> (64 - 8); let raw_compression_type = read_u64_be(f)? >> (64 - 8);
header.compression_type = if raw_compression_type == COMPRESSION_TYPE_ZLIB { header.compression_type = if raw_compression_type == COMPRESSION_TYPE_ZLIB {
Ok(CompressionType::Zlib) Ok(CompressionType::Zlib)
} else if raw_compression_type == COMPRESSION_TYPE_ZSTD { } else if raw_compression_type == COMPRESSION_TYPE_ZSTD {
@@ -620,44 +607,42 @@ impl QcowHeader {
/// Write the header to `file`. /// Write the header to `file`.
pub fn write_to<F: Write + Seek>(&self, file: &mut F) -> Result<()> { pub fn write_to<F: Write + Seek>(&self, file: &mut F) -> Result<()> {
// Writes the next u32 to the file. // Writes the next u32 to the file.
fn write_u32_to_file<F: Write>(f: &mut F, value: u32) -> Result<()> { fn write_u32_be<F: Write>(f: &mut F, value: u32) -> Result<()> {
f.write_u32::<BigEndian>(value) u32::write_be(f, value).map_err(Error::WritingHeader)
.map_err(Error::WritingHeader)
} }
// Writes the next u64 to the file. // Writes the next u64 to the file.
fn write_u64_to_file<F: Write>(f: &mut F, value: u64) -> Result<()> { fn write_u64_be<F: Write>(f: &mut F, value: u64) -> Result<()> {
f.write_u64::<BigEndian>(value) u64::write_be(f, value).map_err(Error::WritingHeader)
.map_err(Error::WritingHeader)
} }
write_u32_to_file(file, self.magic)?; write_u32_be(file, self.magic)?;
write_u32_to_file(file, self.version)?; write_u32_be(file, self.version)?;
write_u64_to_file(file, self.backing_file_offset)?; write_u64_be(file, self.backing_file_offset)?;
write_u32_to_file(file, self.backing_file_size)?; write_u32_be(file, self.backing_file_size)?;
write_u32_to_file(file, self.cluster_bits)?; write_u32_be(file, self.cluster_bits)?;
write_u64_to_file(file, self.size)?; write_u64_be(file, self.size)?;
write_u32_to_file(file, self.crypt_method)?; write_u32_be(file, self.crypt_method)?;
write_u32_to_file(file, self.l1_size)?; write_u32_be(file, self.l1_size)?;
write_u64_to_file(file, self.l1_table_offset)?; write_u64_be(file, self.l1_table_offset)?;
write_u64_to_file(file, self.refcount_table_offset)?; write_u64_be(file, self.refcount_table_offset)?;
write_u32_to_file(file, self.refcount_table_clusters)?; write_u32_be(file, self.refcount_table_clusters)?;
write_u32_to_file(file, self.nb_snapshots)?; write_u32_be(file, self.nb_snapshots)?;
write_u64_to_file(file, self.snapshots_offset)?; write_u64_be(file, self.snapshots_offset)?;
if self.version == 3 { if self.version == 3 {
write_u64_to_file(file, self.incompatible_features)?; write_u64_be(file, self.incompatible_features)?;
write_u64_to_file(file, self.compatible_features)?; write_u64_be(file, self.compatible_features)?;
write_u64_to_file(file, self.autoclear_features)?; write_u64_be(file, self.autoclear_features)?;
write_u32_to_file(file, self.refcount_order)?; write_u32_be(file, self.refcount_order)?;
write_u32_to_file(file, self.header_size)?; write_u32_be(file, self.header_size)?;
if self.header_size > V3_BARE_HEADER_SIZE { if self.header_size > V3_BARE_HEADER_SIZE {
write_u64_to_file(file, 0)?; // no compression write_u64_be(file, 0)?; // no compression
} }
write_u32_to_file(file, 0)?; // header extension type: end of header extension area write_u32_be(file, 0)?; // header extension type: end of header extension area
write_u32_to_file(file, 0)?; // length of header extension data: 0 write_u32_be(file, 0)?; // length of header extension data: 0
} }
if let Some(backing_file_path) = self.backing_file.as_ref().map(|bf| &bf.path) { if let Some(backing_file_path) = self.backing_file.as_ref().map(|bf| &bf.path) {
@@ -689,8 +674,7 @@ impl QcowHeader {
} }
file.seek(SeekFrom::Start(V2_BARE_HEADER_SIZE as u64)) file.seek(SeekFrom::Start(V2_BARE_HEADER_SIZE as u64))
.map_err(Error::WritingHeader)?; .map_err(Error::WritingHeader)?;
file.write_u64::<BigEndian>(self.incompatible_features) u64::write_be(file, self.incompatible_features).map_err(Error::WritingHeader)?;
.map_err(Error::WritingHeader)?;
Ok(()) Ok(())
} }
@@ -909,12 +893,11 @@ impl QcowFile {
let mut refcount_rebuild_required = true; let mut refcount_rebuild_required = true;
file.seek(SeekFrom::Start(header.refcount_table_offset)) file.seek(SeekFrom::Start(header.refcount_table_offset))
.map_err(Error::SeekingFile)?; .map_err(Error::SeekingFile)?;
let first_refblock_addr = file.read_u64::<BigEndian>().map_err(Error::ReadingHeader)?; let first_refblock_addr = u64::read_be(&mut file).map_err(Error::ReadingHeader)?;
if first_refblock_addr != 0 { if first_refblock_addr != 0 {
file.seek(SeekFrom::Start(first_refblock_addr)) file.seek(SeekFrom::Start(first_refblock_addr))
.map_err(Error::SeekingFile)?; .map_err(Error::SeekingFile)?;
let first_cluster_refcount = let first_cluster_refcount = u16::read_be(&mut file).map_err(Error::ReadingHeader)?;
file.read_u16::<BigEndian>().map_err(Error::ReadingHeader)?;
if first_cluster_refcount != 0 { if first_cluster_refcount != 0 {
refcount_rebuild_required = false; refcount_rebuild_required = false;
} }
@@ -2371,7 +2354,7 @@ pub fn convert(
pub fn detect_image_type(file: &mut RawFile) -> Result<ImageType> { pub fn detect_image_type(file: &mut RawFile) -> Result<ImageType> {
let orig_seek = file.stream_position().map_err(Error::SeekingFile)?; let orig_seek = file.stream_position().map_err(Error::SeekingFile)?;
file.rewind().map_err(Error::SeekingFile)?; file.rewind().map_err(Error::SeekingFile)?;
let magic = file.read_u32::<BigEndian>().map_err(Error::ReadingHeader)?; let magic = u32::read_be(file).map_err(Error::ReadingHeader)?;
let image_type = if magic == QCOW_MAGIC { let image_type = if magic == QCOW_MAGIC {
ImageType::Qcow2 ImageType::Qcow2
} else { } else {
@@ -2607,10 +2590,8 @@ mod unit_tests {
disk_file disk_file
.seek(SeekFrom::Start(header.header_size as u64)) .seek(SeekFrom::Start(header.header_size as u64))
.unwrap(); .unwrap();
disk_file.write_u32::<BigEndian>(ext_type).unwrap(); u32::write_be(&mut disk_file, ext_type).unwrap();
disk_file u32::write_be(&mut disk_file, ext_data.len() as u32).unwrap();
.write_u32::<BigEndian>(ext_data.len() as u32)
.unwrap();
disk_file.write_all(ext_data).unwrap(); disk_file.write_all(ext_data).unwrap();
// Add padding to 8-byte boundary // Add padding to 8-byte boundary
@@ -2619,7 +2600,7 @@ mod unit_tests {
disk_file.write_all(&vec![0u8; padding]).unwrap(); disk_file.write_all(&vec![0u8; padding]).unwrap();
} }
disk_file.write_u32::<BigEndian>(HEADER_EXT_END).unwrap(); u32::write_be(&mut disk_file, HEADER_EXT_END).unwrap();
disk_file.rewind().unwrap(); disk_file.rewind().unwrap();
@@ -3910,7 +3891,7 @@ mod unit_tests {
disk_file disk_file
.seek(SeekFrom::Start(V2_BARE_HEADER_SIZE as u64)) .seek(SeekFrom::Start(V2_BARE_HEADER_SIZE as u64))
.unwrap(); .unwrap();
let features_before = disk_file.read_u64::<BigEndian>().unwrap(); let features_before = u64::read_be(&mut disk_file).unwrap();
assert_eq!( assert_eq!(
features_before & IncompatFeatures::DIRTY.bits(), features_before & IncompatFeatures::DIRTY.bits(),
0, 0,
@@ -3926,7 +3907,7 @@ mod unit_tests {
disk_file disk_file
.seek(SeekFrom::Start(V2_BARE_HEADER_SIZE as u64)) .seek(SeekFrom::Start(V2_BARE_HEADER_SIZE as u64))
.unwrap(); .unwrap();
let features_during = disk_file.read_u64::<BigEndian>().unwrap(); let features_during = u64::read_be(&mut disk_file).unwrap();
assert_ne!( assert_ne!(
features_during & IncompatFeatures::DIRTY.bits(), features_during & IncompatFeatures::DIRTY.bits(),
0, 0,
@@ -3940,7 +3921,7 @@ mod unit_tests {
disk_file disk_file
.seek(SeekFrom::Start(V2_BARE_HEADER_SIZE as u64)) .seek(SeekFrom::Start(V2_BARE_HEADER_SIZE as u64))
.unwrap(); .unwrap();
let features_after = disk_file.read_u64::<BigEndian>().unwrap(); let features_after = u64::read_be(&mut disk_file).unwrap();
assert_eq!( assert_eq!(
features_after & IncompatFeatures::DIRTY.bits(), features_after & IncompatFeatures::DIRTY.bits(),
0, 0,
@@ -4003,7 +3984,7 @@ mod unit_tests {
verify_raw verify_raw
.seek(SeekFrom::Start(V2_BARE_HEADER_SIZE as u64)) .seek(SeekFrom::Start(V2_BARE_HEADER_SIZE as u64))
.unwrap(); .unwrap();
let features = verify_raw.read_u64::<BigEndian>().unwrap(); let features = u64::read_be(&mut verify_raw).unwrap();
assert_eq!( assert_eq!(
features & IncompatFeatures::DIRTY.bits(), features & IncompatFeatures::DIRTY.bits(),
0, 0,