github.com/hustcat/docker@v1.3.3-0.20160314103604-901c67a8eeab/daemon/graphdriver/windows/windows.go (about) 1 //+build windows 2 3 package windows 4 5 import ( 6 "bufio" 7 "crypto/sha512" 8 "encoding/json" 9 "fmt" 10 "io" 11 "io/ioutil" 12 "os" 13 "path" 14 "path/filepath" 15 "strings" 16 "sync" 17 "syscall" 18 "time" 19 20 "github.com/Microsoft/go-winio" 21 "github.com/Microsoft/go-winio/archive/tar" 22 "github.com/Microsoft/go-winio/backuptar" 23 "github.com/Microsoft/hcsshim" 24 "github.com/Sirupsen/logrus" 25 "github.com/docker/docker/daemon/graphdriver" 26 "github.com/docker/docker/pkg/archive" 27 "github.com/docker/docker/pkg/chrootarchive" 28 "github.com/docker/docker/pkg/idtools" 29 "github.com/docker/docker/pkg/ioutils" 30 "github.com/vbatts/tar-split/tar/storage" 31 ) 32 33 // init registers the windows graph drivers to the register. 34 func init() { 35 graphdriver.Register("windowsfilter", InitFilter) 36 graphdriver.Register("windowsdiff", InitDiff) 37 } 38 39 const ( 40 // diffDriver is an hcsshim driver type 41 diffDriver = iota 42 // filterDriver is an hcsshim driver type 43 filterDriver 44 ) 45 46 // Driver represents a windows graph driver. 47 type Driver struct { 48 // info stores the shim driver information 49 info hcsshim.DriverInfo 50 // Mutex protects concurrent modification to active 51 sync.Mutex 52 // active stores references to the activated layers 53 active map[string]int 54 } 55 56 var _ graphdriver.DiffGetterDriver = &Driver{} 57 58 // InitFilter returns a new Windows storage filter driver. 59 func InitFilter(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (graphdriver.Driver, error) { 60 logrus.Debugf("WindowsGraphDriver InitFilter at %s", home) 61 d := &Driver{ 62 info: hcsshim.DriverInfo{ 63 HomeDir: home, 64 Flavour: filterDriver, 65 }, 66 active: make(map[string]int), 67 } 68 return d, nil 69 } 70 71 // InitDiff returns a new Windows differencing disk driver. 72 func InitDiff(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (graphdriver.Driver, error) { 73 logrus.Debugf("WindowsGraphDriver InitDiff at %s", home) 74 d := &Driver{ 75 info: hcsshim.DriverInfo{ 76 HomeDir: home, 77 Flavour: diffDriver, 78 }, 79 active: make(map[string]int), 80 } 81 return d, nil 82 } 83 84 // String returns the string representation of a driver. 85 func (d *Driver) String() string { 86 switch d.info.Flavour { 87 case diffDriver: 88 return "windowsdiff" 89 case filterDriver: 90 return "windowsfilter" 91 default: 92 return "Unknown driver flavour" 93 } 94 } 95 96 // Status returns the status of the driver. 97 func (d *Driver) Status() [][2]string { 98 return [][2]string{ 99 {"Windows", ""}, 100 } 101 } 102 103 // Exists returns true if the given id is registered with this driver. 104 func (d *Driver) Exists(id string) bool { 105 rID, err := d.resolveID(id) 106 if err != nil { 107 return false 108 } 109 result, err := hcsshim.LayerExists(d.info, rID) 110 if err != nil { 111 return false 112 } 113 return result 114 } 115 116 // Create creates a new layer with the given id. 117 func (d *Driver) Create(id, parent, mountLabel string) error { 118 rPId, err := d.resolveID(parent) 119 if err != nil { 120 return err 121 } 122 123 parentChain, err := d.getLayerChain(rPId) 124 if err != nil { 125 return err 126 } 127 128 var layerChain []string 129 130 parentIsInit := strings.HasSuffix(rPId, "-init") 131 132 if !parentIsInit && rPId != "" { 133 parentPath, err := hcsshim.GetLayerMountPath(d.info, rPId) 134 if err != nil { 135 return err 136 } 137 layerChain = []string{parentPath} 138 } 139 140 layerChain = append(layerChain, parentChain...) 141 142 if parentIsInit { 143 if len(layerChain) == 0 { 144 return fmt.Errorf("Cannot create a read/write layer without a parent layer.") 145 } 146 if err := hcsshim.CreateSandboxLayer(d.info, id, layerChain[0], layerChain); err != nil { 147 return err 148 } 149 } else { 150 if err := hcsshim.CreateLayer(d.info, id, rPId); err != nil { 151 return err 152 } 153 } 154 155 if _, err := os.Lstat(d.dir(parent)); err != nil { 156 if err2 := hcsshim.DestroyLayer(d.info, id); err2 != nil { 157 logrus.Warnf("Failed to DestroyLayer %s: %s", id, err2) 158 } 159 return fmt.Errorf("Cannot create layer with missing parent %s: %s", parent, err) 160 } 161 162 if err := d.setLayerChain(id, layerChain); err != nil { 163 if err2 := hcsshim.DestroyLayer(d.info, id); err2 != nil { 164 logrus.Warnf("Failed to DestroyLayer %s: %s", id, err2) 165 } 166 return err 167 } 168 169 return nil 170 } 171 172 // dir returns the absolute path to the layer. 173 func (d *Driver) dir(id string) string { 174 return filepath.Join(d.info.HomeDir, filepath.Base(id)) 175 } 176 177 // Remove unmounts and removes the dir information. 178 func (d *Driver) Remove(id string) error { 179 rID, err := d.resolveID(id) 180 if err != nil { 181 return err 182 } 183 os.RemoveAll(filepath.Join(d.info.HomeDir, "sysfile-backups", rID)) // ok to fail 184 return hcsshim.DestroyLayer(d.info, rID) 185 } 186 187 // Get returns the rootfs path for the id. This will mount the dir at it's given path. 188 func (d *Driver) Get(id, mountLabel string) (string, error) { 189 logrus.Debugf("WindowsGraphDriver Get() id %s mountLabel %s", id, mountLabel) 190 var dir string 191 192 d.Lock() 193 defer d.Unlock() 194 195 rID, err := d.resolveID(id) 196 if err != nil { 197 return "", err 198 } 199 200 // Getting the layer paths must be done outside of the lock. 201 layerChain, err := d.getLayerChain(rID) 202 if err != nil { 203 return "", err 204 } 205 206 if d.active[rID] == 0 { 207 if err := hcsshim.ActivateLayer(d.info, rID); err != nil { 208 return "", err 209 } 210 if err := hcsshim.PrepareLayer(d.info, rID, layerChain); err != nil { 211 if err2 := hcsshim.DeactivateLayer(d.info, rID); err2 != nil { 212 logrus.Warnf("Failed to Deactivate %s: %s", id, err) 213 } 214 return "", err 215 } 216 } 217 218 mountPath, err := hcsshim.GetLayerMountPath(d.info, rID) 219 if err != nil { 220 if err2 := hcsshim.DeactivateLayer(d.info, rID); err2 != nil { 221 logrus.Warnf("Failed to Deactivate %s: %s", id, err) 222 } 223 return "", err 224 } 225 226 d.active[rID]++ 227 228 // If the layer has a mount path, use that. Otherwise, use the 229 // folder path. 230 if mountPath != "" { 231 dir = mountPath 232 } else { 233 dir = d.dir(id) 234 } 235 236 return dir, nil 237 } 238 239 // Put adds a new layer to the driver. 240 func (d *Driver) Put(id string) error { 241 logrus.Debugf("WindowsGraphDriver Put() id %s", id) 242 243 rID, err := d.resolveID(id) 244 if err != nil { 245 return err 246 } 247 248 d.Lock() 249 defer d.Unlock() 250 251 if d.active[rID] > 1 { 252 d.active[rID]-- 253 } else if d.active[rID] == 1 { 254 if err := hcsshim.UnprepareLayer(d.info, rID); err != nil { 255 return err 256 } 257 if err := hcsshim.DeactivateLayer(d.info, rID); err != nil { 258 return err 259 } 260 delete(d.active, rID) 261 } 262 263 return nil 264 } 265 266 // Cleanup ensures the information the driver stores is properly removed. 267 func (d *Driver) Cleanup() error { 268 return nil 269 } 270 271 // Diff produces an archive of the changes between the specified 272 // layer and its parent layer which may be "". 273 func (d *Driver) Diff(id, parent string) (_ archive.Archive, err error) { 274 rID, err := d.resolveID(id) 275 if err != nil { 276 return 277 } 278 279 // Getting the layer paths must be done outside of the lock. 280 layerChain, err := d.getLayerChain(rID) 281 if err != nil { 282 return 283 } 284 285 var undo func() 286 287 d.Lock() 288 289 // To support export, a layer must be activated but not prepared. 290 if d.info.Flavour == filterDriver { 291 if d.active[rID] == 0 { 292 if err = hcsshim.ActivateLayer(d.info, rID); err != nil { 293 d.Unlock() 294 return 295 } 296 undo = func() { 297 if err := hcsshim.DeactivateLayer(d.info, rID); err != nil { 298 logrus.Warnf("Failed to Deactivate %s: %s", rID, err) 299 } 300 } 301 } else { 302 if err = hcsshim.UnprepareLayer(d.info, rID); err != nil { 303 d.Unlock() 304 return 305 } 306 undo = func() { 307 if err := hcsshim.PrepareLayer(d.info, rID, layerChain); err != nil { 308 logrus.Warnf("Failed to re-PrepareLayer %s: %s", rID, err) 309 } 310 } 311 } 312 } 313 314 d.Unlock() 315 316 arch, err := d.exportLayer(rID, layerChain) 317 if err != nil { 318 undo() 319 return 320 } 321 return ioutils.NewReadCloserWrapper(arch, func() error { 322 defer undo() 323 return arch.Close() 324 }), nil 325 } 326 327 // Changes produces a list of changes between the specified layer 328 // and its parent layer. If parent is "", then all changes will be ADD changes. 329 func (d *Driver) Changes(id, parent string) ([]archive.Change, error) { 330 rID, err := d.resolveID(id) 331 if err != nil { 332 return nil, err 333 } 334 parentChain, err := d.getLayerChain(rID) 335 if err != nil { 336 return nil, err 337 } 338 339 d.Lock() 340 if d.info.Flavour == filterDriver { 341 if d.active[rID] == 0 { 342 if err = hcsshim.ActivateLayer(d.info, rID); err != nil { 343 d.Unlock() 344 return nil, err 345 } 346 defer func() { 347 if err := hcsshim.DeactivateLayer(d.info, rID); err != nil { 348 logrus.Warnf("Failed to Deactivate %s: %s", rID, err) 349 } 350 }() 351 } else { 352 if err = hcsshim.UnprepareLayer(d.info, rID); err != nil { 353 d.Unlock() 354 return nil, err 355 } 356 defer func() { 357 if err := hcsshim.PrepareLayer(d.info, rID, parentChain); err != nil { 358 logrus.Warnf("Failed to re-PrepareLayer %s: %s", rID, err) 359 } 360 }() 361 } 362 } 363 d.Unlock() 364 365 r, err := hcsshim.NewLayerReader(d.info, id, parentChain) 366 if err != nil { 367 return nil, err 368 } 369 defer r.Close() 370 371 var changes []archive.Change 372 for { 373 name, _, fileInfo, err := r.Next() 374 if err == io.EOF { 375 break 376 } 377 if err != nil { 378 return nil, err 379 } 380 name = filepath.ToSlash(name) 381 if fileInfo == nil { 382 changes = append(changes, archive.Change{name, archive.ChangeDelete}) 383 } else { 384 // Currently there is no way to tell between an add and a modify. 385 changes = append(changes, archive.Change{name, archive.ChangeModify}) 386 } 387 } 388 return changes, nil 389 } 390 391 // ApplyDiff extracts the changeset from the given diff into the 392 // layer with the specified id and parent, returning the size of the 393 // new layer in bytes. 394 func (d *Driver) ApplyDiff(id, parent string, diff archive.Reader) (size int64, err error) { 395 rPId, err := d.resolveID(parent) 396 if err != nil { 397 return 398 } 399 400 if d.info.Flavour == diffDriver { 401 start := time.Now().UTC() 402 logrus.Debugf("WindowsGraphDriver ApplyDiff: Start untar layer") 403 destination := d.dir(id) 404 destination = filepath.Dir(destination) 405 if size, err = chrootarchive.ApplyUncompressedLayer(destination, diff, nil); err != nil { 406 return 407 } 408 logrus.Debugf("WindowsGraphDriver ApplyDiff: Untar time: %vs", time.Now().UTC().Sub(start).Seconds()) 409 410 return 411 } 412 413 parentChain, err := d.getLayerChain(rPId) 414 if err != nil { 415 return 416 } 417 parentPath, err := hcsshim.GetLayerMountPath(d.info, rPId) 418 if err != nil { 419 return 420 } 421 layerChain := []string{parentPath} 422 layerChain = append(layerChain, parentChain...) 423 424 if size, err = d.importLayer(id, diff, layerChain); err != nil { 425 return 426 } 427 428 if err = d.setLayerChain(id, layerChain); err != nil { 429 return 430 } 431 432 return 433 } 434 435 // DiffSize calculates the changes between the specified layer 436 // and its parent and returns the size in bytes of the changes 437 // relative to its base filesystem directory. 438 func (d *Driver) DiffSize(id, parent string) (size int64, err error) { 439 rPId, err := d.resolveID(parent) 440 if err != nil { 441 return 442 } 443 444 changes, err := d.Changes(id, rPId) 445 if err != nil { 446 return 447 } 448 449 layerFs, err := d.Get(id, "") 450 if err != nil { 451 return 452 } 453 defer d.Put(id) 454 455 return archive.ChangesSize(layerFs, changes), nil 456 } 457 458 // CustomImageInfo is the object returned by the driver describing the base 459 // image. 460 type CustomImageInfo struct { 461 ID string 462 Name string 463 Version string 464 Path string 465 Size int64 466 CreatedTime time.Time 467 } 468 469 // GetCustomImageInfos returns the image infos for window specific 470 // base images which should always be present. 471 func (d *Driver) GetCustomImageInfos() ([]CustomImageInfo, error) { 472 strData, err := hcsshim.GetSharedBaseImages() 473 if err != nil { 474 return nil, fmt.Errorf("Failed to restore base images: %s", err) 475 } 476 477 type customImageInfoList struct { 478 Images []CustomImageInfo 479 } 480 481 var infoData customImageInfoList 482 483 if err = json.Unmarshal([]byte(strData), &infoData); err != nil { 484 err = fmt.Errorf("JSON unmarshal returned error=%s", err) 485 logrus.Error(err) 486 return nil, err 487 } 488 489 var images []CustomImageInfo 490 491 for _, imageData := range infoData.Images { 492 folderName := filepath.Base(imageData.Path) 493 494 // Use crypto hash of the foldername to generate a docker style id. 495 h := sha512.Sum384([]byte(folderName)) 496 id := fmt.Sprintf("%x", h[:32]) 497 498 if err := d.Create(id, "", ""); err != nil { 499 return nil, err 500 } 501 // Create the alternate ID file. 502 if err := d.setID(id, folderName); err != nil { 503 return nil, err 504 } 505 506 imageData.ID = id 507 images = append(images, imageData) 508 } 509 510 return images, nil 511 } 512 513 // GetMetadata returns custom driver information. 514 func (d *Driver) GetMetadata(id string) (map[string]string, error) { 515 m := make(map[string]string) 516 m["dir"] = d.dir(id) 517 return m, nil 518 } 519 520 func writeTarFromLayer(r hcsshim.LayerReader, w io.Writer) error { 521 t := tar.NewWriter(w) 522 for { 523 name, size, fileInfo, err := r.Next() 524 if err == io.EOF { 525 break 526 } 527 if err != nil { 528 return err 529 } 530 if fileInfo == nil { 531 // Write a whiteout file. 532 hdr := &tar.Header{ 533 Name: filepath.ToSlash(filepath.Join(filepath.Dir(name), archive.WhiteoutPrefix+filepath.Base(name))), 534 } 535 err := t.WriteHeader(hdr) 536 if err != nil { 537 return err 538 } 539 } else { 540 err = backuptar.WriteTarFileFromBackupStream(t, r, name, size, fileInfo) 541 if err != nil { 542 return err 543 } 544 } 545 } 546 return t.Close() 547 } 548 549 // exportLayer generates an archive from a layer based on the given ID. 550 func (d *Driver) exportLayer(id string, parentLayerPaths []string) (archive.Archive, error) { 551 if hcsshim.IsTP4() { 552 // Export in TP4 format to maintain compatibility with existing images and 553 // because ExportLayer is somewhat broken on TP4 and can't work with the new 554 // scheme. 555 tempFolder, err := ioutil.TempDir("", "hcs") 556 if err != nil { 557 return nil, err 558 } 559 defer func() { 560 if err != nil { 561 os.RemoveAll(tempFolder) 562 } 563 }() 564 565 if err = hcsshim.ExportLayer(d.info, id, tempFolder, parentLayerPaths); err != nil { 566 return nil, err 567 } 568 archive, err := archive.Tar(tempFolder, archive.Uncompressed) 569 if err != nil { 570 return nil, err 571 } 572 return ioutils.NewReadCloserWrapper(archive, func() error { 573 err := archive.Close() 574 os.RemoveAll(tempFolder) 575 return err 576 }), nil 577 } 578 579 var r hcsshim.LayerReader 580 r, err := hcsshim.NewLayerReader(d.info, id, parentLayerPaths) 581 if err != nil { 582 return nil, err 583 } 584 585 archive, w := io.Pipe() 586 go func() { 587 err := writeTarFromLayer(r, w) 588 cerr := r.Close() 589 if err == nil { 590 err = cerr 591 } 592 w.CloseWithError(err) 593 }() 594 595 return archive, nil 596 } 597 598 func writeLayerFromTar(r archive.Reader, w hcsshim.LayerWriter) (int64, error) { 599 t := tar.NewReader(r) 600 hdr, err := t.Next() 601 totalSize := int64(0) 602 buf := bufio.NewWriter(nil) 603 for err == nil { 604 base := path.Base(hdr.Name) 605 if strings.HasPrefix(base, archive.WhiteoutPrefix) { 606 name := path.Join(path.Dir(hdr.Name), base[len(archive.WhiteoutPrefix):]) 607 err = w.Remove(filepath.FromSlash(name)) 608 if err != nil { 609 return 0, err 610 } 611 hdr, err = t.Next() 612 } else { 613 var ( 614 name string 615 size int64 616 fileInfo *winio.FileBasicInfo 617 ) 618 name, size, fileInfo, err = backuptar.FileInfoFromHeader(hdr) 619 if err != nil { 620 return 0, err 621 } 622 err = w.Add(filepath.FromSlash(name), fileInfo) 623 if err != nil { 624 return 0, err 625 } 626 buf.Reset(w) 627 hdr, err = backuptar.WriteBackupStreamFromTarFile(buf, t, hdr) 628 ferr := buf.Flush() 629 if ferr != nil { 630 err = ferr 631 } 632 totalSize += size 633 } 634 } 635 if err != io.EOF { 636 return 0, err 637 } 638 return totalSize, nil 639 } 640 641 // importLayer adds a new layer to the tag and graph store based on the given data. 642 func (d *Driver) importLayer(id string, layerData archive.Reader, parentLayerPaths []string) (size int64, err error) { 643 if hcsshim.IsTP4() { 644 // Import from TP4 format to maintain compatibility with existing images. 645 var tempFolder string 646 tempFolder, err = ioutil.TempDir("", "hcs") 647 if err != nil { 648 return 649 } 650 defer os.RemoveAll(tempFolder) 651 652 if size, err = chrootarchive.ApplyLayer(tempFolder, layerData); err != nil { 653 return 654 } 655 if err = hcsshim.ImportLayer(d.info, id, tempFolder, parentLayerPaths); err != nil { 656 return 657 } 658 return 659 } 660 661 var w hcsshim.LayerWriter 662 w, err = hcsshim.NewLayerWriter(d.info, id, parentLayerPaths) 663 if err != nil { 664 return 665 } 666 667 size, err = writeLayerFromTar(layerData, w) 668 if err != nil { 669 w.Close() 670 return 671 } 672 err = w.Close() 673 if err != nil { 674 return 675 } 676 return 677 } 678 679 // resolveID computes the layerID information based on the given id. 680 func (d *Driver) resolveID(id string) (string, error) { 681 content, err := ioutil.ReadFile(filepath.Join(d.dir(id), "layerID")) 682 if os.IsNotExist(err) { 683 return id, nil 684 } else if err != nil { 685 return "", err 686 } 687 return string(content), nil 688 } 689 690 // setID stores the layerId in disk. 691 func (d *Driver) setID(id, altID string) error { 692 err := ioutil.WriteFile(filepath.Join(d.dir(id), "layerId"), []byte(altID), 0600) 693 if err != nil { 694 return err 695 } 696 return nil 697 } 698 699 // getLayerChain returns the layer chain information. 700 func (d *Driver) getLayerChain(id string) ([]string, error) { 701 jPath := filepath.Join(d.dir(id), "layerchain.json") 702 content, err := ioutil.ReadFile(jPath) 703 if os.IsNotExist(err) { 704 return nil, nil 705 } else if err != nil { 706 return nil, fmt.Errorf("Unable to read layerchain file - %s", err) 707 } 708 709 var layerChain []string 710 err = json.Unmarshal(content, &layerChain) 711 if err != nil { 712 return nil, fmt.Errorf("Failed to unmarshall layerchain json - %s", err) 713 } 714 715 return layerChain, nil 716 } 717 718 // setLayerChain stores the layer chain information in disk. 719 func (d *Driver) setLayerChain(id string, chain []string) error { 720 content, err := json.Marshal(&chain) 721 if err != nil { 722 return fmt.Errorf("Failed to marshall layerchain json - %s", err) 723 } 724 725 jPath := filepath.Join(d.dir(id), "layerchain.json") 726 err = ioutil.WriteFile(jPath, content, 0600) 727 if err != nil { 728 return fmt.Errorf("Unable to write layerchain file - %s", err) 729 } 730 731 return nil 732 } 733 734 type fileGetCloserWithBackupPrivileges struct { 735 path string 736 } 737 738 func (fg *fileGetCloserWithBackupPrivileges) Get(filename string) (io.ReadCloser, error) { 739 var f *os.File 740 // Open the file while holding the Windows backup privilege. This ensures that the 741 // file can be opened even if the caller does not actually have access to it according 742 // to the security descriptor. 743 err := winio.RunWithPrivilege(winio.SeBackupPrivilege, func() error { 744 path := filepath.Join(fg.path, filename) 745 p, err := syscall.UTF16FromString(path) 746 if err != nil { 747 return err 748 } 749 h, err := syscall.CreateFile(&p[0], syscall.GENERIC_READ, syscall.FILE_SHARE_READ, nil, syscall.OPEN_EXISTING, syscall.FILE_FLAG_BACKUP_SEMANTICS, 0) 750 if err != nil { 751 return &os.PathError{Op: "open", Path: path, Err: err} 752 } 753 f = os.NewFile(uintptr(h), path) 754 return nil 755 }) 756 return f, err 757 } 758 759 func (fg *fileGetCloserWithBackupPrivileges) Close() error { 760 return nil 761 } 762 763 type fileGetDestroyCloser struct { 764 storage.FileGetter 765 path string 766 } 767 768 func (f *fileGetDestroyCloser) Close() error { 769 // TODO: activate layers and release here? 770 return os.RemoveAll(f.path) 771 } 772 773 // DiffGetter returns a FileGetCloser that can read files from the directory that 774 // contains files for the layer differences. Used for direct access for tar-split. 775 func (d *Driver) DiffGetter(id string) (graphdriver.FileGetCloser, error) { 776 id, err := d.resolveID(id) 777 if err != nil { 778 return nil, err 779 } 780 781 if hcsshim.IsTP4() { 782 // The export format for TP4 is different from the contents of the layer, so 783 // fall back to exporting the layer and getting file contents from there. 784 layerChain, err := d.getLayerChain(id) 785 if err != nil { 786 return nil, err 787 } 788 789 var tempFolder string 790 tempFolder, err = ioutil.TempDir("", "hcs") 791 if err != nil { 792 return nil, err 793 } 794 defer func() { 795 if err != nil { 796 os.RemoveAll(tempFolder) 797 } 798 }() 799 800 if err = hcsshim.ExportLayer(d.info, id, tempFolder, layerChain); err != nil { 801 return nil, err 802 } 803 804 return &fileGetDestroyCloser{storage.NewPathFileGetter(tempFolder), tempFolder}, nil 805 } 806 807 return &fileGetCloserWithBackupPrivileges{d.dir(id)}, nil 808 }