mirror of
https://chromium.googlesource.com/crosvm/crosvm
synced 2025-01-27 10:36:40 +00:00
qcow: implement WriteZeroes for QcowFile
Add a simple implementation of WriteZeroes for QcowFile that just writes zeroes to allocated clusters and skips clusters that are already unallocated (since they already read back as zeroes). BUG=chromium:850998 TEST=cargo test -p qcow Change-Id: I8f26c8cc4016c129850aaf08c7188dfe08d6dacb Signed-off-by: Daniel Verkamp <dverkamp@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/1187018 Reviewed-by: Dylan Reid <dgreid@chromium.org> Reviewed-by: Zach Reizner <zachr@chromium.org>
This commit is contained in:
parent
a3d11edaa6
commit
95a8868aef
2 changed files with 76 additions and 5 deletions
|
@ -9,6 +9,4 @@ path = "src/qcow.rs"
|
|||
[dependencies]
|
||||
byteorder = "*"
|
||||
libc = "*"
|
||||
|
||||
[dev-dependencies]
|
||||
sys_util = { path = "../sys_util" }
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
extern crate byteorder;
|
||||
extern crate libc;
|
||||
extern crate sys_util;
|
||||
|
||||
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
|
||||
use libc::{EINVAL, ENOTSUP};
|
||||
|
@ -14,6 +15,8 @@ use std::io::{self, Read, Seek, SeekFrom, Write};
|
|||
use std::mem::size_of;
|
||||
use std::os::unix::io::{AsRawFd, RawFd};
|
||||
|
||||
use sys_util::WriteZeroes;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
BackingFilesNotSupported,
|
||||
|
@ -606,6 +609,32 @@ impl Write for QcowFile {
|
|||
}
|
||||
}
|
||||
|
||||
impl WriteZeroes for QcowFile {
|
||||
fn write_zeroes(&mut self, length: usize) -> std::io::Result<usize> {
|
||||
let address: u64 = self.current_offset as u64;
|
||||
let write_count: usize = self.limit_range_file(address, length);
|
||||
|
||||
let mut nwritten: usize = 0;
|
||||
while nwritten < write_count {
|
||||
let curr_addr = address + nwritten as u64;
|
||||
let count = self.limit_range_cluster(curr_addr, write_count - nwritten);
|
||||
|
||||
// Zero out space that was previously allocated.
|
||||
// Any space in unallocated clusters can be left alone, since
|
||||
// unallocated clusters already read back as zeroes.
|
||||
if let Some(offset) = self.file_offset(curr_addr, false)? {
|
||||
// Space was previously allocated for this offset - zero it out.
|
||||
self.file.seek(SeekFrom::Start(offset))?;
|
||||
self.file.write_zeroes(count)?;
|
||||
}
|
||||
|
||||
nwritten += count;
|
||||
}
|
||||
self.current_offset += length as u64;
|
||||
Ok(length)
|
||||
}
|
||||
}
|
||||
|
||||
// Returns an Error if the given offset doesn't align to a cluster boundary.
|
||||
fn offset_is_cluster_boundary(offset: u64, cluster_bits: u32) -> Result<()> {
|
||||
if offset & ((0x01 << cluster_bits) - 1) != 0 {
|
||||
|
@ -636,9 +665,6 @@ fn div_round_up_u32(dividend: u32, divisor: u32) -> u32 {
|
|||
(dividend + divisor - 1) / divisor
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
extern crate sys_util;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::fs::File;
|
||||
|
@ -753,6 +779,53 @@ mod tests {
|
|||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn write_zeroes_read() {
|
||||
with_basic_file(&valid_header(), |disk_file: File| {
|
||||
let mut q = QcowFile::from(disk_file).unwrap();
|
||||
// Write some test data.
|
||||
let b = [0x55u8; 0x1000];
|
||||
q.seek(SeekFrom::Start(0xfff2000)).expect("Failed to seek.");
|
||||
q.write(&b).expect("Failed to write test string.");
|
||||
// Overwrite the test data with zeroes.
|
||||
q.seek(SeekFrom::Start(0xfff2000)).expect("Failed to seek.");
|
||||
let nwritten = q.write_zeroes(0x200).expect("Failed to write zeroes.");
|
||||
assert_eq!(nwritten, 0x200);
|
||||
// Verify that the correct part of the data was zeroed out.
|
||||
let mut buf = [0u8; 0x1000];
|
||||
q.seek(SeekFrom::Start(0xfff2000)).expect("Failed to seek.");
|
||||
q.read(&mut buf).expect("Failed to read.");
|
||||
assert_eq!(buf[0], 0);
|
||||
assert_eq!(buf[0x1FF], 0);
|
||||
assert_eq!(buf[0x200], 0x55);
|
||||
assert_eq!(buf[0xFFF], 0x55);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn write_zeroes_full_cluster() {
|
||||
// Choose a size that is larger than a cluster.
|
||||
// valid_header uses cluster_bits = 12, which corresponds to a cluster size of 4096.
|
||||
const CHUNK_SIZE: usize = 4096 * 2 + 512;
|
||||
with_basic_file(&valid_header(), |disk_file: File| {
|
||||
let mut q = QcowFile::from(disk_file).unwrap();
|
||||
// Write some test data.
|
||||
let b = [0x55u8; CHUNK_SIZE];
|
||||
q.seek(SeekFrom::Start(0)).expect("Failed to seek.");
|
||||
q.write(&b).expect("Failed to write test string.");
|
||||
// Overwrite the full cluster with zeroes.
|
||||
q.seek(SeekFrom::Start(0)).expect("Failed to seek.");
|
||||
let nwritten = q.write_zeroes(CHUNK_SIZE).expect("Failed to write zeroes.");
|
||||
assert_eq!(nwritten, CHUNK_SIZE);
|
||||
// Verify that the data was zeroed out.
|
||||
let mut buf = [0u8; CHUNK_SIZE];
|
||||
q.seek(SeekFrom::Start(0)).expect("Failed to seek.");
|
||||
q.read(&mut buf).expect("Failed to read.");
|
||||
assert_eq!(buf[0], 0);
|
||||
assert_eq!(buf[CHUNK_SIZE - 1], 0);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_header() {
|
||||
with_basic_file(&valid_header(), |disk_file: File| {
|
||||
|
|
Loading…
Reference in a new issue