github.com/anchore/syft@v1.4.2-0.20240516191711-1bec1fc5d397/syft/internal/unionreader/union_reader.go (about) 1 package unionreader 2 3 import ( 4 "bytes" 5 "fmt" 6 "io" 7 8 macho "github.com/anchore/go-macholibre" 9 "github.com/anchore/syft/internal/log" 10 "github.com/anchore/syft/syft/file" 11 ) 12 13 // UnionReader is a single interface with all reading functions needed by multi-arch binary catalogers 14 // cataloger. 15 type UnionReader interface { 16 io.Reader 17 io.ReaderAt 18 io.Seeker 19 io.Closer 20 } 21 22 // GetReaders extracts one or more io.ReaderAt objects representing binaries that can be processed (multiple binaries in the case for multi-architecture binaries). 23 func GetReaders(f UnionReader) ([]io.ReaderAt, error) { 24 if macho.IsUniversalMachoBinary(f) { 25 machoReaders, err := macho.ExtractReaders(f) 26 if err != nil { 27 log.Debugf("extracting readers: %v", err) 28 return nil, err 29 } 30 31 var readers []io.ReaderAt 32 for _, e := range machoReaders { 33 readers = append(readers, e.Reader) 34 } 35 36 return readers, nil 37 } 38 39 return []io.ReaderAt{f}, nil 40 } 41 42 func GetUnionReader(readerCloser io.ReadCloser) (UnionReader, error) { 43 reader, ok := readerCloser.(UnionReader) 44 if ok { 45 return reader, nil 46 } 47 48 // file.LocationReadCloser embeds a ReadCloser, which is likely 49 // to implement UnionReader. Check whether the embedded read closer 50 // implements UnionReader, and just return that if so. 51 r, ok := readerCloser.(file.LocationReadCloser) 52 if ok { 53 ur, ok := r.ReadCloser.(UnionReader) 54 if ok { 55 return ur, nil 56 } 57 } 58 59 b, err := io.ReadAll(readerCloser) 60 if err != nil { 61 return nil, fmt.Errorf("unable to read contents from binary: %w", err) 62 } 63 64 bytesReader := bytes.NewReader(b) 65 66 reader = struct { 67 io.ReadCloser 68 io.ReaderAt 69 io.Seeker 70 }{ 71 ReadCloser: io.NopCloser(bytesReader), 72 ReaderAt: bytesReader, 73 Seeker: bytesReader, 74 } 75 76 return reader, nil 77 }