diff --git a/qcow/src/qcow.rs b/qcow/src/qcow.rs index 34a54f006a..aa923f9ab3 100644 --- a/qcow/src/qcow.rs +++ b/qcow/src/qcow.rs @@ -11,7 +11,7 @@ use libc::{EINVAL, ENOSPC, ENOTSUP}; use remain::sorted; use sys_util::{ error, FileReadWriteAtVolatile, FileReadWriteVolatile, FileSetLen, FileSync, PunchHole, - SeekHole, WriteZeroes, + SeekHole, WriteZeroesAt, }; use std::cmp::{max, min}; @@ -1239,8 +1239,9 @@ impl QcowFile { // unallocated clusters already read back as zeroes. if let Some(offset) = self.file_offset_read(curr_addr)? { // Partial cluster - zero it out. - self.raw_file.file_mut().seek(SeekFrom::Start(offset))?; - self.raw_file.file_mut().write_zeroes_all(count)?; + self.raw_file + .file_mut() + .write_zeroes_all_at(offset, count)?; } } @@ -1581,6 +1582,13 @@ impl PunchHole for QcowFile { } } +impl WriteZeroesAt for QcowFile { + fn write_zeroes_at(&mut self, offset: u64, length: usize) -> io::Result { + self.punch_hole(offset, length as u64)?; + Ok(length) + } +} + impl SeekHole for QcowFile { fn seek_hole(&mut self, offset: u64) -> io::Result> { match self.find_allocated_cluster(offset, false) { @@ -1634,7 +1642,7 @@ mod tests { use super::*; use std::fs::File; use std::io::{Read, Seek, SeekFrom, Write}; - use sys_util::SharedMemory; + use sys_util::{SharedMemory, WriteZeroes}; fn valid_header() -> Vec { vec![ diff --git a/seccomp/arm/block_device.policy b/seccomp/arm/block_device.policy index 1ec4053a51..fad0cc079b 100644 --- a/seccomp/arm/block_device.policy +++ b/seccomp/arm/block_device.policy @@ -10,7 +10,9 @@ fstat64: 1 fsync: 1 ftruncate64: 1 _llseek: 1 +pread64: 1 preadv: 1 +pwrite64: 1 pwritev: 1 timerfd_create: 1 timerfd_gettime: 1 diff --git a/seccomp/x86_64/block_device.policy b/seccomp/x86_64/block_device.policy index 6bb5c328ff..c1ddf26f66 100644 --- a/seccomp/x86_64/block_device.policy +++ b/seccomp/x86_64/block_device.policy @@ -10,7 +10,9 @@ fstat: 1 fsync: 1 ftruncate: 1 lseek: 1 +pread64: 1 preadv: 1 +pwrite64: 1 pwritev: 1 timerfd_create: 1 timerfd_gettime: 1 diff --git a/sys_util/src/lib.rs b/sys_util/src/lib.rs index 331dbace3e..31b1662711 100644 --- a/sys_util/src/lib.rs +++ b/sys_util/src/lib.rs @@ -70,7 +70,7 @@ pub use crate::guest_memory::Error as GuestMemoryError; pub use crate::mmap::Error as MmapError; pub use crate::seek_hole::SeekHole; pub use crate::signalfd::Error as SignalFdError; -pub use crate::write_zeroes::{PunchHole, WriteZeroes}; +pub use crate::write_zeroes::{PunchHole, WriteZeroes, WriteZeroesAt}; use std::ffi::CStr; use std::fs::{remove_file, File}; diff --git a/sys_util/src/write_zeroes.rs b/sys_util/src/write_zeroes.rs index 0e733c7212..7e28f5315a 100644 --- a/sys_util/src/write_zeroes.rs +++ b/sys_util/src/write_zeroes.rs @@ -4,7 +4,8 @@ use std::cmp::min; use std::fs::File; -use std::io::{self, Error, ErrorKind, Seek, SeekFrom, Write}; +use std::io::{self, Error, ErrorKind, Seek, SeekFrom}; +use std::os::unix::fs::FileExt; use crate::fallocate; use crate::FallocateMode; @@ -51,13 +52,43 @@ pub trait WriteZeroes { } } -impl WriteZeroes for T { - fn write_zeroes(&mut self, length: usize) -> io::Result { +/// A trait for writing zeroes to an arbitrary position in a file. +pub trait WriteZeroesAt { + /// Write up to `length` bytes of zeroes starting at `offset`, returning how many bytes were + /// written. + fn write_zeroes_at(&mut self, offset: u64, length: usize) -> io::Result; + + /// Write zeroes starting at `offset` until `length` bytes have been written. + /// + /// This method will continuously call `write_zeroes_at` until the requested + /// `length` is satisfied or an error is encountered. + fn write_zeroes_all_at(&mut self, mut offset: u64, mut length: usize) -> io::Result<()> { + while length > 0 { + match self.write_zeroes_at(offset, length) { + Ok(0) => return Err(Error::from(ErrorKind::WriteZero)), + Ok(bytes_written) => { + length = length + .checked_sub(bytes_written) + .ok_or(Error::from(ErrorKind::Other))?; + offset = offset + .checked_add(bytes_written as u64) + .ok_or(Error::from(ErrorKind::Other))?; + } + Err(e) => { + if e.kind() != ErrorKind::Interrupted { + return Err(e); + } + } + } + } + Ok(()) + } +} + +impl WriteZeroesAt for File { + fn write_zeroes_at(&mut self, offset: u64, length: usize) -> io::Result { // Try to punch a hole first. - let offset = self.seek(SeekFrom::Current(0))?; if let Ok(()) = self.punch_hole(offset, length as u64) { - // Advance the seek cursor as if we had done a real write(). - self.seek(SeekFrom::Current(length as i64))?; return Ok(length); } @@ -71,17 +102,27 @@ impl WriteZeroes for T { while nwritten < length { let remaining = length - nwritten; let write_size = min(remaining, buf_size); - nwritten += self.write(&buf[0..write_size])?; + nwritten += self.write_at(&buf[0..write_size], offset + nwritten as u64)?; } Ok(length) } } +impl WriteZeroes for T { + fn write_zeroes(&mut self, length: usize) -> io::Result { + let offset = self.seek(SeekFrom::Current(0))?; + let nwritten = self.write_zeroes_at(offset, length)?; + // Advance the seek cursor as if we had done a real write(). + self.seek(SeekFrom::Current(nwritten as i64))?; + Ok(length) + } +} + #[cfg(test)] mod tests { use super::*; use std::fs::OpenOptions; - use std::io::{Read, Seek, SeekFrom}; + use std::io::{Read, Seek, SeekFrom, Write}; use tempfile::TempDir; #[test]