diff --git a/block/src/lib.rs b/block/src/lib.rs index 6d093daca..ad25f4593 100644 --- a/block/src/lib.rs +++ b/block/src/lib.rs @@ -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` diff --git a/block/src/qcow_common.rs b/block/src/qcow_common.rs new file mode 100644 index 000000000..f11831425 --- /dev/null +++ b/block/src/qcow_common.rs @@ -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(()) +} diff --git a/block/src/qcow_sync.rs b/block/src/qcow_sync.rs index 7340a7aa4..cde534314 100644 --- a/block/src/qcow_sync.rs +++ b/block/src/qcow_sync.rs @@ -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