Commit Graph

530 Commits

Author SHA1 Message Date
Rob Bradford
7e2e7a164b block: Drain io_uring in-flight operations on teardown
Closing an io_uring fd does not synchronously finish requests that
already reached the kernel. During block worker teardown this can let
an io-wq worker keep using retained guest-memory iovecs after reset.

Drain UringDataIo in Drop: retry any published SQEs and wait for CQEs
until no retained operation remains. If draining fails, leak retained
buffers. Drop QcowAsync's ring before its data fd so retrying
published SQEs still uses a valid descriptor.

To avoid a potential infinite loop when completions fail to be delivered
cap the number of iterations of the loop (2x the number of inflight
requests).

Assisted-by: Codex:GPT-5
Signed-off-by: Rob Bradford <rbradford@meta.com>
2026-06-18 17:35:27 +00:00
Henry Hrvoje Tonkovac
6683ae2d51 block: trim qualified paths in vhdx tests
Import the std modules used in the test module instead of spelling the
full paths at every use site, and drop the now-unnecessary
#[expect(clippy::absolute_paths)] on the vhdx internal test module.

Signed-off-by: Henry Hrvoje Tonkovac <htonkovac@gmail.com>
Assisted-by: Claude:Opus-4.8
2026-06-17 17:14:44 +00:00
Tushar Khatri
7be97937ef block: reevaluate #[allow] attributes
Convert the still-needed #[allow]s to #[expect] so they warn if the
lints stop firing.

Part of #8326.

Signed-off-by: Tushar Khatri <hello@tusharkhatri.in>
2026-06-17 17:14:00 +00:00
Alexander Lvov
e0801bda3b block: vhd: fix incomplete bounds check in sync I/O worker
The sync I/O worker only checked that the operation offset did
not start past the end of the virtual disk (offset >= size) -
did not verify that the operation end (offset + len) stays
within bounds.

A read or write that started inside the image but extended
beyond the logical size was silently passed to the raw backend.

The async io_uring worker already had the correct check
(offset + len > size with overflow protection). I extracted it
into a shared helper in worker/common.rs and reused inside the
sync path to eliminate duplication and close the gap.

Fixes #8311

Signed-off-by: Alexander Lvov <alexander.lvov.git@gmail.com>
2026-06-17 14:26:25 +00:00
Rob Bradford
2bc968ba1d build: Deny clippy::absolute_paths
Removal of absolute paths is currently in progress. To avoid regressing
those changes add a clippy deny at the workspace level and at the crate
level override with #[expect(clippy::absolute_paths)]

See: #7670

Signed-off-by: Rob Bradford <rbradford@meta.com>
2026-06-17 14:38:25 +01:00
Pascal Scholz
257a00547a block: Retry locking when interrupted by EINTR
Acquiring an image lock can be interrupted with EINTR. In this case, we
returned with an error. Instead, we now retry acquiring the lock.

Signed-off-by: Pascal Scholz <pascal.scholz@cyberus-technology.de>
On-behalf-of: SAP pascal.scholz@sap.com
2026-06-17 08:44:50 +00:00
Rob Bradford
b4c1d85327 block: vhdx: Use AlignedFile for O_DIRECT-safe I/O
By redirecting VHDx I/O through the AlignedFile the required RMW
semantics can be achieved for writes less than the logical block size
whilt reusing the same logic used for other backend implementations.

Assisted-by: Claude:Opus-4.8
Signed-off-by: Rob Bradford <rbradford@meta.com>
2026-06-16 14:43:11 +00:00
Rob Bradford
80a0393edd 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>
2026-06-16 14:43:11 +00:00
Rob Bradford
3fe5225a44 block: Add AlignedFile
Provide a single home for O_DIRECT alignment and RMW behavior behind an
std::os::unix::fs::FileExt implementation built on AlignedBuffer.

Unaligned requests are bounced through an AlignedBuffer (applying RMW
for writes) and aligned requests pass straight through to the inner
File.

Assisted-by: Claude:Opus-4.8
Signed-off-by: Rob Bradford <rbradford@meta.com>
2026-06-16 14:43:11 +00:00
Rob Bradford
201ddaef55 block: qcow: Port to FileExt
Replace the use of the pread64/pwrite64 helpers with versions from
std::os::unix::fs::FileExt.

As this was the last use of these pread functions remove them and their
tests.

Assisted-by: Claude:Opus-4.6
Signed-off-by: Rob Bradford <rbradford@meta.com>
2026-06-16 14:43:11 +00:00
Rob Bradford
108d251c1d block: qcow: Port to AlignedBuffer
Replace the qcow specific AlignedBuf along with the pread64/pwrite64
helpers with the new common AlignedBuffer implementation.

