summary refs log tree commit diff
path: root/qcow
diff options
context:
space:
mode:
authorDylan Reid <dgreid@chromium.org>2018-01-18 11:07:15 -0800
committerchrome-bot <chrome-bot@chromium.org>2018-01-22 18:50:09 -0800
commit9277879c517351d0596324a2ae7fa66b47c34931 (patch)
tree52b5b169e54a518428aa524af9413f37f0f21108 /qcow
parent88624f890e7e2a09c122c89f397a4d796c7680fb (diff)
downloadcrosvm-9277879c517351d0596324a2ae7fa66b47c34931.tar
crosvm-9277879c517351d0596324a2ae7fa66b47c34931.tar.gz
crosvm-9277879c517351d0596324a2ae7fa66b47c34931.tar.bz2
crosvm-9277879c517351d0596324a2ae7fa66b47c34931.tar.lz
crosvm-9277879c517351d0596324a2ae7fa66b47c34931.tar.xz
crosvm-9277879c517351d0596324a2ae7fa66b47c34931.tar.zst
crosvm-9277879c517351d0596324a2ae7fa66b47c34931.zip
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>
Diffstat (limited to 'qcow')
-rw-r--r--qcow/src/qcow.rs60
1 files changed, 59 insertions, 1 deletions
diff --git a/qcow/src/qcow.rs b/qcow/src/qcow.rs
index c352e65..0fce8a1 100644
--- a/qcow/src/qcow.rs
+++ b/qcow/src/qcow.rs
@@ -30,7 +30,16 @@ pub enum 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;
+// 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.
 const L1_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 {
     /// Creates a QcowHeader from a reference to a file.
     pub fn new(f: &mut File) -> Result<QcowHeader> {
-        const QCOW_MAGIC: u32 = 0x5146_49fb;
         f.seek(SeekFrom::Start(0)).map_err(Error::ReadingHeader)?;
         let magic = f.read_u32::<BigEndian>().map_err(Error::ReadingHeader)?;
         if magic != QCOW_MAGIC {
@@ -109,6 +117,41 @@ impl QcowHeader {
             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.
@@ -456,6 +499,16 @@ fn write_u64_to_offset(f: &mut File, offset: u64, value: u64) -> std::io::Result
     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)]
 extern crate sys_util;
 
@@ -502,6 +555,11 @@ mod tests {
     }
 
     #[test]
+    fn default_header() {
+        let _ = QcowHeader::create_for_size(0x10_0000);
+    }
+
+    #[test]
     fn header_read() {
         with_basic_file(&valid_header(), |mut disk_file: File| {
             QcowHeader::new(&mut disk_file).expect("Failed to create Header.");