package computestorage import ( "context" "os" "path/filepath" "syscall" "github.com/Microsoft/go-winio/pkg/security" "github.com/Microsoft/go-winio/vhd" "github.com/pkg/errors" "golang.org/x/sys/windows" ) 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 * 1024 * 1024 * 1024, BlockSizeInBytes: defaultVHDXBlockSizeInMB * 1024 * 1024, }, } 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 * 1024 * 1024 * 1024, BlockSizeInBytes: defaultVHDXBlockSizeInMB * 1024 * 1024, }, } 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 }