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