github.com/devseccon/trivy@v0.47.1-0.20231123133102-bd902a0bd996/pkg/fanal/walker/cached_file.go (about) 1 package walker 2 3 import ( 4 "bytes" 5 "io" 6 "os" 7 "sync" 8 9 "golang.org/x/xerrors" 10 11 dio "github.com/aquasecurity/go-dep-parser/pkg/io" 12 ) 13 14 // cachedFile represents a file cached in memory or storage according to the file size. 15 type cachedFile struct { 16 once sync.Once 17 err error 18 19 size int64 20 reader io.Reader 21 22 threshold int64 // Files larger than this threshold are written to file without being read into memory. 23 24 content []byte // It will be populated if this file is small 25 filePath string // It will be populated if this file is large 26 } 27 28 func newCachedFile(size int64, r io.Reader, threshold int64) *cachedFile { 29 return &cachedFile{ 30 size: size, 31 reader: r, 32 threshold: threshold, 33 } 34 } 35 36 // Open opens a file and cache the file. 37 // If the file size is greater than or equal to threshold, it copies the content to a temp file and opens it next time. 38 // If the file size is less than threshold, it opens the file once and the content will be shared so that others analyzers can use the same data. 39 func (o *cachedFile) Open() (dio.ReadSeekCloserAt, error) { 40 o.once.Do(func() { 41 // When the file is large, it will be written down to a temp file. 42 if o.size >= o.threshold { 43 f, err := os.CreateTemp("", "fanal-*") 44 if err != nil { 45 o.err = xerrors.Errorf("failed to create the temp file: %w", err) 46 return 47 } 48 49 if _, err = io.Copy(f, o.reader); err != nil { 50 o.err = xerrors.Errorf("failed to copy: %w", err) 51 return 52 } 53 54 o.filePath = f.Name() 55 } else { 56 b, err := io.ReadAll(o.reader) 57 if err != nil { 58 o.err = xerrors.Errorf("unable to read the file: %w", err) 59 return 60 } 61 o.content = b 62 } 63 }) 64 if o.err != nil { 65 return nil, xerrors.Errorf("failed to open: %w", o.err) 66 } 67 68 return o.open() 69 } 70 71 func (o *cachedFile) open() (dio.ReadSeekCloserAt, error) { 72 if o.filePath != "" { 73 f, err := os.Open(o.filePath) 74 if err != nil { 75 return nil, xerrors.Errorf("failed to open the temp file: %w", err) 76 } 77 return f, nil 78 } 79 80 return dio.NopCloser(bytes.NewReader(o.content)), nil 81 } 82 83 func (o *cachedFile) Clean() error { 84 return os.Remove(o.filePath) 85 }