package bundle import ( "context" "fmt" "gitea.maximumdirect.net/eric/distributor/internal/storage" ) func ValidateSourcePath(path string) error { if err := storage.ValidatePath(path); err != nil { return err } switch path { case ManifestName, storage.StateFileName: return fmt.Errorf("%q is reserved", path) } return nil } func Validate(ctx context.Context, backend storage.Backend, bundleRoot string) (Bundle, error) { return validateAt(ctx, backend, bundleRoot, bundleRoot) } func validateAt(ctx context.Context, backend storage.Backend, bundleRoot, relativeRoot string) (Bundle, error) { if err := storage.ValidatePrefix(bundleRoot); err != nil { return Bundle{}, err } manifestPath, err := storage.Join(bundleRoot, ManifestName) if err != nil { return Bundle{}, err } manifestData, err := backend.ReadFile(ctx, manifestPath) if err != nil { return Bundle{}, fmt.Errorf("read manifest %q: %w", manifestPath, err) } manifest, err := ParseManifest(manifestData) if err != nil { return Bundle{}, fmt.Errorf("bundle %q: %w", displayRoot(relativeRoot), err) } for index, manifestFile := range manifest.Files { filePath, err := storage.Join(bundleRoot, manifestFile.Path) if err != nil { return Bundle{}, fmt.Errorf("bundle %q file %q: %w", displayRoot(relativeRoot), manifestFile.Path, err) } entry, err := backend.Stat(ctx, filePath) if err != nil { return Bundle{}, fmt.Errorf("bundle %q file %q stat: %w", displayRoot(relativeRoot), manifestFile.Path, err) } if entry.Type != storage.EntryTypeFile { return Bundle{}, fmt.Errorf("bundle %q file %q must be a regular file", displayRoot(relativeRoot), manifestFile.Path) } if entry.Size != manifestFile.Size { return Bundle{}, fmt.Errorf("bundle %q file %q size mismatch: got %d want %d", displayRoot(relativeRoot), manifestFile.Path, entry.Size, manifestFile.Size) } data, err := backend.ReadFile(ctx, filePath) if err != nil { return Bundle{}, fmt.Errorf("bundle %q file %q read: %w", displayRoot(relativeRoot), manifestFile.Path, err) } actualDigest := FileDigest(data) if actualDigest != manifestFile.SHA256 { return Bundle{}, fmt.Errorf("bundle %q file %q sha256 mismatch: got %s want %s", displayRoot(relativeRoot), manifestFile.Path, actualDigest, manifestFile.SHA256) } manifest.Files[index].SHA256 = actualDigest manifest.Files[index].Size = int64(len(data)) } actualBundleDigest := BundleDigest(manifest.Files) if actualBundleDigest != manifest.Digest { return Bundle{}, fmt.Errorf("bundle %q digest mismatch: got %s want %s", displayRoot(relativeRoot), actualBundleDigest, manifest.Digest) } return Bundle{ RootRelativePath: relativeRoot, Manifest: manifest, }, nil } func displayRoot(root string) string { if root == "" { return "." } return root }