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