mirror of
https://chromium.googlesource.com/crosvm/crosvm
synced 2025-01-30 04:19:20 +00:00
qcow: Add ability to create QcowHeader for a given size
Allow an empty QcowHeader to be created. Later, this allows QcowFiles to be created in addition to opened. Change-Id: Ifcc2f8ed2a92054fb7b60999d401fb573e98aa73 Signed-off-by: Dylan Reid <dgreid@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/875114 Reviewed-by: Stephen Barber <smbarber@chromium.org>
This commit is contained in:
parent
88624f890e
commit
9277879c51
1 changed files with 59 additions and 1 deletions
|
@ -30,7 +30,16 @@ pub enum Error {
|
||||||
}
|
}
|
||||||
pub type Result<T> = std::result::Result<T, Error>;
|
pub type Result<T> = std::result::Result<T, Error>;
|
||||||
|
|
||||||
|
// QCOW magic constant that starts the header.
|
||||||
|
const QCOW_MAGIC: u32 = 0x5146_49fb;
|
||||||
|
// Default to a cluster size of 2^DEFAULT_CLUSTER_BITS
|
||||||
|
const DEFAULT_CLUSTER_BITS: u32 = 16;
|
||||||
const MAX_CLUSTER_BITS: u32 = 30;
|
const MAX_CLUSTER_BITS: u32 = 30;
|
||||||
|
// Only support 2 byte refcounts, 2^refcount_order bits.
|
||||||
|
const DEFAULT_REFCOUNT_ORDER: u32 = 4;
|
||||||
|
|
||||||
|
const V3_BARE_HEADER_SIZE: u32 = 104;
|
||||||
|
|
||||||
// bits 0-8 and 56-63 are reserved.
|
// bits 0-8 and 56-63 are reserved.
|
||||||
const L1_TABLE_OFFSET_MASK: u64 = 0x00ff_ffff_ffff_fe00;
|
const L1_TABLE_OFFSET_MASK: u64 = 0x00ff_ffff_ffff_fe00;
|
||||||
const L2_TABLE_OFFSET_MASK: u64 = 0x00ff_ffff_ffff_fe00;
|
const L2_TABLE_OFFSET_MASK: u64 = 0x00ff_ffff_ffff_fe00;
|
||||||
|
@ -71,7 +80,6 @@ pub struct QcowHeader {
|
||||||
impl QcowHeader {
|
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 File) -> Result<QcowHeader> {
|
pub fn new(f: &mut File) -> Result<QcowHeader> {
|
||||||
const QCOW_MAGIC: u32 = 0x5146_49fb;
|
|
||||||
f.seek(SeekFrom::Start(0)).map_err(Error::ReadingHeader)?;
|
f.seek(SeekFrom::Start(0)).map_err(Error::ReadingHeader)?;
|
||||||
let magic = f.read_u32::<BigEndian>().map_err(Error::ReadingHeader)?;
|
let magic = f.read_u32::<BigEndian>().map_err(Error::ReadingHeader)?;
|
||||||
if magic != QCOW_MAGIC {
|
if magic != QCOW_MAGIC {
|
||||||
|
@ -109,6 +117,41 @@ impl QcowHeader {
|
||||||
header_size: read_u32_from_file(f)?,
|
header_size: read_u32_from_file(f)?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a header for the given `size`.
|
||||||
|
pub fn create_for_size(size: u64) -> QcowHeader {
|
||||||
|
let cluster_bits: u32 = DEFAULT_CLUSTER_BITS;
|
||||||
|
let cluster_size: u32 = 0x01 << cluster_bits;
|
||||||
|
// L2 blocks are always one cluster long. They contain cluster_size/sizeof(u64) addresses.
|
||||||
|
let l2_size: u32 = cluster_size / size_of::<u64>() as u32;
|
||||||
|
let num_clusters: u32 = div_round_up_u64(size, cluster_size as u64) as u32;
|
||||||
|
let num_l2_clusters: u32 = div_round_up_u32(num_clusters, l2_size);
|
||||||
|
let l1_clusters: u32 = div_round_up_u32(num_l2_clusters, cluster_size);
|
||||||
|
QcowHeader {
|
||||||
|
magic: QCOW_MAGIC,
|
||||||
|
version: 3,
|
||||||
|
backing_file_offset: 0,
|
||||||
|
backing_file_size: 0,
|
||||||
|
cluster_bits: DEFAULT_CLUSTER_BITS,
|
||||||
|
size: size,
|
||||||
|
crypt_method: 0,
|
||||||
|
l1_size: num_l2_clusters,
|
||||||
|
l1_table_offset: cluster_size as u64,
|
||||||
|
refcount_table_offset: (cluster_size * (l1_clusters + 1)) as u64, // After l1 + header.
|
||||||
|
refcount_table_clusters: {
|
||||||
|
let refcount_bytes = (0x01 << DEFAULT_CLUSTER_BITS) / 8;
|
||||||
|
let refcounts_per_cluster = cluster_size / refcount_bytes;
|
||||||
|
div_round_up_u32(num_clusters, refcounts_per_cluster)
|
||||||
|
},
|
||||||
|
nb_snapshots: 0,
|
||||||
|
snapshots_offset: 0,
|
||||||
|
incompatible_features: 0,
|
||||||
|
compatible_features: 0,
|
||||||
|
autoclear_features: 0,
|
||||||
|
refcount_order: DEFAULT_REFCOUNT_ORDER,
|
||||||
|
header_size: V3_BARE_HEADER_SIZE,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Represents a qcow2 file. This is a sparse file format maintained by the qemu project.
|
/// Represents a qcow2 file. This is a sparse file format maintained by the qemu project.
|
||||||
|
@ -456,6 +499,16 @@ fn write_u64_to_offset(f: &mut File, offset: u64, value: u64) -> std::io::Result
|
||||||
f.write_u64::<BigEndian>(value)
|
f.write_u64::<BigEndian>(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ceiling of the division of `dividend`/`divisor`.
|
||||||
|
fn div_round_up_u64(dividend: u64, divisor: u64) -> u64 {
|
||||||
|
(dividend + divisor - 1) / divisor
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ceiling of the division of `dividend`/`divisor`.
|
||||||
|
fn div_round_up_u32(dividend: u32, divisor: u32) -> u32 {
|
||||||
|
(dividend + divisor - 1) / divisor
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
extern crate sys_util;
|
extern crate sys_util;
|
||||||
|
|
||||||
|
@ -501,6 +554,11 @@ mod tests {
|
||||||
testfn(disk_file); // File closed when the function exits.
|
testfn(disk_file); // File closed when the function exits.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn default_header() {
|
||||||
|
let _ = QcowHeader::create_for_size(0x10_0000);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn header_read() {
|
fn header_read() {
|
||||||
with_basic_file(&valid_header(), |mut disk_file: File| {
|
with_basic_file(&valid_header(), |mut disk_file: File| {
|
||||||
|
|
Loading…
Reference in a new issue