Skip to content

Dev v0.2#14

Merged
ming1 merged 28 commits into
mainfrom
dev_v0.2
Jun 12, 2026
Merged

Dev v0.2#14
ming1 merged 28 commits into
mainfrom
dev_v0.2

Conversation

@ming1

@ming1 ming1 commented Jun 10, 2026

Copy link
Copy Markdown
Collaborator
  • big refactor
  • small bug fixes

ming1 added 28 commits June 7, 2026 07:54
Update CLAUDE.md to match the current code: document the four-method
Qcow2IoOps trait, the discard/fallocate/drop test suites, and a new
"Device Setup & Public API" section covering Qcow2DevParams, the
qcow2_setup_dev_* helpers, automatic backing-chain resolution, and the
public Qcow2Dev methods.

Signed-off-by: Ming Lei <tom.leiming@gmail.com>
Remove byteorder (no references anywhere in the crate) and clap_derive
(clap is already pulled with the "derive" feature, which re-exports
Parser/Subcommand/Args). Also de-duplicate tempfile, which was declared
in both [dependencies] and [dev-dependencies]; it stays in
[dependencies] since the public make_temp_qcow2_img helper uses it.

Signed-off-by: Ming Lei <tom.leiming@gmail.com>
The sync, tokio and io_uring backends each carried byte-for-byte copies
of the Linux hole-punch flag mapping + nix fallocate call, the O_DIRECT
fcntl, and the zero-write discard fallback. Hoist these into ops.rs as
linux_punch_hole(), set_direct_io() and zeroed_io_buf(), so each backend
impl is now a thin delegate. The macOS F_PUNCHHOLE soft-fail path in the
tokio backend keeps its specialized logic and only reuses zeroed_io_buf.

Signed-off-by: Ming Lei <tom.leiming@gmail.com>
Qcow2DevParams stored read_only and backing behind RefCell, but the only
mutation site (the setup helpers) already clones the params before
calling mark_backing_dev, so plain fields with &mut self setters work
and remove the runtime borrow checking. Likewise Qcow2IoSync held its
File in a RefCell even though the field is only an ownership anchor
keeping the fd valid; a plain File suffices.

Signed-off-by: Ming Lei <tom.leiming@gmail.com>
Qcow2DevParams::set_read_only and mark_backing_dev changed receiver from
&self to &mut self, dropping interior mutability. That is a source-level
breaking change: callers that mutated params through a shared reference
or an immutable binding no longer compile. Cargo treats "0.1" as ^0.1,
so a 0.1.x release would silently break downstream consumers on update;
bump the minor to 0.2.0 to make the break opt-in.

Signed-off-by: Ming Lei <tom.leiming@gmail.com>
Mechanical, behavior-preserving idiom modernization flagged by clippy:
inline format args, manual (a+b-1)/b -> u64::div_ceil, io::Error::new
(ErrorKind::Other, _) -> io::Error::other, drop a needless as_bytes(),
single-arm match -> if let, and == false -> negation.

Signed-off-by: Ming Lei <tom.leiming@gmail.com>
clippy::repr_packed_without_abi flags bare #[repr(packed)], which
defaults to #[repr(Rust, packed)] and has no stable ABI. Qcow2RawHeader
and Qcow2HeaderExtensionHeader are (de)serialized field-by-field via
bincode, so struct memory layout does not define the on-disk format;
making the existing layout explicit as #[repr(Rust, packed)] silences
the lint with zero behavior change. (repr(C, packed) is deliberately
avoided, as that would reorder fields.)

Signed-off-by: Ming Lei <tom.leiming@gmail.com>
Reformat lines left non-canonical by earlier edits (a clippy one-liner,
a struct literal that now fits one line) and a stale import order in the
basic test. Formatting only, no behavior change.

Signed-off-by: Ming Lei <tom.leiming@gmail.com>
Move src/meta.rs to src/meta/mod.rs and split the Table/TableEntry
traits plus the table-impl macros into a new meta/table.rs submodule.
The macros are re-exported with pub(crate) use and imported back into
mod.rs (all of them, since impl_table_traits! expands to unqualified
calls of the helper macros that must resolve at the invocation site).
No logic changes.

