06 - atomic update
This commit is contained in:
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
|
||||
Reference in new issue
Block a user