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  }