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