...

Source file src/github.com/Microsoft/hcsshim/internal/wclayer/expandscratchsize.go

Documentation: github.com/Microsoft/hcsshim/internal/wclayer

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

View as plain text