Browse code

Update Microsoft/go-winio v0.4.14

Signed-off-by: Justin Terry (VM) <juterry@microsoft.com>

Justin Terry (VM) authored on 2019/08/07 05:31:43
Showing 13 changed files
... ...
@@ -1,6 +1,6 @@
1 1
 github.com/Azure/go-ansiterm                        d6e3b3328b783f23731bc4d058875b0371ff8109
2 2
 github.com/Microsoft/hcsshim                        672e52e9209d1e53718c1b6a7d68cc9272654ab5
3
-github.com/Microsoft/go-winio                       3fe4fa31662f6ede2353d913e93907b8e096e0b6
3
+github.com/Microsoft/go-winio                       6c72808b55902eae4c5943626030429ff20f3b63 # v0.4.14
4 4
 github.com/docker/libtrust                          9cbd2a1374f46905c68a4eb3694a130610adc62a
5 5
 github.com/go-check/check                           4ed411733c5785b40214c70bce814c3a3a689609 https://github.com/cpuguy83/check.git
6 6
 github.com/golang/gddo                              9b12a26f3fbd7397dee4e20939ddca719d840d2a
... ...
@@ -111,7 +111,13 @@ func makeWin32File(h syscall.Handle) (*win32File, error) {
111 111
 }
112 112
 
113 113
 func MakeOpenFile(h syscall.Handle) (io.ReadWriteCloser, error) {
114
-	return makeWin32File(h)
114
+	// If we return the result of makeWin32File directly, it can result in an
115
+	// interface-wrapped nil, rather than a nil interface value.
116
+	f, err := makeWin32File(h)
117
+	if err != nil {
118
+		return nil, err
119
+	}
120
+	return f, nil
115 121
 }
116 122
 
117 123
 // closeHandle closes the resources associated with a Win32 handle
... ...
@@ -271,6 +277,10 @@ func (f *win32File) Flush() error {
271 271
 	return syscall.FlushFileBuffers(f.handle)
272 272
 }
273 273
 
