//go:build linux package host import ( "context" "errors" "net" "os" "reflect" "github.com/carlosframework/platform/internal/hostbox" "github.com/carlosframework/platform/internal/hostclaim" "github.com/carlosframework/platform/internal/hostguard" ) // Called only by the root stop coordinator after its kernel observation and // before publishing the receipt. There is no transport operation for inserting // this evidence. A claim change during the observation or lock wait refuses it. func (p *servingParkRoot) retainLegacyNoProcess(ctx context.Context, o hostclaim.Observed, result StopResult) error { if p == nil || p.ledger == nil || p.root.RequireNewWork == nil || p.root.InventoryActor == nil || p.root.Stop.Coordinator.Guard.Root == nil { return hostbox.ErrUnsupported } s := servingLegacyStop{Observed: o, Result: result} if validateServingLegacyStop(s) != nil { return ErrClaimStopReceipt } return p.ledger.withNamespace(ctx, func() error { return p.root.Stop.Coordinator.Guard.With(ctx, o.Host, hostguard.Captured{Incarnation: result.Target.Incarnation, Stamp: result.Target.Stamp}, func(c *hostclaim.Claim) error { if !reflect.DeepEqual(c, o.Claim) { return hostbox.ErrChanged } return nil }, func() error { if err := p.root.RequireNewWork(ctx); err != nil { return err } actor, err := p.root.InventoryActor(ctx) if err != nil { return err } t := result.Target if !actor.Eligible || actor.Quarantined || actor.Box != t.Box || actor.Region != t.Region || actor.Enrollment != t.Enrollment || actor.Revision == 0 || !servingPriorBinding(t.DirectoryRevision, actor) { return hostbox.ErrChanged } fresh, err := p.root.Stop.Coordinator.Guard.Claims.Read(ctx, o.Host) if err != nil { return err } if fresh.ETag != o.ETag || fresh.Absent { return hostbox.ErrChanged } a := servingArtifactIdentity{Host: o.Host, Box: result.Target.Box, Region: result.Target.Region, Incarnation: result.Target.Incarnation} old, err := p.ledger.legacyStop(a) if err == nil { // Retry reservation and settlement change Gen, ETag and WakeRetry // without admitting a writer. Keep the first receipt across those // changes, but refuse a different holder or enrollment. priorClaim, nextClaim := *old.Observed.Claim, *o.Claim priorHolder, nextHolder := *priorClaim.Holder, *nextClaim.Holder priorHolder.WakeRetry, nextHolder.WakeRetry = nil, nil priorClaim.Holder, nextClaim.Holder = &priorHolder, &nextHolder priorClaim.Gen = nextClaim.Gen if !reflect.DeepEqual(priorClaim, nextClaim) || old.Result.Target.Enrollment != result.Target.Enrollment || old.Result.Target.DirectoryRevision != result.Target.DirectoryRevision { return hostbox.ErrChanged } return nil } if !errors.Is(err, os.ErrNotExist) { return err } return p.ledger.write(servingLegacyKey("legacy-stop", a), s) }) }) } func (p *servingParkRoot) validateLegacyRootEvidence(ctx context.Context, o hostclaim.Observed, view hostguard.FenceView, actor hostbox.Actor, a servingParkAuthorization, e *servingLegacyEvidence, receipts []hostguard.FenceReceipt) error { if validateServingLegacyEvidence(a, e) != nil { return hostbox.ErrChanged } current := a.OriginalSuccessor var entry *hostclaim.AdmissionRecord for i := range o.Claim.Holder.Admissions { r := &o.Claim.Holder.Admissions[i] if r.Session == current.Session { if entry != nil { return hostbox.ErrChanged } entry = r } } if entry == nil || entry.Mode != "cold" || entry.Box != current.Box || entry.Region != current.Region || entry.Generation != current.Generation || entry.Predecessor != e.Selector.Generation { return hostbox.ErrChanged } if e.NoProcess != nil { saved, err := p.ledger.legacyStop(current) if err != nil || !reflect.DeepEqual(saved, *e.NoProcess) || len(receipts) != 0 { return hostbox.ErrChanged } t, h := saved.Result.Target, saved.Observed.Claim.Holder if t.Enrollment != actor.Enrollment || !servingPriorBinding(t.DirectoryRevision, actor) || h.Account != o.Claim.Holder.Account || h.App != o.Claim.Holder.App || h.Key != o.Claim.Holder.Key || !h.CreatedAt.Equal(o.Claim.Holder.CreatedAt) { return hostbox.ErrChanged } for _, r := range o.Claim.Holder.Admissions { if r.Session < current.Session { return hostbox.ErrChanged } } if e.Selector.Empty { if entry.PredecessorRegion != "" || entry.WritesLost != nil { return hostbox.ErrChanged } } else if entry.WritesLost == nil || entry.WritesLost.Box != current.Box || entry.WritesLost.Session != 0 || entry.WritesLost.Generation != "" || entry.PredecessorRegion != current.Region { return hostbox.ErrChanged } return nil } prior, err := servingPredecessor(o.Claim, a.Successor, e.Selector.Session, e.Selector.Generation) if err != nil || prior.Stamp >= current.Stamp { return hostbox.ErrChanged } stops, err := servingRootStops(ctx, view, prior, actor) if err != nil { return err } if !sameServingRootStops(stops, receipts) { return ErrClaimStopReceipt } return nil } func (p *servingParkRoot) parkLegacyServingDatabase(ctx context.Context, conn net.Conn, current servingArtifactIdentity, requested servingLegacyPredecessor) (servingParkAuthorization, error) { var out servingParkAuthorization err := p.withAuthority(ctx, conn, current, func(o hostclaim.Observed, view hostguard.FenceView, actor hostbox.Actor, dir servingArtifactDirectory, tags uint64) error { var err error consumed, err := p.ledger.legacyConsumption(current) if err != nil { return err } a := servingParkAuthorization{Legacy: true, OriginalSuccessor: current, Successor: current, Directory: dir} a.ID = servingRetirementID(a.Predecessor, current, true) e := &servingLegacyEvidence{Selector: requested} var receipts []hostguard.FenceReceipt var saved *servingParkRecord if consumed != nil { if consumed.Collected { return hostbox.ErrChanged } a, e, receipts = consumed.Authorization, consumed.LegacyEvidence, consumed.Receipts if a.Directory != dir || e == nil { return hostbox.ErrChanged } r, readErr := p.ledger.read(a.ID) if readErr == nil { saved = &r } else if !errors.Is(readErr, os.ErrNotExist) { return readErr } } else { if tags != 0 { return hostbox.ErrChanged } if requested.Session == 0 { stop, readErr := p.ledger.legacyStop(current) if readErr != nil { return readErr } e.NoProcess = &stop } else { prior, readErr := servingPredecessor(o.Claim, current, requested.Session, requested.Generation) if readErr != nil { return readErr } receipts, err = servingRootStops(ctx, view, prior, actor) if err != nil { return err } } } if err = p.validateLegacyRootEvidence(ctx, o, view, actor, a, e, receipts); err != nil { return err } active := a if saved != nil { if !sameServingLegacySource(saved.Source, consumed.Source) || saved.Authorization != a || !sameServingLegacyEvidence(saved.LegacyEvidence, e) || !sameServingRootStops(saved.Receipts, receipts) { return hostbox.ErrChanged } if err = validateServingRootHistory(ctx, o, view, actor, *saved); err != nil { return err } active = saved.activeAuthorization() } if tags != active.TagsFrom && tags != active.Successor.Stamp && tags != current.Stamp { return hostbox.ErrChanged } var next servingParkAuthorization var stops []hostguard.FenceReceipt if active.Successor != current { if saved != nil && saved.Complete || active.Successor.Stamp >= current.Stamp { return hostbox.ErrChanged } prior, e := servingPredecessor(o.Claim, current, active.Successor.Stamp, active.Successor.Generation) if e != nil || prior != active.Successor { return hostbox.ErrChanged } stops, err = servingRootStops(ctx, view, prior, actor) if err != nil { return err } next = active next.Successor, next.TagsFrom = current, tags } if err = p.ledger.legacyUnlaunched(ctx, current, actor.Enrollment); err != nil { return err } if consumed == nil && e.Selector.Empty { if p.empty == nil { return hostbox.ErrUnsupported } if a.Successor != current { return hostbox.ErrChanged } in := ClaimLaunchInputs{Empty: true, Admission: hostclaim.Admission{NewGeneration: current.Generation}} if err = p.empty.EmptyAuthority(ctx, o, in); err != nil { return err } } // Remote first-creator proof can wait on I/O; it cannot carry a stale claim // into the first source capture or any replay mutation. fresh, err := p.root.Stop.Coordinator.Guard.Claims.Read(ctx, current.Host) if err != nil { return err } if fresh.Absent || fresh.ETag != o.ETag { return hostbox.ErrChanged } if err = p.prepareServingNamespace(ctx, a, receipts, e); err != nil { return err } r, err := p.ledger.parkWithEvidenceLocked(ctx, a, receipts, e) if err != nil { return err } if err = p.ledger.fs.CheckParked(&r); err != nil { return err } if next.ID != "" { r, err = p.ledger.continueLocked(ctx, a.ID, next, stops) if err != nil { return err } } out = r.activeAuthorization() return nil }) return out, err } func sameServingLegacySource(a, b servingParkSource) bool { if a.Directory != b.Directory || a.Parent != b.Parent || a.Persistent != b.Persistent || a.BootID != b.BootID { return false } for i := range a.Files { if a.Files[i].Persistent != b.Files[i].Persistent || !sameServingParkFile(a.Files[i], b.Files[i]) { return false } } return true }