github.com/lazyboychen7/engine@v17.12.1-ce-rc2+incompatible/image/fs.go (about) 1 package image 2 3 import ( 4 "fmt" 5 "io/ioutil" 6 "os" 7 "path/filepath" 8 "sync" 9 10 "github.com/docker/docker/pkg/ioutils" 11 "github.com/opencontainers/go-digest" 12 "github.com/pkg/errors" 13 "github.com/sirupsen/logrus" 14 ) 15 16 // DigestWalkFunc is function called by StoreBackend.Walk 17 type DigestWalkFunc func(id digest.Digest) error 18 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 28 } 29 30 // fs implements StoreBackend using the filesystem. 31 type fs struct { 32 sync.RWMutex 33 root string 34 } 35 36 const ( 37 contentDirName = "content" 38 metadataDirName = "metadata" 39 ) 40 41 // NewFSStoreBackend returns new filesystem based backend for image.Store 42 func NewFSStoreBackend(root string) (StoreBackend, error) { 43 return newFSStore(root) 44 } 45 46 func newFSStore(root string) (*fs, error) { 47 s := &fs{ 48 root: root, 49 } 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") 52 } 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") 55 } 56 return s, nil 57 } 58 59 func (s *fs) contentFile(dgst digest.Digest) string { 60 return filepath.Join(s.root, contentDirName, string(dgst.Algorithm()), dgst.Hex()) 61 } 62 63 func (s *fs) metadataDir(dgst digest.Digest) string { 64 return filepath.Join(s.root, metadataDirName, string(dgst.Algorithm()), dgst.Hex()) 65 } 66 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 70 s.RLock() 71 dir, err := ioutil.ReadDir(filepath.Join(s.root, contentDirName, string(digest.Canonical))) 72 s.RUnlock() 73 if err != nil { 74 return err 75 } 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) 80 continue 81 } 82 if err := f(dgst); err != nil { 83 return err 84 } 85 } 86 return nil 87 } 88 89 // Get returns the content stored under a given digest. 90 func (s *fs) Get(dgst digest.Digest) ([]byte, error) { 91 s.RLock() 92 defer s.RUnlock() 93 94 return s.get(dgst) 95 } 96 97 func (s *fs) get(dgst digest.Digest) ([]byte, error) { 98 content, err := ioutil.ReadFile(s.contentFile(dgst)) 99 if err != nil { 100 return nil, errors.Wrapf(err, "failed to get digest %s", dgst) 101 } 102 103 // todo: maybe optional 104 if digest.FromBytes(content) != dgst { 105 return nil, fmt.Errorf("failed to verify: %v", dgst) 106 } 107 108 return content, nil 109 } 110 111 // Set stores content by checksum. 112 func (s *fs) Set(data []byte) (digest.Digest, error) { 113 s.Lock() 114 defer s.Unlock() 115 116 if len(data) == 0 { 117 return "", fmt.Errorf("invalid empty data") 118 } 119 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") 123 } 124 125 return dgst, nil 126 } 127 128 // Delete removes content and metadata files associated with the digest. 129 func (s *fs) Delete(dgst digest.Digest) error { 130 s.Lock() 131 defer s.Unlock() 132 133 if err := os.RemoveAll(s.metadataDir(dgst)); err != nil { 134 return err 135 } 136 if err := os.Remove(s.contentFile(dgst)); err != nil { 137 return err 138 } 139 return nil 140 } 141 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 { 144 s.Lock() 145 defer s.Unlock() 146 if _, err := s.get(dgst); err != nil { 147 return err 148 } 149 150 baseDir := filepath.Join(s.metadataDir(dgst)) 151 if err := os.MkdirAll(baseDir, 0700); err != nil { 152 return err 153 } 154 return ioutils.AtomicWriteFile(filepath.Join(s.metadataDir(dgst), key), data, 0600) 155 } 156 157 // GetMetadata returns metadata for a given digest. 158 func (s *fs) GetMetadata(dgst digest.Digest, key string) ([]byte, error) { 159 s.RLock() 160 defer s.RUnlock() 161 162 if _, err := s.get(dgst); err != nil { 163 return nil, err 164 } 165 bytes, err := ioutil.ReadFile(filepath.Join(s.metadataDir(dgst), key)) 166 if err != nil { 167 return nil, errors.Wrap(err, "failed to read metadata") 168 } 169 return bytes, nil 170 } 171 172 // DeleteMetadata removes the metadata associated with a digest. 173 func (s *fs) DeleteMetadata(dgst digest.Digest, key string) error { 174 s.Lock() 175 defer s.Unlock() 176 177 return os.RemoveAll(filepath.Join(s.metadataDir(dgst), key)) 178 }