274
+func (f *win32File) Fd() uintptr {
275
+	return uintptr(f.handle)
276
+}
277
+
274 278
 func (d *deadlineHandler) set(deadline time.Time) error {
275 279
 	d.setLock.Lock()
276 280
 	defer d.setLock.Unlock()
277 281
new file mode 100644
... ...
@@ -0,0 +1,9 @@
0
+module github.com/Microsoft/go-winio
1
+
2
+go 1.12
3
+
4
+require (
5
+	github.com/pkg/errors v0.8.1
6
+	github.com/sirupsen/logrus v1.4.1
7
+	golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b
8
+)
... ...
@@ -46,7 +46,7 @@ func (addr *HvsockAddr) String() string {
46 46
 func VsockServiceID(port uint32) guid.GUID {
47 47
 	g, _ := guid.FromString("00000000-facb-11e6-bd58-64006a7986d3")
48 48
 	g.Data1 = port
49
-	return *g
49
+	return g
50 50
 }
51 51
 
52 52
 func (addr *HvsockAddr) raw() rawHvsockAddr {
... ...
@@ -3,6 +3,7 @@ package etw
3 3
 import (
4 4
 	"bytes"
5 5
 	"encoding/binary"
6
+	"syscall"
6 7
 )
7 8
 
8 9
 // eventData maintains a buffer which builds up the data for an ETW event. It
... ...
@@ -63,3 +64,8 @@ func (ed *eventData) writeUint32(value uint32) {
63 63
 func (ed *eventData) writeUint64(value uint64) {
64 64
 	binary.Write(&ed.buffer, binary.LittleEndian, value)
65 65
 }
66
+
67
+// writeFiletime appends a FILETIME to the buffer.
68
+func (ed *eventData) writeFiletime(value syscall.Filetime) {
69
+	binary.Write(&ed.buffer, binary.LittleEndian, value)
70
+}
... ...
@@ -6,8 +6,8 @@ import (
6 6
 
7 7
 type eventOptions struct {
8 8
 	descriptor        *eventDescriptor
9
-	activityID        *guid.GUID
10
-	relatedActivityID *guid.GUID
9
+	activityID        guid.GUID
10
+	relatedActivityID guid.GUID
11 11
 	tags              uint32
12 12
 }
13 13
 
... ...
@@ -59,14 +59,14 @@ func WithTags(newTags uint32) EventOpt {
59 59
 }
60 60
 
61 61
 // WithActivityID specifies the activity ID of the event to be written.
62
-func WithActivityID(activityID *guid.GUID) EventOpt {
62
+func WithActivityID(activityID guid.GUID) EventOpt {
63 63
 	return func(options *eventOptions) {
64 64
 		options.activityID = activityID
65 65
 	}
66 66
 }
67 67
 
68 68
 // WithRelatedActivityID specifies the parent activity ID of the event to be written.
69
-func WithRelatedActivityID(activityID *guid.GUID) EventOpt {
69
+func WithRelatedActivityID(activityID guid.GUID) EventOpt {
70 70
 	return func(options *eventOptions) {
71 71
 		options.relatedActivityID = activityID
72 72
 	}
... ...
@@ -4,6 +4,8 @@ import (
4 4
 	"fmt"
5 5
 	"math"
6 6
 	"reflect"
7
+	"syscall"
8
+	"time"
7 9
 	"unsafe"
8 10
 )
9 11
 
... ...
@@ -380,6 +382,14 @@ func Struct(name string, opts ...FieldOpt) FieldOpt {
380 380
 	}
381 381
 }
382 382
 
383
+// Time adds a time to the event.
384
+func Time(name string, value time.Time) FieldOpt {
385
+	return func(em *eventMetadata, ed *eventData) {
386
+		em.writeField(name, inTypeFileTime, outTypeDateTimeUTC, 0)
387
+		ed.writeFiletime(syscall.NsecToFiletime(value.UTC().UnixNano()))
388
+	}
389
+}
390
+
383 391
 // Currently, we support logging basic builtin types (int, string, etc), slices
384 392
 // of basic builtin types, error, types derived from the basic types (e.g. "type
385 393
 // foo int"), and structs (recursively logging their fields). We do not support
... ...
@@ -454,6 +464,8 @@ func SmartField(name string, v interface{}) FieldOpt {
454 454
 		return Float64Array(name, v)
455 455
 	case error:
456 456
 		return StringField(name, v.Error())
457
+	case time.Time:
458
+		return Time(name, v)
457 459
 	default:
458 460
 		switch rv := reflect.ValueOf(v); rv.Kind() {
459 461
 		case reflect.Bool:
... ...
@@ -15,7 +15,7 @@ import (
15 15
 // provider ID to be manually specified. This is most useful when there is an
16 16
 // existing provider ID that must be used to conform to existing diagnostic
17 17
 // infrastructure.
18
-func NewProviderWithID(name string, id *guid.GUID, callback EnableCallback) (provider *Provider, err error) {
18
+func NewProviderWithID(name string, id guid.GUID, callback EnableCallback) (provider *Provider, err error) {
19 19
 	providerCallbackOnce.Do(func() {
20 20
 		globalProviderCallback = windows.NewCallback(providerCallbackAdapter)
21 21
 	})
... ...
@@ -29,7 +29,7 @@ func NewProviderWithID(name string, id *guid.GUID, callback EnableCallback) (pro
29 29
 	provider.ID = id
30 30
 	provider.callback = callback
31 31
 
32
-	if err := eventRegister((*windows.GUID)(provider.ID), globalProviderCallback, uintptr(provider.index), &provider.handle); err != nil {
32
+	if err := eventRegister((*windows.GUID)(&provider.ID), globalProviderCallback, uintptr(provider.index), &provider.handle); err != nil {
33 33
 		return nil, err
34 34
 	}
35 35
 
... ...
@@ -7,6 +7,6 @@ import (
7 7
 )
8 8
 
9 9
 // NewProviderWithID returns a nil provider on unsupported platforms.
10
-func NewProviderWithID(name string, id *guid.GUID, callback EnableCallback) (provider *Provider, err error) {
10
+func NewProviderWithID(name string, id guid.GUID, callback EnableCallback) (provider *Provider, err error) {
11 11
 	return nil, nil
12 12
 }
... ...
@@ -14,7 +14,7 @@ import (
14 14
 // name and ID (GUID), which should always have a 1:1 mapping to each other
15 15
 // (e.g. don't use multiple provider names with the same ID, or vice versa).
16 16
 type Provider struct {
17
-	ID         *guid.GUID
17
+	ID         guid.GUID
18 18
 	handle     providerHandle
19 19
 	metadata   []byte
20 20
 	callback   EnableCallback
... ...
@@ -61,9 +61,9 @@ const (
61 61
 
62 62
 // EnableCallback is the form of the callback function that receives provider
63 63
 // enable/disable notifications from ETW.
64
-type EnableCallback func(*guid.GUID, ProviderState, Level, uint64, uint64, uintptr)
64
+type EnableCallback func(guid.GUID, ProviderState, Level, uint64, uint64, uintptr)
65 65
 
66
-func providerCallback(sourceID *guid.GUID, state ProviderState, level Level, matchAnyKeyword uint64, matchAllKeyword uint64, filterData uintptr, i uintptr) {
66
+func providerCallback(sourceID guid.GUID, state ProviderState, level Level, matchAnyKeyword uint64, matchAllKeyword uint64, filterData uintptr, i uintptr) {
67 67
 	provider := providers.getProvider(uint(i))
68 68
 
69 69
 	switch state {
... ...
@@ -86,7 +86,7 @@ func providerCallback(sourceID *guid.GUID, state ProviderState, level Level, mat
86 86
 // different size, it has only pointer-sized arguments, which are then cast to
87 87
 // the appropriate types when calling providerCallback.
88 88
 func providerCallbackAdapter(sourceID *guid.GUID, state uintptr, level uintptr, matchAnyKeyword uintptr, matchAllKeyword uintptr, filterData uintptr, i uintptr) uintptr {
89
-	providerCallback(sourceID, ProviderState(state), Level(level), uint64(matchAnyKeyword), uint64(matchAllKeyword), filterData, i)
89
+	providerCallback(*sourceID, ProviderState(state), Level(level), uint64(matchAnyKeyword), uint64(matchAllKeyword), filterData, i)
90 90
 	return 0
91 91
 }
92 92
 
... ...
@@ -94,26 +94,27 @@ func providerCallbackAdapter(sourceID *guid.GUID, state uintptr, level uintptr,
94 94
 // uses the same algorithm as used by .NET's EventSource class, which is based
95 95
 // on RFC 4122. More information on the algorithm can be found here:
96 96
 // https://blogs.msdn.microsoft.com/dcook/2015/09/08/etw-provider-names-and-guids/
97
-// The algorithm is roughly:
98
-// Hash = Sha1(namespace + arg.ToUpper().ToUtf16be())
99
-// Guid = Hash[0..15], with Hash[7] tweaked according to RFC 4122
100
-func providerIDFromName(name string) *guid.GUID {
97
+//
98
+// The algorithm is roughly the RFC 4122 algorithm for a V5 UUID, but differs in
99
+// the following ways:
100
+// - The input name is first upper-cased, UTF16-encoded, and converted to
101
+//   big-endian.
102
+// - No variant is set on the result UUID.
103
+// - The result UUID is treated as being in little-endian format, rather than
104
+//   big-endian.
105
+func providerIDFromName(name string) guid.GUID {
101 106
 	buffer := sha1.New()
102
-
103
-	namespace := []byte{0x48, 0x2C, 0x2D, 0xB2, 0xC3, 0x90, 0x47, 0xC8, 0x87, 0xF8, 0x1A, 0x15, 0xBF, 0xC1, 0x30, 0xFB}
104
-	buffer.Write(namespace)
105
-
107
+	namespace := guid.GUID{0x482C2DB2, 0xC390, 0x47C8, [8]byte{0x87, 0xF8, 0x1A, 0x15, 0xBF, 0xC1, 0x30, 0xFB}}
108
+	namespaceBytes := namespace.ToArray()
109
+	buffer.Write(namespaceBytes[:])
106 110
 	binary.Write(buffer, binary.BigEndian, utf16.Encode([]rune(strings.ToUpper(name))))
107 111
 
108 112
 	sum := buffer.Sum(nil)
109 113
 	sum[7] = (sum[7] & 0xf) | 0x50
110 114
 
111
-	return &guid.GUID{
112
-		Data1: binary.LittleEndian.Uint32(sum[0:4]),
113
-		Data2: binary.LittleEndian.Uint16(sum[4:6]),
114
-		Data3: binary.LittleEndian.Uint16(sum[6:8]),
115
-		Data4: [8]byte{sum[8], sum[9], sum[10], sum[11], sum[12], sum[13], sum[14], sum[15]},
116
-	}
115
+	a := [16]byte{}
116
+	copy(a[:], sum)
117
+	return guid.FromWindowsArray(a)
117 118
 }
118 119
 
119 120
 // 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
219 219
 // the ETW infrastructure.
220 220
 func (provider *Provider) writeEventRaw(
221 221
 	descriptor *eventDescriptor,
222
-	activityID *guid.GUID,
223
-	relatedActivityID *guid.GUID,
222
+	activityID guid.GUID,
223
+	relatedActivityID guid.GUID,
224 224
 	metadataBlobs [][]byte,
225 225
 	dataBlobs [][]byte) error {
226 226
 
... ...
@@ -235,5 +236,5 @@ func (provider *Provider) writeEventRaw(
235 235
 		dataDescriptors = append(dataDescriptors, newEventDataDescriptor(eventDataDescriptorTypeUserData, blob))
236 236
 	}
237 237
 
238
-	return eventWriteTransfer(provider.handle, descriptor, (*windows.GUID)(activityID), (*windows.GUID)(relatedActivityID), dataDescriptorCount, &dataDescriptors[0])
238
+	return eventWriteTransfer(provider.handle, descriptor, (*windows.GUID)(&activityID), (*windows.GUID)(&relatedActivityID), dataDescriptorCount, &dataDescriptors[0])
239 239
 }
... ...
@@ -1,6 +1,8 @@
1 1
 package etwlogrus
2 2
 
3 3
 import (
4
+	"sort"
5
+
4 6
 	"github.com/Microsoft/go-winio/pkg/etw"
5 7
 	"github.com/sirupsen/logrus"
6 8
 )
... ...
@@ -31,15 +33,7 @@ func NewHookFromProvider(provider *etw.Provider) (*Hook, error) {
31 31
 // Levels returns the set of levels that this hook wants to receive log entries
32 32
 // for.
33 33
 func (h *Hook) Levels() []logrus.Level {
34
-	return []logrus.Level{
35
-		logrus.TraceLevel,
36
-		logrus.DebugLevel,
37
-		logrus.InfoLevel,
38
-		logrus.WarnLevel,
39
-		logrus.ErrorLevel,
40
-		logrus.FatalLevel,
41
-		logrus.PanicLevel,
42
-	}
34
+	return logrus.AllLevels
43 35
 }
44 36
 
45 37
 var logrusToETWLevelMap = map[logrus.Level]etw.Level{
... ...
@@ -62,19 +56,42 @@ func (h *Hook) Fire(e *logrus.Entry) error {
62 62
 		return nil
63 63
 	}
64 64
 
65
-	// Reserve extra space for the message field.
66
-	fields := make([]etw.FieldOpt, 0, len(e.Data)+1)
65
+	// Sort the fields by name so they are consistent in each instance
66
+	// of an event. Otherwise, the fields don't line up in WPA.
67
+	names := make([]string, 0, len(e.Data))
68
+	hasError := false
69
+	for k := range e.Data {
70
+		if k == logrus.ErrorKey {
71
+			// Always put the error last because it is optional in some events.
72
+			hasError = true
73
+		} else {
74
+			names = append(names, k)
75
+		}
76
+	}
77
+	sort.Strings(names)
67 78
 
79
+	// Reserve extra space for the message and time fields.
80
+	fields := make([]etw.FieldOpt, 0, len(e.Data)+2)
68 81
 	fields = append(fields, etw.StringField("Message", e.Message))
69
-
70
-	for k, v := range e.Data {
71
-		fields = append(fields, etw.SmartField(k, v))
82
+	fields = append(fields, etw.Time("Time", e.Time))
83
+	for _, k := range names {
84
+		fields = append(fields, etw.SmartField(k, e.Data[k]))
85
+	}
86
+	if hasError {
87
+		fields = append(fields, etw.SmartField(logrus.ErrorKey, e.Data[logrus.ErrorKey]))
72 88
 	}
73 89
 
74
-	return h.provider.WriteEvent(
90
+	// Firing an ETW event is essentially best effort, as the event write can
91
+	// fail for reasons completely out of the control of the event writer (such
92
+	// as a session listening for the event having no available space in its
93
+	// buffers). Therefore, we don't return the error from WriteEvent, as it is
94
+	// just noise in many cases.
95
+	h.provider.WriteEvent(
75 96
 		"LogrusEntry",
76 97
 		etw.WithEventOpts(etw.WithLevel(level)),
77 98
 		fields)
99
+
100
+	return nil
78 101
 }
79 102
 
80 103
 // Close cleans up the hook and closes the ETW provider. If the provder was
... ...
@@ -1,19 +1,43 @@
1
+// Package guid provides a GUID type. The backing structure for a GUID is
2
+// identical to that used by the golang.org/x/sys/windows GUID type.
3
+// There are two main binary encodings used for a GUID, the big-endian encoding,
4
+// and the Windows (mixed-endian) encoding. See here for details:
5
+// https://en.wikipedia.org/wiki/Universally_unique_identifier#Encoding
1 6
 package guid
2 7
 
3 8
 import (
4 9
 	"crypto/rand"
10
+	"crypto/sha1"
11
+	"encoding"
5 12
 	"encoding/binary"
6
-	"encoding/json"
7 13
 	"fmt"
8 14
 	"strconv"
9
-	"strings"
10 15
 
11
-	"github.com/pkg/errors"
12 16
 	"golang.org/x/sys/windows"
13 17
 )
14 18
 
15
-var _ = (json.Marshaler)(&GUID{})
16
-var _ = (json.Unmarshaler)(&GUID{})
19
+// Variant specifies which GUID variant (or "type") of the GUID. It determines
20
+// how the entirety of the rest of the GUID is interpreted.
21
+type Variant uint8
22
+
23
+// The variants specified by RFC 4122.
24
+const (
25
+	// VariantUnknown specifies a GUID variant which does not conform to one of
26
+	// the variant encodings specified in RFC 4122.
27
+	VariantUnknown Variant = iota
28
+	VariantNCS
29
+	VariantRFC4122
30
+	VariantMicrosoft
31
+	VariantFuture
32
+)
33
+
34
+// Version specifies how the bits in the GUID were generated. For instance, a
35
+// version 4 GUID is randomly generated, and a version 5 is generated from the
36
+// hash of an input string.
37
+type Version uint8
38
+
39
+var _ = (encoding.TextMarshaler)(GUID{})
40
+var _ = (encoding.TextUnmarshaler)(&GUID{})
17 41
 
18 42
 // GUID represents a GUID/UUID. It has the same structure as
19 43
 // golang.org/x/sys/windows.GUID so that it can be used with functions expecting
... ...
@@ -23,24 +47,83 @@ var _ = (json.Unmarshaler)(&GUID{})
23 23
 type GUID windows.GUID
24 24
 
25 25
 // NewV4 returns a new version 4 (pseudorandom) GUID, as defined by RFC 4122.
26
-func NewV4() (*GUID, error) {
26
+func NewV4() (GUID, error) {
27 27
 	var b [16]byte
28 28
 	if _, err := rand.Read(b[:]); err != nil {
29
-		return nil, err
29
+		return GUID{}, err
30 30
 	}
31 31
 
32
+	g := FromArray(b)
33
+	g.setVersion(4) // Version 4 means randomly generated.
34
+	g.setVariant(VariantRFC4122)
35
+
36
+	return g, nil
37
+}
38
+
39
+// NewV5 returns a new version 5 (generated from a string via SHA-1 hashing)
40
+// GUID, as defined by RFC 4122. The RFC is unclear on the encoding of the name,
41
+// and the sample code treats it as a series of bytes, so we do the same here.
42
+//
43
+// Some implementations, such as those found on Windows, treat the name as a
44
+// big-endian UTF16 stream of bytes. If that is desired, the string can be
45
+// encoded as such before being passed to this function.
46
+func NewV5(namespace GUID, name []byte) (GUID, error) {
47
+	b := sha1.New()
48
+	namespaceBytes := namespace.ToArray()
49
+	b.Write(namespaceBytes[:])
50
+	b.Write(name)
51
+
52
+	a := [16]byte{}
53
+	copy(a[:], b.Sum(nil))
54
+
55
+	g := FromArray(a)
56
+	g.setVersion(5) // Version 5 means generated from a string.
57
+	g.setVariant(VariantRFC4122)
58
+
59
+	return g, nil
60
+}
61
+
62
+func fromArray(b [16]byte, order binary.ByteOrder) GUID {
32 63
 	var g GUID
33
-	g.Data1 = binary.LittleEndian.Uint32(b[0:4])
34
-	g.Data2 = binary.LittleEndian.Uint16(b[4:6])
35
-	g.Data3 = binary.LittleEndian.Uint16(b[6:8])
64
+	g.Data1 = order.Uint32(b[0:4])
65
+	g.Data2 = order.Uint16(b[4:6])
66
+	g.Data3 = order.Uint16(b[6:8])
36 67
 	copy(g.Data4[:], b[8:16])
68
+	return g
69
+}
70
+
71
+func (g GUID) toArray(order binary.ByteOrder) [16]byte {
72
+	b := [16]byte{}
73
+	order.PutUint32(b[0:4], g.Data1)
74
+	order.PutUint16(b[4:6], g.Data2)
75
+	order.PutUint16(b[6:8], g.Data3)
76
+	copy(b[8:16], g.Data4[:])
77
+	return b
78
+}
37 79
 
38
-	g.Data3 = (g.Data3 & 0x0fff) | 0x4000   // Version 4 (randomly generated)
39
-	g.Data4[0] = (g.Data4[0] & 0x3f) | 0x80 // RFC4122 variant
40
-	return &g, nil
80
+// FromArray constructs a GUID from a big-endian encoding array of 16 bytes.
81
+func FromArray(b [16]byte) GUID {
82
+	return fromArray(b, binary.BigEndian)
41 83
 }
42 84
 
43
-func (g *GUID) String() string {
85
+// ToArray returns an array of 16 bytes representing the GUID in big-endian
86
+// encoding.
87
+func (g GUID) ToArray() [16]byte {
88
+	return g.toArray(binary.BigEndian)
89
+}
90
+
91
+// FromWindowsArray constructs a GUID from a Windows encoding array of bytes.
92
+func FromWindowsArray(b [16]byte) GUID {
93
+	return fromArray(b, binary.LittleEndian)
94
+}
95
+
96
+// ToWindowsArray returns an array of 16 bytes representing the GUID in Windows
97
+// encoding.
98
+func (g GUID) ToWindowsArray() [16]byte {
99
+	return g.toArray(binary.LittleEndian)
100
+}
101
+
102
+func (g GUID) String() string {
44 103
 	return fmt.Sprintf(
45 104
 		"%08x-%04x-%04x-%04x-%012x",
46 105
 		g.Data1,
... ...
@@ -53,58 +136,100 @@ func (g *GUID) String() string {
53 53
 // FromString parses a string containing a GUID and returns the GUID. The only
54 54
 // format currently supported is the `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx`
55 55
 // format.
56
-func FromString(s string) (*GUID, error) {
56
+func FromString(s string) (GUID, error) {
57 57
 	if len(s) != 36 {
58
-		return nil, errors.New("invalid GUID format (length)")
58
+		return GUID{}, fmt.Errorf("invalid GUID %q", s)
59 59
 	}
60 60
 	if s[8] != '-' || s[13] != '-' || s[18] != '-' || s[23] != '-' {
61
-		return nil, errors.New("invalid GUID format (dashes)")
61
+		return GUID{}, fmt.Errorf("invalid GUID %q", s)
62 62
 	}
63 63
 
64 64
 	var g GUID
65 65
 
66 66
 	data1, err := strconv.ParseUint(s[0:8], 16, 32)
67 67
 	if err != nil {
68
-		return nil, errors.Wrap(err, "invalid GUID format (Data1)")
68
+		return GUID{}, fmt.Errorf("invalid GUID %q", s)
69 69
 	}
70 70
 	g.Data1 = uint32(data1)
71 71
 
72 72
 	data2, err := strconv.ParseUint(s[9:13], 16, 16)
73 73
 	if err != nil {
74
-		return nil, errors.Wrap(err, "invalid GUID format (Data2)")
74
+		return GUID{}, fmt.Errorf("invalid GUID %q", s)
75 75
 	}
76 76
 	g.Data2 = uint16(data2)
77 77
 
78 78
 	data3, err := strconv.ParseUint(s[14:18], 16, 16)
79 79
 	if err != nil {
80
-		return nil, errors.Wrap(err, "invalid GUID format (Data3)")
80
+		return GUID{}, fmt.Errorf("invalid GUID %q", s)
81 81
 	}
82 82
 	g.Data3 = uint16(data3)
83 83
 
84 84
 	for i, x := range []int{19, 21, 24, 26, 28, 30, 32, 34} {
85 85
 		v, err := strconv.ParseUint(s[x:x+2], 16, 8)
86 86
 		if err != nil {
87
-			return nil, errors.Wrap(err, "invalid GUID format (Data4)")
87
+			return GUID{}, fmt.Errorf("invalid GUID %q", s)
88 88
 		}
89 89
 		g.Data4[i] = uint8(v)
90 90
 	}
91 91
 
92
-	return &g, nil
92
+	return g, nil
93
+}
94
+
95
+func (g *GUID) setVariant(v Variant) {
96
+	d := g.Data4[0]
97
+	switch v {
98
+	case VariantNCS:
99
+		d = (d & 0x7f)
100
+	case VariantRFC4122:
101
+		d = (d & 0x3f) | 0x80
102
+	case VariantMicrosoft:
103
+		d = (d & 0x1f) | 0xc0
104
+	case VariantFuture:
105
+		d = (d & 0x0f) | 0xe0
106
+	case VariantUnknown:
107
+		fallthrough
108
+	default:
109
+		panic(fmt.Sprintf("invalid variant: %d", v))
110
+	}
111
+	g.Data4[0] = d
112
+}
113
+
114
+// Variant returns the GUID variant, as defined in RFC 4122.
115
+func (g GUID) Variant() Variant {
116
+	b := g.Data4[0]
117
+	if b&0x80 == 0 {
118
+		return VariantNCS
119
+	} else if b&0xc0 == 0x80 {
120
+		return VariantRFC4122
121
+	} else if b&0xe0 == 0xc0 {
122
+		return VariantMicrosoft
123
+	} else if b&0xe0 == 0xe0 {
124
+		return VariantFuture
125
+	}
126
+	return VariantUnknown
127
+}
128
+
129
+func (g *GUID) setVersion(v Version) {
130
+	g.Data3 = (g.Data3 & 0x0fff) | (uint16(v) << 12)
131
+}
132
+
133
+// Version returns the GUID version, as defined in RFC 4122.
134
+func (g GUID) Version() Version {
135
+	return Version((g.Data3 & 0xF000) >> 12)
93 136
 }
94 137
 
95
-// MarshalJSON marshals the GUID to JSON representation and returns it as a
96
-// slice of bytes.
97
-func (g *GUID) MarshalJSON() ([]byte, error) {
98
-	return json.Marshal(g.String())
138
+// MarshalText returns the textual representation of the GUID.
139
+func (g GUID) MarshalText() ([]byte, error) {
140
+	return []byte(g.String()), nil
99 141
 }
100 142
 
101
-// UnmarshalJSON unmarshals a GUID from JSON representation and sets itself to
102
-// the unmarshaled GUID.
103
-func (g *GUID) UnmarshalJSON(data []byte) error {
104
-	g2, err := FromString(strings.Trim(string(data), "\""))
143
+// UnmarshalText takes the textual representation of a GUID, and unmarhals it
144
+// into this GUID.
145
+func (g *GUID) UnmarshalText(text []byte) error {
146
+	g2, err := FromString(string(text))
105 147
 	if err != nil {
106 148
 		return err
107 149
 	}
108
-	*g = *g2
150
+	*g = g2
109 151
 	return nil
110 152
 }
... ...
@@ -117,9 +117,13 @@ func CreateVhdx(path string, maxSizeInGb, blockSizeInMb uint32) error {
117 117
 	return nil
118 118
 }
119 119
 
120
-// DetachVhd detaches a VHD attached at the given path.
120
+// DetachVhd detaches a mounted container layer vhd found at `path`.
121 121
 func DetachVhd(path string) error {
122
-	handle, err := OpenVirtualDisk(path, VirtualDiskAccessDetach, OpenVirtualDiskFlagNone)
122
+	handle, err := OpenVirtualDisk(
123
+		path,
124
+		VirtualDiskAccessNone,
125
+		OpenVirtualDiskFlagCachedIO|OpenVirtualDiskFlagIgnoreRelativeParentLocator)
126
+
123 127
 	if err != nil {
124 128
 		return err
125 129
 	}
... ...
@@ -127,7 +131,7 @@ func DetachVhd(path string) error {
127 127
 	return detachVirtualDisk(handle, 0, 0)
128 128
 }
129 129
 
130
-// OpenVirtuaDisk obtains a handle to a VHD opened with supplied access mask and flags.
130
+// OpenVirtualDisk obtains a handle to a VHD opened with supplied access mask and flags.
131 131
 func OpenVirtualDisk(path string, accessMask VirtualDiskAccessMask, flag VirtualDiskFlag) (syscall.Handle, error) {
132 132
 	var (
133 133
 		defaultType virtualStorageType