blob: b3b431e351d6f199f647f82c15c2b758de86fb55 [file] [log] [blame]
package wclayer
import (
"os"
"path/filepath"
"syscall"
"unsafe"
"github.com/Microsoft/hcsshim/internal/hcserror"
"github.com/Microsoft/hcsshim/osversion"
"github.com/sirupsen/logrus"
)
// ExpandScratchSize expands the size of a layer to at least size bytes.
func ExpandScratchSize(path string, size uint64) (err error) {
title := "hcsshim::ExpandScratchSize"
fields := logrus.Fields{
"path": path,
"size": size,
}
logrus.WithFields(fields).Debug(title)
defer func() {
if err != nil {
fields[logrus.ErrorKey] = err
logrus.WithFields(fields).Error(err)
} else {
logrus.WithFields(fields).Debug(title + " - succeeded")
}
}()
err = expandSandboxSize(&stdDriverInfo, path, size)
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
}