Browse Source

Update Microsoft/go-winio v0.4.14

Signed-off-by: Justin Terry (VM) <juterry@microsoft.com>
Justin Terry (VM) 6 years ago
parent
commit
35fe16b7eb

+ 1 - 1
vendor.conf

@@ -1,6 +1,6 @@
 github.com/Azure/go-ansiterm                        d6e3b3328b783f23731bc4d058875b0371ff8109
 github.com/Microsoft/hcsshim                        672e52e9209d1e53718c1b6a7d68cc9272654ab5
-github.com/Microsoft/go-winio                       3fe4fa31662f6ede2353d913e93907b8e096e0b6
+github.com/Microsoft/go-winio                       6c72808b55902eae4c5943626030429ff20f3b63 # v0.4.14
 github.com/docker/libtrust                          9cbd2a1374f46905c68a4eb3694a130610adc62a
 github.com/go-check/check                           4ed411733c5785b40214c70bce814c3a3a689609 https://github.com/cpuguy83/check.git
 github.com/golang/gddo                              9b12a26f3fbd7397dee4e20939ddca719d840d2a

+ 11 - 1
vendor/github.com/Microsoft/go-winio/file.go

@@ -111,7 +111,13 @@ func makeWin32File(h syscall.Handle) (*win32File, error) {
 }
 
 func MakeOpenFile(h syscall.Handle) (io.ReadWriteCloser, error) {
-	return makeWin32File(h)
+	// If we return the result of makeWin32File directly, it can result in an
+	// interface-wrapped nil, rather than a nil interface value.
+	f, err := makeWin32File(h)
+	if err != nil {
+		return nil, err
+	}
+	return f, nil
 }
 
 // closeHandle closes the resources associated with a Win32 handle
@@ -271,6 +277,10 @@ func (f *win32File) Flush() error {
 	return syscall.FlushFileBuffers(f.handle)
 }
 
