github.com/nullne/docker@v1.13.0-rc1/layer/filestore.go (about) 1 package layer 2 3 import ( 4 "compress/gzip" 5 "encoding/json" 6 "errors" 7 "fmt" 8 "io" 9 "io/ioutil" 10 "os" 11 "path/filepath" 12 "regexp" 13 "strconv" 14 "strings" 15 16 "github.com/Sirupsen/logrus" 17 "github.com/docker/distribution" 18 "github.com/docker/distribution/digest" 19 "github.com/docker/docker/pkg/ioutils" 20 ) 21 22 var ( 23 stringIDRegexp = regexp.MustCompile(`^[a-f0-9]{64}(-init)?$`) 24 supportedAlgorithms = []digest.Algorithm{ 25 digest.SHA256, 26 // digest.SHA384, // Currently not used 27 // digest.SHA512, // Currently not used 28 } 29 ) 30 31 type fileMetadataStore struct { 32 root string 33 } 34 35 type fileMetadataTransaction struct { 36 store *fileMetadataStore 37 ws *ioutils.AtomicWriteSet 38 } 39 40 // NewFSMetadataStore returns an instance of a metadata store 41 // which is backed by files on disk using the provided root 42 // as the root of metadata files. 43 func NewFSMetadataStore(root string) (MetadataStore, error) { 44 if err := os.MkdirAll(root, 0700); err != nil { 45 return nil, err 46 } 47 return &fileMetadataStore{ 48 root: root, 49 }, nil 50 } 51 52 func (fms *fileMetadataStore) getLayerDirectory(layer ChainID) string { 53 dgst := digest.Digest(layer) 54 return filepath.Join(fms.root, string(dgst.Algorithm()), dgst.Hex()) 55 } 56 57 func (fms *fileMetadataStore) getLayerFilename(layer ChainID, filename string) string { 58 return filepath.Join(fms.getLayerDirectory(layer), filename) 59 } 60 61 func (fms *fileMetadataStore) getMountDirectory(mount string) string { 62 return filepath.Join(fms.root, "mounts", mount) 63 } 64 65 func (fms *fileMetadataStore) getMountFilename(mount, filename string) string { 66 return filepath.Join(fms.getMountDirectory(mount), filename) 67 } 68 69 func (fms *fileMetadataStore) StartTransaction() (MetadataTransaction, error) { 70 tmpDir := filepath.Join(fms.root, "tmp") 71 if err := os.MkdirAll(tmpDir, 0755); err != nil { 72 return nil, err 73 } 74 ws, err := ioutils.NewAtomicWriteSet(tmpDir) 75 if err != nil { 76 return nil, err 77 } 78 79 return &fileMetadataTransaction{ 80 store: fms, 81 ws: ws, 82 }, nil 83 } 84 85 func (fm *fileMetadataTransaction) SetSize(size int64) error { 86 content := fmt.Sprintf("%d", size) 87 return fm.ws.WriteFile("size", []byte(content), 0644) 88 } 89 90 func (fm *fileMetadataTransaction) SetParent(parent ChainID) error { 91 return fm.ws.WriteFile("parent", []byte(digest.Digest(parent).String()), 0644) 92 } 93 94 func (fm *fileMetadataTransaction) SetDiffID(diff DiffID) error { 95 return fm.ws.WriteFile("diff", []byte(digest.Digest(diff).String()), 0644) 96 } 97 98 func (fm *fileMetadataTransaction) SetCacheID(cacheID string) error { 99 return fm.ws.WriteFile("cache-id", []byte(cacheID), 0644) 100 } 101 102 func (fm *fileMetadataTransaction) SetDescriptor(ref distribution.Descriptor) error { 103 jsonRef, err := json.Marshal(ref) 104 if err != nil { 105 return err 106 } 107 return fm.ws.WriteFile("descriptor.json", jsonRef, 0644) 108 } 109 110 func (fm *fileMetadataTransaction) TarSplitWriter(compressInput bool) (io.WriteCloser, error) { 111 f, err := fm.ws.FileWriter("tar-split.json.gz", os.O_TRUNC|os.O_CREATE|os.O_WRONLY, 0644) 112 if err != nil { 113 return nil, err 114 } 115 var wc io.WriteCloser 116 if compressInput { 117 wc = gzip.NewWriter(f) 118 } else { 119 wc = f 120 } 121 122 return ioutils.NewWriteCloserWrapper(wc, func() error { 123 wc.Close() 124 return f.Close() 125 }), nil 126 } 127 128 func (fm *fileMetadataTransaction) Commit(layer ChainID) error { 129 finalDir := fm.store.getLayerDirectory(layer) 130 if err := os.MkdirAll(filepath.Dir(finalDir), 0755); err != nil { 131 return err 132 } 133 134 return fm.ws.Commit(finalDir) 135 } 136 137 func (fm *fileMetadataTransaction) Cancel() error { 138 return fm.ws.Cancel() 139 } 140 141 func (fm *fileMetadataTransaction) String() string { 142 return fm.ws.String() 143 } 144 145 func (fms *fileMetadataStore) GetSize(layer ChainID) (int64, error) { 146 content, err := ioutil.ReadFile(fms.getLayerFilename(layer, "size")) 147 if err != nil { 148 return 0, err 149 } 150 151 size, err := strconv.ParseInt(string(content), 10, 64) 152 if err != nil { 153 return 0, err 154 } 155 156 return size, nil 157 } 158 159 func (fms *fileMetadataStore) GetParent(layer ChainID) (ChainID, error) { 160 content, err := ioutil.ReadFile(fms.getLayerFilename(layer, "parent")) 161 if err != nil { 162 if os.IsNotExist(err) { 163 return "", nil 164 } 165 return "", err 166 } 167 168 dgst, err := digest.ParseDigest(strings.TrimSpace(string(content))) 169 if err != nil { 170 return "", err 171 } 172 173 return ChainID(dgst), nil 174 } 175 176 func (fms *fileMetadataStore) GetDiffID(layer ChainID) (DiffID, error) { 177 content, err := ioutil.ReadFile(fms.getLayerFilename(layer, "diff")) 178 if err != nil { 179 return "", err 180 } 181 182 dgst, err := digest.ParseDigest(strings.TrimSpace(string(content))) 183 if err != nil { 184 return "", err 185 } 186 187 return DiffID(dgst), nil 188 } 189 190 func (fms *fileMetadataStore) GetCacheID(layer ChainID) (string, error) { 191 contentBytes, err := ioutil.ReadFile(fms.getLayerFilename(layer, "cache-id")) 192 if err != nil { 193 return "", err 194 } 195 content := strings.TrimSpace(string(contentBytes)) 196 197 if !stringIDRegexp.MatchString(content) { 198 return "", errors.New("invalid cache id value") 199 } 200 201 return content, nil 202 } 203 204 func (fms *fileMetadataStore) GetDescriptor(layer ChainID) (distribution.Descriptor, error) { 205 content, err := ioutil.ReadFile(fms.getLayerFilename(layer, "descriptor.json")) 206 if err != nil { 207 if os.IsNotExist(err) { 208 // only return empty descriptor to represent what is stored 209 return distribution.Descriptor{}, nil 210 } 211 return distribution.Descriptor{}, err 212 } 213 214 var ref distribution.Descriptor 215 err = json.Unmarshal(content, &ref) 216 if err != nil { 217 return distribution.Descriptor{}, err 218 } 219 return ref, err 220 } 221 222 func (fms *fileMetadataStore) TarSplitReader(layer ChainID) (io.ReadCloser, error) { 223 fz, err := os.Open(fms.getLayerFilename(layer, "tar-split.json.gz")) 224 if err != nil { 225 return nil, err 226 } 227 f, err := gzip.NewReader(fz) 228 if err != nil { 229 return nil, err 230 } 231 232 return ioutils.NewReadCloserWrapper(f, func() error { 233 f.Close() 234 return fz.Close() 235 }), nil 236 } 237 238 func (fms *fileMetadataStore) SetMountID(mount string, mountID string) error { 239 if err := os.MkdirAll(fms.getMountDirectory(mount), 0755); err != nil { 240 return err 241 } 242 return ioutil.WriteFile(fms.getMountFilename(mount, "mount-id"), []byte(mountID), 0644) 243 } 244 245 func (fms *fileMetadataStore) SetInitID(mount string, init string) error { 246 if err := os.MkdirAll(fms.getMountDirectory(mount), 0755); err != nil { 247 return err 248 } 249 return ioutil.WriteFile(fms.getMountFilename(mount, "init-id"), []byte(init), 0644) 250 } 251 252 func (fms *fileMetadataStore) SetMountParent(mount string, parent ChainID) error { 253 if err := os.MkdirAll(fms.getMountDirectory(mount), 0755); err != nil { 254 return err 255 } 256 return ioutil.WriteFile(fms.getMountFilename(mount, "parent"), []byte(digest.Digest(parent).String()), 0644) 257 } 258 259 func (fms *fileMetadataStore) GetMountID(mount string) (string, error) { 260 contentBytes, err := ioutil.ReadFile(fms.getMountFilename(mount, "mount-id")) 261 if err != nil { 262 return "", err 263 } 264 content := strings.TrimSpace(string(contentBytes)) 265 266 if !stringIDRegexp.MatchString(content) { 267 return "", errors.New("invalid mount id value") 268 } 269 270 return content, nil 271 } 272 273 func (fms *fileMetadataStore) GetInitID(mount string) (string, error) { 274 contentBytes, err := ioutil.ReadFile(fms.getMountFilename(mount, "init-id")) 275 if err != nil { 276 if os.IsNotExist(err) { 277 return "", nil 278 } 279 return "", err 280 } 281 content := strings.TrimSpace(string(contentBytes)) 282 283 if !stringIDRegexp.MatchString(content) { 284 return "", errors.New("invalid init id value") 285 } 286 287 return content, nil 288 } 289 290 func (fms *fileMetadataStore) GetMountParent(mount string) (ChainID, error) { 291 content, err := ioutil.ReadFile(fms.getMountFilename(mount, "parent")) 292 if err != nil { 293 if os.IsNotExist(err) { 294 return "", nil 295 } 296 return "", err 297 } 298 299 dgst, err := digest.ParseDigest(strings.TrimSpace(string(content))) 300 if err != nil { 301 return "", err 302 } 303 304 return ChainID(dgst), nil 305 } 306 307 func (fms *fileMetadataStore) List() ([]ChainID, []string, error) { 308 var ids []ChainID 309 for _, algorithm := range supportedAlgorithms { 310 fileInfos, err := ioutil.ReadDir(filepath.Join(fms.root, string(algorithm))) 311 if err != nil { 312 if os.IsNotExist(err) { 313 continue 314 } 315 return nil, nil, err 316 } 317 318 for _, fi := range fileInfos { 319 if fi.IsDir() && fi.Name() != "mounts" { 320 dgst := digest.NewDigestFromHex(string(algorithm), fi.Name()) 321 if err := dgst.Validate(); err != nil { 322 logrus.Debugf("Ignoring invalid digest %s:%s", algorithm, fi.Name()) 323 } else { 324 ids = append(ids, ChainID(dgst)) 325 } 326 } 327 } 328 } 329 330 fileInfos, err := ioutil.ReadDir(filepath.Join(fms.root, "mounts")) 331 if err != nil { 332 if os.IsNotExist(err) { 333 return ids, []string{}, nil 334 } 335 return nil, nil, err 336 } 337 338 var mounts []string 339 for _, fi := range fileInfos { 340 if fi.IsDir() { 341 mounts = append(mounts, fi.Name()) 342 } 343 } 344 345 return ids, mounts, nil 346 } 347 348 func (fms *fileMetadataStore) Remove(layer ChainID) error { 349 return os.RemoveAll(fms.getLayerDirectory(layer)) 350 } 351 352 func (fms *fileMetadataStore) RemoveMount(mount string) error { 353 return os.RemoveAll(fms.getMountDirectory(mount)) 354 }