Repository navigation
Expand file tree
/
Copy pathhex.go
More file actions
127 lines (116 loc) · 2.83 KB
/
Copy pathhex.go
File metadata and controls
127 lines (116 loc) · 2.83 KB
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
package archives
import (
"archive/tar"
"bytes"
"context"
"crypto/sha256"
"encoding/hex"
"io"
"strings"
)
const hexContents = "contents.tar.gz"
// Hex packages have four regular outer entries, in any order.
func isHex(content io.Reader, options StreamOptions) (bool, error) {
tr := tar.NewReader(content)
budget := Stream{options: containerOptions(options)}
var limitErr error
remaining := map[string]bool{"VERSION": true, "CHECKSUM": true, "metadata.config": true, hexContents: true}
for len(remaining) != 0 {
h, err := tr.Next()
if err != nil || !remaining[h.Name] || h.Typeflag != tar.TypeReg {
return false, nil
}
if limitErr == nil {
limitErr = budget.checkEntry(&StreamEntry{FileInfo: fileInfoFromTar(h)})
if limitErr == nil {
budget.entries++
budget.expanded += h.Size
}
}
switch h.Name {
case "VERSION":
if h.Size != 1 {
return false, nil
}
var version [1]byte
if _, err := io.ReadFull(tr, version[:]); err != nil || version[0] != '3' {
return false, nil
}
case "CHECKSUM":
if h.Size != int64(hex.EncodedLen(sha256.Size)) {
return false, nil
}
checksum, err := io.ReadAll(tr)
if err != nil || string(checksum) != strings.ToUpper(string(checksum)) {
return false, nil
}
if _, err := hex.DecodeString(string(checksum)); err != nil {
return false, nil
}
}
delete(remaining, h.Name)
}
return true, limitErr
}
type plainTarStream struct {
ctx context.Context
content io.Reader
options StreamOptions
source streamSource
}
func (s *plainTarStream) Next() (*StreamEntry, error) {
if s.source == nil {
probe := &hexProbe{reader: s.content}
hexPackage, err := isHex(probe, s.options)
if err != nil {
return nil, err
}
input := io.MultiReader(bytes.NewReader(probe.prefix.Bytes()), &probeTail{reader: s.content, err: probe.err})
s.content = nil
if hexPackage {
s.source = &nestedTarStream{outer: newContainerTar(s.ctx, input, s.options), member: hexContents, kind: "Hex"}
} else {
var err error
s.source, err = newTarStream(s.ctx, input, "")
if err != nil {
return nil, err
}
}
}
return s.source.Next()
}
func (s *plainTarStream) Read(p []byte) (int, error) {
if s.source == nil {
return 0, io.EOF
}
return s.source.Read(p)
}
func (s *plainTarStream) Close() error {
s.content = nil
if s.source != nil {
return s.source.Close()
}
return nil
}
// Preserve probe errors as well as bytes so a failed probe cannot hide input errors.
type hexProbe struct {
reader io.Reader
prefix bytes.Buffer
err error
}
func (p *hexProbe) Read(b []byte) (int, error) {
n, err := p.reader.Read(b)
_, _ = p.prefix.Write(b[:n])
p.err = err
return n, err
}
type probeTail struct {
reader io.Reader
err error
}
func (p *probeTail) Read(b []byte) (int, error) {
if p.err != nil {
return 0, p.err
}
return p.reader.Read(b)
}