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:
Anatol Belski
2026-03-20 13:52:04 +01:00
committed by Rob Bradford
parent 73680c38c7
commit 7f3dfe2154
3 changed files with 70 additions and 59 deletions
+67
View 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(())
}