Browse code

Revendor Microsoft/go-winio @ v0.4.5

Signed-off-by: John Howard <jhoward@microsoft.com>

John Howard authored on 2017/08/09 07:55:29
Showing 6 changed files
... ...
@@ -1,7 +1,7 @@
1 1
 # the following lines are in sorted order, FYI
2 2
 github.com/Azure/go-ansiterm 19f72df4d05d31cbe1c56bfc8045c96babff6c7e
3 3
 github.com/Microsoft/hcsshim v0.6.3
4
-github.com/Microsoft/go-winio v0.4.4
4
+github.com/Microsoft/go-winio v0.4.5
5 5
 github.com/moby/buildkit da2b9dc7dab99e824b2b1067ad7d0523e32dd2d9 https://github.com/dmcgowan/buildkit.git
6 6
 github.com/davecgh/go-spew 346938d642f2ec3594ed81d874461961cd0faa76
7 7
 github.com/docker/libtrust 9cbd2a1374f46905c68a4eb3694a130610adc62a
... ...
@@ -68,10 +68,20 @@ func NewBackupStreamReader(r io.Reader) *BackupStreamReader {
68 68
 	return &BackupStreamReader{r, 0}
69 69
 }
70 70
 
71
-// Next returns the next backup stream and prepares for calls to Write(). It skips the remainder of the current stream if
71
+// Next returns the next backup stream and prepares for calls to Read(). It skips the remainder of the current stream if
72 72
 // it was not completely read.
