forked from box-builder/overmount
/
asset.go
97 lines (77 loc) · 2.54 KB
/
asset.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
package overmount
import (
"io"
"github.com/docker/docker/pkg/archive"
digest "github.com/opencontainers/go-digest"
)
const emptyDigest = digest.Digest("sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855")
// Asset is the representation of an on-disk asset. Assets usually are a pair
// of (path, tar) where one direction is applied; f.e., you can copy from the
// tar to the dir, or the dir to the tar using the Read and Write calls.
type Asset struct {
path string
digest digest.Digester
}
// NewAsset constructs a new *Asset that operates on path `path`. A digester
// must be provided. Typically this is a `digest.SHA256.Digester()` but can be
// any algorithm that opencontainers/go-digest supports.
func NewAsset(path string, digest digest.Digester) (*Asset, error) {
a := &Asset{
path: path,
digest: digest,
}
return a, nil
}
// Digest returns the digest of the last pack or unpack.
func (a *Asset) Digest() digest.Digest {
return a.digest.Digest()
}
// LoadDigest processes the digest from the existing contents of the filesystem.
func (a *Asset) LoadDigest() (digest.Digest, error) {
a.resetDigest()
reader, err := archive.Tar(a.path, archive.Uncompressed)
if err != nil {
return a.Digest(), err
}
_, err = io.Copy(a.digest.Hash(), reader)
return a.Digest(), err
}
// Path gets the filesystem path we will be working with.
func (a *Asset) Path() string {
return a.path
}
// Unpack from the io.Reader (must be a tar file!) and unpack to the filesystem.
// Accepts io.Reader, not *tar.Reader!
func (a *Asset) Unpack(reader io.Reader) error {
a.resetDigest()
if err := checkDir(a.path, ErrInvalidAsset); err != nil {
return err
}
// FIXME there's probably a double-unarchive bug here.
err := archive.Unpack(io.TeeReader(reader, a.digest.Hash()), a.path, &archive.TarOptions{WhiteoutFormat: archive.OverlayWhiteoutFormat})
if err != nil {
return err
}
return nil
}
// Pack a tarball from the filesystem. Accepts an io.Writer, not a
// *tar.Writer!
func (a *Asset) Pack(writer io.Writer) error {
a.resetDigest()
if err := checkDir(a.path, ErrInvalidAsset); err != nil {
return err
}
reader, err := archive.Tar(a.path, archive.Uncompressed)
if err != nil {
return err
}
if _, err := io.Copy(writer, io.TeeReader(reader, a.digest.Hash())); err != nil {
return err
}
return nil
}
// resetDigest resets the digester so it can re-calculate e.g. in a scenario
// where more than one read/write (or swapping between the two) is called.
func (a *Asset) resetDigest() {
a.digest = digest.SHA256.Digester()
}