Skip to content

feat: add three-state volume initialization (None → Initializing → Ready)#127

Merged
imlk0 merged 12 commits into
masterfrom
recovery-in-init
Jun 25, 2026
Merged

feat: add three-state volume initialization (None → Initializing → Ready)#127
imlk0 merged 12 commits into
masterfrom
recovery-in-init

Conversation

@imlk0

@imlk0 imlk0 commented Jun 24, 2026

Copy link
Copy Markdown
Collaborator

Summary

Add a three-state initialization marker system for LUKS2 volumes so external scripts and cryptpilot-crypt can detect partially initialized volumes (interrupted during format) vs uninitialized vs fully ready.

Problem

When cryptpilot-crypt init formats a LUKS2 volume, it writes the LUKS2 header first, then creates an optional filesystem, and finally sets the subsystem = "cryptpilot" marker. If the process is interrupted (e.g., timeout, pod restart), the device has a valid LUKS2 header but no subsystem marker. External scripts cannot distinguish between:

  • A raw disk (safe to format)
  • A partially initialized disk with a LUKS2 header but no completion marker (needs re-init)
  • A fully initialized disk ready to open (safe to open)

Solution

Three-state system using the existing LUKS2 subsystem field:

State Subsystem Value Meaning
None empty or missing Raw disk or no marker — safe to format
Initializing cryptpilot-initializing Partial init interrupted — safe to re-init
Ready cryptpilot Fully initialized — ready to open

Changes

cryptpilot-core

  • VolumeInitState enum: None, Initializing, Ready
  • get_init_state(): public API returning the enum
  • mark_volume_as_initializing(): private, called by format()
  • format(): now writes initializing marker right after creating the LUKS2 header
  • is_initialized(): unchanged — returns true only for Ready (backward compatible)

cryptpilot-crypt

  • show: VolumeStatusKind gains Initializing variant, displayed with yellow warning
  • init: auto-retries on Initializing state without requiring --force-reinit
  • Integration tests: 4 new tests covering the full lifecycle

Test Results

  • 20/20 tests passing (16 existing volume tests + 4 new three_state_init tests)
  • cargo fmt --check passes
  • No regressions in existing tests

Commits

645c8c9 test(crypt): add three-state init integration tests
4f3107d feat(crypt): add Initializing state to volume status display
6535fa4 feat(core): write initializing marker in format()
03248b3 feat(core): add VolumeInitState enum and get_init_state() API

🤖 Generated with Claude Code

imlk0 added 4 commits June 24, 2026 16:40
Add three-state initialization detection (None, Initializing, Ready)
for LUKS2 volumes. get_init_state() reads the subsystem field from
the raw LUKS2 header. is_initialized() is refactored to use
get_init_state() internally, preserving backward compatibility.
format() now calls mark_volume_as_initializing() after writing the
LUKS2 header, ensuring the volume starts in Initializing state.
This prevents the ambiguous no-marker state if init is interrupted
after format but before the final ready marker.
Extend VolumeStatusKind with Initializing variant for partially
initialized volumes. determine_status() now uses get_init_state()
to distinguish None, Initializing, and Ready states. The show
command displays Initializing volumes with a warning indicator.
Also handle Initializing state in the init command to allow
re-initialization of interrupted volumes.
Add tests for the full None -> Initializing -> Ready lifecycle:
- Raw device returns None
- After format returns Initializing
- After mark returns Ready
- is_initialized backward compat: true only for Ready
@shankailun-aliyun

Copy link
Copy Markdown

@imlk0 ,您好,您的请求已接收,请耐心等待结果。

@shankailun-aliyun

Copy link
Copy Markdown

@imlk0 ,您好,未检测到有镜像需要构建,如需重新检测请评论 /start

Move set_label for the initializing marker into format()'s
spawn_blocking block, making header creation and marker write
a single atomic operation. Remove the now-unused
mark_volume_as_initializing() function.
@shankailun-aliyun

