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