Dismount sandbox VHD on snapshot remove
Signed-off-by: Justin Terry (VM) <juterry@microsoft.com>
This commit is contained in:
parent
31aa418f84
commit
00242e1668
@ -27,6 +27,7 @@ import (
|
|||||||
"syscall"
|
"syscall"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
|
"github.com/Microsoft/go-winio/vhd"
|
||||||
"github.com/Microsoft/hcsshim"
|
"github.com/Microsoft/hcsshim"
|
||||||
"github.com/containerd/containerd/errdefs"
|
"github.com/containerd/containerd/errdefs"
|
||||||
"github.com/containerd/containerd/log"
|
"github.com/containerd/containerd/log"
|
||||||
@ -206,9 +207,20 @@ func (s *snapshotter) Remove(ctx context.Context, key string) error {
|
|||||||
|
|
||||||
path := s.getSnapshotDir(id)
|
path := s.getSnapshotDir(id)
|
||||||
renamedID := "rm-" + id
|
renamedID := "rm-" + id
|
||||||
renamed := filepath.Join(s.root, "snapshots", renamedID)
|
renamed := s.getSnapshotDir(renamedID)
|
||||||
if err := os.Rename(path, renamed); err != nil && !os.IsNotExist(err) {
|
if err := os.Rename(path, renamed); err != nil && !os.IsNotExist(err) {
|
||||||
return err
|
if !os.IsPermission(err) {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// If permission denied, it's possible that the scratch is still mounted, an
|
||||||
|
// artifact after a hard daemon crash for example. Worth a shot to try detaching it
|
||||||
|
// before retrying the rename.
|
||||||
|
if detachErr := vhd.DetachVhd(filepath.Join(path, "sandbox.vhdx")); detachErr != nil {
|
||||||
|
return errors.Wrapf(err, "failed to detach VHD: %s", detachErr)
|
||||||
|
}
|
||||||
|
if renameErr := os.Rename(path, renamed); renameErr != nil && !os.IsNotExist(renameErr) {
|
||||||
|
return errors.Wrapf(err, "second rename attempt following detach failed: %s", renameErr)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := t.Commit(); err != nil {
|
if err := t.Commit(); err != nil {
|
||||||
|
@ -32,7 +32,7 @@ github.com/opencontainers/image-spec v1.0.1
|
|||||||
golang.org/x/sync 450f422ab23cf9881c94e2db30cac0eb1b7cf80c
|
golang.org/x/sync 450f422ab23cf9881c94e2db30cac0eb1b7cf80c
|
||||||
github.com/BurntSushi/toml a368813c5e648fee92e5f6c30e3944ff9d5e8895
|
github.com/BurntSushi/toml a368813c5e648fee92e5f6c30e3944ff9d5e8895
|
||||||
github.com/grpc-ecosystem/go-grpc-prometheus 6b7015e65d366bf3f19b2b2a000a831940f0f7e0
|
github.com/grpc-ecosystem/go-grpc-prometheus 6b7015e65d366bf3f19b2b2a000a831940f0f7e0
|
||||||
github.com/Microsoft/go-winio v0.4.10
|
github.com/Microsoft/go-winio v0.4.11
|
||||||
github.com/Microsoft/hcsshim v0.7.9
|
github.com/Microsoft/hcsshim v0.7.9
|
||||||
google.golang.org/genproto d80a6e20e776b0b17a324d0ba1ab50a39c8e8944
|
google.golang.org/genproto d80a6e20e776b0b17a324d0ba1ab50a39c8e8944
|
||||||
golang.org/x/text 19e51611da83d6be54ddafce4a4af510cb3e9ea4
|
golang.org/x/text 19e51611da83d6be54ddafce4a4af510cb3e9ea4
|
||||||
|
274
vendor/github.com/Microsoft/go-winio/ea.go
generated
vendored
274
vendor/github.com/Microsoft/go-winio/ea.go
generated
vendored
@ -1,137 +1,137 @@
|
|||||||
package winio
|
package winio
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
type fileFullEaInformation struct {
|
type fileFullEaInformation struct {
|
||||||
NextEntryOffset uint32
|
NextEntryOffset uint32
|
||||||
Flags uint8
|
Flags uint8
|
||||||
NameLength uint8
|
NameLength uint8
|
||||||
ValueLength uint16
|
ValueLength uint16
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
fileFullEaInformationSize = binary.Size(&fileFullEaInformation{})
|
fileFullEaInformationSize = binary.Size(&fileFullEaInformation{})
|
||||||
|
|
||||||
errInvalidEaBuffer = errors.New("invalid extended attribute buffer")
|
errInvalidEaBuffer = errors.New("invalid extended attribute buffer")
|
||||||
errEaNameTooLarge = errors.New("extended attribute name too large")
|
errEaNameTooLarge = errors.New("extended attribute name too large")
|
||||||
errEaValueTooLarge = errors.New("extended attribute value too large")
|
errEaValueTooLarge = errors.New("extended attribute value too large")
|
||||||
)
|
)
|
||||||
|
|
||||||
// ExtendedAttribute represents a single Windows EA.
|
// ExtendedAttribute represents a single Windows EA.
|
||||||
type ExtendedAttribute struct {
|
type ExtendedAttribute struct {
|
||||||
Name string
|
Name string
|
||||||
Value []byte
|
Value []byte
|
||||||
Flags uint8
|
Flags uint8
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseEa(b []byte) (ea ExtendedAttribute, nb []byte, err error) {
|
func parseEa(b []byte) (ea ExtendedAttribute, nb []byte, err error) {
|
||||||
var info fileFullEaInformation
|
var info fileFullEaInformation
|
||||||
err = binary.Read(bytes.NewReader(b), binary.LittleEndian, &info)
|
err = binary.Read(bytes.NewReader(b), binary.LittleEndian, &info)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = errInvalidEaBuffer
|
err = errInvalidEaBuffer
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
nameOffset := fileFullEaInformationSize
|
nameOffset := fileFullEaInformationSize
|
||||||
nameLen := int(info.NameLength)
|
nameLen := int(info.NameLength)
|
||||||
valueOffset := nameOffset + int(info.NameLength) + 1
|
valueOffset := nameOffset + int(info.NameLength) + 1
|
||||||
valueLen := int(info.ValueLength)
|
valueLen := int(info.ValueLength)
|
||||||
nextOffset := int(info.NextEntryOffset)
|
nextOffset := int(info.NextEntryOffset)
|
||||||
if valueLen+valueOffset > len(b) || nextOffset < 0 || nextOffset > len(b) {
|
if valueLen+valueOffset > len(b) || nextOffset < 0 || nextOffset > len(b) {
|
||||||
err = errInvalidEaBuffer
|
err = errInvalidEaBuffer
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
ea.Name = string(b[nameOffset : nameOffset+nameLen])
|
ea.Name = string(b[nameOffset : nameOffset+nameLen])
|
||||||
ea.Value = b[valueOffset : valueOffset+valueLen]
|
ea.Value = b[valueOffset : valueOffset+valueLen]
|
||||||
ea.Flags = info.Flags
|
ea.Flags = info.Flags
|
||||||
if info.NextEntryOffset != 0 {
|
if info.NextEntryOffset != 0 {
|
||||||
nb = b[info.NextEntryOffset:]
|
nb = b[info.NextEntryOffset:]
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// DecodeExtendedAttributes decodes a list of EAs from a FILE_FULL_EA_INFORMATION
|
// DecodeExtendedAttributes decodes a list of EAs from a FILE_FULL_EA_INFORMATION
|
||||||
// buffer retrieved from BackupRead, ZwQueryEaFile, etc.
|
// buffer retrieved from BackupRead, ZwQueryEaFile, etc.
|
||||||
func DecodeExtendedAttributes(b []byte) (eas []ExtendedAttribute, err error) {
|
func DecodeExtendedAttributes(b []byte) (eas []ExtendedAttribute, err error) {
|
||||||
for len(b) != 0 {
|
for len(b) != 0 {
|
||||||
ea, nb, err := parseEa(b)
|
ea, nb, err := parseEa(b)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
eas = append(eas, ea)
|
eas = append(eas, ea)
|
||||||
b = nb
|
b = nb
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeEa(buf *bytes.Buffer, ea *ExtendedAttribute, last bool) error {
|
func writeEa(buf *bytes.Buffer, ea *ExtendedAttribute, last bool) error {
|
||||||
if int(uint8(len(ea.Name))) != len(ea.Name) {
|
if int(uint8(len(ea.Name))) != len(ea.Name) {
|
||||||
return errEaNameTooLarge
|
return errEaNameTooLarge
|
||||||
}
|
}
|
||||||
if int(uint16(len(ea.Value))) != len(ea.Value) {
|
if int(uint16(len(ea.Value))) != len(ea.Value) {
|
||||||
return errEaValueTooLarge
|
return errEaValueTooLarge
|
||||||
}
|
}
|
||||||
entrySize := uint32(fileFullEaInformationSize + len(ea.Name) + 1 + len(ea.Value))
|
entrySize := uint32(fileFullEaInformationSize + len(ea.Name) + 1 + len(ea.Value))
|
||||||
withPadding := (entrySize + 3) &^ 3
|
withPadding := (entrySize + 3) &^ 3
|
||||||
nextOffset := uint32(0)
|
nextOffset := uint32(0)
|
||||||
if !last {
|
if !last {
|
||||||
nextOffset = withPadding
|
nextOffset = withPadding
|
||||||
}
|
}
|
||||||
info := fileFullEaInformation{
|
info := fileFullEaInformation{
|
||||||
NextEntryOffset: nextOffset,
|
NextEntryOffset: nextOffset,
|
||||||
Flags: ea.Flags,
|
Flags: ea.Flags,
|
||||||
NameLength: uint8(len(ea.Name)),
|
NameLength: uint8(len(ea.Name)),
|
||||||
ValueLength: uint16(len(ea.Value)),
|
ValueLength: uint16(len(ea.Value)),
|
||||||
}
|
}
|
||||||
|
|
||||||
err := binary.Write(buf, binary.LittleEndian, &info)
|
err := binary.Write(buf, binary.LittleEndian, &info)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = buf.Write([]byte(ea.Name))
|
_, err = buf.Write([]byte(ea.Name))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = buf.WriteByte(0)
|
err = buf.WriteByte(0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = buf.Write(ea.Value)
|
_, err = buf.Write(ea.Value)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = buf.Write([]byte{0, 0, 0}[0 : withPadding-entrySize])
|
_, err = buf.Write([]byte{0, 0, 0}[0 : withPadding-entrySize])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// EncodeExtendedAttributes encodes a list of EAs into a FILE_FULL_EA_INFORMATION
|
// EncodeExtendedAttributes encodes a list of EAs into a FILE_FULL_EA_INFORMATION
|
||||||
// buffer for use with BackupWrite, ZwSetEaFile, etc.
|
// buffer for use with BackupWrite, ZwSetEaFile, etc.
|
||||||
func EncodeExtendedAttributes(eas []ExtendedAttribute) ([]byte, error) {
|
func EncodeExtendedAttributes(eas []ExtendedAttribute) ([]byte, error) {
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
for i := range eas {
|
for i := range eas {
|
||||||
last := false
|
last := false
|
||||||
if i == len(eas)-1 {
|
if i == len(eas)-1 {
|
||||||
last = true
|
last = true
|
||||||
}
|
}
|
||||||
|
|
||||||
err := writeEa(&buf, &eas[i], last)
|
err := writeEa(&buf, &eas[i], last)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return buf.Bytes(), nil
|
return buf.Bytes(), nil
|
||||||
}
|
}
|
||||||
|
108
vendor/github.com/Microsoft/go-winio/vhd/vhd.go
generated
vendored
Normal file
108
vendor/github.com/Microsoft/go-winio/vhd/vhd.go
generated
vendored
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
// +build windows
|
||||||
|
|
||||||
|
package vhd
|
||||||
|
|
||||||
|
import "syscall"
|
||||||
|
|
||||||
|
//go:generate go run mksyscall_windows.go -output zvhd.go vhd.go
|
||||||
|
|
||||||
|
//sys createVirtualDisk(virtualStorageType *virtualStorageType, path string, virtualDiskAccessMask uint32, securityDescriptor *uintptr, flags uint32, providerSpecificFlags uint32, parameters *createVirtualDiskParameters, o *syscall.Overlapped, handle *syscall.Handle) (err error) [failretval != 0] = VirtDisk.CreateVirtualDisk
|
||||||
|
//sys openVirtualDisk(virtualStorageType *virtualStorageType, path string, virtualDiskAccessMask uint32, flags uint32, parameters *uintptr, handle *syscall.Handle) (err error) [failretval != 0] = VirtDisk.OpenVirtualDisk
|
||||||
|
//sys detachVirtualDisk(handle syscall.Handle, flags uint32, providerSpecificFlags uint32) (err error) [failretval != 0] = VirtDisk.DetachVirtualDisk
|
||||||
|
|
||||||
|
type virtualStorageType struct {
|
||||||
|
DeviceID uint32
|
||||||
|
VendorID [16]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
const virtualDiskAccessNONE uint32 = 0
|
||||||
|
const virtualDiskAccessATTACHRO uint32 = 65536
|
||||||
|
const virtualDiskAccessATTACHRW uint32 = 131072
|
||||||
|
const virtualDiskAccessDETACH uint32 = 262144
|
||||||
|
const virtualDiskAccessGETINFO uint32 = 524288
|
||||||
|
const virtualDiskAccessCREATE uint32 = 1048576
|
||||||
|
const virtualDiskAccessMETAOPS uint32 = 2097152
|
||||||
|
const virtualDiskAccessREAD uint32 = 851968
|
||||||
|
const virtualDiskAccessALL uint32 = 4128768
|
||||||
|
const virtualDiskAccessWRITABLE uint32 = 3276800
|
||||||
|
|
||||||
|
const createVirtualDiskFlagNone uint32 = 0
|
||||||
|
const createVirtualDiskFlagFullPhysicalAllocation uint32 = 1
|
||||||
|
const createVirtualDiskFlagPreventWritesToSourceDisk uint32 = 2
|
||||||
|
const createVirtualDiskFlagDoNotCopyMetadataFromParent uint32 = 4
|
||||||
|
|
||||||
|
type version2 struct {
|
||||||
|
UniqueID [16]byte // GUID
|
||||||
|
MaximumSize uint64
|
||||||
|
BlockSizeInBytes uint32
|
||||||
|
SectorSizeInBytes uint32
|
||||||
|
ParentPath *uint16 // string
|
||||||
|
SourcePath *uint16 // string
|
||||||
|
OpenFlags uint32
|
||||||
|
ParentVirtualStorageType virtualStorageType
|
||||||
|
SourceVirtualStorageType virtualStorageType
|
||||||
|
ResiliencyGUID [16]byte // GUID
|
||||||
|
}
|
||||||
|
|
||||||
|
type createVirtualDiskParameters struct {
|
||||||
|
Version uint32 // Must always be set to 2
|
||||||
|
Version2 version2
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateVhdx will create a simple vhdx file at the given path using default values.
|
||||||
|
func CreateVhdx(path string, maxSizeInGb, blockSizeInMb uint32) error {
|
||||||
|
var defaultType virtualStorageType
|
||||||
|
|
||||||
|
parameters := createVirtualDiskParameters{
|
||||||
|
Version: 2,
|
||||||
|
Version2: version2{
|
||||||
|
MaximumSize: uint64(maxSizeInGb) * 1024 * 1024 * 1024,
|
||||||
|
BlockSizeInBytes: blockSizeInMb * 1024 * 1024,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var handle syscall.Handle
|
||||||
|
|
||||||
|
if err := createVirtualDisk(
|
||||||
|
&defaultType,
|
||||||
|
path,
|
||||||
|
virtualDiskAccessNONE,
|
||||||
|
nil,
|
||||||
|
createVirtualDiskFlagNone,
|
||||||
|
0,
|
||||||
|
¶meters,
|
||||||
|
nil,
|
||||||
|
&handle); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := syscall.CloseHandle(handle); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DetachVhd detaches a VHD attached at the given path.
|
||||||
|
func DetachVhd(path string) error {
|
||||||
|
var (
|
||||||
|
defaultType virtualStorageType
|
||||||
|
handle syscall.Handle
|
||||||
|
)
|
||||||
|
|
||||||
|
if err := openVirtualDisk(
|
||||||
|
&defaultType,
|
||||||
|
path,
|
||||||
|
virtualDiskAccessDETACH,
|
||||||
|
0,
|
||||||
|
nil,
|
||||||
|
&handle); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer syscall.CloseHandle(handle)
|
||||||
|
|
||||||
|
if err := detachVirtualDisk(handle, 0, 0); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
99
vendor/github.com/Microsoft/go-winio/vhd/zvhd.go
generated
vendored
Normal file
99
vendor/github.com/Microsoft/go-winio/vhd/zvhd.go
generated
vendored
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
// MACHINE GENERATED BY 'go generate' COMMAND; DO NOT EDIT
|
||||||
|
|
||||||
|
package vhd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ unsafe.Pointer
|
||||||
|
|
||||||
|
// Do the interface allocations only once for common
|
||||||
|
// Errno values.
|
||||||
|
const (
|
||||||
|
errnoERROR_IO_PENDING = 997
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING)
|
||||||
|
)
|
||||||
|
|
||||||
|
// errnoErr returns common boxed Errno values, to prevent
|
||||||
|
// allocations at runtime.
|
||||||
|
func errnoErr(e syscall.Errno) error {
|
||||||
|
switch e {
|
||||||
|
case 0:
|
||||||
|
return nil
|
||||||
|
case errnoERROR_IO_PENDING:
|
||||||
|
return errERROR_IO_PENDING
|
||||||
|
}
|
||||||
|
// TODO: add more here, after collecting data on the common
|
||||||
|
// error values see on Windows. (perhaps when running
|
||||||
|
// all.bat?)
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
modVirtDisk = windows.NewLazySystemDLL("VirtDisk.dll")
|
||||||
|
|
||||||
|
procCreateVirtualDisk = modVirtDisk.NewProc("CreateVirtualDisk")
|
||||||
|
procOpenVirtualDisk = modVirtDisk.NewProc("OpenVirtualDisk")
|
||||||
|
procDetachVirtualDisk = modVirtDisk.NewProc("DetachVirtualDisk")
|
||||||
|
)
|
||||||
|
|
||||||
|
func createVirtualDisk(virtualStorageType *virtualStorageType, path string, virtualDiskAccessMask uint32, securityDescriptor *uintptr, flags uint32, providerSpecificFlags uint32, parameters *createVirtualDiskParameters, o *syscall.Overlapped, handle *syscall.Handle) (err error) {
|
||||||
|
var _p0 *uint16
|
||||||
|
_p0, err = syscall.UTF16PtrFromString(path)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return _createVirtualDisk(virtualStorageType, _p0, virtualDiskAccessMask, securityDescriptor, flags, providerSpecificFlags, parameters, o, handle)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _createVirtualDisk(virtualStorageType *virtualStorageType, path *uint16, virtualDiskAccessMask uint32, securityDescriptor *uintptr, flags uint32, providerSpecificFlags uint32, parameters *createVirtualDiskParameters, o *syscall.Overlapped, handle *syscall.Handle) (err error) {
|
||||||
|
r1, _, e1 := syscall.Syscall9(procCreateVirtualDisk.Addr(), 9, uintptr(unsafe.Pointer(virtualStorageType)), uintptr(unsafe.Pointer(path)), uintptr(virtualDiskAccessMask), uintptr(unsafe.Pointer(securityDescriptor)), uintptr(flags), uintptr(providerSpecificFlags), uintptr(unsafe.Pointer(parameters)), uintptr(unsafe.Pointer(o)), uintptr(unsafe.Pointer(handle)))
|
||||||
|
if r1 != 0 {
|
||||||
|
if e1 != 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
} else {
|
||||||
|
err = syscall.EINVAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func openVirtualDisk(virtualStorageType *virtualStorageType, path string, virtualDiskAccessMask uint32, flags uint32, parameters *uintptr, handle *syscall.Handle) (err error) {
|
||||||
|
var _p0 *uint16
|
||||||
|
_p0, err = syscall.UTF16PtrFromString(path)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return _openVirtualDisk(virtualStorageType, _p0, virtualDiskAccessMask, flags, parameters, handle)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _openVirtualDisk(virtualStorageType *virtualStorageType, path *uint16, virtualDiskAccessMask uint32, flags uint32, parameters *uintptr, handle *syscall.Handle) (err error) {
|
||||||
|
r1, _, e1 := syscall.Syscall6(procOpenVirtualDisk.Addr(), 6, uintptr(unsafe.Pointer(virtualStorageType)), uintptr(unsafe.Pointer(path)), uintptr(virtualDiskAccessMask), uintptr(flags), uintptr(unsafe.Pointer(parameters)), uintptr(unsafe.Pointer(handle)))
|
||||||
|
if r1 != 0 {
|
||||||
|
if e1 != 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
} else {
|
||||||
|
err = syscall.EINVAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func detachVirtualDisk(handle syscall.Handle, flags uint32, providerSpecificFlags uint32) (err error) {
|
||||||
|
r1, _, e1 := syscall.Syscall(procDetachVirtualDisk.Addr(), 3, uintptr(handle), uintptr(flags), uintptr(providerSpecificFlags))
|
||||||
|
if r1 != 0 {
|
||||||
|
if e1 != 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
} else {
|
||||||
|
err = syscall.EINVAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user