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 }