summary refs log tree commit diff
path: root/disk/src
diff options
context:
space:
mode:
authorA. Cody Schuffelen <schuffelen@google.com>2019-12-26 12:28:11 -0800
committerCommit Bot <commit-bot@chromium.org>2020-02-21 21:52:56 +0000
commitd8144a56e26ca09e2c7ff97ed63c57e7e7965674 (patch)
tree86ae5e5d11c74984f125d7cc3e303f973c53dfc7 /disk/src
parent9ca6039b030a5c83062cfec9a5ff52f42814fa13 (diff)
downloadcrosvm-d8144a56e26ca09e2c7ff97ed63c57e7e7965674.tar
crosvm-d8144a56e26ca09e2c7ff97ed63c57e7e7965674.tar.gz
crosvm-d8144a56e26ca09e2c7ff97ed63c57e7e7965674.tar.bz2
crosvm-d8144a56e26ca09e2c7ff97ed63c57e7e7965674.tar.lz
crosvm-d8144a56e26ca09e2c7ff97ed63c57e7e7965674.tar.xz
crosvm-d8144a56e26ca09e2c7ff97ed63c57e7e7965674.tar.zst
crosvm-d8144a56e26ca09e2c7ff97ed63c57e7e7965674.zip
Read from the backing file if present on read miss
Reads to qcow files with backing files will fall through to the backing
file if there is no allocated cluster. As of this change, a write will
still trash the cluster and hide any data already present.

TEST=unit tests
BUG=b:140069322
Change-Id: Iba353fa1e7c25bb6267eb96b30b8f5a6ac61d423
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/1982831
Tested-by: kokoro <noreply+kokoro@google.com>
Commit-Queue: Daniel Verkamp <dverkamp@chromium.org>
Reviewed-by: Daniel Verkamp <dverkamp@chromium.org>
Diffstat (limited to 'disk/src')
-rw-r--r--disk/src/qcow/mod.rs35
1 files changed, 30 insertions, 5 deletions
diff --git a/disk/src/qcow/mod.rs b/disk/src/qcow/mod.rs
index 1e3a451..dfc8acd 100644
--- a/disk/src/qcow/mod.rs
+++ b/disk/src/qcow/mod.rs
@@ -620,6 +620,10 @@ impl QcowFile {
         Ok(qcow)
     }
 
+    pub fn set_backing_file(&mut self, backing: Option<Box<dyn DiskFile>>) {
+        self.backing_file = backing;
+    }
+
     /// Returns the `QcowHeader` for this file.
     pub fn header(&self) -> &QcowHeader {
         &self.header
@@ -1459,6 +1463,8 @@ impl QcowFile {
 
             if let Some(offset) = file_offset {
                 cb(Some(self.raw_file.file_mut()), nread, offset, count)?;
+            } else if let Some(backing) = self.backing_file.as_mut() {
+                cb(Some(backing.as_mut()), nread, curr_addr, count)?;
             } else {
                 cb(None, nread, 0, count)?;
             }
@@ -1795,17 +1801,20 @@ mod tests {
         ]
     }
 
-    fn with_basic_file<F>(header: &[u8], mut testfn: F)
-    where
-        F: FnMut(File),
-    {
+    fn basic_file(header: &[u8]) -> File {
         let shm = SharedMemory::anon().unwrap();
         let mut disk_file: File = shm.into();
         disk_file.write_all(&header).unwrap();
         disk_file.set_len(0x1_0000_0000).unwrap();
         disk_file.seek(SeekFrom::Start(0)).unwrap();
+        disk_file
+    }
 
-        testfn(disk_file); // File closed when the function exits.
+    fn with_basic_file<F>(header: &[u8], mut testfn: F)
+    where
+        F: FnMut(File),
+    {
+        testfn(basic_file(header)); // File closed when the function exits.
     }
 
     fn with_default_file<F>(file_size: u64, mut testfn: F)
@@ -1971,6 +1980,22 @@ mod tests {
     }
 
     #[test]
+    fn write_read_start_backing() {
+        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 mut buf = [0u8; 4];
+        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.read(&mut buf).expect("Failed to read.");
+        assert_eq!(&buf, b"test");
+    }
+
+    #[test]
     fn offset_write_read() {
         with_basic_file(&valid_header(), |disk_file: File| {
             let mut q = QcowFile::from(disk_file).unwrap();