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