Assisted-by: Claude:Opus-4.6
Signed-off-by: Rob Bradford <rbradford@meta.com>
2026-06-16 14:43:11 +00:00
Rob Bradford
2e2167368e block: qcow: Port backing file support to FileExt
Replace use of raw pread64/pwrite64 functions with
std::os::unix::fs::FileExt for I/O without a cursor.

Assisted-by: Claude:Opus-4.6
Signed-off-by: Rob Bradford <rbradford@meta.com>
2026-06-16 14:43:11 +00:00
Rob Bradford
30e79de26f block: qcow: Port RawFile to AlignedBuffer
Replace the manual alloc_zeroed/dealloc and pread64/pwrite64 with use of
the new AlignedBuffer structure.

Assisted-by: Claude:Opus-4.6
Signed-off-by: Rob Bradford <rbradford@meta.com>
2026-06-16 14:43:11 +00:00
Rob Bradford
4ad95e1cc6 block: Add AlignedBuffer
The block code repeatedly open-codes O_DIRECT alignment calculations and
bounce-buffer allocation at each I/O site. Add an AlignedBuffer struct
that handles the alignment and allocation in one place, using FileExt
(read_exact_at and write_all_at) for the I/O (so no need for custom libc
wrappers).

The caller creates an AlignedBuffer with an offset, length and
alignment, then uses read_from and write_to for aligned I/O and as_slice
and as_mut_slice to access the logical data portion within the aligned
region.

This is a lot like the AlignedBuf that was already existing in the QCOW2
code but is a more generalised version.

Assisted-by: Claude:Opus-4.6
Signed-off-by: Rob Bradford <rbradford@meta.com>
2026-06-16 14:43:11 +00:00
Rob Bradford
a8b059328f block: Implement FileExt for RawFile
Implement std::os::unix::fs::FileExt for RawFile by delegating to the
inner File. This enables callers holding a reference to a RawFile to use
read_exact_at and write_all_at directly for non-cursor I/O (like pread,
etc) without going through custom helper functions.

Assisted-by: Claude:Opus-4.6
Signed-off-by: Rob Bradford <rbradford@meta.com>
2026-06-16 14:43:11 +00:00
Henry Hrvoje Tonkovac
e5f32e986f block: trim qualified paths in io and lib
Import the std modules used in the crate instead of spelling the full
paths at every use site.

Signed-off-by: Henry Hrvoje Tonkovac <htonkovac@gmail.com>
Assisted-by: Claude:Opus-4.8
2026-06-16 10:52:57 +00:00
Anatol Belski
0e4e3277a9 block: Add WriteZeroes sector overflow regression test
Cover the prior commit by constructing a Request directly and a stub
AsyncIo whose backend methods are unreachable, then submit a payload
with sector + num_sectors past u64::MAX and assert BadRequest.

Assisted-by: Claude:Opus-4.7
Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
2026-06-15 13:54:39 +01:00
Anatol Belski
f7ebb4b871 block: Bounds check WriteZeroes before sector multiplication
In Request::execute_async the WriteZeroes arm multiplied wz_sector
by SECTOR_SIZE before the checked_add of sector and num_sectors.
A wz_sector near u64::MAX overflows the multiplication.

Reorder the arm to run the checked_add and disk_nsectors check
first, matching the Discard arm above.

Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
2026-06-15 13:54:39 +01:00
Henry Hrvoje Tonkovac
2c22159802 block: trim qualified paths in formats
Import the std modules used in the disk-format handlers instead of
spelling the full paths at every use site.

Signed-off-by: Henry Hrvoje Tonkovac <htonkovac@gmail.com>
Assisted-by: Claude:Opus-4.8
2026-06-15 08:22:19 +00:00
Rob Bradford
829676e640 block: qcow: Delete QcowFile
Delete the now unused QcowFile implementation it was only used for
creating disk images for the tests and for the performance-metrics. It
was not used for the virtio-block device.

Assisted-by: Claude:claude-opus-4-7
Signed-off-by: Rob Bradford <rbradford@meta.com>
2026-06-08 13:19:32 +00:00
Rob Bradford
1120fe74f5 block: qcow: Extract rebuild_refcounts() function
Pull rebuild_refcounts out of QcowFile so QcowFile can be removed in a
follow up commit. This function is still required by parse_qcow().

