summary refs log tree commit diff
path: root/qcow
diff options
context:
space:
mode:
authorDylan Reid <dgreid@chromium.org>2018-08-28 16:39:32 -0700
committerchrome-bot <chrome-bot@chromium.org>2018-09-13 18:59:02 -0700
commit8b952c2faf82c8e3c1c6e921edc9cc8a1b8c1851 (patch)
tree2cb422738b88a1f27ba9579085c31a36fb8499e3 /qcow
parent96c4a6df2db7da108db0b5fa993a55b7cb835180 (diff)
downloadcrosvm-8b952c2faf82c8e3c1c6e921edc9cc8a1b8c1851.tar
crosvm-8b952c2faf82c8e3c1c6e921edc9cc8a1b8c1851.tar.gz
crosvm-8b952c2faf82c8e3c1c6e921edc9cc8a1b8c1851.tar.bz2
crosvm-8b952c2faf82c8e3c1c6e921edc9cc8a1b8c1851.tar.lz
crosvm-8b952c2faf82c8e3c1c6e921edc9cc8a1b8c1851.tar.xz
crosvm-8b952c2faf82c8e3c1c6e921edc9cc8a1b8c1851.tar.zst
crosvm-8b952c2faf82c8e3c1c6e921edc9cc8a1b8c1851.zip
qcow: Add raw file struct
The raw file struct will be used to hold enough state for basic
operations. This will allow mutating the file without taking a mutable
reference to an entire QcowFile.

Change-Id: Ia0a86537915da039274923df2f85c22d191b9969
Signed-off-by: Dylan Reid <dgreid@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/1207450
Commit-Ready: ChromeOS CL Exonerator Bot <chromiumos-cl-exonerator@appspot.gserviceaccount.com>
Reviewed-by: Chirantan Ekbote <chirantan@chromium.org>
Diffstat (limited to 'qcow')
-rw-r--r--qcow/src/qcow_raw_file.rs129
1 files changed, 129 insertions, 0 deletions
diff --git a/qcow/src/qcow_raw_file.rs b/qcow/src/qcow_raw_file.rs
new file mode 100644
index 0000000..37bdca2
--- /dev/null
+++ b/qcow/src/qcow_raw_file.rs
@@ -0,0 +1,129 @@
+// Copyright 2018 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+use std::fs::File;
+use std::io::{self, Seek, SeekFrom};
+use std::mem::size_of;
+
+use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
+
+/// A qcow file. Allows reading/writing clusters and appending clusters.
+#[derive(Debug)]
+pub struct QcowRawFile {
+    file: File,
+    cluster_size: u64,
+    cluster_mask: u64,
+}
+
+impl QcowRawFile {
+    /// Creates a `QcowRawFile` from the given `File`, `None` is returned if `cluster_size` is not
+    /// a power of two.
+    pub fn from(file: File, cluster_size: u64) -> Option<Self> {
+        if cluster_size.count_ones() != 1 {
+            return None;
+        }
+        Some(QcowRawFile {
+            file,
+            cluster_size,
+            cluster_mask: cluster_size - 1,
+        })
+    }
+
+    /// Reads `count` 64 bit offsets and returns them as a vector.
+    /// `mask` optionally ands out some of the bits on the file.
+    pub fn read_pointer_table(
+        &mut self,
+        offset: u64,
+        count: u64,
+        mask: Option<u64>,
+    ) -> io::Result<Vec<u64>> {
+        let mut table = Vec::with_capacity(count as usize);
+        self.file.seek(SeekFrom::Start(offset))?;
+        let mask = mask.unwrap_or(0xffff_ffff_ffff_ffff);
+        for _ in 0..count {
+            table.push(self.file.read_u64::<BigEndian>()? & mask);
+        }
+        Ok(table)
+    }
+
+    /// Reads a cluster's worth of 64 bit offsets and returns them as a vector.
+    /// `mask` optionally ands out some of the bits on the file.
+    pub fn read_pointer_cluster(&mut self, offset: u64, mask: Option<u64>) -> io::Result<Vec<u64>> {
+        let count = self.cluster_size / size_of::<u64>() as u64;
+        self.read_pointer_table(offset, count, mask)
+    }
+
+    /// Writes `table` of u64 pointers to `offset` in the file.
+    /// `non_zero_flags` will be ORed with all non-zero values in `table`.
+    /// writing.
+    pub fn write_pointer_table(
+        &mut self,
+        offset: u64,
+        table: &[u64],
+        non_zero_flags: u64,
+    ) -> io::Result<()> {
+        self.file.seek(SeekFrom::Start(offset))?;
+        for addr in table {
+            let val = if *addr == 0 {
+                0
+            } else {
+                *addr | non_zero_flags
+            };
+            self.file.write_u64::<BigEndian>(val)?;
+        }
+        Ok(())
+    }
+
+    /// Read a refcount block from the file and returns a Vec containing the block.
+    /// Always returns a cluster's worth of data.
+    pub fn read_refcount_block(&mut self, offset: u64) -> io::Result<Vec<u16>> {
+        let count = self.cluster_size / size_of::<u16>() as u64;
+        let mut table = Vec::with_capacity(count as usize);
+        self.file.seek(SeekFrom::Start(offset))?;
+        for _ in 0..count {
+            table.push(self.file.read_u16::<BigEndian>()?);
+        }
+        Ok(table)
+    }
+
+    /// Writes a refcount block to the file.
+    pub fn write_refcount_block(&mut self, offset: u64, table: &[u16]) -> io::Result<()> {
+        self.file.seek(SeekFrom::Start(offset))?;
+        for count in table {
+            self.file.write_u16::<BigEndian>(*count)?;
+        }
+        Ok(())
+    }
+
+    /// Allocates a new cluster at the end of the current file, return the address.
+    pub fn add_cluster_end(&mut self) -> io::Result<u64> {
+        // Determine where the new end of the file should be and set_len, which
+        // translates to truncate(2).
+        let file_end: u64 = self.file.seek(SeekFrom::End(0))?;
+        let new_cluster_address: u64 = (file_end + self.cluster_size - 1) & !self.cluster_mask;
+        self.file.set_len(new_cluster_address + self.cluster_size)?;
+
+        Ok(new_cluster_address)
+    }
+
+    /// Returns a reference to the underlying file.
+    pub fn file(&self) -> &File {
+        &self.file
+    }
+
+    /// Returns a mutable reference to the underlying file.
+    pub fn file_mut(&mut self) -> &mut File {
+        &mut self.file
+    }
+
+    /// Returns the size of the file's clusters.
+    pub fn cluster_size(&self) -> u64 {
+        self.cluster_size
+    }
+
+    /// Returns the offset of `address` within a cluster.
+    pub fn cluster_offset(&self, address: u64) -> u64 {
+        address & self.cluster_mask
+    }
+}