73 73
 func (r *BackupStreamReader) Next() (*BackupHeader, error) {
74 74
 	if r.bytesLeft > 0 {
75
+		if s, ok := r.r.(io.Seeker); ok {
76
+			// Make sure Seek on io.SeekCurrent sometimes succeeds
77
+			// before trying the actual seek.
78
+			if _, err := s.Seek(0, io.SeekCurrent); err == nil {
79
+				if _, err = s.Seek(r.bytesLeft, io.SeekCurrent); err != nil {
80
+					return nil, err
81
+				}
82
+				r.bytesLeft = 0
83
+			}
84
+		}
75 85
 		if _, err := io.Copy(ioutil.Discard, r); err != nil {
76 86
 			return nil, err
77 87
 		}
... ...
@@ -220,7 +230,7 @@ type BackupFileWriter struct {
220 220
 	ctx             uintptr
221 221
 }
222 222
 
223
-// NewBackupFileWrtier returns a new BackupFileWriter from a file handle. If includeSecurity is true,
223
+// NewBackupFileWriter returns a new BackupFileWriter from a file handle. If includeSecurity is true,
224 224
 // Write() will attempt to restore the security descriptor from the stream.
225 225
 func NewBackupFileWriter(f *os.File, includeSecurity bool) *BackupFileWriter {
226 226
 	w := &BackupFileWriter{f, includeSecurity, 0}
... ...
@@ -36,6 +36,7 @@ const (
36 36
 	hdrSecurityDescriptor    = "sd"
37 37
 	hdrRawSecurityDescriptor = "rawsd"
38 38
 	hdrMountPoint            = "mountpoint"
39
+	hdrEaPrefix              = "xattr."
39 40
 )
40 41
 
41 42
 func writeZeroes(w io.Writer, count int64) error {
... ...
@@ -118,6 +119,21 @@ func BasicInfoHeader(name string, size int64, fileInfo *winio.FileBasicInfo) *ta
118 118
 func WriteTarFileFromBackupStream(t *tar.Writer, r io.Reader, name string, size int64, fileInfo *winio.FileBasicInfo) error {
119 119
 	name = filepath.ToSlash(name)
120 120
 	hdr := BasicInfoHeader(name, size, fileInfo)
121
+
122
+	// If r can be seeked, then this function is two-pass: pass 1 collects the
123
+	// tar header data, and pass 2 copies the data stream. If r cannot be
124
+	// seeked, then some header data (in particular EAs) will be silently lost.
125
+	var (
126
+		restartPos int64
127
+		err        error
128
+	)
129
+	sr, readTwice := r.(io.Seeker)
130
+	if readTwice {
131
+		if restartPos, err = sr.Seek(0, io.SeekCurrent); err != nil {
132
+			readTwice = false
133
+		}
134
+	}
135
+
121 136
 	br := winio.NewBackupStreamReader(r)
122 137
 	var dataHdr *winio.BackupHeader
123 138
 	for dataHdr == nil {
... ...
@@ -131,7 +147,9 @@ func WriteTarFileFromBackupStream(t *tar.Writer, r io.Reader, name string, size
131 131
 		switch bhdr.Id {
132 132
 		case winio.BackupData:
133 133
 			hdr.Mode |= c_ISREG
134
-			dataHdr = bhdr
134
+			if !readTwice {
135
+				dataHdr = bhdr
136
+			}
135 137
 		case winio.BackupSecurity:
136 138
 			sd, err := ioutil.ReadAll(br)
137 139
 			if err != nil {
... ...
@@ -151,18 +169,54 @@ func WriteTarFileFromBackupStream(t *tar.Writer, r io.Reader, name string, size
151 151
 				hdr.Winheaders[hdrMountPoint] = "1"
152 152
 			}
153 153
 			hdr.Linkname = rp.Target
154
-		case winio.BackupEaData, winio.BackupLink, winio.BackupPropertyData, winio.BackupObjectId, winio.BackupTxfsData:
154
+
155
+		case winio.BackupEaData:
156
+			eab, err := ioutil.ReadAll(br)
157
+			if err != nil {
158
+				return err
159
+			}
160
+			eas, err := winio.DecodeExtendedAttributes(eab)
161
+			if err != nil {
162
+				return err
163
+			}
164
+			for _, ea := range eas {
165
+				// Use base64 encoding for the binary value. Note that there
166
+				// is no way to encode the EA's flags, since their use doesn't
167
+				// make any sense for persisted EAs.
168
+				hdr.Winheaders[hdrEaPrefix+ea.Name] = base64.StdEncoding.EncodeToString(ea.Value)
169
+			}
170
+
171
+		case winio.BackupAlternateData, winio.BackupLink, winio.BackupPropertyData, winio.BackupObjectId, winio.BackupTxfsData:
155 172
 			// ignore these streams
156 173
 		default:
157 174
 			return fmt.Errorf("%s: unknown stream ID %d", name, bhdr.Id)
158 175
 		}
159 176
 	}
160 177
 
161
-	err := t.WriteHeader(hdr)
178
+	err = t.WriteHeader(hdr)
162 179
 	if err != nil {
163 180
 		return err
164 181
 	}
165 182
 
183
+	if readTwice {
184
+		// Get back to the data stream.
185
+		if _, err = sr.Seek(restartPos, io.SeekStart); err != nil {
186
+			return err
187
+		}
188
+		for dataHdr == nil {
189
+			bhdr, err := br.Next()
190
+			if err == io.EOF {
191
+				break
192
+			}
193
+			if err != nil {
194
+				return err
195
+			}
196
+			if bhdr.Id == winio.BackupData {
197
+				dataHdr = bhdr
198
+			}
199
+		}
200
+	}
201
+
166 202
 	if dataHdr != nil {
167 203
 		// A data stream was found. Copy the data.
168 204
 		if (dataHdr.Attributes & winio.StreamSparseAttributes) == 0 {
... ...
@@ -293,6 +347,38 @@ func WriteBackupStreamFromTarFile(w io.Writer, t *tar.Reader, hdr *tar.Header) (
293 293
 			return nil, err
294 294
 		}
295 295
 	}
296
+	var eas []winio.ExtendedAttribute
297
+	for k, v := range hdr.Winheaders {
298
+		if !strings.HasPrefix(k, hdrEaPrefix) {
299
+			continue
300
+		}
301
+		data, err := base64.StdEncoding.DecodeString(v)
302
+		if err != nil {
303
+			return nil, err
304
+		}
305
+		eas = append(eas, winio.ExtendedAttribute{
306
+			Name:  k[len(hdrEaPrefix):],
307
+			Value: data,
308
+		})
309
+	}
310
+	if len(eas) != 0 {
311
+		eadata, err := winio.EncodeExtendedAttributes(eas)
312
+		if err != nil {
313
+			return nil, err
314
+		}
315
+		bhdr := winio.BackupHeader{
316
+			Id:   winio.BackupEaData,
317
+			Size: int64(len(eadata)),
318
+		}
319
+		err = bw.WriteHeader(&bhdr)
320
+		if err != nil {
321
+			return nil, err
322
+		}
323
+		_, err = bw.Write(eadata)
324
+		if err != nil {
325
+			return nil, err
326
+		}
327
+	}
296 328
 	if hdr.Typeflag == tar.TypeSymlink {
297 329
 		_, isMountPoint := hdr.Winheaders[hdrMountPoint]
298 330
 		rp := winio.ReparsePoint{
299 331
new file mode 100644
... ...
@@ -0,0 +1,137 @@
0
+package winio
1
+
2
+import (
3
+	"bytes"
4
+	"encoding/binary"
5
+	"errors"
6
+)
7
+
8
+type fileFullEaInformation struct {
9
+	NextEntryOffset uint32
10
+	Flags           uint8
11
+	NameLength      uint8
12
+	ValueLength     uint16
13
+}
14
+
15
+var (
16
+	fileFullEaInformationSize = binary.Size(&fileFullEaInformation{})
17
+
18
+	errInvalidEaBuffer = errors.New("invalid extended attribute buffer")
19
+	errEaNameTooLarge  = errors.New("extended attribute name too large")
20
+	errEaValueTooLarge = errors.New("extended attribute value too large")
21
+)
22
+
23
+// ExtendedAttribute represents a single Windows EA.
24
+type ExtendedAttribute struct {
25
+	Name  string
26
+	Value []byte
27
+	Flags uint8
28
+}
29
+
30
+func parseEa(b []byte) (ea ExtendedAttribute, nb []byte, err error) {
31
+	var info fileFullEaInformation
32
+	err = binary.Read(bytes.NewReader(b), binary.LittleEndian, &info)
33
+	if err != nil {
34
+		err = errInvalidEaBuffer
35
+		return
36
+	}
37
+
38
+	nameOffset := fileFullEaInformationSize
39
+	nameLen := int(info.NameLength)
40
+	valueOffset := nameOffset + int(info.NameLength) + 1
41
+	valueLen := int(info.ValueLength)
42
+	nextOffset := int(info.NextEntryOffset)
43
+	if valueLen+valueOffset > len(b) || nextOffset < 0 || nextOffset > len(b) {
44
+		err = errInvalidEaBuffer
45
+		return
46
+	}
47
+
48
+	ea.Name = string(b[nameOffset : nameOffset+nameLen])
49
+	ea.Value = b[valueOffset : valueOffset+valueLen]
50
+	ea.Flags = info.Flags
51
+	if info.NextEntryOffset != 0 {
52
+		nb = b[info.NextEntryOffset:]
53
+	}
54
+	return
55
+}
56
+
57
+// DecodeExtendedAttributes decodes a list of EAs from a FILE_FULL_EA_INFORMATION
58
+// buffer retrieved from BackupRead, ZwQueryEaFile, etc.
59
+func DecodeExtendedAttributes(b []byte) (eas []ExtendedAttribute, err error) {
60
+	for len(b) != 0 {
61
+		ea, nb, err := parseEa(b)
62
+		if err != nil {
63
+			return nil, err
64
+		}
65
+
66
+		eas = append(eas, ea)
67
+		b = nb
68
+	}
69
+	return
70
+}
71
+
72
+func writeEa(buf *bytes.Buffer, ea *ExtendedAttribute, last bool) error {
73
+	if int(uint8(len(ea.Name))) != len(ea.Name) {
74
+		return errEaNameTooLarge
75
+	}
76
+	if int(uint16(len(ea.Value))) != len(ea.Value) {
77
+		return errEaValueTooLarge
78
+	}
79
+	entrySize := uint32(fileFullEaInformationSize + len(ea.Name) + 1 + len(ea.Value))
80
+	withPadding := (entrySize + 3) &^ 3
81
+	nextOffset := uint32(0)
82
+	if !last {
83
+		nextOffset = withPadding
84
+	}
85
+	info := fileFullEaInformation{
86
+		NextEntryOffset: nextOffset,
87
+		Flags:           ea.Flags,
88
+		NameLength:      uint8(len(ea.Name)),
89
+		ValueLength:     uint16(len(ea.Value)),
90
+	}
91
+
92
+	err := binary.Write(buf, binary.LittleEndian, &info)
93
+	if err != nil {
94
+		return err
95
+	}
96
+
97
+	_, err = buf.Write([]byte(ea.Name))
98
+	if err != nil {
99
+		return err
100
+	}
101
+
102
+	err = buf.WriteByte(0)
103
+	if err != nil {
104
+		return err
105
+	}
106
+
107
+	_, err = buf.Write(ea.Value)
108
+	if err != nil {
109
+		return err
110
+	}
111
+
112
+	_, err = buf.Write([]byte{0, 0, 0}[0 : withPadding-entrySize])
113
+	if err != nil {
114
+		return err
115
+	}
116
+
117
+	return nil
118
+}
119
+
120
+// EncodeExtendedAttributes encodes a list of EAs into a FILE_FULL_EA_INFORMATION
121
+// buffer for use with BackupWrite, ZwSetEaFile, etc.
122
+func EncodeExtendedAttributes(eas []ExtendedAttribute) ([]byte, error) {
123
+	var buf bytes.Buffer
124
+	for i := range eas {
125
+		last := false
126
+		if i == len(eas)-1 {
127
+			last = true
128
+		}
129
+
130
+		err := writeEa(&buf, &eas[i], last)
131
+		if err != nil {
132
+			return nil, err
133
+		}
134
+	}
135
+	return buf.Bytes(), nil
136
+}
... ...
@@ -78,6 +78,7 @@ func initIo() {
78 78
 type win32File struct {
79 79
 	handle        syscall.Handle
80 80
 	wg            sync.WaitGroup
81
+	wgLock        sync.RWMutex
81 82
 	closing       atomicBool
82 83
 	readDeadline  deadlineHandler
83 84
 	writeDeadline deadlineHandler
... ...
@@ -114,14 +115,18 @@ func MakeOpenFile(h syscall.Handle) (io.ReadWriteCloser, error) {
114 114
 
115 115
 // closeHandle closes the resources associated with a Win32 handle
116 116
 func (f *win32File) closeHandle() {
117
+	f.wgLock.Lock()
117 118
 	// Atomically set that we are closing, releasing the resources only once.
118 119
 	if !f.closing.swap(true) {
120
+		f.wgLock.Unlock()
119 121
 		// cancel all IO and wait for it to complete
120 122
 		cancelIoEx(f.handle, nil)
121 123
 		f.wg.Wait()
122 124
 		// at this point, no new IO can start
123 125
 		syscall.Close(f.handle)
124 126
 		f.handle = 0
127
+	} else {
128
+		f.wgLock.Unlock()
125 129
 	}
126 130
 }
127 131
 
... ...
@@ -134,10 +139,13 @@ func (f *win32File) Close() error {
134 134
 // prepareIo prepares for a new IO operation.
135 135
 // The caller must call f.wg.Done() when the IO is finished, prior to Close() returning.
136 136
 func (f *win32File) prepareIo() (*ioOperation, error) {
137
+	f.wgLock.RLock()
137 138
 	if f.closing.isSet() {
139
+		f.wgLock.RUnlock()
138 140
 		return nil, ErrFileClosed
139 141
 	}
140 142
 	f.wg.Add(1)
143
+	f.wgLock.RUnlock()
141 144
 	c := &ioOperation{}
142 145
 	c.ch = make(chan ioResult)
143 146
 	return c, nil
... ...
@@ -265,9 +265,9 @@ func (l *win32PipeListener) listenerRoutine() {
265 265
 			if err == nil {
266 266
 				// Wait for the client to connect.
267 267
 				ch := make(chan error)
268
-				go func() {
268
+				go func(p *win32File) {
269 269
 					ch <- connectPipe(p)
270
-				}()
270
+				}(p)
271 271
 				select {
272 272
 				case err = <-ch:
273 273
 					if err != nil {