github.com/nextlinux/gosbom@v0.81.1-0.20230627115839-1ff50c281391/gosbom/file/cataloger/filedigest/cataloger.go (about)

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