Commit Graph

503 Commits

Author SHA1 Message Date
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
Dylan Reid
ae5f4664ac block: Add AIO async struct
Similar to uring io added in the parent commit. These async ops deal
with buffer ownership across aio calls.

This will be used in the (increasingly rare) case of io_uring not being
available or desirable.

Signed-off-by: Dylan Reid <dgreid@fb.com>
2026-05-27 12:07:03 +00:00
Dylan Reid
b9ec794719 block: Add io_uring async I/O struct
Add the shared helpers and UringDataIo queue that drive uring async I/O
operations. UringDataIo is the key component responsible for keeping the
memory pointers active while async operations are ongoing. It uses the
async core added in previous commits.

Later commits will change the block backends to use this instead of the
lower level abstractions directly.

Signed-off-by: Dylan Reid <dgreid@fb.com>
2026-05-27 12:07:03 +00:00
Dylan Reid
dd1fb36f36 block: Add owned async I/O operations
Add `AsyncIoOperation` and `AsyncIoCompletion` as the owned request and
completion types that will be used to ensure buffers for async io
outlive the operations that use them. Later commits will update the
`AsyncIo` trait to expose apis using only these instead of raw iovecs.

Signed-off-by: Dylan Reid <dgreid@fb.com>
2026-05-27 12:07:03 +00:00
Dylan Reid
5bf029f3b7 block: Add owned async I/O buffers
Add `OwnedIoBuffer` to be used for host owned buffers. These are buffers
backed with either a `Vec` or an aligned allocation and will be used for
bounce buffers. This is host owned memory that can later be copied to
guest memory.

Later commits will use this to ensure backing memory outlives async
operations in a centralized, verified way.

Signed-off-by: Dylan Reid <dgreid@fb.com>
2026-05-27 12:07:03 +00:00
Dylan Reid
110487a55b block: Add guest-memory async I/O targets
Introduce GuestMemoryTarget to own the combination of an Arc to
GuestMemory and a set of ranges/iovecs. This will be used in the
following commits to replace the iovec pointers that are passed to the
backend operations unsafely.

Assisted-by: Codex:GPT-5
Assisted-by: Claude:Opus-4.7
Signed-off-by: Dylan Reid <dgreid@fb.com>
2026-05-27 12:07:03 +00:00
Ian Klemm
2b61bda35a block: qcow: check compressed L2 entries before zero flag
QCOW2 compressed L2 entries encode their extent layout in bits that
overlap with the flags used by standard L2 entries. In particular,
bit 0 can be part of the compressed entry layout, so it must not be
interpreted as ZERO_FLAG until the entry has first been ruled out as
compressed.

Keep compressed deallocation ahead of zero-flag handling in both the
shared QcowMetadata path and the legacy QcowFile path. This ensures
WRITE_ZEROES deallocates compressed clusters instead of treating a
compressed entry with bit 0 set as an existing logical-zero marker.

Add regression coverage that forces bit 0 on a compressed L2 entry
and verifies WRITE_ZEROES still clears the entry through the
compressed-cluster path.

Assisted-by: Codex:GPT-5
Signed-off-by: Ian Klemm <hi@ianklemm.de>
2026-05-22 14:26:04 +00:00
Ian Klemm
39e253ff9c block: qcow: Preserve WRITE_ZEROES with backing files
QCOW2 empty L2 entries in an overlay mean that reads fall
through to the backing file. Reusing the punch_hole path for
WRITE_ZEROES therefore turns a full-cluster zero operation on an
unallocated overlay cluster into backing data exposure.

Keep discard/punch_hole behavior unchanged, but let WRITE_ZEROES
request a logical-zero marker when the image has a backing file.
ZERO_FLAG entries now read as zeros in both the legacy QcowFile
path and the shared runtime metadata path. Partial writes after
such entries seed new clusters from zeros instead of backing data.

Treat ZERO_FLAG entries as logical holes for SEEK_HOLE/SEEK_DATA.
Empty overlay entries with a backing file still report data because
the data exists in the backing file.

Avoid cluster-sized userspace zero buffers when materializing
zero-flagged clusters by zeroing the allocated host range directly.
This keeps recycled clusters safe without making partial writes
allocate large zero-filled Vecs.

Add regression coverage for legacy QcowFile, QcowSync, direct I/O,
QcowAsync/io_uring overlay paths, and a large-cluster partial-write
case.

Assisted-by: Codex:GPT-5

Signed-off-by: Ian Klemm <hi@ianklemm.de>
2026-05-22 14:26:04 +00:00