sys_util: add FileAllocate trait

This trait provides a generic interface for allocating space on the
filesystem within a given file.  It is equivalent to the fallocate(2)
system call with the default mode (mode = 0).

BUG=chromium:858815
TEST=cargo build --features=composite-disk

Change-Id: I2f4e8aceb4878790e8dec2e3d539071915efd205
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/2015828
Reviewed-by: Dylan Reid <dgreid@chromium.org>
Tested-by: kokoro <noreply+kokoro@google.com>
Commit-Queue: Daniel Verkamp <dverkamp@chromium.org>
This commit is contained in:
Daniel Verkamp 2020-01-15 12:53:43 -08:00 committed by Commit Bot
parent e43d300aef
commit 977f873a41
5 changed files with 59 additions and 5 deletions

View file

@ -13,7 +13,9 @@ use crate::{create_disk_file, DiskFile, DiskGetLen, ImageType};
use data_model::VolatileSlice;
use protos::cdisk_spec;
use remain::sorted;
use sys_util::{AsRawFds, FileReadWriteAtVolatile, FileSetLen, FileSync, PunchHole, WriteZeroesAt};
use sys_util::{
AsRawFds, FileAllocate, FileReadWriteAtVolatile, FileSetLen, FileSync, PunchHole, WriteZeroesAt,
};
#[sorted]
#[derive(Debug)]
@ -292,6 +294,27 @@ impl PunchHole for CompositeDiskFile {
}
}
impl FileAllocate for CompositeDiskFile {
fn allocate(&mut self, offset: u64, length: u64) -> io::Result<()> {
let range = offset..(offset + length);
let disks = self.disks_in_range(&range);
for disk in disks {
let intersection = range_intersection(&range, &disk.range());
if intersection.start >= intersection.end {
continue;
}
let result = disk.file.allocate(
intersection.start - disk.offset,
intersection.end - intersection.start,
);
if result.is_err() {
return result;
}
}
Ok(())
}
}
impl WriteZeroesAt for CompositeDiskFile {
fn write_zeroes_at(&mut self, offset: u64, length: usize) -> io::Result<usize> {
let cursor_location = offset;

View file

@ -10,7 +10,8 @@ use std::io::{self, Read, Seek, SeekFrom, Write};
use libc::EINVAL;
use remain::sorted;
use sys_util::{
AsRawFds, FileReadWriteAtVolatile, FileSetLen, FileSync, PunchHole, SeekHole, WriteZeroesAt,
AsRawFds, FileAllocate, FileReadWriteAtVolatile, FileSetLen, FileSync, PunchHole, SeekHole,
WriteZeroesAt,
};
mod qcow;
@ -64,6 +65,7 @@ pub trait DiskFile:
+ FileReadWriteAtVolatile
+ PunchHole
+ WriteZeroesAt
+ FileAllocate
+ Send
+ AsRawFds
+ Debug
@ -76,6 +78,7 @@ impl<
+ PunchHole
+ FileReadWriteAtVolatile
+ WriteZeroesAt
+ FileAllocate
+ Send
+ AsRawFds
+ Debug,

View file

@ -10,8 +10,8 @@ use data_model::{VolatileMemory, VolatileSlice};
use libc::{EINVAL, ENOSPC, ENOTSUP};
use remain::sorted;
use sys_util::{
error, FileReadWriteAtVolatile, FileReadWriteVolatile, FileSetLen, FileSync, PunchHole,
SeekHole, WriteZeroesAt,
error, FileAllocate, FileReadWriteAtVolatile, FileReadWriteVolatile, FileSetLen, FileSync,
PunchHole, SeekHole, WriteZeroesAt,
};
use std::cmp::{max, min};
@ -1582,6 +1582,15 @@ impl DiskGetLen for QcowFile {
}
}
impl FileAllocate for QcowFile {
fn allocate(&mut self, offset: u64, len: u64) -> io::Result<()> {
// Call write_cb with a do-nothing callback, which will have the effect
// of allocating all clusters in the specified range.
self.write_cb(offset, len as usize, |_file, _offset, _count| Ok(()))?;
Ok(())
}
}
impl PunchHole for QcowFile {
fn punch_hole(&mut self, offset: u64, length: u64) -> std::io::Result<()> {
let mut remaining = length;

View file

@ -8,6 +8,8 @@ use std::os::unix::io::{AsRawFd, RawFd};
use data_model::VolatileSlice;
use crate::{fallocate, FallocateMode};
/// A trait for flushing the contents of a file to disk.
/// This is equivalent to File's `sync_all` method, but
/// wrapped in a trait so that it can be implemented for
@ -54,6 +56,20 @@ impl FileGetLen for File {
}
}
/// A trait for allocating disk space in a sparse file.
/// This is equivalent to fallocate() with no special flags.
pub trait FileAllocate {
/// Allocate storage for the region of the file starting at `offset` and extending `len` bytes.
fn allocate(&mut self, offset: u64, len: u64) -> Result<()>;
}
impl FileAllocate for File {
fn allocate(&mut self, offset: u64, len: u64) -> Result<()> {
fallocate(self, FallocateMode::Allocate, true, offset, len)
.map_err(|e| Error::from_raw_os_error(e.errno()))
}
}
/// A trait similar to `Read` and `Write`, but uses volatile memory as buffers.
pub trait FileReadWriteVolatile {
/// Read bytes from this file into the given slice, returning the number of bytes read on

View file

@ -64,7 +64,8 @@ pub use crate::timerfd::*;
pub use poll_token_derive::*;
pub use crate::file_traits::{
AsRawFds, FileGetLen, FileReadWriteAtVolatile, FileReadWriteVolatile, FileSetLen, FileSync,
AsRawFds, FileAllocate, FileGetLen, FileReadWriteAtVolatile, FileReadWriteVolatile, FileSetLen,
FileSync,
};
pub use crate::guest_memory::Error as GuestMemoryError;
pub use crate::mmap::Error as MmapError;
@ -175,6 +176,7 @@ pub fn flock(file: &dyn AsRawFd, op: FlockOperation, nonblocking: bool) -> Resul
pub enum FallocateMode {
PunchHole,
ZeroRange,
Allocate,
}
/// Safe wrapper for `fallocate()`.
@ -200,6 +202,7 @@ pub fn fallocate(
let mut mode = match mode {
FallocateMode::PunchHole => libc::FALLOC_FL_PUNCH_HOLE,
FallocateMode::ZeroRange => libc::FALLOC_FL_ZERO_RANGE,
FallocateMode::Allocate => 0,
};
if keep_size {