github.com/noqcks/syft@v0.0.0-20230920222752-a9e2c4e288e5/syft/file/cataloger/filedigest/cataloger.go (about)

     1  package filedigest
     2  
     3  import (
     4  	"crypto"
     5  	"errors"
     6  
     7  	"github.com/wagoodman/go-partybus"
     8  	"github.com/wagoodman/go-progress"
     9  
    10  	stereoscopeFile "github.com/anchore/stereoscope/pkg/file"
    11  	"github.com/anchore/syft/internal"
    12  	"github.com/anchore/syft/internal/bus"
    13  	intFile "github.com/anchore/syft/internal/file"
    14  	"github.com/anchore/syft/internal/log"
    15  	"github.com/anchore/syft/syft/event"
    16  	"github.com/anchore/syft/syft/file"
    17  	intCataloger "github.com/anchore/syft/syft/file/cataloger/internal"
    18  )
    19  
    20  var ErrUndigestableFile = errors.New("undigestable file")
    21  
    22  type Cataloger struct {
    23  	hashes []crypto.Hash
    24  }
    25  
    26  func NewCataloger(hashes []crypto.Hash) *Cataloger {
    27  	return &Cataloger{
    28  		hashes: hashes,
    29  	}
    30  }
    31  
    32  func (i *Cataloger) Catalog(resolver file.Resolver, coordinates ...file.Coordinates) (map[file.Coordinates][]file.Digest, error) {
    33  	results := make(map[file.Coordinates][]file.Digest)
    34  	var locations []file.Location
    35  
    36  	if len(coordinates) == 0 {
    37  		locations = intCataloger.AllRegularFiles(resolver)
    38  	} else {
    39  		for _, c := range coordinates {
    40  			locations = append(locations, file.NewLocationFromCoordinates(c))
    41  		}
    42  	}
    43  
    44  	stage, prog := digestsCatalogingProgress(int64(len(locations)))
    45  	for _, location := range locations {
    46  		stage.Current = location.RealPath
    47  		result, err := i.catalogLocation(resolver, location)
    48  
    49  		if errors.Is(err, ErrUndigestableFile) {
    50  			continue
    51  		}
    52  
    53  		if internal.IsErrPathPermission(err) {
    54  			log.Debugf("file digests cataloger skipping %q: %+v", location.RealPath, err)
    55  			continue
    56  		}
    57  
    58  		if err != nil {
    59  			return nil, err
    60  		}
    61  		prog.Increment()
    62  		results[location.Coordinates] = result
    63  	}
    64  	log.Debugf("file digests cataloger processed %d files", prog.Current())
    65  	prog.SetCompleted()
    66  	return results, nil
    67  }
    68  
    69  func (i *Cataloger) catalogLocation(resolver file.Resolver, location file.Location) ([]file.Digest, error) {
    70  	meta, err := resolver.FileMetadataByLocation(location)
    71  	if err != nil {
    72  		return nil, err
    73  	}
    74  
    75  	// we should only attempt to report digests for files that are regular files (don't attempt to resolve links)
    76  	if meta.Type != stereoscopeFile.TypeRegular {
    77  		return nil, ErrUndigestableFile
    78  	}
    79  
    80  	contentReader, err := resolver.FileContentsByLocation(location)
    81  	if err != nil {
    82  		return nil, err
    83  	}
    84  	defer internal.CloseAndLogError(contentReader, location.VirtualPath)
    85  
    86  	digests, err := intFile.NewDigestsFromFile(contentReader, i.hashes)
    87  	if err != nil {
    88  		return nil, internal.ErrPath{Context: "digests-cataloger", Path: location.RealPath, Err: err}
    89  	}
    90  
    91  	return digests, nil
    92  }
    93  
    94  func digestsCatalogingProgress(locations int64) (*progress.Stage, *progress.Manual) {
    95  	stage := &progress.Stage{}
    96  	prog := progress.NewManual(locations)
    97  
    98  	bus.Publish(partybus.Event{
    99  		Type: event.FileDigestsCatalogerStarted,
   100  		Value: struct {
   101  			progress.Stager
   102  			progress.Progressable
   103  		}{
   104  			Stager:       progress.Stager(stage),
   105  			Progressable: prog,
   106  		},
   107  	})
   108  
   109  	return stage, prog
   110  }