expandscratchsize.go 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. //go:build windows
  2. package wclayer
  3. import (
  4. "context"
  5. "os"
  6. "path/filepath"
  7. "syscall"
  8. "unsafe"
  9. "github.com/Microsoft/hcsshim/internal/hcserror"
  10. "github.com/Microsoft/hcsshim/internal/oc"
  11. "github.com/Microsoft/hcsshim/osversion"
  12. "go.opencensus.io/trace"
  13. )
  14. // ExpandScratchSize expands the size of a layer to at least size bytes.
  15. func ExpandScratchSize(ctx context.Context, path string, size uint64) (err error) {
  16. title := "hcsshim::ExpandScratchSize"
  17. ctx, span := oc.StartSpan(ctx, title)
  18. defer span.End()
  19. defer func() { oc.SetSpanStatus(span, err) }()
  20. span.AddAttributes(
  21. trace.StringAttribute("path", path),
  22. trace.Int64Attribute("size", int64(size)))
  23. err = expandSandboxSize(&stdDriverInfo, path, size)
  24. if err != nil {
  25. return hcserror.New(err, title, "")
  26. }
  27. // Manually expand the volume now in order to work around bugs in 19H1 and
  28. // prerelease versions of Vb. Remove once this is fixed in Windows.
  29. if build := osversion.Build(); build >= osversion.V19H1 && build < 19020 {
  30. err = expandSandboxVolume(ctx, path)
  31. if err != nil {
  32. return err
  33. }
  34. }
  35. return nil
  36. }
  37. type virtualStorageType struct {
  38. DeviceID uint32
  39. VendorID [16]byte
  40. }
  41. type openVersion2 struct {
  42. GetInfoOnly int32 // bool but 4-byte aligned
  43. ReadOnly int32 // bool but 4-byte aligned
  44. ResiliencyGUID [16]byte // GUID
  45. }
  46. type openVirtualDiskParameters struct {
  47. Version uint32 // Must always be set to 2
  48. Version2 openVersion2
  49. }
  50. func attachVhd(path string) (syscall.Handle, error) {
  51. var (
  52. defaultType virtualStorageType
  53. handle syscall.Handle
  54. )
  55. parameters := openVirtualDiskParameters{Version: 2}
  56. err := openVirtualDisk(
  57. &defaultType,
  58. path,
  59. 0,
  60. 0,
  61. &parameters,
  62. &handle)
  63. if err != nil {
  64. return 0, &os.PathError{Op: "OpenVirtualDisk", Path: path, Err: err}
  65. }
  66. err = attachVirtualDisk(handle, 0, 0, 0, 0, 0)
  67. if err != nil {
  68. syscall.Close(handle)
  69. return 0, &os.PathError{Op: "AttachVirtualDisk", Path: path, Err: err}
  70. }
  71. return handle, nil
  72. }
  73. func expandSandboxVolume(ctx context.Context, path string) error {
  74. // Mount the sandbox VHD temporarily.
  75. vhdPath := filepath.Join(path, "sandbox.vhdx")
  76. vhd, err := attachVhd(vhdPath)
  77. if err != nil {
  78. return &os.PathError{Op: "OpenVirtualDisk", Path: vhdPath, Err: err}
  79. }
  80. defer syscall.Close(vhd)
  81. // Open the volume.
  82. volumePath, err := GetLayerMountPath(ctx, path)
  83. if err != nil {
  84. return err
  85. }
  86. if volumePath[len(volumePath)-1] == '\\' {
  87. volumePath = volumePath[:len(volumePath)-1]
  88. }
  89. volume, err := os.OpenFile(volumePath, os.O_RDWR, 0)
  90. if err != nil {
  91. return err
  92. }
  93. defer volume.Close()
  94. // Get the volume's underlying partition size in NTFS clusters.
  95. var (
  96. partitionSize int64
  97. bytes uint32
  98. )
  99. const _IOCTL_DISK_GET_LENGTH_INFO = 0x0007405C
  100. err = syscall.DeviceIoControl(syscall.Handle(volume.Fd()), _IOCTL_DISK_GET_LENGTH_INFO, nil, 0, (*byte)(unsafe.Pointer(&partitionSize)), 8, &bytes, nil)
  101. if err != nil {
  102. return &os.PathError{Op: "IOCTL_DISK_GET_LENGTH_INFO", Path: volume.Name(), Err: err}
  103. }
  104. const (
  105. clusterSize = 4096
  106. sectorSize = 512
  107. )
  108. targetClusters := partitionSize / clusterSize
  109. // Get the volume's current size in NTFS clusters.
  110. var volumeSize int64
  111. err = getDiskFreeSpaceEx(volume.Name()+"\\", nil, &volumeSize, nil)
  112. if err != nil {
  113. return &os.PathError{Op: "GetDiskFreeSpaceEx", Path: volume.Name(), Err: err}
  114. }
  115. volumeClusters := volumeSize / clusterSize
  116. // Only resize the volume if there is space to grow, otherwise this will
  117. // fail with invalid parameter. NTFS reserves one cluster.
  118. if volumeClusters+1 < targetClusters {
  119. targetSectors := targetClusters * (clusterSize / sectorSize)
  120. const _FSCTL_EXTEND_VOLUME = 0x000900F0
  121. err = syscall.DeviceIoControl(syscall.Handle(volume.Fd()), _FSCTL_EXTEND_VOLUME, (*byte)(unsafe.Pointer(&targetSectors)), 8, nil, 0, &bytes, nil)
  122. if err != nil {
  123. return &os.PathError{Op: "FSCTL_EXTEND_VOLUME", Path: volume.Name(), Err: err}
  124. }
  125. }
  126. return nil
  127. }