github.com/lineaje-labs/syft@v0.98.1-0.20231227153149-9e393f60ff1b/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/syft/event"
    12  	"github.com/anchore/syft/syft/file"
    13  	"github.com/lineaje-labs/syft/internal"
    14  	"github.com/lineaje-labs/syft/internal/bus"
    15  	intFile "github.com/lineaje-labs/syft/internal/file"
    16  	"github.com/lineaje-labs/syft/internal/log"
    17  	intCataloger "github.com/lineaje-labs/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(
    33  	resolver file.Resolver, coordinates ...file.Coordinates,
    34  ) (map[file.Coordinates][]file.Digest, error) {
    35  	results := make(map[file.Coordinates][]file.Digest)
    36  	var locations []file.Location
    37  
    38  	if len(coordinates) == 0 {
    39  		locations = intCataloger.AllRegularFiles(resolver)
    40  	} else {
    41  		for _, c := range coordinates {
    42  			locations = append(locations, file.NewLocationFromCoordinates(c))
    43  		}
    44  	}
    45  
    46  	stage, prog := digestsCatalogingProgress(int64(len(locations)))
    47  	for _, location := range locations {
    48  		stage.Current = location.RealPath
    49  		result, err := i.catalogLocation(resolver, location)
    50  
    51  		if errors.Is(err, ErrUndigestableFile) {
    52  			continue
    53  		}
    54  
    55  		if internal.IsErrPathPermission(err) {
    56  			log.Debugf("file digests cataloger skipping %q: %+v", location.RealPath, err)
    57  			continue
    58  		}
    59  
    60  		if err != nil {
    61  			return nil, err
    62  		}
    63  		prog.Increment()
    64  		results[location.Coordinates] = result
    65  	}
    66  	log.Debugf("file digests cataloger processed %d files", prog.Current())
    67  	prog.SetCompleted()
    68  	return results, nil
    69  }
    70  
    71  func (i *Cataloger) catalogLocation(resolver file.Resolver, location file.Location) ([]file.Digest, error) {
    72  	meta, err := resolver.FileMetadataByLocation(location)
    73  	if err != nil {
    74  		return nil, err
    75  	}
    76  
    77  	// we should only attempt to report digests for files that are regular files (don't attempt to resolve links)
    78  	if meta.Type != stereoscopeFile.TypeRegular {
    79  		return nil, ErrUndigestableFile
    80  	}
    81  
    82  	contentReader, err := resolver.FileContentsByLocation(location)
    83  	if err != nil {
    84  		return nil, err
    85  	}
    86  	defer internal.CloseAndLogError(contentReader, location.AccessPath)
    87  
    88  	digests, err := intFile.NewDigestsFromFile(contentReader, i.hashes)
    89  	if err != nil {
    90  		return nil, internal.ErrPath{Context: "digests-cataloger", Path: location.RealPath, Err: err}
    91  	}
    92  
    93  	return digests, nil
    94  }
    95  
    96  func digestsCatalogingProgress(locations int64) (*progress.Stage, *progress.Manual) {
    97  	stage := &progress.Stage{}
    98  	prog := progress.NewManual(locations)
    99  
   100  	bus.Publish(partybus.Event{
   101  		Type: event.FileDigestsCatalogerStarted,
   102  		Value: struct {
   103  			progress.Stager
   104  			progress.Progressable
   105  		}{
   106  			Stager:       progress.Stager(stage),
   107  			Progressable: prog,
   108  		},
   109  	})
   110  
   111  	return stage, prog
   112  }