github.com/damirazo/docker@v1.9.0/graph/graph.go (about) 1 package graph 2 3 import ( 4 "compress/gzip" 5 "encoding/json" 6 "errors" 7 "fmt" 8 "io" 9 "io/ioutil" 10 "os" 11 "path/filepath" 12 "runtime" 13 "strconv" 14 "strings" 15 "sync" 16 "time" 17 18 "github.com/Sirupsen/logrus" 19 "github.com/docker/distribution/digest" 20 "github.com/docker/docker/autogen/dockerversion" 21 "github.com/docker/docker/daemon/graphdriver" 22 "github.com/docker/docker/image" 23 "github.com/docker/docker/pkg/archive" 24 "github.com/docker/docker/pkg/idtools" 25 "github.com/docker/docker/pkg/progressreader" 26 "github.com/docker/docker/pkg/streamformatter" 27 "github.com/docker/docker/pkg/stringid" 28 "github.com/docker/docker/pkg/truncindex" 29 "github.com/docker/docker/runconfig" 30 "github.com/vbatts/tar-split/tar/asm" 31 "github.com/vbatts/tar-split/tar/storage" 32 ) 33 34 // v1Descriptor is a non-content-addressable image descriptor 35 type v1Descriptor struct { 36 img *image.Image 37 } 38 39 // ID returns the image ID specified in the image structure. 40 func (img v1Descriptor) ID() string { 41 return img.img.ID 42 } 43 44 // Parent returns the parent ID specified in the image structure. 45 func (img v1Descriptor) Parent() string { 46 return img.img.Parent 47 } 48 49 // MarshalConfig renders the image structure into JSON. 50 func (img v1Descriptor) MarshalConfig() ([]byte, error) { 51 return json.Marshal(img.img) 52 } 53 54 // The type is used to protect pulling or building related image 55 // layers from deleteing when filtered by dangling=true 56 // The key of layers is the images ID which is pulling or building 57 // The value of layers is a slice which hold layer IDs referenced to 58 // pulling or building images 59 type retainedLayers struct { 60 layerHolders map[string]map[string]struct{} // map[layerID]map[sessionID] 61 sync.Mutex 62 } 63 64 func (r *retainedLayers) Add(sessionID string, layerIDs []string) { 65 r.Lock() 66 defer r.Unlock() 67 for _, layerID := range layerIDs { 68 if r.layerHolders[layerID] == nil { 69 r.layerHolders[layerID] = map[string]struct{}{} 70 } 71 r.layerHolders[layerID][sessionID] = struct{}{} 72 } 73 } 74 75 func (r *retainedLayers) Delete(sessionID string, layerIDs []string) { 76 r.Lock() 77 defer r.Unlock() 78 for _, layerID := range layerIDs { 79 holders, ok := r.layerHolders[layerID] 80 if !ok { 81 continue 82 } 83 delete(holders, sessionID) 84 if len(holders) == 0 { 85 delete(r.layerHolders, layerID) // Delete any empty reference set. 86 } 87 } 88 } 89 90 func (r *retainedLayers) Exists(layerID string) bool { 91 r.Lock() 92 _, exists := r.layerHolders[layerID] 93 r.Unlock() 94 return exists 95 } 96 97 // A Graph is a store for versioned filesystem images and the relationship between them. 98 type Graph struct { 99 root string 100 idIndex *truncindex.TruncIndex 101 driver graphdriver.Driver 102 imagesMutex sync.Mutex 103 imageMutex imageMutex // protect images in driver. 104 retained *retainedLayers 105 tarSplitDisabled bool 106 uidMaps []idtools.IDMap 107 gidMaps []idtools.IDMap 108 109 // access to parentRefs must be protected with imageMutex locking the image id 110 // on the key of the map (e.g. imageMutex.Lock(img.ID), parentRefs[img.ID]...) 111 parentRefs map[string]int 112 } 113 114 // file names for ./graph/<ID>/ 115 const ( 116 jsonFileName = "json" 117 layersizeFileName = "layersize" 118 digestFileName = "checksum" 119 tarDataFileName = "tar-data.json.gz" 120 v1CompatibilityFileName = "v1Compatibility" 121 parentFileName = "parent" 122 ) 123 124 var ( 125 // ErrDigestNotSet is used when request the digest for a layer 126 // but the layer has no digest value or content to compute the 127 // the digest. 128 ErrDigestNotSet = errors.New("digest is not set for layer") 129 ) 130 131 // NewGraph instantiates a new graph at the given root path in the filesystem. 132 // `root` will be created if it doesn't exist. 133 func NewGraph(root string, driver graphdriver.Driver, uidMaps, gidMaps []idtools.IDMap) (*Graph, error) { 134 abspath, err := filepath.Abs(root) 135 if err != nil { 136 return nil, err 137 } 138 139 rootUID, rootGID, err := idtools.GetRootUIDGID(uidMaps, gidMaps) 140 if err != nil { 141 return nil, err 142 } 143 // Create the root directory if it doesn't exists 144 if err := idtools.MkdirAllAs(root, 0700, rootUID, rootGID); err != nil && !os.IsExist(err) { 145 return nil, err 146 } 147 148 graph := &Graph{ 149 root: abspath, 150 idIndex: truncindex.NewTruncIndex([]string{}), 151 driver: driver, 152 retained: &retainedLayers{layerHolders: make(map[string]map[string]struct{})}, 153 uidMaps: uidMaps, 154 gidMaps: gidMaps, 155 parentRefs: make(map[string]int), 156 } 157 158 // Windows does not currently support tarsplit functionality. 159 if runtime.GOOS == "windows" { 160 graph.tarSplitDisabled = true 161 } 162 163 if err := graph.restore(); err != nil { 164 return nil, err 165 } 166 return graph, nil 167 } 168 169 // IsHeld returns whether the given layerID is being used by an ongoing pull or build. 170 func (graph *Graph) IsHeld(layerID string) bool { 171 return graph.retained.Exists(layerID) 172 } 173 174 func (graph *Graph) restore() error { 175 dir, err := ioutil.ReadDir(graph.root) 176 if err != nil { 177 return err 178 } 179 var ids = []string{} 180 for _, v := range dir { 181 id := v.Name() 182 if graph.driver.Exists(id) { 183 img, err := graph.loadImage(id) 184 if err != nil { 185 if err != io.EOF { 186 return fmt.Errorf("could not restore image %s: %v", id, err) 187 } 188 logrus.Warnf("could not restore image %s due to corrupted files", id) 189 continue 190 } 191 graph.imageMutex.Lock(img.Parent) 192 graph.parentRefs[img.Parent]++ 193 graph.imageMutex.Unlock(img.Parent) 194 ids = append(ids, id) 195 } 196 } 197 198 graph.idIndex = truncindex.NewTruncIndex(ids) 199 logrus.Debugf("Restored %d elements", len(ids)) 200 return nil 201 } 202 203 // IsNotExist detects whether an image exists by parsing the incoming error 204 // message. 205 func (graph *Graph) IsNotExist(err error, id string) bool { 206 // FIXME: Implement error subclass instead of looking at the error text 207 // Note: This is the way golang implements os.IsNotExists on Plan9 208 return err != nil && (strings.Contains(strings.ToLower(err.Error()), "does not exist") || strings.Contains(strings.ToLower(err.Error()), "no such")) && strings.Contains(err.Error(), id) 209 } 210 211 // Exists returns true if an image is registered at the given id. 212 // If the image doesn't exist or if an error is encountered, false is returned. 213 func (graph *Graph) Exists(id string) bool { 214 if _, err := graph.Get(id); err != nil { 215 return false 216 } 217 return true 218 } 219 220 // Get returns the image with the given id, or an error if the image doesn't exist. 221 func (graph *Graph) Get(name string) (*image.Image, error) { 222 id, err := graph.idIndex.Get(name) 223 if err != nil { 224 return nil, fmt.Errorf("could not find image: %v", err) 225 } 226 img, err := graph.loadImage(id) 227 if err != nil { 228 return nil, err 229 } 230 if img.ID != id { 231 return nil, fmt.Errorf("Image stored at '%s' has wrong id '%s'", id, img.ID) 232 } 233 234 if img.Size < 0 { 235 size, err := graph.driver.DiffSize(img.ID, img.Parent) 236 if err != nil { 237 return nil, fmt.Errorf("unable to calculate size of image id %q: %s", img.ID, err) 238 } 239 240 img.Size = size 241 if err := graph.saveSize(graph.imageRoot(id), img.Size); err != nil { 242 return nil, err 243 } 244 } 245 return img, nil 246 } 247 248 // Create creates a new image and registers it in the graph. 249 func (graph *Graph) Create(layerData io.Reader, containerID, containerImage, comment, author string, containerConfig, config *runconfig.Config) (*image.Image, error) { 250 img := &image.Image{ 251 ID: stringid.GenerateRandomID(), 252 Comment: comment, 253 Created: time.Now().UTC(), 254 DockerVersion: dockerversion.VERSION, 255 Author: author, 256 Config: config, 257 Architecture: runtime.GOARCH, 258 OS: runtime.GOOS, 259 } 260 261 if containerID != "" { 262 img.Parent = containerImage 263 img.Container = containerID 264 img.ContainerConfig = *containerConfig 265 } 266 267 if err := graph.Register(v1Descriptor{img}, layerData); err != nil { 268 return nil, err 269 } 270 return img, nil 271 } 272 273 // Register imports a pre-existing image into the graph. 274 // Returns nil if the image is already registered. 275 func (graph *Graph) Register(im image.Descriptor, layerData io.Reader) (err error) { 276 imgID := im.ID() 277 278 if err := image.ValidateID(imgID); err != nil { 279 return err 280 } 281 282 // this is needed cause pull_v2 attemptIDReuse could deadlock 283 graph.imagesMutex.Lock() 284 defer graph.imagesMutex.Unlock() 285 286 // We need this entire operation to be atomic within the engine. Note that 287 // this doesn't mean Register is fully safe yet. 288 graph.imageMutex.Lock(imgID) 289 defer graph.imageMutex.Unlock(imgID) 290 291 return graph.register(im, layerData) 292 } 293 294 func (graph *Graph) register(im image.Descriptor, layerData io.Reader) (err error) { 295 imgID := im.ID() 296 297 // Skip register if image is already registered 298 if graph.Exists(imgID) { 299 return nil 300 } 301 302 // The returned `error` must be named in this function's signature so that 303 // `err` is not shadowed in this deferred cleanup. 304 defer func() { 305 // If any error occurs, remove the new dir from the driver. 306 // Don't check for errors since the dir might not have been created. 307 if err != nil { 308 graph.driver.Remove(imgID) 309 } 310 }() 311 312 // Ensure that the image root does not exist on the filesystem 313 // when it is not registered in the graph. 314 // This is common when you switch from one graph driver to another 315 if err := os.RemoveAll(graph.imageRoot(imgID)); err != nil && !os.IsNotExist(err) { 316 return err 317 } 318 319 // If the driver has this ID but the graph doesn't, remove it from the driver to start fresh. 320 // (the graph is the source of truth). 321 // Ignore errors, since we don't know if the driver correctly returns ErrNotExist. 322 // (FIXME: make that mandatory for drivers). 323 graph.driver.Remove(imgID) 324 325 tmp, err := graph.mktemp() 326 if err != nil { 327 return err 328 } 329 defer os.RemoveAll(tmp) 330 331 parent := im.Parent() 332 333 // Create root filesystem in the driver 334 if err := createRootFilesystemInDriver(graph, imgID, parent, layerData); err != nil { 335 return err 336 } 337 338 // Apply the diff/layer 339 config, err := im.MarshalConfig() 340 if err != nil { 341 return err 342 } 343 if err := graph.storeImage(imgID, parent, config, layerData, tmp); err != nil { 344 return err 345 } 346 // Commit 347 if err := os.Rename(tmp, graph.imageRoot(imgID)); err != nil { 348 return err 349 } 350 351 graph.idIndex.Add(imgID) 352 353 graph.imageMutex.Lock(parent) 354 graph.parentRefs[parent]++ 355 graph.imageMutex.Unlock(parent) 356 357 return nil 358 } 359 360 func createRootFilesystemInDriver(graph *Graph, id, parent string, layerData io.Reader) error { 361 if err := graph.driver.Create(id, parent); err != nil { 362 return fmt.Errorf("Driver %s failed to create image rootfs %s: %s", graph.driver, id, err) 363 } 364 return nil 365 } 366 367 // TempLayerArchive creates a temporary archive of the given image's filesystem layer. 368 // The archive is stored on disk and will be automatically deleted as soon as has been read. 369 // If output is not nil, a human-readable progress bar will be written to it. 370 func (graph *Graph) TempLayerArchive(id string, sf *streamformatter.StreamFormatter, output io.Writer) (*archive.TempArchive, error) { 371 image, err := graph.Get(id) 372 if err != nil { 373 return nil, err 374 } 375 tmp, err := graph.mktemp() 376 if err != nil { 377 return nil, err 378 } 379 defer os.RemoveAll(tmp) 380 a, err := graph.TarLayer(image) 381 if err != nil { 382 return nil, err 383 } 384 progressReader := progressreader.New(progressreader.Config{ 385 In: a, 386 Out: output, 387 Formatter: sf, 388 Size: 0, 389 NewLines: false, 390 ID: stringid.TruncateID(id), 391 Action: "Buffering to disk", 392 }) 393 defer progressReader.Close() 394 return archive.NewTempArchive(progressReader, tmp) 395 } 396 397 // mktemp creates a temporary sub-directory inside the graph's filesystem. 398 func (graph *Graph) mktemp() (string, error) { 399 dir := filepath.Join(graph.root, "_tmp", stringid.GenerateNonCryptoID()) 400 rootUID, rootGID, err := idtools.GetRootUIDGID(graph.uidMaps, graph.gidMaps) 401 if err != nil { 402 return "", err 403 } 404 if err := idtools.MkdirAllAs(dir, 0700, rootUID, rootGID); err != nil { 405 return "", err 406 } 407 return dir, nil 408 } 409 410 // Delete atomically removes an image from the graph. 411 func (graph *Graph) Delete(name string) error { 412 id, err := graph.idIndex.Get(name) 413 if err != nil { 414 return err 415 } 416 img, err := graph.Get(id) 417 if err != nil { 418 return err 419 } 420 graph.idIndex.Delete(id) 421 tmp, err := graph.mktemp() 422 if err != nil { 423 tmp = graph.imageRoot(id) 424 } else { 425 if err := os.Rename(graph.imageRoot(id), tmp); err != nil { 426 // On err make tmp point to old dir and cleanup unused tmp dir 427 os.RemoveAll(tmp) 428 tmp = graph.imageRoot(id) 429 } 430 } 431 // Remove rootfs data from the driver 432 graph.driver.Remove(id) 433 434 graph.imageMutex.Lock(img.Parent) 435 graph.parentRefs[img.Parent]-- 436 if graph.parentRefs[img.Parent] == 0 { 437 delete(graph.parentRefs, img.Parent) 438 } 439 graph.imageMutex.Unlock(img.Parent) 440 441 // Remove the trashed image directory 442 return os.RemoveAll(tmp) 443 } 444 445 // Map returns a list of all images in the graph, addressable by ID. 446 func (graph *Graph) Map() map[string]*image.Image { 447 images := make(map[string]*image.Image) 448 graph.walkAll(func(image *image.Image) { 449 images[image.ID] = image 450 }) 451 return images 452 } 453 454 // walkAll iterates over each image in the graph, and passes it to a handler. 455 // The walking order is undetermined. 456 func (graph *Graph) walkAll(handler func(*image.Image)) { 457 graph.idIndex.Iterate(func(id string) { 458 img, err := graph.Get(id) 459 if err != nil { 460 return 461 } 462 if handler != nil { 463 handler(img) 464 } 465 }) 466 } 467 468 // ByParent returns a lookup table of images by their parent. 469 // If an image of key ID has 3 children images, then the value for key ID 470 // will be a list of 3 images. 471 // If an image has no children, it will not have an entry in the table. 472 func (graph *Graph) ByParent() map[string][]*image.Image { 473 byParent := make(map[string][]*image.Image) 474 graph.walkAll(func(img *image.Image) { 475 parent, err := graph.Get(img.Parent) 476 if err != nil { 477 return 478 } 479 if children, exists := byParent[parent.ID]; exists { 480 byParent[parent.ID] = append(children, img) 481 } else { 482 byParent[parent.ID] = []*image.Image{img} 483 } 484 }) 485 return byParent 486 } 487 488 // HasChildren returns whether the given image has any child images. 489 func (graph *Graph) HasChildren(imgID string) bool { 490 graph.imageMutex.Lock(imgID) 491 count := graph.parentRefs[imgID] 492 graph.imageMutex.Unlock(imgID) 493 return count > 0 494 } 495 496 // Retain keeps the images and layers that are in the pulling chain so that 497 // they are not deleted. If not retained, they may be deleted by rmi. 498 func (graph *Graph) Retain(sessionID string, layerIDs ...string) { 499 graph.retained.Add(sessionID, layerIDs) 500 } 501 502 // Release removes the referenced image ID from the provided set of layers. 503 func (graph *Graph) Release(sessionID string, layerIDs ...string) { 504 graph.retained.Delete(sessionID, layerIDs) 505 } 506 507 // Heads returns all heads in the graph, keyed by id. 508 // A head is an image which is not the parent of another image in the graph. 509 func (graph *Graph) Heads() map[string]*image.Image { 510 heads := make(map[string]*image.Image) 511 graph.walkAll(func(image *image.Image) { 512 // if it has no children, then it's not a parent, so it's an head 513 if !graph.HasChildren(image.ID) { 514 heads[image.ID] = image 515 } 516 }) 517 return heads 518 } 519 520 // TarLayer returns a tar archive of the image's filesystem layer. 521 func (graph *Graph) TarLayer(img *image.Image) (arch io.ReadCloser, err error) { 522 rdr, err := graph.assembleTarLayer(img) 523 if err != nil { 524 logrus.Debugf("[graph] TarLayer with traditional differ: %s", img.ID) 525 return graph.driver.Diff(img.ID, img.Parent) 526 } 527 return rdr, nil 528 } 529 530 func (graph *Graph) imageRoot(id string) string { 531 return filepath.Join(graph.root, id) 532 } 533 534 // loadImage fetches the image with the given id from the graph. 535 func (graph *Graph) loadImage(id string) (*image.Image, error) { 536 root := graph.imageRoot(id) 537 538 // Open the JSON file to decode by streaming 539 jsonSource, err := os.Open(jsonPath(root)) 540 if err != nil { 541 return nil, err 542 } 543 defer jsonSource.Close() 544 545 img := &image.Image{} 546 dec := json.NewDecoder(jsonSource) 547 548 // Decode the JSON data 549 if err := dec.Decode(img); err != nil { 550 return nil, err 551 } 552 553 if img.ID == "" { 554 img.ID = id 555 } 556 557 if img.Parent == "" && img.ParentID != "" && img.ParentID.Validate() == nil { 558 img.Parent = img.ParentID.Hex() 559 } 560 561 // compatibilityID for parent 562 parent, err := ioutil.ReadFile(filepath.Join(root, parentFileName)) 563 if err == nil && len(parent) > 0 { 564 img.Parent = string(parent) 565 } 566 567 if err := image.ValidateID(img.ID); err != nil { 568 return nil, err 569 } 570 571 if buf, err := ioutil.ReadFile(filepath.Join(root, layersizeFileName)); err != nil { 572 if !os.IsNotExist(err) { 573 return nil, err 574 } 575 // If the layersize file does not exist then set the size to a negative number 576 // because a layer size of 0 (zero) is valid 577 img.Size = -1 578 } else { 579 // Using Atoi here instead would temporarily convert the size to a machine 580 // dependent integer type, which causes images larger than 2^31 bytes to 581 // display negative sizes on 32-bit machines: 582 size, err := strconv.ParseInt(string(buf), 10, 64) 583 if err != nil { 584 return nil, err 585 } 586 img.Size = int64(size) 587 } 588 589 return img, nil 590 } 591 592 // saveSize stores the `size` in the provided graph `img` directory `root`. 593 func (graph *Graph) saveSize(root string, size int64) error { 594 if err := ioutil.WriteFile(filepath.Join(root, layersizeFileName), []byte(strconv.FormatInt(size, 10)), 0600); err != nil { 595 return fmt.Errorf("Error storing image size in %s/%s: %s", root, layersizeFileName, err) 596 } 597 return nil 598 } 599 600 // SetLayerDigest sets the digest for the image layer to the provided value. 601 func (graph *Graph) SetLayerDigest(id string, dgst digest.Digest) error { 602 graph.imageMutex.Lock(id) 603 defer graph.imageMutex.Unlock(id) 604 605 return graph.setLayerDigest(id, dgst) 606 } 607 func (graph *Graph) setLayerDigest(id string, dgst digest.Digest) error { 608 root := graph.imageRoot(id) 609 if err := ioutil.WriteFile(filepath.Join(root, digestFileName), []byte(dgst.String()), 0600); err != nil { 610 return fmt.Errorf("Error storing digest in %s/%s: %s", root, digestFileName, err) 611 } 612 return nil 613 } 614 615 // GetLayerDigest gets the digest for the provide image layer id. 616 func (graph *Graph) GetLayerDigest(id string) (digest.Digest, error) { 617 graph.imageMutex.Lock(id) 618 defer graph.imageMutex.Unlock(id) 619 620 return graph.getLayerDigest(id) 621 } 622 623 func (graph *Graph) getLayerDigest(id string) (digest.Digest, error) { 624 root := graph.imageRoot(id) 625 cs, err := ioutil.ReadFile(filepath.Join(root, digestFileName)) 626 if err != nil { 627 if os.IsNotExist(err) { 628 return "", ErrDigestNotSet 629 } 630 return "", err 631 } 632 return digest.ParseDigest(string(cs)) 633 } 634 635 // SetV1CompatibilityConfig stores the v1Compatibility JSON data associated 636 // with the image in the manifest to the disk 637 func (graph *Graph) SetV1CompatibilityConfig(id string, data []byte) error { 638 graph.imageMutex.Lock(id) 639 defer graph.imageMutex.Unlock(id) 640 641 return graph.setV1CompatibilityConfig(id, data) 642 } 643 func (graph *Graph) setV1CompatibilityConfig(id string, data []byte) error { 644 root := graph.imageRoot(id) 645 return ioutil.WriteFile(filepath.Join(root, v1CompatibilityFileName), data, 0600) 646 } 647 648 // GetV1CompatibilityConfig reads the v1Compatibility JSON data for the image 649 // from the disk 650 func (graph *Graph) GetV1CompatibilityConfig(id string) ([]byte, error) { 651 graph.imageMutex.Lock(id) 652 defer graph.imageMutex.Unlock(id) 653 654 return graph.getV1CompatibilityConfig(id) 655 } 656 657 func (graph *Graph) getV1CompatibilityConfig(id string) ([]byte, error) { 658 root := graph.imageRoot(id) 659 return ioutil.ReadFile(filepath.Join(root, v1CompatibilityFileName)) 660 } 661 662 // GenerateV1CompatibilityChain makes sure v1Compatibility JSON data exists 663 // for the image. If it doesn't it generates and stores it for the image and 664 // all of it's parents based on the image config JSON. 665 func (graph *Graph) GenerateV1CompatibilityChain(id string) ([]byte, error) { 666 graph.imageMutex.Lock(id) 667 defer graph.imageMutex.Unlock(id) 668 669 if v1config, err := graph.getV1CompatibilityConfig(id); err == nil { 670 return v1config, nil 671 } 672 673 // generate new, store it to disk 674 img, err := graph.Get(id) 675 if err != nil { 676 return nil, err 677 } 678 679 digestPrefix := string(digest.Canonical) + ":" 680 img.ID = strings.TrimPrefix(img.ID, digestPrefix) 681 682 if img.Parent != "" { 683 parentConfig, err := graph.GenerateV1CompatibilityChain(img.Parent) 684 if err != nil { 685 return nil, err 686 } 687 var parent struct{ ID string } 688 err = json.Unmarshal(parentConfig, &parent) 689 if err != nil { 690 return nil, err 691 } 692 img.Parent = parent.ID 693 } 694 695 json, err := json.Marshal(img) 696 if err != nil { 697 return nil, err 698 } 699 if err := graph.setV1CompatibilityConfig(id, json); err != nil { 700 return nil, err 701 } 702 return json, nil 703 } 704 705 // RawJSON returns the JSON representation for an image as a byte array. 706 func (graph *Graph) RawJSON(id string) ([]byte, error) { 707 root := graph.imageRoot(id) 708 709 buf, err := ioutil.ReadFile(jsonPath(root)) 710 if err != nil { 711 return nil, fmt.Errorf("Failed to read json for image %s: %s", id, err) 712 } 713 714 return buf, nil 715 } 716 717 func jsonPath(root string) string { 718 return filepath.Join(root, jsonFileName) 719 } 720 721 // storeImage stores file system layer data for the given image to the 722 // graph's storage driver. Image metadata is stored in a file 723 // at the specified root directory. 724 func (graph *Graph) storeImage(id, parent string, config []byte, layerData io.Reader, root string) (err error) { 725 var size int64 726 // Store the layer. If layerData is not nil, unpack it into the new layer 727 if layerData != nil { 728 if size, err = graph.disassembleAndApplyTarLayer(id, parent, layerData, root); err != nil { 729 return err 730 } 731 } 732 733 if err := graph.saveSize(root, size); err != nil { 734 return err 735 } 736 737 if err := ioutil.WriteFile(jsonPath(root), config, 0600); err != nil { 738 return err 739 } 740 741 // If image is pointing to a parent via CompatibilityID write the reference to disk 742 img, err := image.NewImgJSON(config) 743 if err != nil { 744 return err 745 } 746 747 if img.ParentID.Validate() == nil && parent != img.ParentID.Hex() { 748 if err := ioutil.WriteFile(filepath.Join(root, parentFileName), []byte(parent), 0600); err != nil { 749 return err 750 } 751 } 752 return nil 753 } 754 755 func (graph *Graph) disassembleAndApplyTarLayer(id, parent string, layerData io.Reader, root string) (size int64, err error) { 756 var ar io.Reader 757 758 if graph.tarSplitDisabled { 759 ar = layerData 760 } else { 761 // this is saving the tar-split metadata 762 mf, err := os.OpenFile(filepath.Join(root, tarDataFileName), os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.FileMode(0600)) 763 if err != nil { 764 return 0, err 765 } 766 767 mfz := gzip.NewWriter(mf) 768 metaPacker := storage.NewJSONPacker(mfz) 769 defer mf.Close() 770 defer mfz.Close() 771 772 inflatedLayerData, err := archive.DecompressStream(layerData) 773 if err != nil { 774 return 0, err 775 } 776 777 // we're passing nil here for the file putter, because the ApplyDiff will 778 // handle the extraction of the archive 779 rdr, err := asm.NewInputTarStream(inflatedLayerData, metaPacker, nil) 780 if err != nil { 781 return 0, err 782 } 783 784 ar = archive.Reader(rdr) 785 } 786 787 if size, err = graph.driver.ApplyDiff(id, parent, ar); err != nil { 788 return 0, err 789 } 790 791 return 792 } 793 794 func (graph *Graph) assembleTarLayer(img *image.Image) (io.ReadCloser, error) { 795 root := graph.imageRoot(img.ID) 796 mFileName := filepath.Join(root, tarDataFileName) 797 mf, err := os.Open(mFileName) 798 if err != nil { 799 if !os.IsNotExist(err) { 800 logrus.Errorf("failed to open %q: %s", mFileName, err) 801 } 802 return nil, err 803 } 804 pR, pW := io.Pipe() 805 // this will need to be in a goroutine, as we are returning the stream of a 806 // tar archive, but can not close the metadata reader early (when this 807 // function returns)... 808 go func() { 809 defer mf.Close() 810 // let's reassemble! 811 logrus.Debugf("[graph] TarLayer with reassembly: %s", img.ID) 812 mfz, err := gzip.NewReader(mf) 813 if err != nil { 814 pW.CloseWithError(fmt.Errorf("[graph] error with %s: %s", mFileName, err)) 815 return 816 } 817 defer mfz.Close() 818 819 // get our relative path to the container 820 fsLayer, err := graph.driver.Get(img.ID, "") 821 if err != nil { 822 pW.CloseWithError(err) 823 return 824 } 825 defer graph.driver.Put(img.ID) 826 827 metaUnpacker := storage.NewJSONUnpacker(mfz) 828 fileGetter := storage.NewPathFileGetter(fsLayer) 829 logrus.Debugf("[graph] %s is at %q", img.ID, fsLayer) 830 ots := asm.NewOutputTarStream(fileGetter, metaUnpacker) 831 defer ots.Close() 832 if _, err := io.Copy(pW, ots); err != nil { 833 pW.CloseWithError(err) 834 return 835 } 836 pW.Close() 837 }() 838 return pR, nil 839 }