| index 58acc08f4..4f524dbc7 100644 |
| --- a/internal/host/movesfiles.go |
| +++ b/internal/host/movesfiles.go |
| @@ -367,17 +367,15 @@ func (f *movesFiles) manifestVersion(ctx context.Context, key string) (string, e |
| } |
| |
| // newestManifest is the newest valid manifest of the chain, read by exact |
| -// version, with the chain's prefixes as the origins its entries may name. |
| -// A generation without a manifest is passed over; the first manifest found |
| -// is validated and is the baseline, malformed or not (a malformed newest |
| -// manifest refuses, never an older one in its place). found is false when |
| -// no generation of the chain has a manifest. |
| +// version, with the origins its entries name (movesFilesOrigins: the |
| +// location's rule, not the chain's). A generation without a manifest is |
| +// passed over; the first manifest found is validated and is the baseline, |
| +// malformed or not (a malformed newest manifest refuses, never an older |
| +// one in its place). found is false when no generation of the chain has a |
| +// manifest. |
| func (f *movesFiles) newestManifest(ctx context.Context, location string, chain []string) (hostfiles.Manifest, []string, bool, error) { |
| - origins := make([]string, 0, len(chain)) |
| for _, g := range chain { |
| - origins = append(origins, movesFilesPrefix(location, g)) |
| - } |
| - for _, prefix := range origins { |
| + prefix := movesFilesPrefix(location, g) |
| key := hostfiles.ManifestKey(prefix) |
| version, err := f.manifestVersion(ctx, key) |
| if err != nil { |
| @@ -393,6 +391,10 @@ func (f *movesFiles) newestManifest(ctx context.Context, location string, chain |
| if !found { |
| return hostfiles.Manifest{}, nil, false, fmt.Errorf("host: the files manifest %s@%s is not readable", key, version) |
| } |
| + origins, err := movesFilesOrigins(location, prefix, m) |
| + if err != nil { |
| + return hostfiles.Manifest{}, nil, false, fmt.Errorf("host: the files manifest %s@%s: %w", key, version, err) |
| + } |
| if err := hostfiles.ValidateManifest(m, origins); err != nil { |
| return hostfiles.Manifest{}, nil, false, fmt.Errorf("host: the files manifest %s@%s: %w", key, version, err) |
| } |
| @@ -401,13 +403,54 @@ func (f *movesFiles) newestManifest(ctx context.Context, location string, chain |
| return hostfiles.Manifest{}, nil, false, nil |
| } |
| |
| +// movesFilesOrigins is the set of origins a manifest's objects name, each |
| +// held to the claim's provenance rule (P6-D3): an immutable object within |
| +// the authorized incarnation location in this region's bucket, under a |
| +// generation's files prefix, <location>/files/g-<generation>/. A manifest |
| +// naming nothing has its own prefix as its one origin. The admitted chain |
| +// selects which manifest is newest; it does not bound the origins that |
| +// manifest may name, because AdmitWriter keeps eight admissions while a |
| +// copied manifest carries its oldest objects for as long as they are live |
| +// (fix round 1, finding 6). The same location rule is the files live |
| +// set's (pinFilesLiveSet), so every object a live manifest names stays |
| +// pinned under it. |
| +func movesFilesOrigins(location, prefix string, m hostfiles.Manifest) ([]string, error) { |
| + base := strings.TrimSuffix(location, "/") + "/files/g-" |
| + seen := map[string]bool{} |
| + var out []string |
| + for _, entries := range []map[string]hostfiles.Entry{m.Entries, m.Platform} { |
| + for name, e := range entries { |
| + if e.Object == "" || e.Deleted || e.Skipped != "" { |
| + continue |
| + } |
| + origin, _, ok := strings.Cut(e.Object, "objects/") |
| + rest, under := strings.CutPrefix(origin, base) |
| + gen, slash := strings.CutSuffix(rest, "/") |
| + if !ok || !under || !slash || !movesFilesGeneration(gen) { |
| + return nil, fmt.Errorf("%w: %s names %s, not an object under a generation's files prefix of %s", hostfiles.ErrOrigin, name, e.Object, location) |
| + } |
| + if !seen[origin] { |
| + seen[origin] = true |
| + out = append(out, origin) |
| + } |
| + } |
| + } |
| + if len(out) == 0 { |
| + out = []string{prefix} |
| + } |
| + return out, nil |
| +} |
| + |
| // openPrepared is the probe's baseline (in.Prepared): P's prepared |
| // manifest at exactly the version the op journal's binding names (fix |
| // round 1, finding 2). The binding is decoded and held to P's key, the |
| // claim's op and the holder's stamp (the install captured the stamp the |
| // probe's first admission keeps); the bytes are read at that version, |
| // checked against the journalled digest, and validated as a prepared |
| -// manifest (sequence 0, objects within the location). The key must |
| +// manifest (sequence 0, objects within the location by the claim's rule, |
| +// movesFilesOrigins: a cross-region landing's names destination-local |
| +// objects by its build, a same-region relocation's may retain source |
| +// generation objects). The key must |
| // currently hold either those bytes (the first establishment, which the |
| // returned publisher replaces by compare-and-swap) or this box's own |
| // establishment of them (a retry after a crash past the commit: the same |
| @@ -442,7 +485,11 @@ func (f *movesFiles) openPrepared(ctx context.Context, o hostclaim.Observed, gen |
| if err != nil || !found { |
| return nil, fmt.Errorf("host: the prepared files manifest %s@%s: %v", key, pm.Version, err) |
| } |
| - if err := hostfiles.ValidateManifest(prepared, []string{prefix}); err != nil { |
| + origins, err := movesFilesOrigins(h.Location, prefix, prepared) |
| + if err != nil { |
| + return nil, fmt.Errorf("host: the prepared files manifest %s@%s: %w", key, pm.Version, err) |
| + } |
| + if err := hostfiles.ValidateManifest(prepared, origins); err != nil { |
| return nil, fmt.Errorf("host: the prepared files manifest %s@%s: %w", key, pm.Version, err) |
| } |
| if prepared.Seq != 0 { |