Copy link
Copy Markdown

@imlk0 ,您好,您的请求已接收,请耐心等待结果。

@shankailun-aliyun

Copy link
Copy Markdown

@imlk0 ,您好,未检测到有镜像需要构建,如需重新检测请评论 /start

imlk0 added 2 commits June 24, 2026 19:23
…field, and parallel lifecycle test

- format() now sets subsystem='cryptpilot-initializing' directly in
  CryptParamsLuks2 during format(), eliminating the post-format set_label
  call. Always pass params_ref (not just for integrity) so subsystem is
  written regardless of integrity type.

- blkid.rs: add 'subsystem' field to BlkidProbeResult::KnownSignature
  to expose the LUKS2 SUBSYSTEM field from blkid -p output.

- Add test_full_init_lifecycle_with_blkid_probe: runs blkid -p in a
  parallel monitor while performing format + mkfs + mark, verifying
  all three states are observable via blkid.

- before_sysroot.rs: update KnownSignature pattern to include new field.
…ries

- Add serial_test dev-dependency to prevent parallel test interference
- Rewrite test_full_init_lifecycle_with_blkid_probe: uses get_init_state()
  for reliable state verification at each step, with wait_for_blkid_subsystem()
  retry loop (up to 3s/5s) to detect blkid SUBSYSTEM changes
- Add sync() calls after format and mark to flush kernel page cache
@shankailun-aliyun

Copy link
Copy Markdown

@imlk0 ,您好,您的请求已接收,请耐心等待结果。

@shankailun-aliyun

Copy link
Copy Markdown

@imlk0 ,您好,未检测到有镜像需要构建,如需重新检测请评论 /start

imlk0 added 3 commits June 24, 2026 19:49
Add the blkid subsystem field to the KnownSignature pattern match
and include it in the persistent-mode data-protection error message
for better diagnostics.
… test

The spawned _monitor task recorded SUBSYSTEM transitions into
observed_subsystems but never asserted on them. All verification is
already done by wait_for_blkid_subsystem() which uses the same blkid
polling logic with proper wait/retry semantics.

Also removes the unused 300ms startup delay and Arc/Mutex imports.
When delta_location is DiskPersist and the volume is recreated
(format + mkfs), call mark_volume_as_initialized() so that
is_initialized() returns true on the next boot and the delta
volume is reused instead of being wiped again.

Before this fix, DiskPersist volumes were reformatted on every
boot because format() writes 'cryptpilot-initializing' and nothing
ever transitioned the marker to 'cryptpilot'.
@shankailun-aliyun

Copy link
Copy Markdown

@imlk0 ,您好,您的请求已接收,请耐心等待结果。

@shankailun-aliyun

Copy link
Copy Markdown

@imlk0 ,您好,未检测到有镜像需要构建,如需重新检测请评论 /start

format() always writes subsystem='cryptpilot-initializing'.
After the full init sequence (format + mkfs), always call
mark_volume_as_initialized() to transition to 'cryptpilot'
(Ready), regardless of delta_location or provider type.

This ensures every format() call is followed by a complete
lifecycle, keeping the volume state consistent.
@shankailun-aliyun

Copy link
Copy Markdown

@imlk0 ,您好,您的请求已接收,请耐心等待结果。

@shankailun-aliyun

Copy link
Copy Markdown

@imlk0 ,您好,未检测到有镜像需要构建,如需重新检测请评论 /start

- Remove needless borrows in wait_for_blkid_subsystem calls
- Replace Command::output() with run_with_status_checker()
  to satisfy disallowed_methods lint
@shankailun-aliyun

Copy link
Copy Markdown

@imlk0 ,您好,您的请求已接收,请耐心等待结果。

@shankailun-aliyun

Copy link
Copy Markdown

@imlk0 ,您好,未检测到有镜像需要构建,如需重新检测请评论 /start

@imlk0 imlk0 merged commit ccc2073 into master Jun 25, 2026
26 of 30 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.

2 participants