package host import ( "encoding/json" "os" "strings" "github.com/carlosframework/platform/internal/release" "github.com/carlosframework/platform/internal/rootstate" ) func recoveryResultKey(a rollbackAttempt) string { return rollbackHostKey(a.Host) + "." + a.ID + ".recovery" } func recoveryHistoryOutcome(a rollbackAttempt) (release.HistoryRecovery, error) { if !a.Independent || a.History == nil || a.HistoryBinding.Host != a.Host || a.History.ID != a.ID || len(a.ID) != 32 { return release.HistoryRecovery{}, os.ErrPermission } return release.HistoryRecovery{Binding: a.HistoryBinding, Attempt: *a.History, Settled: a.Settled, Failed: a.Failed, Recovering: a.Recovering, Verified: a.Verified}, nil } func (c rootRestartChecks) copyRecoveryHistory(h *release.RootHistory, binding release.HistoryBinding) ([]release.HistoryRecovery, error) { if c.attempts == nil || h == nil { return nil, os.ErrPermission } s, err := c.attempts() if err != nil { return nil, err } defer s.Close() names, err := s.Names() if err != nil { return nil, err } var unresolved []release.HistoryRecovery for _, name := range names { if !strings.HasSuffix(name, ".attempt") && !strings.HasSuffix(name, ".recovery") { continue } raw, err := s.Read(name) if err != nil { return nil, err } var a rollbackAttempt if len(raw) > rootstate.MaxRecord || json.Unmarshal(raw, &a) != nil || (name != rollbackJournalKey(a.Host) && name != recoveryResultKey(a)) { return nil, os.ErrPermission } if !a.Independent { continue } r, err := recoveryHistoryOutcome(a) if err != nil { return nil, err } if r.Binding.Account != binding.Account || r.Binding.App != binding.App { continue } if err := s.Write(name, raw); err != nil { return nil, err } if err := h.RetainRecovery(r); err != nil { return nil, err } if !r.Settled || (!r.Failed && !r.Recovering && !r.Verified) { unresolved = append(unresolved, r) } } return unresolved, nil } func (c rootRestartChecks) retainRecoveryHistory(h *release.RootHistory, binding release.HistoryBinding) error { unresolved, err := c.copyRecoveryHistory(h, binding) if err != nil { return err } return checkUnresolvedRecoveries(h, unresolved) } func checkUnresolvedRecoveries(h *release.RootHistory, unresolved []release.HistoryRecovery) error { for _, r := range unresolved { if !r.Settled { return os.ErrPermission } retired, err := h.RecoveryRetired(r) if err != nil { return err } if !retired { return os.ErrPermission } } return nil } func (c rootRestartChecks) mintRefusals(h *release.RootHistory, binding release.HistoryBinding) ([]release.Refusal, error) { if err := c.retainRecoveryHistory(h, binding); err != nil { return nil, err } refs, err := h.MintRefusals(binding) if err != nil { return nil, err } if len(refs) > 256 { return nil, os.ErrPermission } return refs, nil }