Keep one generation back, and verify before touching the existing build
Without this, a successful transfer of a BROKEN build stranded you: the only
copy of a working test program had already been overwritten, so there was no
way to attempt another transfer without an SD card reader.
Write order is now temp -> verify -> rotate, and the ordering is the point:
- The image goes to "<name>.tmp" and is read back and compared BEFORE
anything existing is touched. If the transfer, the write or the verify
fails, the build you are running is still on the card untouched and you
can simply try again. Previously a failed write left the destination
half-overwritten.
- On success the existing build is rotated to "<name>.prev.dol" and the temp
file takes its place. So if the NEW build turns out to be broken, boot
the previous one from Swiss and transfer again from there. One generation
back is enough never to need a card reader.
The two extensions are chosen deliberately:
- the temp file has NO .dol extension, because Swiss lists .dol files and a
half-written image Swiss offers to boot is exactly what this avoids;
- the backup KEEPS .dol, because booting it from Swiss is the whole point of
keeping it.
remove()/rename() on a file that is not there is ignored -- that is just the
first update on a fresh card. If the final rename somehow fails, the message
names the .prev.dol path rather than leaving the user to guess where their
working build went.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+17
-4
@@ -81,12 +81,25 @@ Press **Z**, then on the host:
|
||||
sudo ./scripts/send-dol.py <iface> gc_bba_test/gc_bba_test.dol
|
||||
```
|
||||
|
||||
The flow is deliberately **receive → write to SD → boot Swiss**:
|
||||
The flow is deliberately **receive → temp file → verify → rotate → boot Swiss**:
|
||||
|
||||
1. The new build arrives over the adapter into a staging buffer.
|
||||
2. It is written to the SD card (default `/gc_bba_test.dol`, override with
|
||||
`--dest`) and **read back and compared**.
|
||||
3. The app then boots **`/boot.dol`** — your known-good Swiss.
|
||||
2. It is written to `<name>.tmp` and **read back and compared**.
|
||||
3. Only then is it rotated in: the existing build becomes
|
||||
**`<name>.prev.dol`** and the temp file becomes `<name>.dol`
|
||||
(default `/gc_bba_test.dol`, override with `--dest`).
|
||||
4. The app then boots **`/boot.dol`** — your known-good Swiss.
|
||||
|
||||
**Ordering is the point.** If the transfer, the write or the verify fails, the
|
||||
build you are currently running is still on the card untouched, so you can
|
||||
just try again. And once an update succeeds, the previous build is kept as
|
||||
`<name>.prev.dol` — so if the *new* one turns out to be broken, boot the
|
||||
previous one from Swiss and transfer again from there. One generation back is
|
||||
enough to never get stranded needing an SD card reader.
|
||||
|
||||
The temp file deliberately has **no `.dol` extension**: Swiss lists `.dol`
|
||||
files, and a half-written image Swiss offers to boot is exactly what this
|
||||
avoids. The backup keeps `.dol` precisely because booting it is the point.
|
||||
|
||||
**The received image is never launched.** The only thing this ever jumps into
|
||||
is Swiss, which was already known good. So a broken test build costs nothing:
|
||||
|
||||
Reference in New Issue
Block a user