-
Notifications
You must be signed in to change notification settings - Fork 2.4k
/
Copy pathtar.go
116 lines (99 loc) · 2.4 KB
/
tar.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
package walker
import (
"archive/tar"
"io"
"io/fs"
"path/filepath"
"strings"
"golang.org/x/xerrors"
)
const (
opq string = ".wh..wh..opq"
wh string = ".wh."
)
type LayerTar struct {
walker
threshold int64
}
func NewLayerTar(skipFiles, skipDirs []string, slow bool) LayerTar {
threshold := defaultSizeThreshold
if slow {
threshold = slowSizeThreshold
}
return LayerTar{
walker: newWalker(skipFiles, skipDirs, slow),
threshold: threshold,
}
}
func (w LayerTar) Walk(layer io.Reader, analyzeFn WalkFunc) ([]string, []string, error) {
var opqDirs, whFiles, skipDirs []string
tr := tar.NewReader(layer)
for {
hdr, err := tr.Next()
if err == io.EOF {
break
} else if err != nil {
return nil, nil, xerrors.Errorf("failed to extract the archive: %w", err)
}
filePath := hdr.Name
filePath = strings.TrimLeft(filepath.Clean(filePath), "/")
fileDir, fileName := filepath.Split(filePath)
// e.g. etc/.wh..wh..opq
if opq == fileName {
opqDirs = append(opqDirs, fileDir)
continue
}
// etc/.wh.hostname
if strings.HasPrefix(fileName, wh) {
name := strings.TrimPrefix(fileName, wh)
fpath := filepath.Join(fileDir, name)
whFiles = append(whFiles, fpath)
continue
}
switch hdr.Typeflag {
case tar.TypeDir:
if w.shouldSkipDir(filePath) {
skipDirs = append(skipDirs, filePath)
continue
}
case tar.TypeReg:
if w.shouldSkipFile(filePath) {
continue
}
// symlinks and hardlinks have no content in reader, skip them
default:
continue
}
if underSkippedDir(filePath, skipDirs) {
continue
}
// A symbolic/hard link or regular file will reach here.
if err = w.processFile(filePath, tr, hdr.FileInfo(), analyzeFn); err != nil {
return nil, nil, xerrors.Errorf("failed to process the file: %w", err)
}
}
return opqDirs, whFiles, nil
}
func (w LayerTar) processFile(filePath string, tr *tar.Reader, fi fs.FileInfo, analyzeFn WalkFunc) error {
cf := newCachedFile(fi.Size(), tr, w.threshold)
defer func() {
// nolint
_ = cf.Clean()
}()
if err := analyzeFn(filePath, fi, cf.Open); err != nil {
return xerrors.Errorf("failed to analyze file: %w", err)
}
return nil
}
func underSkippedDir(filePath string, skipDirs []string) bool {
for _, skipDir := range skipDirs {
rel, err := filepath.Rel(skipDir, filePath)
if err != nil {
return false
}
if !strings.HasPrefix(rel, "../") {
return true
}
}
return false
}