sysupgrade: ignore --no_reboot when the live flash was rewritten (#2231) - #2236
Merged
Conversation
On a camera that flashes its own rootfs, `-x` finished the flash and then
declined to reboot -- leaving a corpse. free_resources() drops the page cache
to free RAM for the download, so once flashcp has rewritten the partition
backing the mounted squashfs, every read that misses the cache comes back as
the new image at stale offsets: SSH auth, libc and /etc return garbage. The
closing "please reboot manually" was advice nobody could act on, because the
shell that would issue the reboot was one of the things that stopped working.
The camera still answered ping and only a power cycle recovered it.
Decide the reboot on what was actually written and whether this camera runs
from it, instead of on skip_reboot alone:
- get_system_info() records root_on_flash, probing /proc/cmdline for a root=
that names something other than flash. Anchored to the root= token rather
than matching a bare nfs/mmcblk/ram anywhere in the line as init(1) does:
a false "not on flash" is the one error here that bricks a camera, and the
stock command line already carries mmz=anonymous and an mtdparts label
ending -(rootfs_data). Anything not positively proven to live elsewhere --
NAND root=ubi0:rootfs, an unreadable /proc/cmdline -- counts as flash,
which at worst costs a reboot.
- do_update_rootfs, do_update_firmware and do_wipe_overlay mark the run
before the write, not after: flashcp erases before it writes, so a
half-erased partition is just as unreadable, and do_update_firmware
reaches reboot_system through `|| die` by exactly that path.
do_wipe_overlay counts because the jffs2 it erases is the upperdir of the
running overlay. do_update_kernel does not -- that partition is not
mounted, which is what keeps -x useful.
- reboot_system() reboots anyway when the live flash was rewritten, and says
why. A pre-flight notice lands before the trap that shields flashcp from a
dying TTY, so the operator still has Ctrl-C.
So -x still means what it says for a kernel-only update, an NFS/SD/ram root,
or a same-version run that writes nothing -- and no longer promises a working
camera it cannot deliver.
reboot_system() now returns instead of exiting on the honoured path, so the
main flow's `exit 0` is reached and die() keeps its own `exit 1`. A deliberate
--no_reboot run used to report failure.
Verified on the lab hi3516ev300 (NOR, overlay root, squashfs on mtd3): a real
upgrade with -x prints the notice, flashes, overrides -x and comes back on the
new build with key auth intact; a forced kernel-only write with -x is honoured
and the box stays up, exit 0. 20 new behaviour cases and 12 drift assertions in
test_sysupgrade.sh, all of which fail against 1.0.55.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.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.
Fixes #2231.
The problem
On a camera that flashes its own rootfs,
sysupgrade … -xfinished the flash and thendeclined to reboot — leaving a corpse.
free_resources()drops the page cache to free RAM forthe download, so once
flashcphas rewritten the partition backing the mounted squashfs, everyread that misses the cache comes back as the new image at stale offsets: SSH key auth, libc
and
/etcreturn garbage. The closing "please reboot manually" was advice nobody could acton, because the shell that would issue the reboot was one of the things that stopped working.
The camera still answered ping, and only a power cycle recovered it.
Confirmed on the lab hi3516ev300 — the partitions sysupgrade erases are the live mount:
The fix
Decide the reboot on what was actually written and whether this camera runs from it,
rather than on
skip_rebootalone.get_system_info()recordsroot_on_flashby probing/proc/cmdlinefor aroot=namingsomething other than flash. Anchored to the
root=token rather than matching a barenfs/mmcblk/ramanywhere in the line asgeneral/overlay/initdoes: a false "not onflash" is the one error here that bricks a camera, and the stock command line already carries
mmz=anonymousand an mtdparts label ending-(rootfs_data). Anything not positively provento live elsewhere — NAND
root=ubi0:rootfs, an unreadable/proc/cmdline— counts as flash,which at worst costs a reboot.
do_update_rootfs,do_update_firmwareanddo_wipe_overlaymark the run before thewrite, not after:
flashcperases before it writes, so a half-erased partition is just asunreadable, and
do_update_firmwarereachesreboot_systemthrough|| dieby exactly thatpath.
do_wipe_overlaycounts because the jffs2 it erases is the upperdir of the runningoverlay.
do_update_kerneldoes not — that partition is not mounted, which is what keeps-xuseful.reboot_system()reboots anyway when the live flash was rewritten, and says why. A pre-flightnotice lands before the trap that shields
flashcpfrom a dying TTY, so the operator stillhas Ctrl-C (window is
abort_wait, default 5s).So
-xstill means what it says for a kernel-only update, an NFS/SD/ram root, or a same-versionrun that writes nothing — and no longer promises a working camera it cannot deliver.
Also:
reboot_system()now returns instead of exiting on the honoured path, so the main flow'sexit 0is reached anddie()keeps its ownexit 1. A deliberate--no_rebootrun used toreport failure.
Verified on hardware (lab hi3516ev300, NOR, overlay root, squashfs on mtd3)
-z --build=nightly-20260724-04fe2e1 -x(real upgrade)--no_reboot ignored, rebooted, came back on the new build with key auth intact-z --kernel=… --force_ver -x(forced kernel write)-xhonoured, box stayed up,exit 0-z --rootfs=… -x(same version)-xhonoured, box stayed upThe script also parse-checks under the camera's own busybox ash.
Tests
.github/scripts/test_sysupgrade.shgrows a Part 1b (20 behaviour cases) and 12 Part 2 driftassertions: the override itself, kernel-only/same-version/NFS/mmcblk/ram honoured paths, both
combined-image paths, a write that fails after it started,
--wipe_overlay, a decoy commandline carrying
ramdisk_size=/mmcblkishto pin the anchoring, and a no--xregression guard.Every one of them fails against 1.0.55:
🤖 Generated with Claude Code