github.com/devseccon/trivy@v0.47.1-0.20231123133102-bd902a0bd996/pkg/fanal/analyzer/fs.go (about)

     1  package analyzer
     2  
     3  import (
     4  	"errors"
     5  	"io"
     6  	"io/fs"
     7  	"os"
     8  	"path"
     9  
    10  	"golang.org/x/xerrors"
    11  
    12  	"github.com/devseccon/trivy/pkg/mapfs"
    13  	"github.com/devseccon/trivy/pkg/x/sync"
    14  )
    15  
    16  // CompositeFS contains multiple filesystems for post-analyzers
    17  type CompositeFS struct {
    18  	group AnalyzerGroup
    19  	dir   string
    20  	files *sync.Map[Type, *mapfs.FS]
    21  }
    22  
    23  func NewCompositeFS(group AnalyzerGroup) (*CompositeFS, error) {
    24  	tmpDir, err := os.MkdirTemp("", "analyzer-fs-*")
    25  	if err != nil {
    26  		return nil, xerrors.Errorf("unable to create temporary directory: %w", err)
    27  	}
    28  
    29  	return &CompositeFS{
    30  		group: group,
    31  		dir:   tmpDir,
    32  		files: new(sync.Map[Type, *mapfs.FS]),
    33  	}, nil
    34  }
    35  
    36  // CopyFileToTemp takes a file path and information, opens the file, copies its contents to a temporary file
    37  func (c *CompositeFS) CopyFileToTemp(opener Opener, info os.FileInfo) (string, error) {
    38  	// Create a temporary file to which the file in the layer will be copied
    39  	// so that all the files will not be loaded into memory
    40  	f, err := os.CreateTemp(c.dir, "file-*")
    41  	if err != nil {
    42  		return "", xerrors.Errorf("create temp error: %w", err)
    43  	}
    44  	defer f.Close()
    45  
    46  	// Open a file in the layer
    47  	r, err := opener()
    48  	if err != nil {
    49  		return "", xerrors.Errorf("file open error: %w", err)
    50  	}
    51  	defer r.Close()
    52  
    53  	// Copy file content into the temporary file
    54  	if _, err = io.Copy(f, r); err != nil {
    55  		return "", xerrors.Errorf("copy error: %w", err)
    56  	}
    57  
    58  	if err = os.Chmod(f.Name(), info.Mode()); err != nil {
    59  		return "", xerrors.Errorf("chmod error: %w", err)
    60  	}
    61  
    62  	return f.Name(), nil
    63  }
    64  
    65  // CreateLink creates a link in the virtual filesystem that corresponds to a real file.
    66  // The linked virtual file will have the same path as the real file path provided.
    67  func (c *CompositeFS) CreateLink(analyzerTypes []Type, rootDir, virtualPath, realPath string) error {
    68  	// Create fs.FS for each post-analyzer that wants to analyze the current file
    69  	for _, t := range analyzerTypes {
    70  		// Since filesystem scanning may require access outside the specified path, (e.g. Terraform modules)
    71  		// it allows "../" access with "WithUnderlyingRoot".
    72  		var opts []mapfs.Option
    73  		if rootDir != "" {
    74  			opts = append(opts, mapfs.WithUnderlyingRoot(rootDir))
    75  		}
    76  		mfs, _ := c.files.LoadOrStore(t, mapfs.New(opts...))
    77  		if d := path.Dir(virtualPath); d != "." {
    78  			if err := mfs.MkdirAll(d, os.ModePerm); err != nil && !errors.Is(err, fs.ErrExist) {
    79  				return xerrors.Errorf("mapfs mkdir error: %w", err)
    80  			}
    81  		}
    82  		if err := mfs.WriteFile(virtualPath, realPath); err != nil {
    83  			return xerrors.Errorf("mapfs write error: %w", err)
    84  		}
    85  	}
    86  	return nil
    87  }
    88  
    89  // Set sets the fs.FS for the specified post-analyzer
    90  func (c *CompositeFS) Set(t Type, mfs *mapfs.FS) {
    91  	c.files.Store(t, mfs)
    92  }
    93  
    94  // Get returns the fs.FS for the specified post-analyzer
    95  func (c *CompositeFS) Get(t Type) (*mapfs.FS, bool) {
    96  	return c.files.Load(t)
    97  }
    98  
    99  // Cleanup removes the temporary directory
   100  func (c *CompositeFS) Cleanup() error {
   101  	return os.RemoveAll(c.dir)
   102  }