Signed-off-by: Ming Lei <tom.leiming@gmail.com>
Move the guest-offset address-splitting type out of meta/mod.rs into a
dedicated meta/addr.rs submodule, re-exported from mod.rs. No logic
changes.

Signed-off-by: Ming Lei <tom.leiming@gmail.com>
Move Qcow2RawHeader, the header-extension types, the feature-type enum
and Qcow2Header (with its parse/format/serialize logic) into a new
meta/header.rs submodule, re-exported from mod.rs. The remaining table
types stay in mod.rs and header.rs pulls the refcount table types it
needs for format_qcow2 from super. No logic changes.

Signed-off-by: Ming Lei <tom.leiming@gmail.com>
Move L1Entry, L1Table, their trait impls and the test_l1_table unit
test into a new meta/l1.rs submodule. The test moves with the code
because it constructs L1Entry via its private tuple field, which is
only reachable from within the type's own module. No logic changes.

Signed-off-by: Ming Lei <tom.leiming@gmail.com>
Move L2Entry, Mapping, MappingSource, L2Table, their trait impls and
the test_l2_table unit test into a new meta/l2.rs submodule. The test
moves with the code because it reads L2Table's private cluster_bits
field. No logic changes.

Signed-off-by: Ming Lei <tom.leiming@gmail.com>
Move RefTableEntry, RefTable, RefBlockEntry, RefBlock, their trait
impls and the three refcount unit tests into a new meta/refcount.rs
submodule. test_refcount_block2 moves with the code because it builds
RefBlockEntry via its private tuple field. meta/mod.rs is now reduced
to module declarations and re-exports. No logic changes.

Signed-off-by: Ming Lei <tom.leiming@gmail.com>
Relocate the format/parse round-trip unit test next to the Qcow2Header
code it exercises. meta/mod.rs is now purely module declarations and
re-exports. No logic changes.

Signed-off-by: Ming Lei <tom.leiming@gmail.com>
Move src/dev.rs to src/dev/mod.rs and split the standalone Qcow2Info
and Qcow2DevParams data types into a new dev/info.rs submodule,
re-exported from mod.rs. Two Qcow2Info helper methods used by the
allocator (rb_slice_entries, is_read_only) are widened to pub(crate)
since they are now accessed across the module boundary. No logic
changes.

Signed-off-by: Ming Lei <tom.leiming@gmail.com>
Move HostCluster and the refcount-based cluster allocator (grow_reftable,
free_clusters, refblock cache helpers, allocate_clusters and friends)
plus the three allocator unit tests into a new dev/alloc.rs submodule as
an impl extension of Qcow2Dev. Methods and HostCluster called across the
new module boundary are widened from private to pub(crate); this is the
mechanical cost of splitting the single Qcow2Dev impl block. No logic
changes.

Signed-off-by: Ming Lei <tom.leiming@gmail.com>
Move the read/mapping path (get_mapping, get_l2_entry/entries, the
do_read_* helpers and read_at) into a new dev/read.rs submodule as an
impl extension of Qcow2Dev. do_read_compressed and get_l2_entry are
widened to pub(crate) since the COW write path and checker call them.
No logic changes.

Signed-off-by: Ming Lei <tom.leiming@gmail.com>
Move the write path (ensure_l2_offset, the COW helpers, write-mapping
construction and write_at) into a new dev/write.rs submodule as an impl
extension of Qcow2Dev. No visibility changes were needed: it calls the
already-pub(crate) read/alloc helpers and the parent's private cache
kernel, and exposes only the existing pub write_at. No logic changes.

Signed-off-by: Ming Lei <tom.leiming@gmail.com>
The grow_reftable doc comment was left behind in mod.rs when the
allocator was extracted; reunite it with the function it describes.

