github.com/lineaje-labs/syft@v0.98.1-0.20231227153149-9e393f60ff1b/internal/file/rpm_file_traversal.go (about)

     1  package file
     2  
     3  import (
     4  	"io"
     5  	"os"
     6  	"path/filepath"
     7  
     8  	"github.com/sassoftware/go-rpmutils"
     9  )
    10  
    11  // ExtractGlobsFromRPMToUniqueTempFile extracts paths matching the given globs within the given RPM to a temporary directory, returning file openers for each file extracted.
    12  func ExtractGlobsFromRPMToUniqueTempFile(rpmPath, dir string, globs ...string) (map[string]Opener, error) {
    13  	results := make(map[string]Opener)
    14  	var err error
    15  	// don't allow for full traversal, only select traversal from given paths
    16  	if len(globs) == 0 {
    17  		return results, nil
    18  	}
    19  	f, err := os.Open(rpmPath)
    20  	if err != nil {
    21  		return results, nil
    22  	}
    23  	defer f.Close()
    24  	rpm, err := rpmutils.ReadRpm(f)
    25  	if err != nil {
    26  		return results, nil
    27  	}
    28  	var files int
    29  	payload, err := rpm.PayloadReaderExtended()
    30  	if err != nil {
    31  		return results, nil
    32  	}
    33  	for {
    34  		file, err := payload.Next()
    35  		if err == io.EOF {
    36  			break
    37  		}
    38  		if !matchesAnyGlob(file.Name(), globs...) {
    39  			continue
    40  		}
    41  
    42  		// we have a file we want to extract....
    43  		tempfilePrefix := filepath.Base(filepath.Clean(file.Name())) + "-"
    44  		tempFile, err := os.CreateTemp(dir, tempfilePrefix)
    45  		if err != nil {
    46  			continue
    47  		}
    48  		// we shouldn't try and keep the tempfile open as the returned result may have several files, which takes up
    49  		// resources (leading to "too many open files"). Instead we'll return a file opener to the caller which
    50  		// provides a ReadCloser. It is up to the caller to handle closing the file explicitly.
    51  		if err := safeCopy(tempFile, payload); err != nil {
    52  			tempFile.Close()
    53  			continue
    54  		}
    55  		results[file.Name()] = Opener{path: tempFile.Name()}
    56  		tempFile.Close()
    57  		files++
    58  	}
    59  
    60  	return results, nil
    61  }