| //go:build windows | |
| package computestorage | |
| import ( | |
| "context" | |
| "os" | |
| "path/filepath" | |
| "syscall" | |
| "github.com/Microsoft/go-winio/vhd" | |
| "github.com/Microsoft/hcsshim/internal/memory" | |
| "github.com/pkg/errors" | |
| "golang.org/x/sys/windows" | |
| "github.com/Microsoft/hcsshim/internal/security" | |
| ) | |
| const ( | |
| defaultVHDXBlockSizeInMB = 1 | |
| ) | |
| // SetupContainerBaseLayer is a helper to setup a containers scratch. It | |
| // will create and format the vhdx's inside and the size is configurable with the sizeInGB | |
| // parameter. | |
| // | |
| // `layerPath` is the path to the base container layer on disk. | |
| // | |
| // `baseVhdPath` is the path to where the base vhdx for the base layer should be created. | |
| // | |
| // `diffVhdPath` is the path where the differencing disk for the base layer should be created. | |
| // | |
| // `sizeInGB` is the size in gigabytes to make the base vhdx. | |
| func SetupContainerBaseLayer(ctx context.Context, layerPath, baseVhdPath, diffVhdPath string, sizeInGB uint64) (err error) { | |
| var ( | |
| hivesPath = filepath.Join(layerPath, "Hives") | |
| layoutPath = filepath.Join(layerPath, "Layout") | |
| ) | |
| // We need to remove the hives directory and layout file as `SetupBaseOSLayer` fails if these files | |
| // already exist. `SetupBaseOSLayer` will create these files internally. We also remove the base and | |
| // differencing disks if they exist in case we're asking for a different size. | |
| if _, err := os.Stat(hivesPath); err == nil { | |
| if err := os.RemoveAll(hivesPath); err != nil { | |
| return errors.Wrap(err, "failed to remove prexisting hives directory") | |
| } | |
| } | |
| if _, err := os.Stat(layoutPath); err == nil { | |
| if err := os.RemoveAll(layoutPath); err != nil { | |
| return errors.Wrap(err, "failed to remove prexisting layout file") | |
| } | |
| } | |
| if _, err := os.Stat(baseVhdPath); err == nil { | |
| if err := os.RemoveAll(baseVhdPath); err != nil { | |
| return errors.Wrap(err, "failed to remove base vhdx path") | |
| } | |
| } | |
| if _, err := os.Stat(diffVhdPath); err == nil { | |
| if err := os.RemoveAll(diffVhdPath); err != nil { | |
| return errors.Wrap(err, "failed to remove differencing vhdx") | |
| } | |
| } | |
| createParams := &vhd.CreateVirtualDiskParameters{ | |
| Version: 2, | |
| Version2: vhd.CreateVersion2{ | |
| MaximumSize: sizeInGB * memory.GiB, | |
| BlockSizeInBytes: defaultVHDXBlockSizeInMB * memory.MiB, | |
| }, | |
| } | |
| handle, err := vhd.CreateVirtualDisk(baseVhdPath, vhd.VirtualDiskAccessNone, vhd.CreateVirtualDiskFlagNone, createParams) | |
| if err != nil { | |
| return errors.Wrap(err, "failed to create vhdx") | |
| } | |
| defer func() { | |
| if err != nil { | |
| _ = syscall.CloseHandle(handle) | |
| os.RemoveAll(baseVhdPath) | |
| os.RemoveAll(diffVhdPath) | |
| } | |
| }() | |
| if err = FormatWritableLayerVhd(ctx, windows.Handle(handle)); err != nil { | |
| return err | |
| } | |
| // Base vhd handle must be closed before calling SetupBaseLayer in case of Container layer | |
| if err = syscall.CloseHandle(handle); err != nil { | |
| return errors.Wrap(err, "failed to close vhdx handle") | |
| } | |
| options := OsLayerOptions{ | |
| Type: OsLayerTypeContainer, | |
| } | |
| // SetupBaseOSLayer expects an empty vhd handle for a container layer and will | |
| // error out otherwise. | |
| if err = SetupBaseOSLayer(ctx, layerPath, 0, options); err != nil { | |
| return err | |
| } | |
| // Create the differencing disk that will be what's copied for the final rw layer | |
| // for a container. | |
| if err = vhd.CreateDiffVhd(diffVhdPath, baseVhdPath, defaultVHDXBlockSizeInMB); err != nil { | |
| return errors.Wrap(err, "failed to create differencing disk") | |
| } | |
| if err = security.GrantVmGroupAccess(baseVhdPath); err != nil { | |
| return errors.Wrapf(err, "failed to grant vm group access to %s", baseVhdPath) | |
| } | |
| if err = security.GrantVmGroupAccess(diffVhdPath); err != nil { | |
| return errors.Wrapf(err, "failed to grant vm group access to %s", diffVhdPath) | |
| } | |
| return nil | |
| } | |
| // SetupUtilityVMBaseLayer is a helper to setup a UVMs scratch space. It will create and format | |
| // the vhdx inside and the size is configurable by the sizeInGB parameter. | |
| // | |
| // `uvmPath` is the path to the UtilityVM filesystem. | |
| // | |
| // `baseVhdPath` is the path to where the base vhdx for the UVM should be created. | |
| // | |
| // `diffVhdPath` is the path where the differencing disk for the UVM should be created. | |
| // | |
| // `sizeInGB` specifies the size in gigabytes to make the base vhdx. | |
| func SetupUtilityVMBaseLayer(ctx context.Context, uvmPath, baseVhdPath, diffVhdPath string, sizeInGB uint64) (err error) { | |
| // Remove the base and differencing disks if they exist in case we're asking for a different size. | |
| if _, err := os.Stat(baseVhdPath); err == nil { | |
| if err := os.RemoveAll(baseVhdPath); err != nil { | |
| return errors.Wrap(err, "failed to remove base vhdx") | |
| } | |
| } | |
| if _, err := os.Stat(diffVhdPath); err == nil { | |
| if err := os.RemoveAll(diffVhdPath); err != nil { | |
| return errors.Wrap(err, "failed to remove differencing vhdx") | |
| } | |
| } | |
| // Just create the vhdx for utilityVM layer, no need to format it. | |
| createParams := &vhd.CreateVirtualDiskParameters{ | |
| Version: 2, | |
| Version2: vhd.CreateVersion2{ | |
| MaximumSize: sizeInGB * memory.GiB, | |
| BlockSizeInBytes: defaultVHDXBlockSizeInMB * memory.MiB, | |
| }, | |
| } | |
| handle, err := vhd.CreateVirtualDisk(baseVhdPath, vhd.VirtualDiskAccessNone, vhd.CreateVirtualDiskFlagNone, createParams) | |
| if err != nil { | |
| return errors.Wrap(err, "failed to create vhdx") | |
| } | |
| defer func() { | |
| if err != nil { | |
| _ = syscall.CloseHandle(handle) | |
| os.RemoveAll(baseVhdPath) | |
| os.RemoveAll(diffVhdPath) | |
| } | |
| }() | |
| // If it is a UtilityVM layer then the base vhdx must be attached when calling | |
| // `SetupBaseOSLayer` | |
| attachParams := &vhd.AttachVirtualDiskParameters{ | |
| Version: 2, | |
| } | |
| if err := vhd.AttachVirtualDisk(handle, vhd.AttachVirtualDiskFlagNone, attachParams); err != nil { | |
| return errors.Wrapf(err, "failed to attach virtual disk") | |
| } | |
| options := OsLayerOptions{ | |
| Type: OsLayerTypeVM, | |
| } | |
| if err := SetupBaseOSLayer(ctx, uvmPath, windows.Handle(handle), options); err != nil { | |
| return err | |
| } | |
| // Detach and close the handle after setting up the layer as we don't need the handle | |
| // for anything else and we no longer need to be attached either. | |
| if err = vhd.DetachVirtualDisk(handle); err != nil { | |
| return errors.Wrap(err, "failed to detach vhdx") | |
| } | |
| if err = syscall.CloseHandle(handle); err != nil { | |
| return errors.Wrap(err, "failed to close vhdx handle") | |
| } | |
| // Create the differencing disk that will be what's copied for the final rw layer | |
| // for a container. | |
| if err = vhd.CreateDiffVhd(diffVhdPath, baseVhdPath, defaultVHDXBlockSizeInMB); err != nil { | |
| return errors.Wrap(err, "failed to create differencing disk") | |
| } | |
| if err := security.GrantVmGroupAccess(baseVhdPath); err != nil { | |
| return errors.Wrapf(err, "failed to grant vm group access to %s", baseVhdPath) | |
| } | |
| if err := security.GrantVmGroupAccess(diffVhdPath); err != nil { | |
| return errors.Wrapf(err, "failed to grant vm group access to %s", diffVhdPath) | |
| } | |
| return nil | |
| } |