mirror of
https://github.com/anchore/syft.git
synced 2026-11-01 15:06:36 +01:00
fix: separate golang license caches from mod dir (#2852)
Signed-off-by: Keith Zantow <kzantow@gmail.com>
This commit is contained in:
Vendored
+120
@@ -0,0 +1,120 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/afero"
|
||||
|
||||
"github.com/anchore/syft/internal/log"
|
||||
)
|
||||
|
||||
// NewFromDir creates a new cache manager which returns caches stored on disk, rooted at the given directory
|
||||
func NewFromDir(dir string, ttl time.Duration) (Manager, error) {
|
||||
dir = filepath.Clean(dir)
|
||||
fsys, err := subFs(afero.NewOsFs(), dir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &filesystemCache{
|
||||
dir: dir,
|
||||
fs: fsys,
|
||||
ttl: ttl,
|
||||
}, nil
|
||||
}
|
||||
|
||||
const filePermissions = 0700
|
||||
const dirPermissions = os.ModeDir | filePermissions
|
||||
|
||||
type filesystemCache struct {
|
||||
dir string
|
||||
fs afero.Fs
|
||||
ttl time.Duration
|
||||
}
|
||||
|
||||
func (d *filesystemCache) GetCache(name, version string) Cache {
|
||||
fsys, err := subFs(d.fs, name, version)
|
||||
if err != nil {
|
||||
log.Warnf("error getting cache for: %s/%s: %v", name, version, err)
|
||||
return &bypassedCache{}
|
||||
}
|
||||
return &filesystemCache{
|
||||
dir: filepath.Join(d.dir, name, version),
|
||||
fs: fsys,
|
||||
ttl: d.ttl,
|
||||
}
|
||||
}
|
||||
|
||||
func (d *filesystemCache) RootDirs() []string {
|
||||
if d.dir == "" {
|
||||
return nil
|
||||
}
|
||||
return []string{d.dir}
|
||||
}
|
||||
|
||||
func (d *filesystemCache) Read(key string) (ReaderAtCloser, error) {
|
||||
path := makeDiskKey(key)
|
||||
f, err := d.fs.Open(path)
|
||||
if err != nil {
|
||||
log.Tracef("no cache entry for %s %s: %v", d.dir, key, err)
|
||||
return nil, errNotFound
|
||||
} else if stat, err := f.Stat(); err != nil || stat == nil || time.Since(stat.ModTime()) > d.ttl {
|
||||
log.Tracef("cache entry is too old for %s %s", d.dir, key)
|
||||
return nil, errExpired
|
||||
}
|
||||
log.Tracef("using cache for %s %s", d.dir, key)
|
||||
return f, nil
|
||||
}
|
||||
|
||||
func (d *filesystemCache) Write(key string, contents io.Reader) error {
|
||||
path := makeDiskKey(key)
|
||||
return afero.WriteReader(d.fs, path, contents)
|
||||
}
|
||||
|
||||
// subFs returns a writable directory with the given name under the root cache directory returned from findRoot,
|
||||
// the directory will be created if it does not exist
|
||||
func subFs(fsys afero.Fs, subDirs ...string) (afero.Fs, error) {
|
||||
dir := filepath.Join(subDirs...)
|
||||
dir = filepath.Clean(dir)
|
||||
stat, err := fsys.Stat(dir)
|
||||
if errors.Is(err, afero.ErrFileNotFound) {
|
||||
err = fsys.MkdirAll(dir, dirPermissions)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to create directory at '%s/%s': %v", dir, strings.Join(subDirs, "/"), err)
|
||||
}
|
||||
stat, err = fsys.Stat(dir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if err != nil || stat == nil || !stat.IsDir() {
|
||||
return nil, fmt.Errorf("unable to verify directory '%s': %v", dir, err)
|
||||
}
|
||||
fsys = afero.NewBasePathFs(fsys, dir)
|
||||
return fsys, err
|
||||
}
|
||||
|
||||
var keyReplacer = regexp.MustCompile("[^-._/a-zA-Z0-9]")
|
||||
|
||||
// makeDiskKey makes a safe sub-path but not escape forward slashes, this allows for logical partitioning on disk
|
||||
func makeDiskKey(key string) string {
|
||||
// encode single dot directory
|
||||
if key == "." {
|
||||
return "%2E"
|
||||
}
|
||||
// replace any disallowed chars with encoded form
|
||||
key = keyReplacer.ReplaceAllStringFunc(key, url.QueryEscape)
|
||||
// allow . in names but not ..
|
||||
key = strings.ReplaceAll(key, "..", "%2E%2E")
|
||||
return key
|
||||
}
|
||||
|
||||
var errNotFound = fmt.Errorf("not found")
|
||||
var errExpired = fmt.Errorf("expired")
|
||||
Reference in New Issue
Block a user