Commit Graph

510 Commits

Author SHA1 Message Date
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
Anatol Belski
2689cf9bdb block: vhd: Rename FixedVhdDisk to VhdDisk
FixedVhdDisk is the format level DiskFile wrapper for VHD images.
Rename it to VhdDisk to match the <Format>Disk convention used by
RawDisk, QcowDisk, and VhdxDisk. The Fixed prefix remains on the
workers (FixedVhdSync, FixedVhdAsync) since those are specific to
the fixed subformat.

Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
2026-05-28 11:58:58 +00:00
Anatol Belski
82e1c08e1f block: vhdx: Rename VhdxDiskSync to VhdxDisk
Align with the <Format>Disk wrapper naming convention. VHDx
has a single on disk format so no variant suffix is needed.
The async backend will be added to VhdxDisk.

No functional change.

Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
2026-05-28 11:58:58 +00:00
Anatol Belski
45fd51652f block: raw: Rename RawFileAsyncAio to RawAio
Apply the consistent <Format><Backend> naming convention.

No functional change.

Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
2026-05-28 11:58:58 +00:00
Anatol Belski
ac3c53cebb block: raw: Rename RawFileAsync to RawAsync
Apply the consistent <Format><Backend> naming convention.

No functional change.

Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
2026-05-28 11:58:58 +00:00
Anatol Belski
6279214dff block: raw: Rename RawFileSync to RawSync
Apply the consistent <Format><Backend> naming convention.

No functional change.

Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
2026-05-28 11:58:58 +00:00
Dylan Reid
cc2e528c88 block: Avoid raw iovecs in VHDX sync I/O
The VHDX synchronous async I/O backend still converted owned
AsyncIoOperation targets back into raw iovec slices before calling the
VHDX read and write helpers. That kept pointer dereferences in the owned
path and allowed a backend mistake to violate the safety boundary.

Handle owned VHDX reads and writes through AsyncIoOperation copy helpers
instead. The VHDX file operations still run synchronously, but data is
copied through operation-owned buffers or guest-memory targets without
reconstructing Rust slices from raw iovec pointers.

Assisted-by: Codex:GPT-5
Signed-off-by: Dylan Reid <dgreid@fb.com>
2026-05-27 12:07:03 +00:00
Dylan Reid
92d9a53ed3 block: Avoid raw iovecs in qcow sync I/O
This removes the qcow sync raw-iovec unsafe path and drops the
now-unused qcow iovec scatter/gather helpers.

Assisted-by: Codex:GPT-5.5
Signed-off-by: Dylan Reid <dgreid@fb.com>
2026-05-27 12:07:03 +00:00
Dylan Reid
892bc16806 block: Avoid raw iovec access in qcow async fallbacks
Similar to the other functions fixed in this series, qcow has helpers
that dereference whatever pointers are passed but are labeled safe.
Use the newly added ops helpers to, instead, provide a safe interface
and implementation.

Assisted-by: Codex:GPT-5.5
Signed-off-by: Dylan Reid <dgreid@fb.com>
2026-05-27 12:07:03 +00:00
Dylan Reid
1b2326fde4 block: Remove legacy async I/O API
Drop the borrowed iovec AsyncIo entry points now that all callers use
owned operations. Rename the transitional owned batch and completion
methods to the final trait names and remove the borrowed submission
helpers from the queue wrappers.

This removes a bunch of known safety foot-guns so future-us don't
accidentally use them.

Assisted-by: Codex:GPT-5.5
Signed-off-by: Dylan Reid <dgreid@fb.com>
2026-05-27 12:07:03 +00:00
Dylan Reid
2da8507d21 block, virtio-devices: Use owned async I/O requests
Switch virtio-blk request construction and the users to the owned
AsyncIo data path added in the series. Read bounce buffers now return
through AsyncIoCompletion before being copied back to guest memory.

This makes the main virtio async block I/O path use retained request
memory. qcow still has raw-iovec fallback paths at this point; those
are removed in follow-up commits.

Leave the legacy borrowed iovec trait methods in place for a follow-up
cleanup commit to minimize single-commit churn.

Signed-off-by: Dylan Reid <dgreid@fb.com>
2026-05-27 12:07:03 +00:00
Dylan Reid
358f7671ff block: Add owned async request helpers
Add helper routines for building and transferring data to/from async
io requests.

