Repository navigation
feat(factory-reset): drop mode 4 from the accepted reset modes - #128
Closed
JanZachmann wants to merge 1 commit into
Closed
JanZachmann wants to merge 1 commit into
JanZachmann wants to merge 1 commit into
Conversation
Mode 4 ran a customer-supplied wipe hook in the initramfs. The hook is gone and the initramfs no longer accepts the mode: a trigger carrying it fails to parse, the device boots normally and no result is reported at all, so a reset requested with it would never be answered. The web API rejects the value now, instead of passing it on. Signed-off-by: Jan Zachmann <50990105+JanZachmann@users.noreply.github.com>
JanZachmann
added a commit
to omnect/omnect-os-init
that referenced
this pull request
Sep 24, 2026
## Summary Adds the factory-reset wipe modes 2 and 3. Mode 2 overwrites `etc` and `data` with data from `/dev/urandom`, mode 3 discards every block of both with the `BLKDISCARD` ioctl. The wipe runs between the backup and the reformat and never aborts the reset: a failure on one partition does not skip the other, reformat and restore still run, and the outcome is reported as `Error` with the reason in `error`. Mode 4 is no longer an accepted mode. It also closes what the on-device tests found: a trigger the init cannot parse is answered instead of ignored, and a preserve configuration that says nothing is rejected instead of skipped. A blank trigger is not one of those — it is no reset request at all. Design: `docs/superpowers/specs/2026-08-11-factory-reset-modes-2-3-4-design.md`, with a correction of what it claimed about rejected triggers. ## Reason The legacy shell init implemented the wipe with external tools. Both are replaced by native code, so the behaviour differs in ways worth stating. **Mode 2** covers the whole partition from byte 0. The legacy `dd` started at offset 2048 so an interrupted wipe kept the volume label readable; the init finds partitions by number, never by label, and re-creates the filesystem anyway. Privacy is the same or better. The legacy `dd` also used `oflag=direct`; the write loop goes through the page cache instead and flushes every gigabyte and again at the end, so after a power loss at most one gigabyte of already overwritten blocks can still hold the old content. **Mode 3** issues one `BLKDISCARD` over the whole partition. The legacy path mounted it, removed the files and ran `fstrim`, which trims only the blocks the remove had freed and leaves journal remnants — and counted a failed mount as a successful wipe. Without discard support the ioctl now fails cleanly and the reset reports it. Discard stays a hint on some disks, so the "no total privacy guarantee" note in the layer README remains true. Both modes open the device with `O_EXCL`, so a partition that is still mounted fails with `EBUSY` rather than being overwritten under a live filesystem. **Mode 4 is dropped, not ported.** It handed the wipe — the one step the caller asked for — to a customer-supplied hook that is not built or tested here, and modes 2 and 3 cover rotating disks and flash. The removal has to land in the same release wherever the mode is known: the mode table in the meta layer README, and the mode type in omnect/omnect-device-service#207 and omnect/omnect-ui#128. A failed wipe reports `Error`, not `Warning`: the device is usable, but the operation the caller asked for did not happen. For modes 2 and 3 the destructive boundary moves to the wipe, so every outcome from there on reports `data_wiped` — a partly written overwrite destroys the data even when the reformat never runs. **A trigger the init cannot use is now answered.** It used to produce a kmsg warning only: the boot continued as Normal, no `factory_reset` object was written, and the trigger kept its value, so the warning repeated on every boot while the caller waited for a result. Such a trigger is now cleared like any other and reported with the status the shell implementation used — anything about `mode` is `Invalid`, anything about the preserve configuration is `ConfigError`. That also makes `preserve` mandatory again, and a file in `factory-reset.d` without a usable `paths` array an error rather than something to skip, since skipping it wipes the paths the caller asked to keep and still reports success. **A blank trigger is not a reset request.** GRUB reports a key set to an empty value as present where U-Boot reports it as unset, and the shell init cleared the key by writing an empty string. A device coming from it would otherwise be answered with one "reset failed" nobody asked for. The statuses a configuration problem reports are now the same wherever it occurs: a `factory-reset.json` that does not exist reports `ConfigError` like a key missing inside it, which is what the shell implementation did too. Only an absent file counts — a path that exists but cannot be read stays an I/O error and reports `Error`, because the request was usable and the storage was not, which is worth retrying. The `error` field itself has one shape everywhere now: it sits inside the factory-reset result, so no writer repeats the outer `Factory reset error:` prefix. **Known gap.** Nothing pins that the wipe runs after the backup and after the unmount — moving `wipe_and_rebuild` above `backup_all` passes the whole suite. The `wipe_and_rebuild` seam starts after both steps, so covering the order against them needs a mount/backup seam as well. Tracked separately, together with the `data_dev` / `etc_dev` argument swap, which has the same shape. The result schema is unchanged and the initramfs image needs no new packages: the wipe uses one ioctl and `/dev/urandom` instead of `fstrim`, `blkdiscard` or `dd`. --------- Signed-off-by: Jan Zachmann <50990105+JanZachmann@users.noreply.github.com>
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
Removes mode 4 from the factory-reset modes the web API accepts. A request carrying it is rejected when the body is deserialized; modes 1 to 3 are unchanged.
Reason
Mode 4 ran a customer-supplied wipe hook inside the initramfs. The hook is gone with the initramfs rewrite and the new init does not accept the mode: a request carrying it comes back as
Invalid, a reset that never runs. Rejecting the value here keeps the request from being sent at all.This has to reach a release together with the initramfs change and the matching change in omnect-device-service, so no component still offers a mode the device refuses.