+func (f *win32File) Fd() uintptr {
+	return uintptr(f.handle)
+}
+
 func (d *deadlineHandler) set(deadline time.Time) error {
 	d.setLock.Lock()
 	defer d.setLock.Unlock()

+ 9 - 0
vendor/github.com/Microsoft/go-winio/go.mod

@@ -0,0 +1,9 @@
+module github.com/Microsoft/go-winio
+
+go 1.12
+
+require (
+	github.com/pkg/errors v0.8.1
+	github.com/sirupsen/logrus v1.4.1
+	golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b
+)

+ 1 - 1
vendor/github.com/Microsoft/go-winio/hvsock.go

@@ -46,7 +46,7 @@ func (addr *HvsockAddr) String() string {
 func VsockServiceID(port uint32) guid.GUID {
 	g, _ := guid.FromString("00000000-facb-11e6-bd58-64006a7986d3")
 	g.Data1 = port
-	return *g
+	return g
 }
 
 func (addr *HvsockAddr) raw() rawHvsockAddr {

+ 6 - 0
vendor/github.com/Microsoft/go-winio/pkg/etw/eventdata.go

@@ -3,6 +3,7 @@ package etw
 import (
 	"bytes"
 	"encoding/binary"
+	"syscall"
 )
 
 // eventData maintains a buffer which builds up the data for an ETW event. It
@@ -63,3 +64,8 @@ func (ed *eventData) writeUint32(value uint32) {
 func (ed *eventData) writeUint64(value uint64) {
 	binary.Write(&ed.buffer, binary.LittleEndian, value)
 }
+
+// writeFiletime appends a FILETIME to the buffer.
+func (ed *eventData) writeFiletime(value syscall.Filetime) {
+	binary.Write(&ed.buffer, binary.LittleEndian, value)
+}

+ 4 - 4
vendor/github.com/Microsoft/go-winio/pkg/etw/eventopt.go

@@ -6,8 +6,8 @@ import (
 
 type eventOptions struct {
 	descriptor        *eventDescriptor
-	activityID        *guid.GUID
-	relatedActivityID *guid.GUID
+	activityID        guid.GUID
+	relatedActivityID guid.GUID
 	tags              uint32
 }
 
@@ -59,14 +59,14 @@ func WithTags(newTags uint32) EventOpt {
 }
 
 // WithActivityID specifies the activity ID of the event to be written.
-func WithActivityID(activityID *guid.GUID) EventOpt {
+func WithActivityID(activityID guid.GUID) EventOpt {
 	return func(options *eventOptions) {
 		options.activityID = activityID
 	}
 }
 
 // WithRelatedActivityID specifies the parent activity ID of the event to be written.
-func WithRelatedActivityID(activityID *guid.GUID) EventOpt {
+func WithRelatedActivityID(activityID guid.GUID) EventOpt {
 	return func(options *eventOptions) {
 		options.relatedActivityID = activityID
 	}

+ 12 - 0
vendor/github.com/Microsoft/go-winio/pkg/etw/fieldopt.go

@@ -4,6 +4,8 @@ import (
 	"fmt"
 	"math"
 	"reflect"
+	"syscall"
+	"time"
 	"unsafe"
 )
 
@@ -380,6 +382,14 @@ func Struct(name string, opts ...FieldOpt) FieldOpt {
 	}
 }
 
+// Time adds a time to the event.
+func Time(name string, value time.Time) FieldOpt {
+	return func(em *eventMetadata, ed *eventData) {
+		em.writeField(name, inTypeFileTime, outTypeDateTimeUTC, 0)
+		ed.writeFiletime(syscall.NsecToFiletime(value.UTC().UnixNano()))
+	}
+}
+
 // Currently, we support logging basic builtin types (int, string, etc), slices
 // of basic builtin types, error, types derived from the basic types (e.g. "type
 // foo int"), and structs (recursively logging their fields). We do not support
@@ -454,6 +464,8 @@ func SmartField(name string, v interface{}) FieldOpt {
 		return Float64Array(name, v)
 	case error:
 		return StringField(name, v.Error())
+	case time.Time:
+		return Time(name, v)
 	default:
 		switch rv := reflect.ValueOf(v); rv.Kind() {
 		case reflect.Bool:

+ 2 - 2
vendor/github.com/Microsoft/go-winio/pkg/etw/newprovider.go

@@ -15,7 +15,7 @@ import (
 // provider ID to be manually specified. This is most useful when there is an
 // existing provider ID that must be used to conform to existing diagnostic
 // infrastructure.
-func NewProviderWithID(name string, id *guid.GUID, callback EnableCallback) (provider *Provider, err error) {
+func NewProviderWithID(name string, id guid.GUID, callback EnableCallback) (provider *Provider, err error) {
 	providerCallbackOnce.Do(func() {
 		globalProviderCallback = windows.NewCallback(providerCallbackAdapter)
 	})
@@ -29,7 +29,7 @@ func NewProviderWithID(name string, id *guid.GUID, callback EnableCallback) (pro
 	provider.ID = id
 	provider.callback = callback
 
-	if err := eventRegister((*windows.GUID)(provider.ID), globalProviderCallback, uintptr(provider.index), &provider.handle); err != nil {
+	if err := eventRegister((*windows.GUID)(&provider.ID), globalProviderCallback, uintptr(provider.index), &provider.handle); err != nil {
 		return nil, err
 	}
 

+ 1 - 1
vendor/github.com/Microsoft/go-winio/pkg/etw/newprovider_unsupported.go

@@ -7,6 +7,6 @@ import (
 )
 
 // NewProviderWithID returns a nil provider on unsupported platforms.
-func NewProviderWithID(name string, id *guid.GUID, callback EnableCallback) (provider *Provider, err error) {
+func NewProviderWithID(name string, id guid.GUID, callback EnableCallback) (provider *Provider, err error) {
 	return nil, nil
 }

+ 22 - 21
vendor/github.com/Microsoft/go-winio/pkg/etw/provider.go

@@ -14,7 +14,7 @@ import (
 // name and ID (GUID), which should always have a 1:1 mapping to each other
 // (e.g. don't use multiple provider names with the same ID, or vice versa).
 type Provider struct {
-	ID         *guid.GUID
+	ID         guid.GUID
 	handle     providerHandle
 	metadata   []byte
 	callback   EnableCallback
@@ -61,9 +61,9 @@ const (
 
 // EnableCallback is the form of the callback function that receives provider
 // enable/disable notifications from ETW.
-type EnableCallback func(*guid.GUID, ProviderState, Level, uint64, uint64, uintptr)
+type EnableCallback func(guid.GUID, ProviderState, Level, uint64, uint64, uintptr)
 
-func providerCallback(sourceID *guid.GUID, state ProviderState, level Level, matchAnyKeyword uint64, matchAllKeyword uint64, filterData uintptr, i uintptr) {
+func providerCallback(sourceID guid.GUID, state ProviderState, level Level, matchAnyKeyword uint64, matchAllKeyword uint64, filterData uintptr, i uintptr) {
 	provider := providers.getProvider(uint(i))
 
 	switch state {
@@ -86,7 +86,7 @@ func providerCallback(sourceID *guid.GUID, state ProviderState, level Level, mat
 // different size, it has only pointer-sized arguments, which are then cast to
 // the appropriate types when calling providerCallback.
 func providerCallbackAdapter(sourceID *guid.GUID, state uintptr, level uintptr, matchAnyKeyword uintptr, matchAllKeyword uintptr, filterData uintptr, i uintptr) uintptr {
-	providerCallback(sourceID, ProviderState(state), Level(level), uint64(matchAnyKeyword), uint64(matchAllKeyword), filterData, i)
+	providerCallback(*sourceID, ProviderState(state), Level(level), uint64(matchAnyKeyword), uint64(matchAllKeyword), filterData, i)
 	return 0
 }
 
@@ -94,26 +94,27 @@ func providerCallbackAdapter(sourceID *guid.GUID, state uintptr, level uintptr,
 // uses the same algorithm as used by .NET's EventSource class, which is based
 // on RFC 4122. More information on the algorithm can be found here:
 // https://blogs.msdn.microsoft.com/dcook/2015/09/08/etw-provider-names-and-guids/
-// The algorithm is roughly:
-// Hash = Sha1(namespace + arg.ToUpper().ToUtf16be())
-// Guid = Hash[0..15], with Hash[7] tweaked according to RFC 4122
-func providerIDFromName(name string) *guid.GUID {
+//
+// The algorithm is roughly the RFC 4122 algorithm for a V5 UUID, but differs in
+// the following ways:
+// - The input name is first upper-cased, UTF16-encoded, and converted to
+//   big-endian.
+// - No variant is set on the result UUID.
+// - The result UUID is treated as being in little-endian format, rather than
+//   big-endian.
+func providerIDFromName(name string) guid.GUID {
 	buffer := sha1.New()
-
-	namespace := []byte{0x48, 0x2C, 0x2D, 0xB2, 0xC3, 0x90, 0x47, 0xC8, 0x87, 0xF8, 0x1A, 0x15, 0xBF, 0xC1, 0x30, 0xFB}
-	buffer.Write(namespace)
-
+	namespace := guid.GUID{0x482C2DB2, 0xC390, 0x47C8, [8]byte{0x87, 0xF8, 0x1A, 0x15, 0xBF, 0xC1, 0x30, 0xFB}}
+	namespaceBytes := namespace.ToArray()
+	buffer.Write(namespaceBytes[:])
 	binary.Write(buffer, binary.BigEndian, utf16.Encode([]rune(strings.ToUpper(name))))
 
 	sum := buffer.Sum(nil)
 	sum[7] = (sum[7] & 0xf) | 0x50
 
-	return &guid.GUID{
-		Data1: binary.LittleEndian.Uint32(sum[0:4]),
-		Data2: binary.LittleEndian.Uint16(sum[4:6]),
-		Data3: binary.LittleEndian.Uint16(sum[6:8]),
-		Data4: [8]byte{sum[8], sum[9], sum[10], sum[11], sum[12], sum[13], sum[14], sum[15]},
-	}
+	a := [16]byte{}
+	copy(a[:], sum)
+	return guid.FromWindowsArray(a)
 }
 
 // NewProvider creates and registers a new ETW provider. The provider ID is
@@ -219,8 +220,8 @@ func (provider *Provider) WriteEvent(name string, eventOpts []EventOpt, fieldOpt
 // the ETW infrastructure.
 func (provider *Provider) writeEventRaw(
 	descriptor *eventDescriptor,
-	activityID *guid.GUID,
-	relatedActivityID *guid.GUID,
+	activityID guid.GUID,
+	relatedActivityID guid.GUID,
 	metadataBlobs [][]byte,
 	dataBlobs [][]byte) error {
 
@@ -235,5 +236,5 @@ func (provider *Provider) writeEventRaw(
 		dataDescriptors = append(dataDescriptors, newEventDataDescriptor(eventDataDescriptorTypeUserData, blob))
 	}
 
-	return eventWriteTransfer(provider.handle, descriptor, (*windows.GUID)(activityID), (*windows.GUID)(relatedActivityID), dataDescriptorCount, &dataDescriptors[0])
+	return eventWriteTransfer(provider.handle, descriptor, (*windows.GUID)(&activityID), (*windows.GUID)(&relatedActivityID), dataDescriptorCount, &dataDescriptors[0])
 }

+ 32 - 15
vendor/github.com/Microsoft/go-winio/pkg/etwlogrus/hook.go

@@ -1,6 +1,8 @@
 package etwlogrus
 
 import (
+	"sort"
+
 	"github.com/Microsoft/go-winio/pkg/etw"
 	"github.com/sirupsen/logrus"
 )
@@ -31,15 +33,7 @@ func NewHookFromProvider(provider *etw.Provider) (*Hook, error) {
 // Levels returns the set of levels that this hook wants to receive log entries
 // for.
 func (h *Hook) Levels() []logrus.Level {
-	return []logrus.Level{
-		logrus.TraceLevel,
-		logrus.DebugLevel,
-		logrus.InfoLevel,
-		logrus.WarnLevel,
-		logrus.ErrorLevel,
-		logrus.FatalLevel,
-		logrus.PanicLevel,
-	}
+	return logrus.AllLevels
 }
 
 var logrusToETWLevelMap = map[logrus.Level]etw.Level{
@@ -62,19 +56,42 @@ func (h *Hook) Fire(e *logrus.Entry) error {
 		return nil
 	}
 
-	// Reserve extra space for the message field.
-	fields := make([]etw.FieldOpt, 0, len(e.Data)+1)
+	// Sort the fields by name so they are consistent in each instance
+	// of an event. Otherwise, the fields don't line up in WPA.
+	names := make([]string, 0, len(e.Data))
+	hasError := false
+	for k := range e.Data {
+		if k == logrus.ErrorKey {
+			// Always put the error last because it is optional in some events.
+			hasError = true
+		} else {
+			names = append(names, k)
+		}
+	}
+	sort.Strings(names)
 
+	// Reserve extra space for the message and time fields.
+	fields := make([]etw.FieldOpt, 0, len(e.Data)+2)
 	fields = append(fields, etw.StringField("Message", e.Message))
-
-	for k, v := range e.Data {
-		fields = append(fields, etw.SmartField(k, v))
+	fields = append(fields, etw.Time("Time", e.Time))
+	for _, k := range names {
+		fields = append(fields, etw.SmartField(k, e.Data[k]))
+	}
+	if hasError {
+		fields = append(fields, etw.SmartField(logrus.ErrorKey, e.Data[logrus.ErrorKey]))
 	}
 
-	return h.provider.WriteEvent(
+	// Firing an ETW event is essentially best effort, as the event write can
+	// fail for reasons completely out of the control of the event writer (such
+	// as a session listening for the event having no available space in its
+	// buffers). Therefore, we don't return the error from WriteEvent, as it is
+	// just noise in many cases.
+	h.provider.WriteEvent(
 		"LogrusEntry",
 		etw.WithEventOpts(etw.WithLevel(level)),
 		fields)
+
+	return nil
 }
 
 // Close cleans up the hook and closes the ETW provider. If the provder was

+ 156 - 31
vendor/github.com/Microsoft/go-winio/pkg/guid/guid.go

@@ -1,19 +1,43 @@
+// Package guid provides a GUID type. The backing structure for a GUID is
+// identical to that used by the golang.org/x/sys/windows GUID type.
+// There are two main binary encodings used for a GUID, the big-endian encoding,
+// and the Windows (mixed-endian) encoding. See here for details:
+// https://en.wikipedia.org/wiki/Universally_unique_identifier#Encoding
 package guid
 
 import (
 	"crypto/rand"
+	"crypto/sha1"
+	"encoding"
 	"encoding/binary"
-	"encoding/json"
 	"fmt"
 	"strconv"
-	"strings"
 
-	"github.com/pkg/errors"
 	"golang.org/x/sys/windows"
 )
 
-var _ = (json.Marshaler)(&GUID{})
-var _ = (json.Unmarshaler)(&GUID{})
+// Variant specifies which GUID variant (or "type") of the GUID. It determines
+// how the entirety of the rest of the GUID is interpreted.
+type Variant uint8
+
+// The variants specified by RFC 4122.
+const (
+	// VariantUnknown specifies a GUID variant which does not conform to one of
+	// the variant encodings specified in RFC 4122.
+	VariantUnknown Variant = iota
+	VariantNCS
+	VariantRFC4122
+	VariantMicrosoft
+	VariantFuture
+)
+
+// Version specifies how the bits in the GUID were generated. For instance, a
+// version 4 GUID is randomly generated, and a version 5 is generated from the
+// hash of an input string.
+type Version uint8
+
+var _ = (encoding.TextMarshaler)(GUID{})
+var _ = (encoding.TextUnmarshaler)(&GUID{})
 
 // GUID represents a GUID/UUID. It has the same structure as
 // golang.org/x/sys/windows.GUID so that it can be used with functions expecting
@@ -23,24 +47,83 @@ var _ = (json.Unmarshaler)(&GUID{})
 type GUID windows.GUID
 
 // NewV4 returns a new version 4 (pseudorandom) GUID, as defined by RFC 4122.
-func NewV4() (*GUID, error) {
+func NewV4() (GUID, error) {
 	var b [16]byte
 	if _, err := rand.Read(b[:]); err != nil {
-		return nil, err
+		return GUID{}, err
 	}
 
+	g := FromArray(b)
+	g.setVersion(4) // Version 4 means randomly generated.
+	g.setVariant(VariantRFC4122)
+
+	return g, nil
+}
+
+// NewV5 returns a new version 5 (generated from a string via SHA-1 hashing)
+// GUID, as defined by RFC 4122. The RFC is unclear on the encoding of the name,
+// and the sample code treats it as a series of bytes, so we do the same here.
+//
+// Some implementations, such as those found on Windows, treat the name as a
+// big-endian UTF16 stream of bytes. If that is desired, the string can be
+// encoded as such before being passed to this function.
+func NewV5(namespace GUID, name []byte) (GUID, error) {
+	b := sha1.New()
+	namespaceBytes := namespace.ToArray()
+	b.Write(namespaceBytes[:])
+	b.Write(name)
+
+	a := [16]byte{}
+	copy(a[:], b.Sum(nil))
+
+	g := FromArray(a)
+	g.setVersion(5) // Version 5 means generated from a string.
+	g.setVariant(VariantRFC4122)
+
+	return g, nil
+}
+
+func fromArray(b [16]byte, order binary.ByteOrder) GUID {
 	var g GUID
-	g.Data1 = binary.LittleEndian.Uint32(b[0:4])
-	g.Data2 = binary.LittleEndian.Uint16(b[4:6])
-	g.Data3 = binary.LittleEndian.Uint16(b[6:8])
+	g.Data1 = order.Uint32(b[0:4])
+	g.Data2 = order.Uint16(b[4:6])
+	g.Data3 = order.Uint16(b[6:8])
 	copy(g.Data4[:], b[8:16])
+	return g
+}
+
+func (g GUID) toArray(order binary.ByteOrder) [16]byte {
+	b := [16]byte{}
+	order.PutUint32(b[0:4], g.Data1)
+	order.PutUint16(b[4:6], g.Data2)
+	order.PutUint16(b[6:8], g.Data3)
+	copy(b[8:16], g.Data4[:])
+	return b
+}
 
-	g.Data3 = (g.Data3 & 0x0fff) | 0x4000   // Version 4 (randomly generated)
-	g.Data4[0] = (g.Data4[0] & 0x3f) | 0x80 // RFC4122 variant
-	return &g, nil
+// FromArray constructs a GUID from a big-endian encoding array of 16 bytes.
+func FromArray(b [16]byte) GUID {
+	return fromArray(b, binary.BigEndian)
 }
 
-func (g *GUID) String() string {
+// ToArray returns an array of 16 bytes representing the GUID in big-endian
+// encoding.
+func (g GUID) ToArray() [16]byte {
+	return g.toArray(binary.BigEndian)
+}
+
+// FromWindowsArray constructs a GUID from a Windows encoding array of bytes.
+func FromWindowsArray(b [16]byte) GUID {
+	return fromArray(b, binary.LittleEndian)
+}
+
+// ToWindowsArray returns an array of 16 bytes representing the GUID in Windows
+// encoding.
+func (g GUID) ToWindowsArray() [16]byte {
+	return g.toArray(binary.LittleEndian)
+}
+
+func (g GUID) String() string {
 	return fmt.Sprintf(
 		"%08x-%04x-%04x-%04x-%012x",
 		g.Data1,
@@ -53,58 +136,100 @@ func (g *GUID) String() string {
 // FromString parses a string containing a GUID and returns the GUID. The only
 // format currently supported is the `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx`
 // format.
-func FromString(s string) (*GUID, error) {
+func FromString(s string) (GUID, error) {
 	if len(s) != 36 {
-		return nil, errors.New("invalid GUID format (length)")
+		return GUID{}, fmt.Errorf("invalid GUID %q", s)
 	}
 	if s[8] != '-' || s[13] != '-' || s[18] != '-' || s[23] != '-' {
-		return nil, errors.New("invalid GUID format (dashes)")
+		return GUID{}, fmt.Errorf("invalid GUID %q", s)
 	}
 
 	var g GUID
 
 	data1, err := strconv.ParseUint(s[0:8], 16, 32)
 	if err != nil {
-		return nil, errors.Wrap(err, "invalid GUID format (Data1)")
+		return GUID{}, fmt.Errorf("invalid GUID %q", s)
 	}
 	g.Data1 = uint32(data1)
 
 	data2, err := strconv.ParseUint(s[9:13], 16, 16)
 	if err != nil {
-		return nil, errors.Wrap(err, "invalid GUID format (Data2)")
+		return GUID{}, fmt.Errorf("invalid GUID %q", s)
 	}
 	g.Data2 = uint16(data2)
 
 	data3, err := strconv.ParseUint(s[14:18], 16, 16)
 	if err != nil {
-		return nil, errors.Wrap(err, "invalid GUID format (Data3)")
+		return GUID{}, fmt.Errorf("invalid GUID %q", s)
 	}
 	g.Data3 = uint16(data3)
 
 	for i, x := range []int{19, 21, 24, 26, 28, 30, 32, 34} {
 		v, err := strconv.ParseUint(s[x:x+2], 16, 8)
 		if err != nil {
-			return nil, errors.Wrap(err, "invalid GUID format (Data4)")
+			return GUID{}, fmt.Errorf("invalid GUID %q", s)
 		}
 		g.Data4[i] = uint8(v)
 	}
 
-	return &g, nil
+	return g, nil
+}
+
+func (g *GUID) setVariant(v Variant) {
+	d := g.Data4[0]
+	switch v {
+	case VariantNCS:
+		d = (d & 0x7f)
+	case VariantRFC4122:
+		d = (d & 0x3f) | 0x80
+	case VariantMicrosoft:
+		d = (d & 0x1f) | 0xc0
+	case VariantFuture:
+		d = (d & 0x0f) | 0xe0
+	case VariantUnknown:
+		fallthrough
+	default:
+		panic(fmt.Sprintf("invalid variant: %d", v))
+	}
+	g.Data4[0] = d
+}
+
+// Variant returns the GUID variant, as defined in RFC 4122.
+func (g GUID) Variant() Variant {
+	b := g.Data4[0]
+	if b&0x80 == 0 {
+		return VariantNCS
+	} else if b&0xc0 == 0x80 {
+		return VariantRFC4122
+	} else if b&0xe0 == 0xc0 {
+		return VariantMicrosoft
+	} else if b&0xe0 == 0xe0 {
+		return VariantFuture
+	}
+	return VariantUnknown
+}
+
+func (g *GUID) setVersion(v Version) {
+	g.Data3 = (g.Data3 & 0x0fff) | (uint16(v) << 12)
+}
+
+// Version returns the GUID version, as defined in RFC 4122.
+func (g GUID) Version() Version {
+	return Version((g.Data3 & 0xF000) >> 12)
 }
 
-// MarshalJSON marshals the GUID to JSON representation and returns it as a
-// slice of bytes.
-func (g *GUID) MarshalJSON() ([]byte, error) {
-	return json.Marshal(g.String())
+// MarshalText returns the textual representation of the GUID.
+func (g GUID) MarshalText() ([]byte, error) {
+	return []byte(g.String()), nil
 }
 
-// UnmarshalJSON unmarshals a GUID from JSON representation and sets itself to
-// the unmarshaled GUID.
-func (g *GUID) UnmarshalJSON(data []byte) error {
-	g2, err := FromString(strings.Trim(string(data), "\""))
+// UnmarshalText takes the textual representation of a GUID, and unmarhals it
+// into this GUID.
+func (g *GUID) UnmarshalText(text []byte) error {
+	g2, err := FromString(string(text))
 	if err != nil {
 		return err
 	}
-	*g = *g2
+	*g = g2
 	return nil
 }

+ 7 - 3
vendor/github.com/Microsoft/go-winio/vhd/vhd.go

@@ -117,9 +117,13 @@ func CreateVhdx(path string, maxSizeInGb, blockSizeInMb uint32) error {
 	return nil
 }
 
-// DetachVhd detaches a VHD attached at the given path.
+// DetachVhd detaches a mounted container layer vhd found at `path`.
 func DetachVhd(path string) error {
-	handle, err := OpenVirtualDisk(path, VirtualDiskAccessDetach, OpenVirtualDiskFlagNone)
+	handle, err := OpenVirtualDisk(
+		path,
+		VirtualDiskAccessNone,
+		OpenVirtualDiskFlagCachedIO|OpenVirtualDiskFlagIgnoreRelativeParentLocator)
+
 	if err != nil {
 		return err
 	}
@@ -127,7 +131,7 @@ func DetachVhd(path string) error {
 	return detachVirtualDisk(handle, 0, 0)
 }
 
-// OpenVirtuaDisk obtains a handle to a VHD opened with supplied access mask and flags.
+// OpenVirtualDisk obtains a handle to a VHD opened with supplied access mask and flags.
 func OpenVirtualDisk(path string, accessMask VirtualDiskAccessMask, flag VirtualDiskFlag) (syscall.Handle, error) {
 	var (
 		defaultType virtualStorageType