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