06 - atomic update

This commit is contained in:
deeaitch committed 2026-10-20 12:27:04 -04:00
1 parent 6d63457737
commit 2bdacaf37a
21 files changed
+1302 -365

No files matched your search

@@ -0,0 +1,154 @@
#include "simulation.hpp"
#include <stdexcept>
namespace ota::installation {
const std::string &expected_contents() {
static const std::string contents = "release-2:kernel+device-tree+rootfs";
return contents;
}
const char *name(RecoveryAction action) {
switch (action) {
case RecoveryAction::RestartPreparation:
return "restart preparation; keep A";
case RecoveryAction::ReadyToActivate:
return "B verified again; wait for activation policy";
case RecoveryAction::PreserveTrial:
return "preserve saved B trial and attempts";
case RecoveryAction::SeparateRecovery:
return "stop; separate recovery required";
}
throw std::logic_error("Unknown recovery action");
}
const char *boot_choice(BootRecord record) {
switch (record) {
case BootRecord::ASelected:
return "A";
case BootRecord::BTrial:
return "B trial (attempt handling is outside this model)";
case BootRecord::Invalid:
return "separate recovery";
}
throw std::logic_error("Unknown boot record");
}
void Installer::require_writable() const {
if (storage_.boot != BootRecord::ASelected) {
throw std::logic_error("B is selected or boot state is invalid");
}
}
void Installer::begin() {
require_writable();
verified_ = false;
storage_.release = "release-2";
storage_.progress = Progress::Writing;
}
void Installer::write_partial() {
require_writable();
if (storage_.progress != Progress::Writing || storage_.release != "release-2") {
throw std::logic_error("Save matching intent before writing");
}
verified_ = false;
storage_.b_contents = expected_contents().substr(0, 8);
}
void Installer::finish_write() {
require_writable();
if (storage_.progress != Progress::Writing || storage_.release != "release-2") {
throw std::logic_error("Save matching intent before writing");
}
verified_ = false;
storage_.b_contents = expected_contents();
}
void Installer::verify() {
require_writable();
verified_ = false;
if (storage_.release != "release-2" || storage_.b_contents != expected_contents()) {
throw std::runtime_error("Installed release does not match expected contents");
}
verified_ = true;
}
void Installer::save_prepared() {
require_writable();
if (!verified_) throw std::logic_error("Verify B before saving prepared state");
storage_.progress = Progress::Prepared;
}
void Installer::request_trial(WriteResult result) {
require_writable();
if (!verified_ || storage_.progress != Progress::Prepared) {
throw std::logic_error("Verify B and save prepared state before activation");
}
// Supplied outcomes stand for the storage contract of boot control.
// OldRecord leaves all fields intact; NewRecord saves one complete record.
if (result == WriteResult::OldRecord) {
throw std::runtime_error("Interrupted request; old record remains");
}
if (result == WriteResult::InvalidRecord) {
storage_.boot = BootRecord::Invalid;
throw std::runtime_error("Interrupted request; no valid record remains");
}
storage_.boot = BootRecord::BTrial;
storage_.attempts_left = 2;
}
void Installer::save_requested() {
if (storage_.boot != BootRecord::BTrial) {
throw std::logic_error("No saved B trial request");
}
storage_.progress = Progress::TrialRequested;
}
RecoveryAction Installer::recover() {
verified_ = false;
// Boot state takes priority over progress. Never write B or renew attempts
// when a trial is already selected. Invalid boot state is not confirmation.
if (storage_.boot == BootRecord::Invalid) return RecoveryAction::SeparateRecovery;
if (storage_.boot == BootRecord::BTrial) {
save_requested();
return RecoveryAction::PreserveTrial;
}
if (storage_.release != "release-2" || storage_.b_contents != expected_contents()) {
return RecoveryAction::RestartPreparation;
}
verify();
save_prepared();
return RecoveryAction::ReadyToActivate;
}
void perform_step(Installer &installer, int step) {
switch (step) {
case 0:
installer.begin();
break;
case 1:
installer.write_partial();
break;
case 2:
installer.finish_write();
break;
case 3:
installer.verify();
break;
case 4:
installer.save_prepared();
break;
case 5:
installer.request_trial();
break;
case 6:
installer.save_requested();
break;
default:
throw std::logic_error("Unknown installation step");
}
}
}// namespace ota::installation