mirror of
https://chromium.googlesource.com/crosvm/crosvm
synced 2025-02-06 02:25:23 +00:00
sys_util: add WriteZeroesAt trait
Add a variant of WriteZeroes that allows the caller to specify the offset explicitly instead of using the file's cursor. This gets rid of one of the last bits of shared state between disk file users, which will help in implementing multi-queue support. Additionally, modify the WriteZeroes trait to use a generic implementation based on WriteZeroesAt + Seek when possible. BUG=chromium:858815 TEST=Boot Termina in crosvm Change-Id: If710159771aeeb55f4f7746dd4354b6c042144e8 Signed-off-by: Daniel Verkamp <dverkamp@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/1913519
This commit is contained in:
parent
3064a7164a
commit
6eadef77a3
5 changed files with 66 additions and 13 deletions
|
@ -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<usize> {
|
||||
self.punch_hole(offset, length as u64)?;
|
||||
Ok(length)
|
||||
}
|
||||
}
|
||||
|
||||
impl SeekHole for QcowFile {
|
||||
fn seek_hole(&mut self, offset: u64) -> io::Result<Option<u64>> {
|
||||
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<u8> {
|
||||
vec![
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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};
|
||||
|
|
|
@ -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<T: PunchHole + Seek + Write> WriteZeroes for T {
|
||||
fn write_zeroes(&mut self, length: usize) -> io::Result<usize> {
|
||||
/// 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<usize>;
|
||||
|
||||
/// 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<usize> {
|
||||
// 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<T: PunchHole + Seek + Write> 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<T: WriteZeroesAt + Seek> WriteZeroes for T {
|
||||
fn write_zeroes(&mut self, length: usize) -> io::Result<usize> {
|
||||
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]
|
||||
|
|
Loading…
Reference in a new issue