github.com/jiasir/docker@v1.3.3-0.20170609024000-252e610103e7/daemon/graphdriver/windows/windows.go (about) 1 //+build windows 2 3 package windows 4 5 import ( 6 "bufio" 7 "bytes" 8 "encoding/json" 9 "errors" 10 "fmt" 11 "io" 12 "io/ioutil" 13 "os" 14 "path" 15 "path/filepath" 16 "strconv" 17 "strings" 18 "sync" 19 "syscall" 20 "time" 21 "unsafe" 22 23 "github.com/Microsoft/go-winio" 24 "github.com/Microsoft/go-winio/archive/tar" 25 "github.com/Microsoft/go-winio/backuptar" 26 "github.com/Microsoft/hcsshim" 27 "github.com/Sirupsen/logrus" 28 "github.com/docker/docker/daemon/graphdriver" 29 "github.com/docker/docker/pkg/archive" 30 "github.com/docker/docker/pkg/idtools" 31 "github.com/docker/docker/pkg/ioutils" 32 "github.com/docker/docker/pkg/longpath" 33 "github.com/docker/docker/pkg/reexec" 34 "github.com/docker/docker/pkg/system" 35 units "github.com/docker/go-units" 36 "golang.org/x/sys/windows" 37 ) 38 39 // filterDriver is an HCSShim driver type for the Windows Filter driver. 40 const filterDriver = 1 41 42 var ( 43 // mutatedFiles is a list of files that are mutated by the import process 44 // and must be backed up and restored. 45 mutatedFiles = map[string]string{ 46 "UtilityVM/Files/EFI/Microsoft/Boot/BCD": "bcd.bak", 47 "UtilityVM/Files/EFI/Microsoft/Boot/BCD.LOG": "bcd.log.bak", 48 "UtilityVM/Files/EFI/Microsoft/Boot/BCD.LOG1": "bcd.log1.bak", 49 "UtilityVM/Files/EFI/Microsoft/Boot/BCD.LOG2": "bcd.log2.bak", 50 } 51 noreexec = false 52 ) 53 54 // init registers the windows graph drivers to the register. 55 func init() { 56 graphdriver.Register("windowsfilter", InitFilter) 57 // DOCKER_WINDOWSFILTER_NOREEXEC allows for inline processing which makes 58 // debugging issues in the re-exec codepath significantly easier. 59 if os.Getenv("DOCKER_WINDOWSFILTER_NOREEXEC") != "" { 60 logrus.Warnf("WindowsGraphDriver is set to not re-exec. This is intended for debugging purposes only.") 61 noreexec = true 62 } else { 63 reexec.Register("docker-windows-write-layer", writeLayerReexec) 64 } 65 } 66 67 type checker struct { 68 } 69 70 func (c *checker) IsMounted(path string) bool { 71 return false 72 } 73 74 // Driver represents a windows graph driver. 75 type Driver struct { 76 // info stores the shim driver information 77 info hcsshim.DriverInfo 78 ctr *graphdriver.RefCounter 79 // it is safe for windows to use a cache here because it does not support 80 // restoring containers when the daemon dies. 81 cacheMu sync.Mutex 82 cache map[string]string 83 } 84 85 // InitFilter returns a new Windows storage filter driver. 86 func InitFilter(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (graphdriver.Driver, error) { 87 logrus.Debugf("WindowsGraphDriver InitFilter at %s", home) 88 89 fsType, err := getFileSystemType(string(home[0])) 90 if err != nil { 91 return nil, err 92 } 93 if strings.ToLower(fsType) == "refs" { 94 return nil, fmt.Errorf("%s is on an ReFS volume - ReFS volumes are not supported", home) 95 } 96 97 d := &Driver{ 98 info: hcsshim.DriverInfo{ 99 HomeDir: home, 100 Flavour: filterDriver, 101 }, 102 cache: make(map[string]string), 103 ctr: graphdriver.NewRefCounter(&checker{}), 104 } 105 return d, nil 106 } 107 108 // win32FromHresult is a helper function to get the win32 error code from an HRESULT 109 func win32FromHresult(hr uintptr) uintptr { 110 if hr&0x1fff0000 == 0x00070000 { 111 return hr & 0xffff 112 } 113 return hr 114 } 115 116 // getFileSystemType obtains the type of a file system through GetVolumeInformation 117 // https://msdn.microsoft.com/en-us/library/windows/desktop/aa364993(v=vs.85).aspx 118 func getFileSystemType(drive string) (fsType string, hr error) { 119 var ( 120 modkernel32 = windows.NewLazySystemDLL("kernel32.dll") 121 procGetVolumeInformation = modkernel32.NewProc("GetVolumeInformationW") 122 buf = make([]uint16, 255) 123 size = syscall.MAX_PATH + 1 124 ) 125 if len(drive) != 1 { 126 hr = errors.New("getFileSystemType must be called with a drive letter") 127 return 128 } 129 drive += `:\` 130 n := uintptr(unsafe.Pointer(nil)) 131 r0, _, _ := syscall.Syscall9(procGetVolumeInformation.Addr(), 8, uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(drive))), n, n, n, n, n, uintptr(unsafe.Pointer(&buf[0])), uintptr(size), 0) 132 if int32(r0) < 0 { 133 hr = syscall.Errno(win32FromHresult(r0)) 134 } 135 fsType = syscall.UTF16ToString(buf) 136 return 137 } 138 139 // String returns the string representation of a driver. This should match 140 // the name the graph driver has been registered with. 141 func (d *Driver) String() string { 142 return "windowsfilter" 143 } 144 145 // Status returns the status of the driver. 146 func (d *Driver) Status() [][2]string { 147 return [][2]string{ 148 {"Windows", ""}, 149 } 150 } 151 152 // Exists returns true if the given id is registered with this driver. 153 func (d *Driver) Exists(id string) bool { 154 rID, err := d.resolveID(id) 155 if err != nil { 156 return false 157 } 158 result, err := hcsshim.LayerExists(d.info, rID) 159 if err != nil { 160 return false 161 } 162 return result 163 } 164 165 // CreateReadWrite creates a layer that is writable for use as a container 166 // file system. 167 func (d *Driver) CreateReadWrite(id, parent string, opts *graphdriver.CreateOpts) error { 168 if opts != nil { 169 return d.create(id, parent, opts.MountLabel, false, opts.StorageOpt) 170 } 171 return d.create(id, parent, "", false, nil) 172 } 173 174 // Create creates a new read-only layer with the given id. 175 func (d *Driver) Create(id, parent string, opts *graphdriver.CreateOpts) error { 176 if opts != nil { 177 return d.create(id, parent, opts.MountLabel, true, opts.StorageOpt) 178 } 179 return d.create(id, parent, "", true, nil) 180 } 181 182 func (d *Driver) create(id, parent, mountLabel string, readOnly bool, storageOpt map[string]string) error { 183 rPId, err := d.resolveID(parent) 184 if err != nil { 185 return err 186 } 187 188 parentChain, err := d.getLayerChain(rPId) 189 if err != nil { 190 return err 191 } 192 193 var layerChain []string 194 195 if rPId != "" { 196 parentPath, err := hcsshim.GetLayerMountPath(d.info, rPId) 197 if err != nil { 198 return err 199 } 200 if _, err := os.Stat(filepath.Join(parentPath, "Files")); err == nil { 201 // This is a legitimate parent layer (not the empty "-init" layer), 202 // so include it in the layer chain. 203 layerChain = []string{parentPath} 204 } 205 } 206 207 layerChain = append(layerChain, parentChain...) 208 209 if readOnly { 210 if err := hcsshim.CreateLayer(d.info, id, rPId); err != nil { 211 return err 212 } 213 } else { 214 var parentPath string 215 if len(layerChain) != 0 { 216 parentPath = layerChain[0] 217 } 218 219 if err := hcsshim.CreateSandboxLayer(d.info, id, parentPath, layerChain); err != nil { 220 return err 221 } 222 223 storageOptions, err := parseStorageOpt(storageOpt) 224 if err != nil { 225 return fmt.Errorf("Failed to parse storage options - %s", err) 226 } 227 228 if storageOptions.size != 0 { 229 if err := hcsshim.ExpandSandboxSize(d.info, id, storageOptions.size); err != nil { 230 return err 231 } 232 } 233 } 234 235 if _, err := os.Lstat(d.dir(parent)); err != nil { 236 if err2 := hcsshim.DestroyLayer(d.info, id); err2 != nil { 237 logrus.Warnf("Failed to DestroyLayer %s: %s", id, err2) 238 } 239 return fmt.Errorf("Cannot create layer with missing parent %s: %s", parent, err) 240 } 241 242 if err := d.setLayerChain(id, layerChain); err != nil { 243 if err2 := hcsshim.DestroyLayer(d.info, id); err2 != nil { 244 logrus.Warnf("Failed to DestroyLayer %s: %s", id, err2) 245 } 246 return err 247 } 248 249 return nil 250 } 251 252 // dir returns the absolute path to the layer. 253 func (d *Driver) dir(id string) string { 254 return filepath.Join(d.info.HomeDir, filepath.Base(id)) 255 } 256 257 // Remove unmounts and removes the dir information. 258 func (d *Driver) Remove(id string) error { 259 rID, err := d.resolveID(id) 260 if err != nil { 261 return err 262 } 263 264 // This retry loop is due to a bug in Windows (Internal bug #9432268) 265 // if GetContainers fails with ErrVmcomputeOperationInvalidState 266 // it is a transient error. Retry until it succeeds. 267 var computeSystems []hcsshim.ContainerProperties 268 retryCount := 0 269 osv := system.GetOSVersion() 270 for { 271 // Get and terminate any template VMs that are currently using the layer. 272 // Note: It is unfortunate that we end up in the graphdrivers Remove() call 273 // for both containers and images, but the logic for template VMs is only 274 // needed for images - specifically we are looking to see if a base layer 275 // is in use by a template VM as a result of having started a Hyper-V 276 // container at some point. 277 // 278 // We have a retry loop for ErrVmcomputeOperationInvalidState and 279 // ErrVmcomputeOperationAccessIsDenied as there is a race condition 280 // in RS1 and RS2 building during enumeration when a silo is going away 281 // for example under it, in HCS. AccessIsDenied added to fix 30278. 282 // 283 // TODO @jhowardmsft - For RS3, we can remove the retries. Also consider 284 // using platform APIs (if available) to get this more succinctly. Also 285 // consider enlighting the Remove() interface to have context of why 286 // the remove is being called - that could improve efficiency by not 287 // enumerating compute systems during a remove of a container as it's 288 // not required. 289 computeSystems, err = hcsshim.GetContainers(hcsshim.ComputeSystemQuery{}) 290 if err != nil { 291 if (osv.Build < 15139) && 292 ((err == hcsshim.ErrVmcomputeOperationInvalidState) || (err == hcsshim.ErrVmcomputeOperationAccessIsDenied)) { 293 if retryCount >= 500 { 294 break 295 } 296 retryCount++ 297 time.Sleep(10 * time.Millisecond) 298 continue 299 } 300 return err 301 } 302 break 303 } 304 305 for _, computeSystem := range computeSystems { 306 if strings.Contains(computeSystem.RuntimeImagePath, id) && computeSystem.IsRuntimeTemplate { 307 container, err := hcsshim.OpenContainer(computeSystem.ID) 308 if err != nil { 309 return err 310 } 311 defer container.Close() 312 err = container.Terminate() 313 if hcsshim.IsPending(err) { 314 err = container.Wait() 315 } else if hcsshim.IsAlreadyStopped(err) { 316 err = nil 317 } 318 319 if err != nil { 320 return err 321 } 322 } 323 } 324 325 layerPath := filepath.Join(d.info.HomeDir, rID) 326 tmpID := fmt.Sprintf("%s-removing", rID) 327 tmpLayerPath := filepath.Join(d.info.HomeDir, tmpID) 328 if err := os.Rename(layerPath, tmpLayerPath); err != nil && !os.IsNotExist(err) { 329 return err 330 } 331 if err := hcsshim.DestroyLayer(d.info, tmpID); err != nil { 332 logrus.Errorf("Failed to DestroyLayer %s: %s", id, err) 333 } 334 335 return nil 336 } 337 338 // Get returns the rootfs path for the id. This will mount the dir at its given path. 339 func (d *Driver) Get(id, mountLabel string) (string, error) { 340 logrus.Debugf("WindowsGraphDriver Get() id %s mountLabel %s", id, mountLabel) 341 var dir string 342 343 rID, err := d.resolveID(id) 344 if err != nil { 345 return "", err 346 } 347 if count := d.ctr.Increment(rID); count > 1 { 348 return d.cache[rID], nil 349 } 350 351 // Getting the layer paths must be done outside of the lock. 352 layerChain, err := d.getLayerChain(rID) 353 if err != nil { 354 d.ctr.Decrement(rID) 355 return "", err 356 } 357 358 if err := hcsshim.ActivateLayer(d.info, rID); err != nil { 359 d.ctr.Decrement(rID) 360 return "", err 361 } 362 if err := hcsshim.PrepareLayer(d.info, rID, layerChain); err != nil { 363 d.ctr.Decrement(rID) 364 if err2 := hcsshim.DeactivateLayer(d.info, rID); err2 != nil { 365 logrus.Warnf("Failed to Deactivate %s: %s", id, err) 366 } 367 return "", err 368 } 369 370 mountPath, err := hcsshim.GetLayerMountPath(d.info, rID) 371 if err != nil { 372 d.ctr.Decrement(rID) 373 if err := hcsshim.UnprepareLayer(d.info, rID); err != nil { 374 logrus.Warnf("Failed to Unprepare %s: %s", id, err) 375 } 376 if err2 := hcsshim.DeactivateLayer(d.info, rID); err2 != nil { 377 logrus.Warnf("Failed to Deactivate %s: %s", id, err) 378 } 379 return "", err 380 } 381 d.cacheMu.Lock() 382 d.cache[rID] = mountPath 383 d.cacheMu.Unlock() 384 385 // If the layer has a mount path, use that. Otherwise, use the 386 // folder path. 387 if mountPath != "" { 388 dir = mountPath 389 } else { 390 dir = d.dir(id) 391 } 392 393 return dir, nil 394 } 395 396 // Put adds a new layer to the driver. 397 func (d *Driver) Put(id string) error { 398 logrus.Debugf("WindowsGraphDriver Put() id %s", id) 399 400 rID, err := d.resolveID(id) 401 if err != nil { 402 return err 403 } 404 if count := d.ctr.Decrement(rID); count > 0 { 405 return nil 406 } 407 d.cacheMu.Lock() 408 _, exists := d.cache[rID] 409 delete(d.cache, rID) 410 d.cacheMu.Unlock() 411 412 // If the cache was not populated, then the layer was left unprepared and deactivated 413 if !exists { 414 return nil 415 } 416 417 if err := hcsshim.UnprepareLayer(d.info, rID); err != nil { 418 return err 419 } 420 return hcsshim.DeactivateLayer(d.info, rID) 421 } 422 423 // Cleanup ensures the information the driver stores is properly removed. 424 // We use this opportunity to cleanup any -removing folders which may be 425 // still left if the daemon was killed while it was removing a layer. 426 func (d *Driver) Cleanup() error { 427 428 items, err := ioutil.ReadDir(d.info.HomeDir) 429 if err != nil { 430 if os.IsNotExist(err) { 431 return nil 432 } 433 return err 434 } 435 436 // Note we don't return an error below - it's possible the files 437 // are locked. However, next time around after the daemon exits, 438 // we likely will be able to to cleanup successfully. Instead we log 439 // warnings if there are errors. 440 for _, item := range items { 441 if item.IsDir() && strings.HasSuffix(item.Name(), "-removing") { 442 if err := hcsshim.DestroyLayer(d.info, item.Name()); err != nil { 443 logrus.Warnf("Failed to cleanup %s: %s", item.Name(), err) 444 } else { 445 logrus.Infof("Cleaned up %s", item.Name()) 446 } 447 } 448 } 449 450 return nil 451 } 452 453 // Diff produces an archive of the changes between the specified 454 // layer and its parent layer which may be "". 455 // The layer should be mounted when calling this function 456 func (d *Driver) Diff(id, parent string) (_ io.ReadCloser, err error) { 457 rID, err := d.resolveID(id) 458 if err != nil { 459 return 460 } 461 462 layerChain, err := d.getLayerChain(rID) 463 if err != nil { 464 return 465 } 466 467 // this is assuming that the layer is unmounted 468 if err := hcsshim.UnprepareLayer(d.info, rID); err != nil { 469 return nil, err 470 } 471 prepare := func() { 472 if err := hcsshim.PrepareLayer(d.info, rID, layerChain); err != nil { 473 logrus.Warnf("Failed to Deactivate %s: %s", rID, err) 474 } 475 } 476 477 arch, err := d.exportLayer(rID, layerChain) 478 if err != nil { 479 prepare() 480 return 481 } 482 return ioutils.NewReadCloserWrapper(arch, func() error { 483 err := arch.Close() 484 prepare() 485 return err 486 }), nil 487 } 488 489 // Changes produces a list of changes between the specified layer 490 // and its parent layer. If parent is "", then all changes will be ADD changes. 491 // The layer should not be mounted when calling this function. 492 func (d *Driver) Changes(id, parent string) ([]archive.Change, error) { 493 rID, err := d.resolveID(id) 494 if err != nil { 495 return nil, err 496 } 497 parentChain, err := d.getLayerChain(rID) 498 if err != nil { 499 return nil, err 500 } 501 502 if err := hcsshim.ActivateLayer(d.info, rID); err != nil { 503 return nil, err 504 } 505 defer func() { 506 if err2 := hcsshim.DeactivateLayer(d.info, rID); err2 != nil { 507 logrus.Errorf("changes() failed to DeactivateLayer %s %s: %s", id, rID, err2) 508 } 509 }() 510 511 var changes []archive.Change 512 err = winio.RunWithPrivilege(winio.SeBackupPrivilege, func() error { 513 r, err := hcsshim.NewLayerReader(d.info, id, parentChain) 514 if err != nil { 515 return err 516 } 517 defer r.Close() 518 519 for { 520 name, _, fileInfo, err := r.Next() 521 if err == io.EOF { 522 return nil 523 } 524 if err != nil { 525 return err 526 } 527 name = filepath.ToSlash(name) 528 if fileInfo == nil { 529 changes = append(changes, archive.Change{Path: name, Kind: archive.ChangeDelete}) 530 } else { 531 // Currently there is no way to tell between an add and a modify. 532 changes = append(changes, archive.Change{Path: name, Kind: archive.ChangeModify}) 533 } 534 } 535 }) 536 if err != nil { 537 return nil, err 538 } 539 540 return changes, nil 541 } 542 543 // ApplyDiff extracts the changeset from the given diff into the 544 // layer with the specified id and parent, returning the size of the 545 // new layer in bytes. 546 // The layer should not be mounted when calling this function 547 func (d *Driver) ApplyDiff(id, parent string, diff io.Reader) (int64, error) { 548 var layerChain []string 549 if parent != "" { 550 rPId, err := d.resolveID(parent) 551 if err != nil { 552 return 0, err 553 } 554 parentChain, err := d.getLayerChain(rPId) 555 if err != nil { 556 return 0, err 557 } 558 parentPath, err := hcsshim.GetLayerMountPath(d.info, rPId) 559 if err != nil { 560 return 0, err 561 } 562 layerChain = append(layerChain, parentPath) 563 layerChain = append(layerChain, parentChain...) 564 } 565 566 size, err := d.importLayer(id, diff, layerChain) 567 if err != nil { 568 return 0, err 569 } 570 571 if err = d.setLayerChain(id, layerChain); err != nil { 572 return 0, err 573 } 574 575 return size, nil 576 } 577 578 // DiffSize calculates the changes between the specified layer 579 // and its parent and returns the size in bytes of the changes 580 // relative to its base filesystem directory. 581 func (d *Driver) DiffSize(id, parent string) (size int64, err error) { 582 rPId, err := d.resolveID(parent) 583 if err != nil { 584 return 585 } 586 587 changes, err := d.Changes(id, rPId) 588 if err != nil { 589 return 590 } 591 592 layerFs, err := d.Get(id, "") 593 if err != nil { 594 return 595 } 596 defer d.Put(id) 597 598 return archive.ChangesSize(layerFs, changes), nil 599 } 600 601 // GetMetadata returns custom driver information. 602 func (d *Driver) GetMetadata(id string) (map[string]string, error) { 603 m := make(map[string]string) 604 m["dir"] = d.dir(id) 605 return m, nil 606 } 607 608 func writeTarFromLayer(r hcsshim.LayerReader, w io.Writer) error { 609 t := tar.NewWriter(w) 610 for { 611 name, size, fileInfo, err := r.Next() 612 if err == io.EOF { 613 break 614 } 615 if err != nil { 616 return err 617 } 618 if fileInfo == nil { 619 // Write a whiteout file. 620 hdr := &tar.Header{ 621 Name: filepath.ToSlash(filepath.Join(filepath.Dir(name), archive.WhiteoutPrefix+filepath.Base(name))), 622 } 623 err := t.WriteHeader(hdr) 624 if err != nil { 625 return err 626 } 627 } else { 628 err = backuptar.WriteTarFileFromBackupStream(t, r, name, size, fileInfo) 629 if err != nil { 630 return err 631 } 632 } 633 } 634 return t.Close() 635 } 636 637 // exportLayer generates an archive from a layer based on the given ID. 638 func (d *Driver) exportLayer(id string, parentLayerPaths []string) (io.ReadCloser, error) { 639 archive, w := io.Pipe() 640 go func() { 641 err := winio.RunWithPrivilege(winio.SeBackupPrivilege, func() error { 642 r, err := hcsshim.NewLayerReader(d.info, id, parentLayerPaths) 643 if err != nil { 644 return err 645 } 646 647 err = writeTarFromLayer(r, w) 648 cerr := r.Close() 649 if err == nil { 650 err = cerr 651 } 652 return err 653 }) 654 w.CloseWithError(err) 655 }() 656 657 return archive, nil 658 } 659 660 // writeBackupStreamFromTarAndSaveMutatedFiles reads data from a tar stream and 661 // writes it to a backup stream, and also saves any files that will be mutated 662 // by the import layer process to a backup location. 663 func writeBackupStreamFromTarAndSaveMutatedFiles(buf *bufio.Writer, w io.Writer, t *tar.Reader, hdr *tar.Header, root string) (nextHdr *tar.Header, err error) { 664 var bcdBackup *os.File 665 var bcdBackupWriter *winio.BackupFileWriter 666 if backupPath, ok := mutatedFiles[hdr.Name]; ok { 667 bcdBackup, err = os.Create(filepath.Join(root, backupPath)) 668 if err != nil { 669 return nil, err 670 } 671 defer func() { 672 cerr := bcdBackup.Close() 673 if err == nil { 674 err = cerr 675 } 676 }() 677 678 bcdBackupWriter = winio.NewBackupFileWriter(bcdBackup, false) 679 defer func() { 680 cerr := bcdBackupWriter.Close() 681 if err == nil { 682 err = cerr 683 } 684 }() 685 686 buf.Reset(io.MultiWriter(w, bcdBackupWriter)) 687 } else { 688 buf.Reset(w) 689 } 690 691 defer func() { 692 ferr := buf.Flush() 693 if err == nil { 694 err = ferr 695 } 696 }() 697 698 return backuptar.WriteBackupStreamFromTarFile(buf, t, hdr) 699 } 700 701 func writeLayerFromTar(r io.Reader, w hcsshim.LayerWriter, root string) (int64, error) { 702 t := tar.NewReader(r) 703 hdr, err := t.Next() 704 totalSize := int64(0) 705 buf := bufio.NewWriter(nil) 706 for err == nil { 707 base := path.Base(hdr.Name) 708 if strings.HasPrefix(base, archive.WhiteoutPrefix) { 709 name := path.Join(path.Dir(hdr.Name), base[len(archive.WhiteoutPrefix):]) 710 err = w.Remove(filepath.FromSlash(name)) 711 if err != nil { 712 return 0, err 713 } 714 hdr, err = t.Next() 715 } else if hdr.Typeflag == tar.TypeLink { 716 err = w.AddLink(filepath.FromSlash(hdr.Name), filepath.FromSlash(hdr.Linkname)) 717 if err != nil { 718 return 0, err 719 } 720 hdr, err = t.Next() 721 } else { 722 var ( 723 name string 724 size int64 725 fileInfo *winio.FileBasicInfo 726 ) 727 name, size, fileInfo, err = backuptar.FileInfoFromHeader(hdr) 728 if err != nil { 729 return 0, err 730 } 731 err = w.Add(filepath.FromSlash(name), fileInfo) 732 if err != nil { 733 return 0, err 734 } 735 hdr, err = writeBackupStreamFromTarAndSaveMutatedFiles(buf, w, t, hdr, root) 736 totalSize += size 737 } 738 } 739 if err != io.EOF { 740 return 0, err 741 } 742 return totalSize, nil 743 } 744 745 // importLayer adds a new layer to the tag and graph store based on the given data. 746 func (d *Driver) importLayer(id string, layerData io.Reader, parentLayerPaths []string) (size int64, err error) { 747 if !noreexec { 748 cmd := reexec.Command(append([]string{"docker-windows-write-layer", d.info.HomeDir, id}, parentLayerPaths...)...) 749 output := bytes.NewBuffer(nil) 750 cmd.Stdin = layerData 751 cmd.Stdout = output 752 cmd.Stderr = output 753 754 if err = cmd.Start(); err != nil { 755 return 756 } 757 758 if err = cmd.Wait(); err != nil { 759 return 0, fmt.Errorf("re-exec error: %v: output: %s", err, output) 760 } 761 762 return strconv.ParseInt(output.String(), 10, 64) 763 } 764 return writeLayer(layerData, d.info.HomeDir, id, parentLayerPaths...) 765 } 766 767 // writeLayerReexec is the re-exec entry point for writing a layer from a tar file 768 func writeLayerReexec() { 769 size, err := writeLayer(os.Stdin, os.Args[1], os.Args[2], os.Args[3:]...) 770 if err != nil { 771 fmt.Fprint(os.Stderr, err) 772 os.Exit(1) 773 } 774 fmt.Fprint(os.Stdout, size) 775 } 776 777 // writeLayer writes a layer from a tar file. 778 func writeLayer(layerData io.Reader, home string, id string, parentLayerPaths ...string) (int64, error) { 779 err := winio.EnableProcessPrivileges([]string{winio.SeBackupPrivilege, winio.SeRestorePrivilege}) 780 if err != nil { 781 return 0, err 782 } 783 if noreexec { 784 defer func() { 785 if err := winio.DisableProcessPrivileges([]string{winio.SeBackupPrivilege, winio.SeRestorePrivilege}); err != nil { 786 // This should never happen, but just in case when in debugging mode. 787 // See https://github.com/docker/docker/pull/28002#discussion_r86259241 for rationale. 788 panic("Failed to disabled process privileges while in non re-exec mode") 789 } 790 }() 791 } 792 793 info := hcsshim.DriverInfo{ 794 Flavour: filterDriver, 795 HomeDir: home, 796 } 797 798 w, err := hcsshim.NewLayerWriter(info, id, parentLayerPaths) 799 if err != nil { 800 return 0, err 801 } 802 803 size, err := writeLayerFromTar(layerData, w, filepath.Join(home, id)) 804 if err != nil { 805 return 0, err 806 } 807 808 err = w.Close() 809 if err != nil { 810 return 0, err 811 } 812 813 return size, nil 814 } 815 816 // resolveID computes the layerID information based on the given id. 817 func (d *Driver) resolveID(id string) (string, error) { 818 content, err := ioutil.ReadFile(filepath.Join(d.dir(id), "layerID")) 819 if os.IsNotExist(err) { 820 return id, nil 821 } else if err != nil { 822 return "", err 823 } 824 return string(content), nil 825 } 826 827 // setID stores the layerId in disk. 828 func (d *Driver) setID(id, altID string) error { 829 return ioutil.WriteFile(filepath.Join(d.dir(id), "layerId"), []byte(altID), 0600) 830 } 831 832 // getLayerChain returns the layer chain information. 833 func (d *Driver) getLayerChain(id string) ([]string, error) { 834 jPath := filepath.Join(d.dir(id), "layerchain.json") 835 content, err := ioutil.ReadFile(jPath) 836 if os.IsNotExist(err) { 837 return nil, nil 838 } else if err != nil { 839 return nil, fmt.Errorf("Unable to read layerchain file - %s", err) 840 } 841 842 var layerChain []string 843 err = json.Unmarshal(content, &layerChain) 844 if err != nil { 845 return nil, fmt.Errorf("Failed to unmarshall layerchain json - %s", err) 846 } 847 848 return layerChain, nil 849 } 850 851 // setLayerChain stores the layer chain information in disk. 852 func (d *Driver) setLayerChain(id string, chain []string) error { 853 content, err := json.Marshal(&chain) 854 if err != nil { 855 return fmt.Errorf("Failed to marshall layerchain json - %s", err) 856 } 857 858 jPath := filepath.Join(d.dir(id), "layerchain.json") 859 err = ioutil.WriteFile(jPath, content, 0600) 860 if err != nil { 861 return fmt.Errorf("Unable to write layerchain file - %s", err) 862 } 863 864 return nil 865 } 866 867 type fileGetCloserWithBackupPrivileges struct { 868 path string 869 } 870 871 func (fg *fileGetCloserWithBackupPrivileges) Get(filename string) (io.ReadCloser, error) { 872 if backupPath, ok := mutatedFiles[filename]; ok { 873 return os.Open(filepath.Join(fg.path, backupPath)) 874 } 875 876 var f *os.File 877 // Open the file while holding the Windows backup privilege. This ensures that the 878 // file can be opened even if the caller does not actually have access to it according 879 // to the security descriptor. Also use sequential file access to avoid depleting the 880 // standby list - Microsoft VSO Bug Tracker #9900466 881 err := winio.RunWithPrivilege(winio.SeBackupPrivilege, func() error { 882 path := longpath.AddPrefix(filepath.Join(fg.path, filename)) 883 p, err := syscall.UTF16FromString(path) 884 if err != nil { 885 return err 886 } 887 const fileFlagSequentialScan = 0x08000000 // FILE_FLAG_SEQUENTIAL_SCAN 888 h, err := syscall.CreateFile(&p[0], syscall.GENERIC_READ, syscall.FILE_SHARE_READ, nil, syscall.OPEN_EXISTING, syscall.FILE_FLAG_BACKUP_SEMANTICS|fileFlagSequentialScan, 0) 889 if err != nil { 890 return &os.PathError{Op: "open", Path: path, Err: err} 891 } 892 f = os.NewFile(uintptr(h), path) 893 return nil 894 }) 895 return f, err 896 } 897 898 func (fg *fileGetCloserWithBackupPrivileges) Close() error { 899 return nil 900 } 901 902 // DiffGetter returns a FileGetCloser that can read files from the directory that 903 // contains files for the layer differences. Used for direct access for tar-split. 904 func (d *Driver) DiffGetter(id string) (graphdriver.FileGetCloser, error) { 905 id, err := d.resolveID(id) 906 if err != nil { 907 return nil, err 908 } 909 910 return &fileGetCloserWithBackupPrivileges{d.dir(id)}, nil 911 } 912 913 type storageOptions struct { 914 size uint64 915 } 916 917 func parseStorageOpt(storageOpt map[string]string) (*storageOptions, error) { 918 options := storageOptions{} 919 920 // Read size to change the block device size per container. 921 for key, val := range storageOpt { 922 key := strings.ToLower(key) 923 switch key { 924 case "size": 925 size, err := units.RAMInBytes(val) 926 if err != nil { 927 return nil, err 928 } 929 options.size = uint64(size) 930 default: 931 return nil, fmt.Errorf("Unknown storage option: %s", key) 932 } 933 } 934 return &options, nil 935 }