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