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