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