Skip to content

Commit 9eb2b9b

Browse files
weltlingrbradford
authored andcommitted
block: qcow: Implement extension parsing for QCOW v3
Add support for parsing QCOW v3 header extensions to read the backing file format. The QCOW v3 spec allows optional header extensions between the fixed header and the backing file name. Implement read_header_extensions() to parse the extension area, which starts at the header_size offset. At the moment it is used to read the backing file format. Further extension processing is open in folow up implementations. Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
1 parent 10394da commit 9eb2b9b

File tree

1 file changed

+50
-0
lines changed

1 file changed

+50
-0
lines changed

block/src/qcow/mod.rs

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,11 @@ const COMPATIBLE_FEATURES_LAZY_REFCOUNTS: u64 = 1;
202202
const COMPRESSION_TYPE_ZLIB: u64 = 0; // zlib/deflate <https://www.ietf.org/rfc/rfc1951.txt>
203203
const COMPRESSION_TYPE_ZSTD: u64 = 1; // zstd <http://github.com/facebook/zstd>
204204

205+
// Header extension types
206+
const HEADER_EXT_END: u32 = 0x00000000;
207+
// Backing file format name (raw, qcow2)
208+
const HEADER_EXT_BACKING_FORMAT: u32 = 0xe2792aca;
209+
205210
// The format supports a "header extension area", that crosvm does not use.
206211
const QCOW_EMPTY_HEADER_EXTENSION_SIZE: u32 = 8;
207212

@@ -278,6 +283,46 @@ pub struct QcowHeader {
278283
}
279284

280285
impl QcowHeader {
286+
fn read_header_extensions(f: &mut RawFile, header: &mut QcowHeader) -> Result<()> {
287+
// Extensions start directly after the header
288+
f.seek(SeekFrom::Start(header.header_size as u64))
289+
.map_err(Error::ReadingHeader)?;
290+
291+
loop {
292+
let ext_type = f.read_u32::<BigEndian>().map_err(Error::ReadingHeader)?;
293+
if ext_type == HEADER_EXT_END {
294+
break;
295+
}
296+
297+
let ext_length = f.read_u32::<BigEndian>().map_err(Error::ReadingHeader)?;
298+
299+
match ext_type {
300+
HEADER_EXT_BACKING_FORMAT => {
301+
let mut format_bytes = vec![0u8; ext_length as usize];
302+
f.read_exact(&mut format_bytes)
303+
.map_err(Error::ReadingHeader)?;
304+
let format_str = String::from_utf8(format_bytes)
305+
.map_err(|err| Error::InvalidBackingFileName(err.utf8_error()))?;
306+
if let Some(backing_file) = &mut header.backing_file {
307+
backing_file.format = Some(format_str.parse()?);
308+
}
309+
}
310+
_ => {
311+
// Skip unknown extension
312+
f.seek(SeekFrom::Current(ext_length as i64))
313+
.map_err(Error::ReadingHeader)?;
314+
}
315+
}
316+
317+
// Skip to the next 8 byte boundary
318+
let padding = (8 - (ext_length % 8)) % 8;
319+
f.seek(SeekFrom::Current(padding as i64))
320+
.map_err(Error::ReadingHeader)?;
321+
}
322+
323+
Ok(())
324+
}
325+
281326
/// Creates a QcowHeader from a reference to a file.
282327
pub fn new(f: &mut RawFile) -> Result<QcowHeader> {
283328
f.rewind().map_err(Error::ReadingHeader)?;
@@ -363,6 +408,11 @@ impl QcowHeader {
363408
.map_err(|err| Error::InvalidBackingFileName(err.utf8_error()))?;
364409
header.backing_file = Some(BackingFileConfig { path, format: None });
365410
}
411+
412+
if version == 3 && header.header_size > V3_BARE_HEADER_SIZE {
413+
Self::read_header_extensions(f, &mut header)?;
414+
}
415+
366416
Ok(header)
367417
}
368418

0 commit comments

Comments
 (0)