Assisted-by: Claude:claude-opus-4-7
Signed-off-by: Rob Bradford <rbradford@meta.com>
2026-06-08 13:19:32 +00:00
Rob Bradford
9da113ff1f block: qcow: Port internal tests to QcowDisk
Port the internal/mod.rs tests from QcowFile to QcowDisk so
the surviving tests exercise the code paths that are hit when used via
QcowDisk (and thus virtio-block).

Assisted-by: Claude:claude-opus-4-7
Signed-off-by: Rob Bradford <rbradford@meta.com>
2026-06-08 13:19:32 +00:00
Rob Bradford
34e8e3dbf9 performance-metrics: Switch to QcowDisk
Switch from QcowFile to QcowDisk taking advantage of QcowTempDisk where
appropriate.

Assisted-by: Claude:claude-opus-4-7
Signed-off-by: Rob Bradford <rbradford@meta.com>
2026-06-08 13:19:32 +00:00
Rob Bradford
cfce14edd1 block: qcow: Port tests to QcowTempDisk
Port tests over to QcowTempDisk and also over to QcowDisk rather than
QcowFile where necessary.

Assisted-by: Claude:claude-opus-4-7
Signed-off-by: Rob Bradford <rbradford@meta.com>
2026-06-08 13:19:32 +00:00
Rob Bradford
31ee5e99e4 block: qcow: Add QcowTempDisk helper
A common pattern in the test code is to create a temporary file, format
it as QCOW2 and then open it as a QcowDisk. Create a helper struct that
can be used in those tests. This is marked as #[cfg(test)] as initially
it will only be used by the test suite.

Assisted-by: Claude:claude-opus-4-7
Signed-off-by: Rob Bradford <rbradford@meta.com>
2026-06-08 13:19:32 +00:00
Rob Bradford
b3cf8a84cd block: qcow: Add qcow::create_image()
Add a method to format a file as a QCOW2 file which will mainly be used
by the test infrastructure. This copies the logic from QcowFile. It
doesn't refactor it as the removal of QcowFile is planned.

Assisted-by: Claude:claude-opus-4-7
Signed-off-by: Rob Bradford <rbradford@meta.com>
2026-06-08 13:19:32 +00:00
Philipp Schuster
e9fa6e6295 block: remove unneeded #[allow(dead_code)]
On-behalf-of: SAP philipp.schuster@sap.com
Signed-off-by: Philipp Schuster <philipp.schuster@cyberus-technology.de>
2026-06-03 13:56:31 +00:00
Philipp Schuster
1c484e8725 block: streamline inclusion of test-only code
On-behalf-of: SAP philipp.schuster@sap.com
Signed-off-by: Philipp Schuster <philipp.schuster@cyberus-technology.de>
2026-06-03 13:56:31 +00:00
Anatol Belski
362a9ecc4f block: qcow: Test rejection of backing file offset with zero size
Cover the malformed header case where backing_file_offset is non
zero but backing_file_size is zero, which must be rejected with the
new dedicated error.

Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
2026-06-01 16:27:50 +00:00
Anatol Belski
679892f56e block: qcow: Test rejection of backing file size with zero offset
Cover the malformed header case where backing_file_offset is zero
but backing_file_size is non zero, which must be rejected with the
new dedicated error.

Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
2026-06-01 16:27:50 +00:00
Anatol Belski
05cac5657c block: qcow: Test rejection when backing file overlaps header
Cover the case where backing_file_offset points inside the fixed
header fields, which the new header overlap check must reject.

Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
2026-06-01 16:27:50 +00:00
Anatol Belski
9c85aab85d block: qcow: Test backing file fitting exactly at cluster end
Cover the boundary positive case where backing_file_offset plus
backing_file_size equals the cluster size, which the spec allows
and the new bound check must accept.

Assisted-by: Claude:Opus-4.7
Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
2026-06-01 16:27:50 +00:00
Anatol Belski
cdd7220384 block: qcow: Test rejection when backing file end exceeds cluster
Cover the case where backing_file_offset lies inside the first
cluster but backing_file_offset + backing_file_size crosses the
cluster boundary, so the end of the name spills outside.

Assisted-by: Claude:Opus-4.7
Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
2026-06-01 16:27:50 +00:00
Anatol Belski
a4e8d79650 block: qcow: Test rejection when backing file offset exceeds cluster
Cover an offset that lies well past the end of the first cluster
to make sure the bound check fires for arbitrary out of range
offsets rather than only the boundary case.

Assisted-by: Claude:Opus-4.7
Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
2026-06-01 16:27:50 +00:00
Anatol Belski
2d8811ad82 block: qcow: Test rejection when backing file offset equals cluster size
A backing file string placed exactly at cluster_size starts past
the first cluster boundary, so QcowHeader::new must reject it.

