fs.go 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. package image
  2. import (
  3. "fmt"
  4. "io/ioutil"
  5. "os"
  6. "path/filepath"
  7. "sync"
  8. "github.com/Sirupsen/logrus"
  9. "github.com/docker/docker/pkg/ioutils"
  10. "github.com/opencontainers/go-digest"
  11. )
  12. // DigestWalkFunc is function called by StoreBackend.Walk
  13. type DigestWalkFunc func(id digest.Digest) error
  14. // StoreBackend provides interface for image.Store persistence
  15. type StoreBackend interface {
  16. Walk(f DigestWalkFunc) error
  17. Get(id digest.Digest) ([]byte, error)
  18. Set(data []byte) (digest.Digest, error)
  19. Delete(id digest.Digest) error
  20. SetMetadata(id digest.Digest, key string, data []byte) error
  21. GetMetadata(id digest.Digest, key string) ([]byte, error)
  22. DeleteMetadata(id digest.Digest, key string) error
  23. }
  24. // fs implements StoreBackend using the filesystem.
  25. type fs struct {
  26. sync.RWMutex
  27. root string
  28. }
  29. const (
  30. contentDirName = "content"
  31. metadataDirName = "metadata"
  32. )
  33. // NewFSStoreBackend returns new filesystem based backend for image.Store
  34. func NewFSStoreBackend(root string) (StoreBackend, error) {
  35. return newFSStore(root)
  36. }
  37. func newFSStore(root string) (*fs, error) {
  38. s := &fs{
  39. root: root,
  40. }
  41. if err := os.MkdirAll(filepath.Join(root, contentDirName, string(digest.Canonical)), 0700); err != nil {
  42. return nil, err
  43. }
  44. if err := os.MkdirAll(filepath.Join(root, metadataDirName, string(digest.Canonical)), 0700); err != nil {
  45. return nil, err
  46. }
  47. return s, nil
  48. }
  49. func (s *fs) contentFile(dgst digest.Digest) string {
  50. return filepath.Join(s.root, contentDirName, string(dgst.Algorithm()), dgst.Hex())
  51. }
  52. func (s *fs) metadataDir(dgst digest.Digest) string {
  53. return filepath.Join(s.root, metadataDirName, string(dgst.Algorithm()), dgst.Hex())
  54. }
  55. // Walk calls the supplied callback for each image ID in the storage backend.
  56. func (s *fs) Walk(f DigestWalkFunc) error {
  57. // Only Canonical digest (sha256) is currently supported
  58. s.RLock()
  59. dir, err := ioutil.ReadDir(filepath.Join(s.root, contentDirName, string(digest.Canonical)))
  60. s.RUnlock()
  61. if err != nil {
  62. return err
  63. }
  64. for _, v := range dir {
  65. dgst := digest.NewDigestFromHex(string(digest.Canonical), v.Name())
  66. if err := dgst.Validate(); err != nil {
  67. logrus.Debugf("skipping invalid digest %s: %s", dgst, err)
  68. continue
  69. }
  70. if err := f(dgst); err != nil {
  71. return err
  72. }
  73. }
  74. return nil
  75. }
  76. // Get returns the content stored under a given digest.
  77. func (s *fs) Get(dgst digest.Digest) ([]byte, error) {
  78. s.RLock()
  79. defer s.RUnlock()
  80. return s.get(dgst)
  81. }
  82. func (s *fs) get(dgst digest.Digest) ([]byte, error) {
  83. content, err := ioutil.ReadFile(s.contentFile(dgst))
  84. if err != nil {
  85. return nil, err
  86. }
  87. // todo: maybe optional
  88. if digest.FromBytes(content) != dgst {
  89. return nil, fmt.Errorf("failed to verify: %v", dgst)
  90. }
  91. return content, nil
  92. }
  93. // Set stores content by checksum.
  94. func (s *fs) Set(data []byte) (digest.Digest, error) {
  95. s.Lock()
  96. defer s.Unlock()
  97. if len(data) == 0 {
  98. return "", fmt.Errorf("invalid empty data")
  99. }
  100. dgst := digest.FromBytes(data)
  101. if err := ioutils.AtomicWriteFile(s.contentFile(dgst), data, 0600); err != nil {
  102. return "", err
  103. }
  104. return dgst, nil
  105. }
  106. // Delete removes content and metadata files associated with the digest.
  107. func (s *fs) Delete(dgst digest.Digest) error {
  108. s.Lock()
  109. defer s.Unlock()
  110. if err := os.RemoveAll(s.metadataDir(dgst)); err != nil {
  111. return err
  112. }
  113. if err := os.Remove(s.contentFile(dgst)); err != nil {
  114. return err
  115. }
  116. return nil
  117. }
  118. // SetMetadata sets metadata for a given ID. It fails if there's no base file.
  119. func (s *fs) SetMetadata(dgst digest.Digest, key string, data []byte) error {
  120. s.Lock()
  121. defer s.Unlock()
  122. if _, err := s.get(dgst); err != nil {
  123. return err
  124. }
  125. baseDir := filepath.Join(s.metadataDir(dgst))
  126. if err := os.MkdirAll(baseDir, 0700); err != nil {
  127. return err
  128. }
  129. return ioutils.AtomicWriteFile(filepath.Join(s.metadataDir(dgst), key), data, 0600)
  130. }
  131. // GetMetadata returns metadata for a given digest.
  132. func (s *fs) GetMetadata(dgst digest.Digest, key string) ([]byte, error) {
  133. s.RLock()
  134. defer s.RUnlock()
  135. if _, err := s.get(dgst); err != nil {
  136. return nil, err
  137. }
  138. return ioutil.ReadFile(filepath.Join(s.metadataDir(dgst), key))
  139. }
  140. // DeleteMetadata removes the metadata associated with a digest.
  141. func (s *fs) DeleteMetadata(dgst digest.Digest, key string) error {
  142. s.Lock()
  143. defer s.Unlock()
  144. return os.RemoveAll(filepath.Join(s.metadataDir(dgst), key))
  145. }