package mfer import ( "bytes" "errors" "io" "github.com/klauspost/compress/zstd" "github.com/spf13/afero" "google.golang.org/protobuf/proto" "sneak.berlin/go/mfer/internal/bork" "sneak.berlin/go/mfer/internal/log" ) func (m *manifest) deserializeInner() error { if m.pbOuter.Version != MFFileOuter_VERSION_ONE { return errors.New("unknown version") } if m.pbOuter.CompressionType != MFFileOuter_COMPRESSION_ZSTD { return errors.New("unknown compression type") } bb := bytes.NewBuffer(m.pbOuter.InnerMessage) zr, err := zstd.NewReader(bb) if err != nil { return err } defer zr.Close() dat, err := io.ReadAll(zr) if err != nil { return err } isize := len(dat) if int64(isize) != m.pbOuter.Size { log.Debugf("truncated data, got %d expected %d", isize, m.pbOuter.Size) return bork.ErrFileTruncated } // Deserialize inner message m.pbInner = new(MFFile) if err := proto.Unmarshal(dat, m.pbInner); err != nil { return err } log.Infof("loaded manifest with %d files", len(m.pbInner.Files)) return nil } func validateMagic(dat []byte) bool { ml := len([]byte(MAGIC)) if len(dat) < ml { return false } got := dat[0:ml] expected := []byte(MAGIC) return bytes.Equal(got, expected) } // NewManifestFromReader reads a manifest from an io.Reader. func NewManifestFromReader(input io.Reader) (*manifest, error) { m := New() dat, err := io.ReadAll(input) if err != nil { return nil, err } if !validateMagic(dat) { return nil, errors.New("invalid file format") } // remove magic bytes prefix: ml := len([]byte(MAGIC)) bb := bytes.NewBuffer(dat[ml:]) dat = bb.Bytes() // deserialize outer: m.pbOuter = new(MFFileOuter) if err := proto.Unmarshal(dat, m.pbOuter); err != nil { return nil, err } // deserialize inner: if err := m.deserializeInner(); err != nil { return nil, err } return m, nil } // NewManifestFromFile reads a manifest from a file path using the given filesystem. // If fs is nil, the real filesystem (OsFs) is used. func NewManifestFromFile(fs afero.Fs, path string) (*manifest, error) { if fs == nil { fs = afero.NewOsFs() } f, err := fs.Open(path) if err != nil { return nil, err } defer func() { _ = f.Close() }() return NewManifestFromReader(f) } // NewFromProto is deprecated, use NewManifestFromReader instead. func NewFromProto(input io.Reader) (*manifest, error) { return NewManifestFromReader(input) }