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