Signed-off-by: Dylan Reid <dgreid@fb.com>
2026-05-27 12:07:03 +00:00
Dylan Reid
afdc87e971 block: Add owned qcow io_uring I/O path
Similar to the previous commits, use UringDataIo for qcow async. Again,
the legacy interfaces are kept(at the expense of some temporary code).
The temporary code is unsound, like the existing code, but will be
removed soon.

Signed-off-by: Dylan Reid <dgreid@fb.com>
2026-05-27 12:07:03 +00:00
Dylan Reid
9143039805 block: Add owned raw Linux AIO path
Start using AioDataIo from RawFileAsyncAio. This adds a temporary
submit_borrowed_operation to enable preserving the unsafe iovec api
until we can remove it in the forthcoming commits.

Signed-off-by: Dylan Reid <dgreid@fb.com>
2026-05-27 12:07:03 +00:00
Dylan Reid
a541baa9e4 block: Add owned fixed VHD io_uring I/O path
Add the new AsyncIo apis to fixed_vhd_async. Later commits update
callers to use them and remove their unsound counterparts that take
iovecs.

While doing this, make the owned path validate offset plus length
instead of only the starting offset, so requests that extend past the
VHD size are rejected.

Signed-off-by: Dylan Reid <dgreid@fb.com>
2026-05-27 12:07:03 +00:00
Dylan Reid
849dace891 block: Add owned raw io_uring I/O path
Route RawFileAsync data I/O through UringDataIo's owned-operation
retention path while keeping borrowed submissions available for the
legacy AsyncIo calls during the transition.

This mostly moves code around, temporarily moving uring handling from
RawFileAsync to the UringDataIo, including the unsafe iovec access.

This enables the UringIo to be added to RawFileAsync incrementally.
Later commits will remove the unsafe paths when the callers are updated
to use the new functions.

Signed-off-by: Dylan Reid <dgreid@fb.com>
2026-05-27 12:07:03 +00:00
Dylan Reid
6a0c0191c8 block: Add owned qcow sync I/O path
Implement the new AsyncIo members for qcow_sync while keeping the
legacy borrowed iovec methods in place.

The code before and after this commit is equally unsound. This
intermediate state is not a safety regression and allows for a
bisectable transition to the fully sound code at the end of the
series.

This temporary state breaks out the iovec accesses to helpers used
from both the old and new code and updates the safety comments to
reflect reality.

Signed-off-by: Dylan Reid <dgreid@fb.com>
2026-05-27 12:07:03 +00:00
Dylan Reid
491f4e3343 block: Add owned VHDX sync I/O path
Refactor VhdxSync around shared iovec helpers which are marked unsafe.
Use these to implement safe wrappers for the new AsyncIo trait.

Leave the existing, unsound read/write vectored calls in place until
all callers are converted to the new interface later in this series.

In addition VHDX code assume it's safe to create slices to GuestMemory
via the AsyncAdaptor in existing code and explicitly after this
change. This is technically unsound as it can easily create multiple
mut refs. At least this is 'llvm update breaks the code' UB, not guest
exploitable UB...

Signed-off-by: Dylan Reid <dgreid@fb.com>
2026-05-27 12:07:03 +00:00
Dylan Reid
0bb4b87bcd block: Add owned fixed VHD sync I/O path
Implement the safe AsyncIo interface the fixed VHD synchronous wrapper
and delegate the actual I/O through RawFileSync. This maintains the
existing interfaces until the callers are converted later in the series.

Signed-off-by: Dylan Reid <dgreid@fb.com>
2026-05-27 12:07:03 +00:00
Dylan Reid
68b9ddd01c block: Add owned raw sync I/O path
Implement the owned AsyncIo path for RawFileSync while keeping the
legacy borrowed iovec methods available until all callers can be
converted.

Signed-off-by: Dylan Reid <dgreid@fb.com>
2026-05-27 12:07:03 +00:00
Dylan Reid
83a294505e block: Add owned AsyncIo trait methods
Add owned data-operation, completion, and batch methods to AsyncIo.
These will be used as safe alternatives to the existing, comically
unsafe, but marked safe interface.

Over the course of the following commits, users are converted to the
new interface and after all users of the unsound interfaces are
removed, they are removed.

Signed-off-by: Dylan Reid <dgreid@fb.com>
2026-05-27 12:07:03 +00:00