forked from quay/claircore
-
Notifications
You must be signed in to change notification settings - Fork 0
/
layer.go
132 lines (118 loc) · 3.22 KB
/
layer.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
package claircore
import (
"bytes"
"errors"
"fmt"
"io"
"io/fs"
"os"
"path/filepath"
"github.com/quay/claircore/pkg/tarfs"
)
// Layer is a container image filesystem layer. Layers are stacked
// on top of each other to comprise the final filesystem of the container image.
type Layer struct {
// Hash is a content addressable hash uniqely identifying this layer.
// Libindex will treat layers with this same hash as identical.
Hash Digest `json:"hash"`
URI string `json:"uri"`
Headers map[string][]string `json:"headers"`
// path to local file containing uncompressed tar archive of the layer's content
localPath string
}
func (l *Layer) SetLocal(f string) error {
l.localPath = f
return nil
}
func (l *Layer) Fetched() bool {
_, err := os.Stat(l.localPath)
return err == nil
}
// Reader returns a ReadAtCloser of the layer.
//
// It should also implement io.Seeker, and should be a tar stream.
func (l *Layer) Reader() (ReadAtCloser, error) {
if l.localPath == "" {
return nil, fmt.Errorf("claircore: Layer not fetched")
}
f, err := os.Open(l.localPath)
if err != nil {
return nil, fmt.Errorf("claircore: unable to open tar: %w", err)
}
return f, nil
}
// ReadAtCloser is an io.ReadCloser and also an io.ReaderAt
type ReadAtCloser interface {
io.ReadCloser
io.ReaderAt
}
// NormalizeIn is used to make sure paths are tar-root relative.
func normalizeIn(in, p string) string {
p = filepath.Clean(p)
if !filepath.IsAbs(p) {
p = filepath.Join(in, p)
}
if filepath.IsAbs(p) {
p = p[1:]
}
return p
}
// ErrNotFound is returned by Layer.Files if none of the requested files are
// found.
var ErrNotFound = errors.New("claircore: unable to find any requested files")
// Files retrieves specific files from the layer's tar archive.
//
// An error is returned only if none of the requested files are found.
//
// The returned map may contain more entries than the number of paths requested.
// All entries in the map are keyed by paths that are relative to the tar-root.
// For example, requesting paths of "/etc/os-release", "./etc/os-release", and
// "etc/os-release" will all result in any found content being stored with the
// key "etc/os-release".
//
// Deprecated: Callers should instead use `pkg/tarfs` and the `io/fs` package.
func (l *Layer) Files(paths ...string) (map[string]*bytes.Buffer, error) {
r, err := l.Reader()
if err != nil {
return nil, err
}
defer r.Close()
sys, err := tarfs.New(r.(*os.File))
if err != nil {
return nil, err
}
// Clean the input paths.
want := make(map[string]struct{})
for i, p := range paths {
p := normalizeIn("/", p)
paths[i] = p
want[p] = struct{}{}
}
f := make(map[string]*bytes.Buffer)
// Walk the fs. ReadFile will handle symlink resolution.
if err := fs.WalkDir(sys, ".", func(p string, d fs.DirEntry, err error) error {
switch {
case err != nil:
return err
case d.IsDir():
return nil
}
if _, ok := want[p]; !ok {
return nil
}
delete(want, p)
b, err := fs.ReadFile(sys, p)
if err != nil {
return err
}
f[p] = bytes.NewBuffer(b)
return nil
}); err != nil {
return nil, err
}
// If there's nothing in the "f" map, we didn't find anything.
if len(f) == 0 {
return nil, ErrNotFound
}
return f, nil
}