diff options
author | Daniel Verkamp <dverkamp@chromium.org> | 2018-08-22 14:32:20 -0700 |
---|---|---|
committer | chrome-bot <chrome-bot@chromium.org> | 2018-09-05 01:25:50 -0700 |
commit | 95a8868aef2c1018b13b30f3afc2e11bb3ae2066 (patch) | |
tree | 05860990f264b62389a3b767bf4ba8697b28ac28 /qcow | |
parent | a3d11edaa6d728adcad0693ab63a7e9e49690126 (diff) | |
download | crosvm-95a8868aef2c1018b13b30f3afc2e11bb3ae2066.tar crosvm-95a8868aef2c1018b13b30f3afc2e11bb3ae2066.tar.gz crosvm-95a8868aef2c1018b13b30f3afc2e11bb3ae2066.tar.bz2 crosvm-95a8868aef2c1018b13b30f3afc2e11bb3ae2066.tar.lz crosvm-95a8868aef2c1018b13b30f3afc2e11bb3ae2066.tar.xz crosvm-95a8868aef2c1018b13b30f3afc2e11bb3ae2066.tar.zst crosvm-95a8868aef2c1018b13b30f3afc2e11bb3ae2066.zip |
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>
Diffstat (limited to 'qcow')
-rw-r--r-- | qcow/Cargo.toml | 2 | ||||
-rw-r--r-- | qcow/src/qcow.rs | 79 |
2 files changed, 76 insertions, 5 deletions
diff --git a/qcow/Cargo.toml b/qcow/Cargo.toml index 366449e..980d0d6 100644 --- a/qcow/Cargo.toml +++ b/qcow/Cargo.toml @@ -9,6 +9,4 @@ path = "src/qcow.rs" [dependencies] byteorder = "*" libc = "*" - -[dev-dependencies] sys_util = { path = "../sys_util" } diff --git a/qcow/src/qcow.rs b/qcow/src/qcow.rs index 2d49968..7402402 100644 --- a/qcow/src/qcow.rs +++ b/qcow/src/qcow.rs @@ -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 { @@ -637,9 +666,6 @@ fn div_round_up_u32(dividend: u32, divisor: u32) -> u32 { } #[cfg(test)] -extern crate sys_util; - -#[cfg(test)] mod tests { use std::fs::File; use std::io::{Read, Seek, SeekFrom, Write}; @@ -754,6 +780,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| { let q = QcowFile::from(disk_file).unwrap(); |