Introduce a read_header_with_patched_backing helper that builds
a valid header, patches backing_file_offset and backing_file_size,
writes it out and re-parses it. Use it to cover this case.

Assisted-by: Claude:Opus-4.7
Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
2026-06-01 16:27:50 +00:00
Anatol Belski
53a9ae08c2 block: qcow: Reject backing file offset with zero size
A qcow2 header with non-zero backing_file_offset that points at a
zero length name is malformed. The parser would otherwise read an
empty path string and store it as a backing file. Reject it with a
dedicated error so the user gets a clear diagnostic.

Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
2026-06-01 16:27:50 +00:00
Anatol Belski
39e9376f5b block: qcow: Reject backing file size with zero offset
A qcow2 header with backing_file_offset == 0 indicates the image
has no backing file, so any non-zero backing_file_size is malformed.
Qemu silently ignores the size in this case, which hides image
corruption. Reject it explicitly with a dedicated error so the user
gets a clear diagnostic.

Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
2026-06-01 16:27:50 +00:00
Anatol Belski
4294a4b862 block: qcow: Reject backing file name overlapping the header
The qcow2 spec requires the backing file name string to live in the
remaining space between the end of the header extension area and
the end of the first cluster. Header parsing accepted any backing
file offset above zero, including offsets pointing into the fixed
header fields themselves, so a corrupt or malicious image could
redirect the parser into reinterpreting header bytes as the backing
path.

Reject any backing_file_offset that is less than header.header_size
via a new BackingFileOverlapsHeader error. The check fires before
the existing first cluster bound, since an overlap is a different
class of corruption and deserves a distinct diagnostic.

Ref: #8261
Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
2026-06-01 16:27:50 +00:00
Anatol Belski
4a2a9390be block: qcow: Reject backing file name outside first cluster
The qcow2 spec requires the backing file name string to live
entirely within the first cluster, between the end of the header
extension area and the cluster boundary. The parser previously
only validated the 1023 byte cap on the name length and accepted
any backing_file_offset, so a corrupt or malicious image could
place the name string anywhere in the file.

Add the cluster bound check in QcowHeader::new and report it via
a new BackingFileOutsideFirstCluster error.

Fixes: #8261

Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
2026-06-01 16:27:50 +00:00
Ian Klemm
a667d85055 block: qcow: reject out-of-bounds cluster offsets
Standard L2 data offsets and L1-referenced L2 table offsets must be
aligned and covered by the current refcount table. The write path
checked both constraints at one call site, while read, cache population,
and deallocation paths only checked alignment or relied on later
refcount lookup errors.

Centralize the validation in QcowState and use it before reading L2
tables, mapping standard L2 entries for reads and writes, and
deallocating existing clusters. Invalid offsets set the corrupt bit and
fail with EIO before data I/O or refcount updates.

Add QcowSync regression tests that corrupt a standard L2 entry past the
refcount-addressable range and verify that reads and writes fail with
EIO and mark the header corrupt.

Assisted-by: Codex:GPT-5
Signed-off-by: Ian Klemm <hi@ianklemm.de>
2026-05-29 15:22:57 +00:00
Ian Klemm
17b6fd91ca block: qcow: decouple pointer table writes from cursor state
write_pointer_table() used a BufWriter over a cloned fd because the
per-entry callback also needs mutable access to QcowRawFile.

That couples the final write location to the ambient kernel cursor while
the callback is allowed to perform metadata I/O. Materialize the encoded
entries first, then seek and write the table after callback execution
has finished.

This keeps the pointer-table write independent from current and future
callback behavior without depending on proving that a cursor-moving
callback is reachable in today's synchronous CH path.

Apply the same materialize-then-write shape to
write_pointer_table_direct() for consistent semantics, and cover both
paths with unit tests.

