Skip to content

Commit

Permalink
Merge pull request #718 from jstarks/workaround_expandlayer
Browse files Browse the repository at this point in the history
wclayer: Work around Windows bug when expanding sandbox size
  • Loading branch information
jterry75 authored Oct 21, 2019
2 parents 725d456 + d2042e6 commit d2849cb
Show file tree
Hide file tree
Showing 3 changed files with 179 additions and 0 deletions.
115 changes: 115 additions & 0 deletions internal/wclayer/expandscratchsize.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
package wclayer

import (
"os"
"path/filepath"
"syscall"
"unsafe"

"github.com/Microsoft/hcsshim/internal/hcserror"
"github.com/Microsoft/hcsshim/osversion"
"github.com/sirupsen/logrus"
)

Expand All @@ -26,5 +32,114 @@ func ExpandScratchSize(path string, size uint64) (err error) {
if err != nil {
return hcserror.New(err, title+" - failed", "")
}

// Manually expand the volume now in order to work around bugs in 19H1 and
// prerelease versions of Vb. Remove once this is fixed in Windows.
if build := osversion.Get().Build; build >= osversion.V19H1 && build < 19020 {
err = expandSandboxVolume(path)
if err != nil {
return err
}
}
return nil
}

type virtualStorageType struct {
DeviceID uint32
VendorID [16]byte
}

type openVersion2 struct {
GetInfoOnly int32 // bool but 4-byte aligned
ReadOnly int32 // bool but 4-byte aligned
ResiliencyGUID [16]byte // GUID
}

type openVirtualDiskParameters struct {
Version uint32 // Must always be set to 2
Version2 openVersion2
}

func attachVhd(path string) (syscall.Handle, error) {
var (
defaultType virtualStorageType
handle syscall.Handle
)
parameters := openVirtualDiskParameters{Version: 2}
err := openVirtualDisk(
&defaultType,
path,
0,
0,
&parameters,
&handle)
if err != nil {
return 0, &os.PathError{Op: "OpenVirtualDisk", Path: path, Err: err}
}
err = attachVirtualDisk(handle, 0, 0, 0, 0, 0)
if err != nil {
syscall.Close(handle)
return 0, &os.PathError{Op: "AttachVirtualDisk", Path: path, Err: err}
}
return handle, nil
}

func expandSandboxVolume(path string) error {
// Mount the sandbox VHD temporarily.
vhdPath := filepath.Join(path, "sandbox.vhdx")
vhd, err := attachVhd(vhdPath)
if err != nil {
return &os.PathError{Op: "OpenVirtualDisk", Path: vhdPath, Err: err}
}
defer syscall.Close(vhd)

// Open the volume.
volumePath, err := GetLayerMountPath(path)
if err != nil {
return err
}
if volumePath[len(volumePath)-1] == '\\' {
volumePath = volumePath[:len(volumePath)-1]
}
volume, err := os.OpenFile(volumePath, os.O_RDWR, 0)
if err != nil {
return err
}
defer volume.Close()

// Get the volume's underlying partition size in NTFS clusters.
var (
partitionSize int64
bytes uint32
)
const _IOCTL_DISK_GET_LENGTH_INFO = 0x0007405C
err = syscall.DeviceIoControl(syscall.Handle(volume.Fd()), _IOCTL_DISK_GET_LENGTH_INFO, nil, 0, (*byte)(unsafe.Pointer(&partitionSize)), 8, &bytes, nil)
if err != nil {
return &os.PathError{Op: "IOCTL_DISK_GET_LENGTH_INFO", Path: volume.Name(), Err: err}
}
const (
clusterSize = 4096
sectorSize = 512
)
targetClusters := partitionSize / clusterSize

// Get the volume's current size in NTFS clusters.
var volumeSize int64
err = getDiskFreeSpaceEx(volume.Name()+"\\", nil, &volumeSize, nil)
if err != nil {
return &os.PathError{Op: "GetDiskFreeSpaceEx", Path: volume.Name(), Err: err}
}
volumeClusters := volumeSize / clusterSize

// Only resize the volume if there is space to grow, otherwise this will
// fail with invalid parameter. NTFS reserves one cluster.
if volumeClusters+1 < targetClusters {
targetSectors := targetClusters * (clusterSize / sectorSize)
const _FSCTL_EXTEND_VOLUME = 0x000900F0
err = syscall.DeviceIoControl(syscall.Handle(volume.Fd()), _FSCTL_EXTEND_VOLUME, (*byte)(unsafe.Pointer(&targetSectors)), 8, nil, 0, &bytes, nil)
if err != nil {
return &os.PathError{Op: "FSCTL_EXTEND_VOLUME", Path: volume.Name(), Err: err}
}
}
return nil
}
5 changes: 5 additions & 0 deletions internal/wclayer/wclayer.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,9 @@ import "github.com/Microsoft/go-winio/pkg/guid"

//sys grantVmAccess(vmid string, filepath string) (hr error) = vmcompute.GrantVmAccess?

//sys openVirtualDisk(virtualStorageType *virtualStorageType, path string, virtualDiskAccessMask uint32, flags uint32, parameters *openVirtualDiskParameters, handle *syscall.Handle) (err error) [failretval != 0] = virtdisk.OpenVirtualDisk
//sys attachVirtualDisk(handle syscall.Handle, sd uintptr, flags uint32, providerFlags uint32, params uintptr, overlapped uintptr) (err error) [failretval != 0] = virtdisk.AttachVirtualDisk

//sys getDiskFreeSpaceEx(directoryName string, freeBytesAvailableToCaller *int64, totalNumberOfBytes *int64, totalNumberOfFreeBytes *int64) (err error) = GetDiskFreeSpaceExW

type _guid = guid.GUID
59 changes: 59 additions & 0 deletions internal/wclayer/zsyscall_windows.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit d2849cb

Please sign in to comment.