Signed-off-by: Ming Lei <tom.leiming@gmail.com>
Move discard and __discard_one_cluster into a new dev/discard.rs
submodule as an impl extension of Qcow2Dev. No visibility changes
needed. No logic changes.

Signed-off-by: Ming Lei <tom.leiming@gmail.com>
Move the cluster-usage accounting (qcow2_cluster_usage and its
add_*_clusters helpers) and the integrity checker (check and its
check_*/cluster_is_allocated helpers) into a new dev/check.rs submodule
as an impl extension of Qcow2Dev. No visibility changes needed. No logic
changes.

Signed-off-by: Ming Lei <tom.leiming@gmail.com>
Move the IO-op wrappers (call_*), table loaders, L2-slice cache
management and the soft-update flush logic (flush_*, flush_meta) into a
new dev/cache.rs submodule as an impl extension of Qcow2Dev. The kernel
methods invoked by the read/write/alloc/check/discard submodules are
widened to pub(crate); call_read keeps its #[inline] and mark_need_flush
keeps #[inline(always)]. dev/mod.rs is now just the struct, constructor,
lifecycle helpers and prep/flush flags. No logic changes.

Signed-off-by: Ming Lei <tom.leiming@gmail.com>
The allocator tests (alloc.rs) and the io test (mod.rs) each inherited
the full test-module import block; drop the imports each no longer uses.

Signed-off-by: Ming Lei <tom.leiming@gmail.com>
The blocks_before/after_write and blocks_after_discard bindings in
discard_releases_host_cluster feed only #[cfg(unix)] assertions, so they
were unused (and warned) on non-unix targets. Gate the let bindings with
#[cfg(unix)] to match their asserts; host_blocks already has a
not(unix) stub.

Signed-off-by: Ming Lei <tom.leiming@gmail.com>
Reflect the extraction of the monolithic src/dev.rs and src/meta.rs
into the src/dev/ and src/meta/ submodule trees.

Signed-off-by: Ming Lei <tom.leiming@gmail.com>
These pre-existing issues became visible while reviewing the relocated
code; none are regressions from the refactor itself.

- meta/refcount: __get/__set shifted 2/4-bit refcount entries by the
  entry index instead of its in-byte bit offset, causing entries to
  alias. Shift by (index % 4) * 2 and (index % 2) * 4 respectively.
- ops: FALLOC_FL_PUNCH_HOLE|FALLOC_FL_ZERO_RANGE is an invalid combo
  and PUNCH_HOLE requires KEEP_SIZE, so both branches returned EINVAL.
  Use ZERO_RANGE|KEEP_SIZE and PUNCH_HOLE|KEEP_SIZE; also fix the
  FALLOCATE_ZERO_RAGE -> FALLOCATE_ZERO_RANGE typo across callers.
- dev/read: clamp EOF-straddling reads to the in-image length
  (vsize - offset) rather than the past-EOF overflow, and replace the
  unreachable i-1 == res.len() check with i == res.len() - 1.
- dev/write: propagate allocate_clusters/allocate_cluster errors with
  ? instead of unwrap()/expect() that panic on Ok(None).

Signed-off-by: Ming Lei <tom.leiming@gmail.com>
The prior fix mapped FALLOCATE_ZERO_RANGE to
FALLOC_FL_ZERO_RANGE | FALLOC_FL_KEEP_SIZE, which only zeroes the
range. On filesystems that don't deallocate zeroed extents (e.g.
overlayfs on GitHub CI) the call succeeds without freeing blocks, so
discard stops reclaiming space and the
fallocate_punch_hole_shrinks_st_blocks test fails (before == after).

Use FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, which deallocates the
range and reads back as zero, satisfying both the discard and
reads-as-zero contracts and matching the macOS F_PUNCHHOLE backend.
Unlike ZERO_RANGE, PUNCH_HOLE either frees blocks or errors, so it
never silently succeeds without reclaiming space.

Signed-off-by: Ming Lei <tom.leiming@gmail.com>
@ming1 ming1 merged commit d3c39fb into main Jun 12, 2026
12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant