10 "github.com/Sirupsen/logrus"
11 "github.com/docker/docker/pkg/ioutils"
12 "github.com/opencontainers/go-digest"
13 "github.com/pkg/errors"
16 // DigestWalkFunc is function called by StoreBackend.Walk
17 type DigestWalkFunc func(id digest.Digest) error
19 // StoreBackend provides interface for image.Store persistence
20 type StoreBackend interface {
21 Walk(f DigestWalkFunc) error
22 Get(id digest.Digest) ([]byte, error)
23 Set(data []byte) (digest.Digest, error)
24 Delete(id digest.Digest) error
25 SetMetadata(id digest.Digest, key string, data []byte) error
26 GetMetadata(id digest.Digest, key string) ([]byte, error)
27 DeleteMetadata(id digest.Digest, key string) error
30 // fs implements StoreBackend using the filesystem.
37 contentDirName = "content"
38 metadataDirName = "metadata"
41 // NewFSStoreBackend returns new filesystem based backend for image.Store
42 func NewFSStoreBackend(root string) (StoreBackend, error) {
43 return newFSStore(root)
46 func newFSStore(root string) (*fs, error) {
50 if err := os.MkdirAll(filepath.Join(root, contentDirName, string(digest.Canonical)), 0700); err != nil {
51 return nil, errors.Wrap(err, "failed to create storage backend")
53 if err := os.MkdirAll(filepath.Join(root, metadataDirName, string(digest.Canonical)), 0700); err != nil {
54 return nil, errors.Wrap(err, "failed to create storage backend")
59 func (s *fs) contentFile(dgst digest.Digest) string {
60 return filepath.Join(s.root, contentDirName, string(dgst.Algorithm()), dgst.Hex())
63 func (s *fs) metadataDir(dgst digest.Digest) string {
64 return filepath.Join(s.root, metadataDirName, string(dgst.Algorithm()), dgst.Hex())
67 // Walk calls the supplied callback for each image ID in the storage backend.
68 func (s *fs) Walk(f DigestWalkFunc) error {
69 // Only Canonical digest (sha256) is currently supported
71 dir, err := ioutil.ReadDir(filepath.Join(s.root, contentDirName, string(digest.Canonical)))
76 for _, v := range dir {
77 dgst := digest.NewDigestFromHex(string(digest.Canonical), v.Name())
78 if err := dgst.Validate(); err != nil {
79 logrus.Debugf("skipping invalid digest %s: %s", dgst, err)
82 if err := f(dgst); err != nil {
89 // Get returns the content stored under a given digest.
90 func (s *fs) Get(dgst digest.Digest) ([]byte, error) {
97 func (s *fs) get(dgst digest.Digest) ([]byte, error) {
98 content, err := ioutil.ReadFile(s.contentFile(dgst))
100 return nil, errors.Wrapf(err, "failed to get digest %s", dgst)
103 // todo: maybe optional
104 if digest.FromBytes(content) != dgst {
105 return nil, fmt.Errorf("failed to verify: %v", dgst)
111 // Set stores content by checksum.
112 func (s *fs) Set(data []byte) (digest.Digest, error) {
117 return "", fmt.Errorf("invalid empty data")
120 dgst := digest.FromBytes(data)
121 if err := ioutils.AtomicWriteFile(s.contentFile(dgst), data, 0600); err != nil {
122 return "", errors.Wrap(err, "failed to write digest data")
128 // Delete removes content and metadata files associated with the digest.
129 func (s *fs) Delete(dgst digest.Digest) error {
133 if err := os.RemoveAll(s.metadataDir(dgst)); err != nil {
136 if err := os.Remove(s.contentFile(dgst)); err != nil {
142 // SetMetadata sets metadata for a given ID. It fails if there's no base file.
143 func (s *fs) SetMetadata(dgst digest.Digest, key string, data []byte) error {
146 if _, err := s.get(dgst); err != nil {
150 baseDir := filepath.Join(s.metadataDir(dgst))
151 if err := os.MkdirAll(baseDir, 0700); err != nil {
154 return ioutils.AtomicWriteFile(filepath.Join(s.metadataDir(dgst), key), data, 0600)
157 // GetMetadata returns metadata for a given digest.
158 func (s *fs) GetMetadata(dgst digest.Digest, key string) ([]byte, error) {
162 if _, err := s.get(dgst); err != nil {
165 bytes, err := ioutil.ReadFile(filepath.Join(s.metadataDir(dgst), key))
167 return nil, errors.Wrap(err, "failed to read metadata")
172 // DeleteMetadata removes the metadata associated with a digest.
173 func (s *fs) DeleteMetadata(dgst digest.Digest, key string) error {
177 return os.RemoveAll(filepath.Join(s.metadataDir(dgst), key))