mirror of
https://github.com/anchore/syft.git
synced 2026-10-31 06:26:30 +01:00
* fix: re-use embedded union reader if possible Previously, because file.LocationReadCloser embeds a ReadCloser that might be a UnionReader, but doesn't implement the interface itself, the type assertion would fall and Syft would fall back to io.ReadAll to enable seeking on the underlying reader, resulting in a potentially large extra allocation. Instead, check whether the passed ReadCloser is a file.LocationReadCloser, and if so, try to use the embedded ReadCloser as a UnionReader. Signed-off-by: Will Murphy <will.murphy@anchore.com> * lint fix Signed-off-by: Will Murphy <will.murphy@anchore.com> * Assert that underlying reader is returned Signed-off-by: Will Murphy <will.murphy@anchore.com> --------- Signed-off-by: Will Murphy <will.murphy@anchore.com>
78 lines
1.7 KiB
Go
78 lines
1.7 KiB
Go
package unionreader
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"io"
|
|
|
|
macho "github.com/anchore/go-macholibre"
|
|
"github.com/anchore/syft/internal/log"
|
|
"github.com/anchore/syft/syft/file"
|
|
)
|
|
|
|
// UnionReader is a single interface with all reading functions needed by multi-arch binary catalogers
|
|
// cataloger.
|
|
type UnionReader interface {
|
|
io.Reader
|
|
io.ReaderAt
|
|
io.Seeker
|
|
io.Closer
|
|
}
|
|
|
|
// GetReaders extracts one or more io.ReaderAt objects representing binaries that can be processed (multiple binaries in the case for multi-architecture binaries).
|
|
func GetReaders(f UnionReader) ([]io.ReaderAt, error) {
|
|
if macho.IsUniversalMachoBinary(f) {
|
|
machoReaders, err := macho.ExtractReaders(f)
|
|
if err != nil {
|
|
log.Debugf("extracting readers: %v", err)
|
|
return nil, err
|
|
}
|
|
|
|
var readers []io.ReaderAt
|
|
for _, e := range machoReaders {
|
|
readers = append(readers, e.Reader)
|
|
}
|
|
|
|
return readers, nil
|
|
}
|
|
|
|
return []io.ReaderAt{f}, nil
|
|
}
|
|
|
|
func GetUnionReader(readerCloser io.ReadCloser) (UnionReader, error) {
|
|
reader, ok := readerCloser.(UnionReader)
|
|
if ok {
|
|
return reader, nil
|
|
}
|
|
|
|
// file.LocationReadCloser embeds a ReadCloser, which is likely
|
|
// to implement UnionReader. Check whether the embedded read closer
|
|
// implements UnionReader, and just return that if so.
|
|
r, ok := readerCloser.(file.LocationReadCloser)
|
|
if ok {
|
|
ur, ok := r.ReadCloser.(UnionReader)
|
|
if ok {
|
|
return ur, nil
|
|
}
|
|
}
|
|
|
|
b, err := io.ReadAll(readerCloser)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("unable to read contents from binary: %w", err)
|
|
}
|
|
|
|
bytesReader := bytes.NewReader(b)
|
|
|
|
reader = struct {
|
|
io.ReadCloser
|
|
io.ReaderAt
|
|
io.Seeker
|
|
}{
|
|
ReadCloser: io.NopCloser(bytesReader),
|
|
ReaderAt: bytesReader,
|
|
Seeker: bytesReader,
|
|
}
|
|
|
|
return reader, nil
|
|
}
|