summary refs log tree commit diff
path: root/disk/src/qcow/mod.rs
diff options
context:
space:
mode:
Diffstat (limited to 'disk/src/qcow/mod.rs')
-rw-r--r--disk/src/qcow/mod.rs54
1 files changed, 46 insertions, 8 deletions
diff --git a/disk/src/qcow/mod.rs b/disk/src/qcow/mod.rs
index dfc8acd..c5e119d 100644
--- a/disk/src/qcow/mod.rs
+++ b/disk/src/qcow/mod.rs
@@ -1065,7 +1065,7 @@ impl QcowFile {
             let l2_table = if l2_addr_disk == 0 {
                 // Allocate a new cluster to store the L2 table and update the L1 table to point
                 // to the new table.
-                let new_addr: u64 = self.get_new_cluster()?;
+                let new_addr: u64 = self.get_new_cluster(None)?;
                 // The cluster refcount starts at one meaning it is used but doesn't need COW.
                 set_refcounts.push((new_addr, 1));
                 self.l1_table[l1_index] = new_addr;
@@ -1086,8 +1086,19 @@ impl QcowFile {
 
         let cluster_addr = match self.l2_cache.get(&l1_index).unwrap()[l2_index] {
             0 => {
+                let initial_data = if let Some(backing) = self.backing_file.as_mut() {
+                    let cluster_size = self.raw_file.cluster_size();
+                    let cluster_begin = address - (address % cluster_size);
+                    let mut cluster_data = vec![0u8; cluster_size as usize];
+                    let raw_slice = cluster_data.as_mut_slice();
+                    let volatile_slice = raw_slice.get_slice(0, cluster_size).unwrap();
+                    backing.read_exact_at_volatile(volatile_slice, cluster_begin)?;
+                    Some(cluster_data)
+                } else {
+                    None
+                };
                 // Need to allocate a data cluster
-                let cluster_addr = self.append_data_cluster()?;
+                let cluster_addr = self.append_data_cluster(initial_data)?;
                 self.update_cluster_addr(l1_index, l2_index, cluster_addr, &mut set_refcounts)?;
                 cluster_addr
             }
@@ -1124,7 +1135,7 @@ impl QcowFile {
             // Allocate a new cluster to store the L2 table and update the L1 table to point
             // to the new table. The cluster will be written when the cache is flushed, no
             // need to copy the data now.
-            let new_addr: u64 = self.get_new_cluster()?;
+            let new_addr: u64 = self.get_new_cluster(None)?;
             // The cluster refcount starts at one indicating it is used but doesn't need
             // COW.
             set_refcounts.push((new_addr, 1));
@@ -1136,15 +1147,22 @@ impl QcowFile {
     }
 
     // Allocate a new cluster and return its offset within the raw file.
-    fn get_new_cluster(&mut self) -> std::io::Result<u64> {
+    fn get_new_cluster(&mut self, initial_data: Option<Vec<u8>>) -> std::io::Result<u64> {
         // First use a pre allocated cluster if one is available.
         if let Some(free_cluster) = self.avail_clusters.pop() {
-            self.raw_file.zero_cluster(free_cluster)?;
+            if let Some(initial_data) = initial_data {
+                self.raw_file.write_cluster(free_cluster, initial_data)?;
+            } else {
+                self.raw_file.zero_cluster(free_cluster)?;
+            }
             return Ok(free_cluster);
         }
 
         let max_valid_cluster_offset = self.refcounts.max_valid_cluster_offset();
         if let Some(new_cluster) = self.raw_file.add_cluster_end(max_valid_cluster_offset)? {
+            if let Some(initial_data) = initial_data {
+                self.raw_file.write_cluster(new_cluster, initial_data)?;
+            }
             Ok(new_cluster)
         } else {
             error!("No free clusters in get_new_cluster()");
@@ -1154,8 +1172,8 @@ impl QcowFile {
 
     // Allocate and initialize a new data cluster. Returns the offset of the
     // cluster in to the file on success.
-    fn append_data_cluster(&mut self) -> std::io::Result<u64> {
-        let new_addr: u64 = self.get_new_cluster()?;
+    fn append_data_cluster(&mut self, initial_data: Option<Vec<u8>>) -> std::io::Result<u64> {
+        let new_addr: u64 = self.get_new_cluster(initial_data)?;
         // The cluster refcount starts at one indicating it is used but doesn't need COW.
         let mut newly_unref = self.set_cluster_refcount(new_addr, 1)?;
         self.unref_clusters.append(&mut newly_unref);
@@ -1386,7 +1404,7 @@ impl QcowFile {
                 }
                 Err(refcount::Error::NeedNewCluster) => {
                     // Allocate the cluster and call set_cluster_refcount again.
-                    let addr = self.get_new_cluster()?;
+                    let addr = self.get_new_cluster(None)?;
                     added_clusters.push(addr);
                     new_cluster = Some((
                         addr,
@@ -1996,6 +2014,26 @@ mod tests {
     }
 
     #[test]
+    fn write_read_start_backing_overlap() {
+        let disk_file = basic_file(&valid_header());
+        let mut backing = QcowFile::from(disk_file).unwrap();
+        backing
+            .write(b"test first bytes")
+            .expect("Failed to write test string.");
+        let wrapping_disk_file = basic_file(&valid_header());
+        let mut wrapping = QcowFile::from(wrapping_disk_file).unwrap();
+        wrapping.set_backing_file(Some(Box::new(backing)));
+        wrapping.seek(SeekFrom::Start(0)).expect("Failed to seek.");
+        wrapping
+            .write(b"TEST")
+            .expect("Failed to write second test string.");
+        let mut buf = [0u8; 10];
+        wrapping.seek(SeekFrom::Start(0)).expect("Failed to seek.");
+        wrapping.read(&mut buf).expect("Failed to read.");
+        assert_eq!(&buf, b"TEST first");
+    }
+
+    #[test]
     fn offset_write_read() {
         with_basic_file(&valid_header(), |disk_file: File| {
             let mut q = QcowFile::from(disk_file).unwrap();