mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2026-08-05 02:19:16 +00:00
block: qcow: Extract positional I/O helpers into a common module
These position independent I/O helpers use pread64/pwrite64 to avoid races on the shared file position when multiple queues operate on duplicated file descriptors. Extracting them prepares for reuse by the upcoming qcow_async backend. Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
This commit is contained in:
committed by
Rob Bradford
parent
73680c38c7
commit
7f3dfe2154
@@ -18,6 +18,7 @@ pub mod fixed_vhd;
|
||||
pub mod fixed_vhd_async;
|
||||
pub mod fixed_vhd_sync;
|
||||
pub mod qcow;
|
||||
pub(crate) mod qcow_common;
|
||||
pub mod qcow_sync;
|
||||
#[cfg(feature = "io_uring")]
|
||||
/// Async primitives based on `io-uring`
|
||||
|
||||
67
block/src/qcow_common.rs
Normal file
67
block/src/qcow_common.rs
Normal file
@@ -0,0 +1,67 @@
|
||||
// Copyright © 2021 Intel Corporation
|
||||
//
|
||||
// Copyright 2026 The Cloud Hypervisor Authors. All rights reserved.
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
|
||||
|
||||
//! Shared helpers for QCOW2 sync and async backends.
|
||||
//!
|
||||
//! Position-independent I/O (`pread_exact`, `pwrite_all`) and iovec
|
||||
//! scatter/gather helpers used by both `qcow_sync` and `qcow_async`.
|
||||
|
||||
use std::io;
|
||||
use std::os::fd::RawFd;
|
||||
|
||||
// -- Position independent I/O helpers --
|
||||
//
|
||||
// Duplicated file descriptors share the kernel file description and thus the
|
||||
// file position. Using seek then read from multiple queues races on that
|
||||
// shared position. pread64 and pwrite64 are atomic and never touch the position.
|
||||
|
||||
/// Read exactly the requested bytes at offset, looping on short reads.
|
||||
pub fn pread_exact(fd: RawFd, buf: &mut [u8], offset: u64) -> io::Result<()> {
|
||||
let mut total = 0usize;
|
||||
while total < buf.len() {
|
||||
// SAFETY: buf and fd are valid for the lifetime of the call.
|
||||
let ret = unsafe {
|
||||
libc::pread64(
|
||||
fd,
|
||||
buf[total..].as_mut_ptr() as *mut libc::c_void,
|
||||
buf.len() - total,
|
||||
(offset + total as u64) as libc::off_t,
|
||||
)
|
||||
};
|
||||
if ret < 0 {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
if ret == 0 {
|
||||
return Err(io::Error::from(io::ErrorKind::UnexpectedEof));
|
||||
}
|
||||
total += ret as usize;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Write all bytes to fd at offset, looping on short writes.
|
||||
pub fn pwrite_all(fd: RawFd, buf: &[u8], offset: u64) -> io::Result<()> {
|
||||
let mut total = 0usize;
|
||||
while total < buf.len() {
|
||||
// SAFETY: buf and fd are valid for the lifetime of the call.
|
||||
let ret = unsafe {
|
||||
libc::pwrite64(
|
||||
fd,
|
||||
buf[total..].as_ptr() as *const libc::c_void,
|
||||
buf.len() - total,
|
||||
(offset + total as u64) as libc::off_t,
|
||||
)
|
||||
};
|
||||
if ret < 0 {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
if ret == 0 {
|
||||
return Err(io::Error::other("pwrite64 wrote 0 bytes"));
|
||||
}
|
||||
total += ret as usize;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -5,7 +5,7 @@
|
||||
use std::cmp::min;
|
||||
use std::collections::VecDeque;
|
||||
use std::fs::File;
|
||||
use std::os::fd::{AsFd, AsRawFd, BorrowedFd, OwnedFd, RawFd};
|
||||
use std::os::fd::{AsFd, AsRawFd, BorrowedFd, OwnedFd};
|
||||
use std::sync::Arc;
|
||||
use std::{fmt, io, ptr, slice};
|
||||
|
||||
@@ -22,6 +22,7 @@ use crate::qcow::qcow_raw_file::QcowRawFile;
|
||||
use crate::qcow::{
|
||||
BackingFile, BackingKind, Error as QcowError, MAX_NESTING_DEPTH, RawFile, parse_qcow,
|
||||
};
|
||||
use crate::qcow_common::{pread_exact, pwrite_all};
|
||||
|
||||
/// Raw backing file using pread64 on a duplicated fd.
|
||||
struct RawBacking {
|
||||
@@ -322,64 +323,6 @@ impl QcowSync {
|
||||
}
|
||||
}
|
||||
|
||||
// -- Position independent I/O helpers --
|
||||
//
|
||||
// Duplicated file descriptors share the kernel file description and thus the
|
||||
// file position. Using seek then read from multiple queues races on that
|
||||
// shared position. pread64 and pwrite64 are atomic and never touch the position.
|
||||
|
||||
/// Read exactly the requested bytes at offset, looping on short reads.
|
||||
fn pread_exact(fd: RawFd, buf: &mut [u8], offset: u64) -> io::Result<()> {
|
||||
let mut total = 0usize;
|
||||
while total < buf.len() {
|
||||
// SAFETY: buf and fd are valid for the lifetime of the call.
|
||||
let ret = unsafe {
|
||||
libc::pread64(
|
||||
fd,
|
||||
buf[total..].as_mut_ptr() as *mut libc::c_void,
|
||||
buf.len() - total,
|
||||
(offset + total as u64) as libc::off_t,
|
||||
)
|
||||
};
|
||||
if ret < 0 {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
if ret == 0 {
|
||||
return Err(io::Error::from(io::ErrorKind::UnexpectedEof));
|
||||
}
|
||||
total += ret as usize;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Write all bytes to fd at offset, looping on short writes.
|
||||
fn pwrite_all(fd: RawFd, buf: &[u8], offset: u64) -> io::Result<()> {
|
||||
let mut total = 0usize;
|
||||
while total < buf.len() {
|
||||
// SAFETY: buf and fd are valid for the lifetime of the call.
|
||||
let ret = unsafe {
|
||||
libc::pwrite64(
|
||||
fd,
|
||||
buf[total..].as_ptr() as *const libc::c_void,
|
||||
buf.len() - total,
|
||||
(offset + total as u64) as libc::off_t,
|
||||
)
|
||||
};
|
||||
if ret < 0 {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
if ret == 0 {
|
||||
return Err(io::Error::other("pwrite64 wrote 0 bytes"));
|
||||
}
|
||||
total += ret as usize;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// -- iovec helper functions --
|
||||
//
|
||||
// Operate on the iovec array as a flat byte stream.
|
||||
|
||||
/// Copy data into iovecs starting at the given byte offset.
|
||||
///
|
||||
/// # Safety
|
||||
|
||||
Reference in New Issue
Block a user