Assisted-by: Codex:GPT-5
Signed-off-by: Ian Klemm <hi@ianklemm.de>
2026-05-29 15:22:57 +00:00
Anatol Belski
4192f5101b block: Validate descriptor buffer ranges via checked iterator
Switch Request::parse over to the CheckedDescriptorIter helper from
vm-virtio so the block crate validates each descriptor's translated
(addr, len) range against guest memory through the same centralized
path used by virtio-devices. Any descriptor whose buffer is not fully
backed by guest RAM is now rejected before any I/O is set up against
it.

Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
2026-05-28 21:41:45 +00:00
Anatol Belski
c80aaa1b58 block: Remove vhdx compat alias
Re-export Vhdx from formats::vhdx and update the fuzz target to
use block::formats::vhdx::Vhdx. Remove the vhdx compat alias
from lib.rs.

Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
2026-05-28 11:58:58 +00:00
Anatol Belski
2c8978bbf2 block: Remove qcow compat alias
Update external consumers to use formats::qcow::internal instead
of the top level qcow alias. Keep a crate private use for the
QcowError variant in lib.rs.

Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
2026-05-28 11:58:58 +00:00
Anatol Belski
f8327faa33 block: Remove raw_disk compat alias
Update external consumers and internal test modules to use
formats::raw instead of the raw_disk alias, then remove the
re-export from lib.rs.

Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
2026-05-28 11:58:58 +00:00
Anatol Belski
25afb8898c block: Move QCOW2 format files into formats/qcow/
Move QCOW2 format implementation into a structured directory layout:

  qcow/              -> formats/qcow/internal/  (filenames unchanged)
  qcow_disk.rs       -> formats/qcow/mod.rs               (QcowDisk)
  qcow_sync.rs       -> formats/qcow/worker/sync.rs       (QcowSync)
  qcow_async.rs      -> formats/qcow/worker/async_uring.rs (QcowAsync)
  qcow_common.rs     -> formats/qcow/common.rs

All internal cross references continue to resolve through
re-exports in lib.rs: formats::qcow::internal as qcow,
formats::qcow as qcow_disk, and
formats::qcow::common as qcow_common.

Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
2026-05-28 11:58:58 +00:00
Anatol Belski
b595f1dbc3 block: Move VHDX format files into formats/vhdx/
Move VHDX format implementation into a structured directory layout:

  vhdx/mod.rs        -> formats/vhdx/internal/mod.rs      (Vhdx)
  vhdx/vhdx_bat.rs   -> formats/vhdx/internal/bat.rs
  vhdx/vhdx_header.rs -> formats/vhdx/internal/header.rs
  vhdx/vhdx_io.rs    -> formats/vhdx/internal/io.rs
  vhdx/vhdx_metadata.rs -> formats/vhdx/internal/metadata.rs
  vhdx_sync.rs       -> formats/vhdx/mod.rs               (VhdxDisk)

Extract VhdxSync from vhdx_sync.rs into formats/vhdx/worker/sync.rs.
Drop the vhdx_ prefix from internal file names since the parent
directory already provides the namespace. Update all internal cross
references to use the new module paths. Re-export formats::vhdx as
vhdx_sync in lib.rs for backward compatibility.

Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
2026-05-28 11:58:58 +00:00
Anatol Belski
1ca0c39b4d block: Move VHD format files into formats/vhd/
Move VHD format implementation into a structured directory layout:

  fixed_vhd.rs       -> formats/vhd/internal/fixed.rs     (FixedVhd)
  fixed_vhd_disk.rs  -> formats/vhd/mod.rs                (VhdDisk)
  vhd.rs             -> formats/vhd/internal/footer.rs     (VhdFooter)
  fixed_vhd_sync.rs  -> formats/vhd/worker/sync.rs         (FixedVhdSync)
  fixed_vhd_async.rs -> formats/vhd/worker/async_uring.rs  (FixedVhdAsync)

Add #[allow(dead_code)] to VhdFooter struct and impl because the
module is now pub(crate) and the compiler can see that several
fields and getters are only exercised by unit tests. Re-export
formats::vhd as fixed_vhd_disk in lib.rs for backward
compatibility with external consumers.

Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
2026-05-28 11:58:58 +00:00
Anatol Belski
a11f551572 block: Move raw format files into formats/raw/
Move raw format implementation into a structured directory layout:

  raw_disk.rs        -> formats/raw/mod.rs                (RawDisk)
  raw_sync.rs        -> formats/raw/worker/sync.rs        (RawSync)
  raw_async.rs       -> formats/raw/worker/async_uring.rs (RawAsync)
  raw_async_aio.rs   -> formats/raw/worker/async_aio.rs   (RawAio)
  raw_async_io_tests.rs -> formats/raw/worker/tests.rs

Update imports in fixed_vhd_sync.rs and fixed_vhd_async.rs to use
the new paths. Re-export formats::raw as raw_disk in lib.rs to
preserve the external API.

Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
2026-05-28 11:58:58 +00:00
Anatol Belski
b68349f8b3 block: Create io/ module for shared I/O infrastructure
Move async_io.rs, fcntl.rs, and request.rs into block/src/io/. These
files are generic I/O infrastructure shared by all formats rather
than format specific code.

The io/ directory name clashes with std::io in lib.rs, so the module
is declared as io_impl via #[path] and the submodules are re-exported
at the crate root to keep existing import paths working.

Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
2026-05-28 11:58:58 +00:00