mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2026-08-05 02:19:16 +00:00
block: qcow: Port RawFile to AlignedFile
Reuse the functionality in the AlignedFile wrapper for the QCOW RawFile wrapper. This makes alignment handling more transparent. Assisted-by: Claude:Opus-4.8 Signed-off-by: Rob Bradford <rbradford@meta.com>
This commit is contained in:
@@ -31,7 +31,6 @@ pub(crate) struct AlignedFile {
|
|||||||
alignment: usize,
|
alignment: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(not(test), expect(dead_code))]
|
|
||||||
impl AlignedFile {
|
impl AlignedFile {
|
||||||
/// Wrap `file`, probing the O_DIRECT block alignment when `direct_io`.
|
/// Wrap `file`, probing the O_DIRECT block alignment when `direct_io`.
|
||||||
pub fn new(file: File, direct_io: bool) -> Self {
|
pub fn new(file: File, direct_io: bool) -> Self {
|
||||||
|
|||||||
@@ -19,82 +19,47 @@ use vmm_sys_util::file_traits::FileSync;
|
|||||||
use vmm_sys_util::seek_hole::SeekHole;
|
use vmm_sys_util::seek_hole::SeekHole;
|
||||||
use vmm_sys_util::write_zeroes::{PunchHole, WriteZeroesAt};
|
use vmm_sys_util::write_zeroes::{PunchHole, WriteZeroesAt};
|
||||||
|
|
||||||
use crate::aligned_buffer::AlignedBuffer;
|
use crate::aligned_file::AlignedFile;
|
||||||
use crate::{BlockBackend, query_device_size};
|
use crate::{BlockBackend, query_device_size};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct RawFile {
|
pub struct RawFile {
|
||||||
file: File,
|
aligned: AlignedFile,
|
||||||
alignment: usize,
|
|
||||||
position: u64,
|
position: u64,
|
||||||
direct_io: bool,
|
direct_io: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
const BLK_ALIGNMENTS: [usize; 2] = [512, 4096];
|
|
||||||
|
|
||||||
fn is_valid_alignment(file: &File, alignment: usize) -> bool {
|
|
||||||
let mut abuf = match AlignedBuffer::new(alignment as u64, alignment, alignment) {
|
|
||||||
Ok(b) => b,
|
|
||||||
Err(_) => return false,
|
|
||||||
};
|
|
||||||
abuf.read_from(file).is_ok()
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RawFile {
|
impl RawFile {
|
||||||
pub fn new(file: File, direct_io: bool) -> Self {
|
pub fn new(file: File, direct_io: bool) -> Self {
|
||||||
// Assume no alignment restrictions if we aren't using O_DIRECT.
|
|
||||||
let mut alignment = 0;
|
|
||||||
if direct_io {
|
|
||||||
for align in &BLK_ALIGNMENTS {
|
|
||||||
if is_valid_alignment(&file, *align) {
|
|
||||||
alignment = *align;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
RawFile {
|
RawFile {
|
||||||
file,
|
aligned: AlignedFile::new(file, direct_io),
|
||||||
alignment,
|
|
||||||
position: 0,
|
position: 0,
|
||||||
direct_io,
|
direct_io,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_aligned(&self, buf: &[u8]) -> bool {
|
|
||||||
if self.alignment == 0 {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
let align64: u64 = self.alignment.try_into().unwrap();
|
|
||||||
|
|
||||||
self.position.is_multiple_of(align64)
|
|
||||||
&& (buf.as_ptr() as usize).is_multiple_of(self.alignment)
|
|
||||||
&& buf.len().is_multiple_of(self.alignment)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_len(&self, size: u64) -> io::Result<()> {
|
pub fn set_len(&self, size: u64) -> io::Result<()> {
|
||||||
self.file.set_len(size)
|
self.aligned.file().set_len(size)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn metadata(&self) -> io::Result<Metadata> {
|
pub fn metadata(&self) -> io::Result<Metadata> {
|
||||||
self.file.metadata()
|
self.aligned.file().metadata()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn try_clone(&self) -> io::Result<RawFile> {
|
pub fn try_clone(&self) -> io::Result<RawFile> {
|
||||||
Ok(RawFile {
|
Ok(RawFile {
|
||||||
file: self.file.try_clone().expect("RawFile cloning failed"),
|
aligned: self.aligned.try_clone()?,
|
||||||
alignment: self.alignment,
|
|
||||||
position: self.position,
|
position: self.position,
|
||||||
direct_io: self.direct_io,
|
direct_io: self.direct_io,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sync_all(&self) -> io::Result<()> {
|
pub fn sync_all(&self) -> io::Result<()> {
|
||||||
self.file.sync_all()
|
self.aligned.file().sync_all()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sync_data(&self) -> io::Result<()> {
|
pub fn sync_data(&self) -> io::Result<()> {
|
||||||
self.file.sync_data()
|
self.aligned.file().sync_data()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_direct(&self) -> bool {
|
pub fn is_direct(&self) -> bool {
|
||||||
@@ -102,13 +67,13 @@ impl RawFile {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn alignment(&self) -> usize {
|
pub fn alignment(&self) -> usize {
|
||||||
self.alignment
|
self.aligned.alignment()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns true if the file was opened with write access.
|
/// Returns true if the file was opened with write access.
|
||||||
pub fn is_writable(&self) -> bool {
|
pub fn is_writable(&self) -> bool {
|
||||||
// SAFETY: fcntl with F_GETFL is safe and doesn't modify the file descriptor
|
// SAFETY: fcntl with F_GETFL is safe and doesn't modify the file descriptor
|
||||||
let flags = unsafe { libc::fcntl(self.file.as_raw_fd(), libc::F_GETFL) };
|
let flags = unsafe { libc::fcntl(self.aligned.file().as_raw_fd(), libc::F_GETFL) };
|
||||||
if flags < 0 {
|
if flags < 0 {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -119,92 +84,66 @@ impl RawFile {
|
|||||||
|
|
||||||
impl Read for RawFile {
|
impl Read for RawFile {
|
||||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||||
if buf.is_empty() {
|
let n = self.aligned.read_at(buf, self.position)?;
|
||||||
return Ok(0);
|
self.position += n as u64;
|
||||||
}
|
Ok(n)
|
||||||
|
|
||||||
if self.is_aligned(buf) {
|
|
||||||
match self.file.read(buf) {
|
|
||||||
Ok(r) => {
|
|
||||||
self.position = self.position.checked_add(r.try_into().unwrap()).unwrap();
|
|
||||||
Ok(r)
|
|
||||||
}
|
|
||||||
Err(e) => Err(e),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
let buf_len = buf.len();
|
|
||||||
let mut abuf = AlignedBuffer::new(self.position, buf_len, self.alignment)?;
|
|
||||||
let bytes_read = abuf.read_from(&self.file)?;
|
|
||||||
buf[..bytes_read].copy_from_slice(&abuf.as_slice()[..bytes_read]);
|
|
||||||
self.seek(SeekFrom::Current(bytes_read.try_into().unwrap()))?;
|
|
||||||
Ok(bytes_read)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Write for RawFile {
|
impl Write for RawFile {
|
||||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||||
if buf.is_empty() {
|
let n = self.aligned.write_at(buf, self.position)?;
|
||||||
return Ok(0);
|
self.position += n as u64;
|
||||||
}
|
Ok(n)
|
||||||
|
|
||||||
if self.is_aligned(buf) {
|
|
||||||
match self.file.write(buf) {
|
|
||||||
Ok(r) => {
|
|
||||||
self.position = self.position.checked_add(r.try_into().unwrap()).unwrap();
|
|
||||||
Ok(r)
|
|
||||||
}
|
|
||||||
Err(e) => Err(e),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
let buf_len = buf.len();
|
|
||||||
let mut abuf = AlignedBuffer::new(self.position, buf_len, self.alignment)?;
|
|
||||||
abuf.read_from(&self.file)?;
|
|
||||||
abuf.as_mut_slice().copy_from_slice(buf);
|
|
||||||
abuf.write_to(&self.file)?;
|
|
||||||
self.seek(SeekFrom::Current(buf_len.try_into().unwrap()))?;
|
|
||||||
Ok(buf_len)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn flush(&mut self) -> io::Result<()> {
|
fn flush(&mut self) -> io::Result<()> {
|
||||||
self.file.sync_all()
|
self.aligned.file().sync_all()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Seek for RawFile {
|
impl Seek for RawFile {
|
||||||
fn seek(&mut self, newpos: SeekFrom) -> io::Result<u64> {
|
fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
|
||||||
match self.file.seek(newpos) {
|
let newpos = match pos {
|
||||||
Ok(pos) => {
|
SeekFrom::Start(o) => o,
|
||||||
self.position = pos;
|
SeekFrom::Current(d) => self
|
||||||
Ok(pos)
|
.position
|
||||||
}
|
.checked_add_signed(d)
|
||||||
Err(e) => Err(e),
|
.ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, "invalid seek"))?,
|
||||||
}
|
SeekFrom::End(d) => self
|
||||||
|
.aligned
|
||||||
|
.file()
|
||||||
|
.metadata()?
|
||||||
|
.len()
|
||||||
|
.checked_add_signed(d)
|
||||||
|
.ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, "invalid seek"))?,
|
||||||
|
};
|
||||||
|
self.position = newpos;
|
||||||
|
Ok(newpos)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WriteZeroesAt for RawFile {
|
impl WriteZeroesAt for RawFile {
|
||||||
fn write_zeroes_at(&mut self, offset: u64, length: usize) -> io::Result<usize> {
|
fn write_zeroes_at(&mut self, offset: u64, length: usize) -> io::Result<usize> {
|
||||||
self.file.write_zeroes_at(offset, length)
|
self.aligned.file_mut().write_zeroes_at(offset, length)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PunchHole for RawFile {
|
impl PunchHole for RawFile {
|
||||||
fn punch_hole(&mut self, offset: u64, length: u64) -> io::Result<()> {
|
fn punch_hole(&mut self, offset: u64, length: u64) -> io::Result<()> {
|
||||||
self.file.punch_hole(offset, length)
|
self.aligned.file_mut().punch_hole(offset, length)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FileSync for RawFile {
|
impl FileSync for RawFile {
|
||||||
fn fsync(&mut self) -> io::Result<()> {
|
fn fsync(&mut self) -> io::Result<()> {
|
||||||
self.file.fsync()
|
self.aligned.file_mut().fsync()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SeekHole for RawFile {
|
impl SeekHole for RawFile {
|
||||||
fn seek_hole(&mut self, offset: u64) -> io::Result<Option<u64>> {
|
fn seek_hole(&mut self, offset: u64) -> io::Result<Option<u64>> {
|
||||||
match self.file.seek_hole(offset) {
|
match self.aligned.file_mut().seek_hole(offset) {
|
||||||
Ok(pos) => {
|
Ok(pos) => {
|
||||||
if let Some(p) = pos {
|
if let Some(p) = pos {
|
||||||
self.position = p;
|
self.position = p;
|
||||||
@@ -216,7 +155,7 @@ impl SeekHole for RawFile {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn seek_data(&mut self, offset: u64) -> io::Result<Option<u64>> {
|
fn seek_data(&mut self, offset: u64) -> io::Result<Option<u64>> {
|
||||||
match self.file.seek_data(offset) {
|
match self.aligned.file_mut().seek_data(offset) {
|
||||||
Ok(pos) => {
|
Ok(pos) => {
|
||||||
if let Some(p) = pos {
|
if let Some(p) = pos {
|
||||||
self.position = p;
|
self.position = p;
|
||||||
@@ -230,13 +169,13 @@ impl SeekHole for RawFile {
|
|||||||
|
|
||||||
impl BlockBackend for RawFile {
|
impl BlockBackend for RawFile {
|
||||||
fn logical_size(&self) -> result::Result<u64, crate::Error> {
|
fn logical_size(&self) -> result::Result<u64, crate::Error> {
|
||||||
Ok(query_device_size(&self.file)
|
Ok(query_device_size(self.aligned.file())
|
||||||
.map_err(crate::Error::RawFileError)?
|
.map_err(crate::Error::RawFileError)?
|
||||||
.0)
|
.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn physical_size(&self) -> result::Result<u64, crate::Error> {
|
fn physical_size(&self) -> result::Result<u64, crate::Error> {
|
||||||
Ok(query_device_size(&self.file)
|
Ok(query_device_size(self.aligned.file())
|
||||||
.map_err(crate::Error::RawFileError)?
|
.map_err(crate::Error::RawFileError)?
|
||||||
.1)
|
.1)
|
||||||
}
|
}
|
||||||
@@ -245,8 +184,7 @@ impl BlockBackend for RawFile {
|
|||||||
impl Clone for RawFile {
|
impl Clone for RawFile {
|
||||||
fn clone(&self) -> Self {
|
fn clone(&self) -> Self {
|
||||||
RawFile {
|
RawFile {
|
||||||
file: self.file.try_clone().expect("RawFile cloning failed"),
|
aligned: self.aligned.try_clone().expect("RawFile cloning failed"),
|
||||||
alignment: self.alignment,
|
|
||||||
position: self.position,
|
position: self.position,
|
||||||
direct_io: self.direct_io,
|
direct_io: self.direct_io,
|
||||||
}
|
}
|
||||||
@@ -255,23 +193,23 @@ impl Clone for RawFile {
|
|||||||
|
|
||||||
impl AsRawFd for RawFile {
|
impl AsRawFd for RawFile {
|
||||||
fn as_raw_fd(&self) -> RawFd {
|
fn as_raw_fd(&self) -> RawFd {
|
||||||
self.file.as_raw_fd()
|
self.aligned.file().as_raw_fd()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AsFd for RawFile {
|
impl AsFd for RawFile {
|
||||||
fn as_fd(&self) -> BorrowedFd<'_> {
|
fn as_fd(&self) -> BorrowedFd<'_> {
|
||||||
self.file.as_fd()
|
self.aligned.file().as_fd()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FileExt for RawFile {
|
impl FileExt for RawFile {
|
||||||
fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
|
fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
|
||||||
self.file.read_at(buf, offset)
|
self.aligned.read_at(buf, offset)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
|
fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
|
||||||
self.file.write_at(buf, offset)
|
self.aligned.write_at(buf, offset)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -296,19 +234,11 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn raw_with_alignment(file: File) -> RawFile {
|
fn raw_with_alignment(file: File) -> RawFile {
|
||||||
RawFile {
|
// A tempfile is not O_DIRECT, but its aligned probe read still
|
||||||
file,
|
// succeeds, so RawFile::new selects the smallest candidate (512).
|
||||||
alignment: TEST_ALIGNMENT,
|
let raw = RawFile::new(file, true);
|
||||||
position: 0,
|
assert_eq!(raw.alignment(), TEST_ALIGNMENT);
|
||||||
direct_io: true,
|
raw
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_alignment_probe_accepts_short_read() {
|
|
||||||
let tf = create_pattern_file(1);
|
|
||||||
|
|
||||||
assert!(is_valid_alignment(tf.as_file(), TEST_ALIGNMENT));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@@ -112,6 +112,7 @@ fn virtio_block_thread_rules() -> Vec<(i64, Vec<SeccompRule>)> {
|
|||||||
(libc::SYS_fallocate, vec![]),
|
(libc::SYS_fallocate, vec![]),
|
||||||
(libc::SYS_fcntl, vec![]),
|
(libc::SYS_fcntl, vec![]),
|
||||||
(libc::SYS_fdatasync, vec![]),
|
(libc::SYS_fdatasync, vec![]),
|
||||||
|
(libc::SYS_fstat, vec![]),
|
||||||
(libc::SYS_fsync, vec![]),
|
(libc::SYS_fsync, vec![]),
|
||||||
(libc::SYS_ftruncate, vec![]),
|
(libc::SYS_ftruncate, vec![]),
|
||||||
(libc::SYS_getrandom, vec![]),
|
(libc::SYS_getrandom, vec![]),
|
||||||
@@ -121,6 +122,7 @@ fn virtio_block_thread_rules() -> Vec<(i64, Vec<SeccompRule>)> {
|
|||||||
(libc::SYS_io_submit, vec![]),
|
(libc::SYS_io_submit, vec![]),
|
||||||
(libc::SYS_io_uring_enter, vec![]),
|
(libc::SYS_io_uring_enter, vec![]),
|
||||||
(libc::SYS_lseek, vec![]),
|
(libc::SYS_lseek, vec![]),
|
||||||
|
(libc::SYS_newfstatat, vec![]),
|
||||||
(libc::SYS_pread64, vec![]),
|
(libc::SYS_pread64, vec![]),
|
||||||
(libc::SYS_preadv, vec![]),
|
(libc::SYS_preadv, vec![]),
|
||||||
(libc::SYS_pwritev, vec![]),
|
(libc::SYS_pwritev, vec![]),
|
||||||
@@ -128,6 +130,7 @@ fn virtio_block_thread_rules() -> Vec<(i64, Vec<SeccompRule>)> {
|
|||||||
(libc::SYS_sched_getaffinity, vec![]),
|
(libc::SYS_sched_getaffinity, vec![]),
|
||||||
(libc::SYS_sched_setaffinity, vec![]),
|
(libc::SYS_sched_setaffinity, vec![]),
|
||||||
(libc::SYS_set_robust_list, vec![]),
|
(libc::SYS_set_robust_list, vec![]),
|
||||||
|
(libc::SYS_statx, vec![]),
|
||||||
(libc::SYS_timerfd_settime, vec![]),
|
(libc::SYS_timerfd_settime, vec![]),
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user