diff options
author | Dylan Reid <dgreid@chromium.org> | 2018-08-28 16:39:32 -0700 |
---|---|---|
committer | chrome-bot <chrome-bot@chromium.org> | 2018-09-13 18:59:02 -0700 |
commit | 8b952c2faf82c8e3c1c6e921edc9cc8a1b8c1851 (patch) | |
tree | 2cb422738b88a1f27ba9579085c31a36fb8499e3 /qcow | |
parent | 96c4a6df2db7da108db0b5fa993a55b7cb835180 (diff) | |
download | crosvm-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.rs | 129 |
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 + } +} |