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