feat: verify drive at startup, make UMS prep cancellable - #5
Open
teal-bauer wants to merge 1 commit into
Open
Conversation
Two shortcomings made UMS mode switching feel unresponsive:
1. Drive integrity was only checked lazily inside Mount(), so corruption
surfaced on the first UMS entry after boot, then blocked the user
behind a full drive recreation. Creation itself was a 10-second
dd-of-1GB.
2. handleModeChange held the service mutex across the entire prep, so
a mode=normal request mid-prep queued behind a multi-second mount,
copy, diagnostics collection (journalctl + 30s DBC SSH), and unmount.
scootui kept showing "preparing storage" long after the user had
asked to exit.
Changes:
- disk.Manager.Initialize now runs verifyOrRecreate at service start:
fsck.fat on the existing file, recreate if missing/smaller than
configured/corrupt. Drops fsck from Mount's hot path.
- createDriveFile uses os.Truncate instead of dd, producing a sparse
1GB file. mkfs.fat writes only FAT metadata (~2.1MB on eMMC) so the
file stays sparse until the host writes to it. Cold creation drops
from ~10s to ~1s.
- Introduced operation { target, ctx, cancel, done } and a single
requestMode dispatcher. Mode changes cancel any in-flight op, wait
for teardown, then install a new op. Ops run in goroutines.
- Context threaded through settings, update, maps, wireguard,
diagnostics, rpm, scripts Prepare/Copy calls and through disk
Mount/Unmount/CleanDrive. exec.Command replaced with
exec.CommandContext so SIGKILL propagates on cancel.
- On cancel, publisher.SetMany atomically writes {status:idle, step:""}
before waiting for teardown, so scootui flips out of "preparing"
immediately.
- Brake-hold handler checks for any in-flight UMS op, not just an
active gadget mode, so exiting works during prep too.
- Post-UMS processing phase (Mender install, DBC transfers) keeps a
Background context. Cancelling mid-install is unsafe.
Tested on deep-blue: startup drive recreation is ~1s, file is 2.1MB
physical / 1GB apparent.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
os.Truncate; 1GB creation drops from ~10s to ~1s (2.1MB physical on eMMC)mode=normalrequest mid-prep cancels the in-flight op, flips{status,step}to idle immediately, and unwinds in the backgroundBackground
Two shortcomings made UMS switching feel unresponsive:
diskMgr.Initializeonly checked whether the file existed.fsck.fatwas deferred toMount(), so the first UMS entry after corruption blocked the user behind a full drive recreation. And that recreation was add if=/dev/zero bs=1M count=1024, which takes >30 seconds on eMMC.handleModeChangehelds.muacross the entire prep. Amode=normalrequest mid-prep queued behind mount, settings copy, diagnostics collection (journalctl 8h + 30s DBC SSH), and unmount. scootui kept showing "preparing storage" for tens of seconds after the user asked to exit.Changes
pkg/disk/manager.goInitializeruns newverifyOrRecreate: fsck on existing, recreate if missing, smaller than configured, or fsck fails. Larger-than-configured file is kept (don't destroy user data on size decrease).createDriveFileusesos.Truncatefor sparse allocation.mkfs.fatwrites FAT metadata only, rest stays sparse.Mountno longer runs fsck.Mount/Unmount/CleanDrivetakecontext.Context; subprocess calls useexec.CommandContext.Unmountintentionally ignores its context because a stale loopback mount blocks the next session.Service lifecycle (
internal/service/service.go)operation { target, ctx, cancel, done }type and singlerequestMode(target)dispatcher. All three callers (hash watcher, detach loop, brake-hold) route through it.dispatchMuserializes transitions.s.muis only held for short state updates, not across I/O.cur.cancel()+publishIdle()viaSetMany({status:idle, step:""})+ wait oncur.done.op.ctx.Err()between each step; on cancel, deferred teardown unmounts usingcontext.Background().context.Background(). A Mender install mid-cancel is unsafe, so that phase is not cancellable by design.currentOpas a stablenormalstate at startup so dispatcher logic has a defined starting point.Context propagation
settings.{CopyToUSB, CopyFromUSB}update.PrepareUSBmaps.PrepareUSBwireguard.{PrepareUSB, CopyToUSB, SyncFromUSB}diagnostics.CollectToUSB(biggest win: SSH to DBC and journalctl now get SIGKILL on cancel)rpm.PrepareUSB,scripts.PrepareUSBinternal/service/brake_exit.gos.currentOp.targetinstead ofusbCtrl.GetCurrentMode(), so exit works during prep.Test plan
Tested on deep-blue:
redis-cli HSET usb mode umsduring prep, thenHSET usb mode normalmid-diagnostics: status flips to idle immediately, background teardown completes cleanly, next UMS request succeeds.ums-by-dbctwo-disconnect flow still works.