Browse code

Revendor Microsoft/go-winio and Microsoft/hcsshim

Signed-off-by: John Starks <jostarks@microsoft.com>

John Starks authored on 2016/03/03 07:17:13
Showing 27 changed files
... ...
@@ -7,7 +7,8 @@ source 'hack/.vendor-helpers.sh'
7 7
 
8 8
 # the following lines are in sorted order, FYI
9 9
 clone git github.com/Azure/go-ansiterm 70b2c90b260171e829f1ebd7c17f600c11858dbe
10
-clone git github.com/Microsoft/go-winio eb176a9831c54b88eaf9eb4fbc24b94080d910ad
10
+clone git github.com/Microsoft/hcsshim 9488dda5ab5d3c1af26e17d3d9fc2e9f29009a7b
11
+clone git github.com/Microsoft/go-winio c40bf24f405ab3cc8e1383542d474e813332de6d
11 12
 clone git github.com/Sirupsen/logrus v0.9.0 # logrus is a common dependency among multiple deps
12 13
 clone git github.com/docker/libtrust 9cbd2a1374f46905c68a4eb3694a130610adc62a
13 14
 clone git github.com/go-check/check 11d3bc7aa68e238947792f30573146a3231fc0f1
... ...
@@ -16,7 +17,6 @@ clone git github.com/gorilla/mux e444e69cbd
16 16
 clone git github.com/kr/pty 5cf931ef8f
17 17
 clone git github.com/mattn/go-shellwords v1.0.0
18 18
 clone git github.com/mattn/go-sqlite3 v1.1.0
19
-clone git github.com/Microsoft/hcsshim 43858ef3c5c944dfaaabfbe8b6ea093da7f28dba
20 19
 clone git github.com/mistifyio/go-zfs v2.1.1
21 20
 clone git github.com/tchap/go-patricia v2.1.0
22 21
 clone git github.com/vdemeester/shakers 24d7f1d6a71aa5d9cbe7390e4afb66b7eef9e1b3
23 22
new file mode 100644
... ...
@@ -0,0 +1,27 @@
0
+Copyright (c) 2012 The Go Authors. All rights reserved.
1
+
2
+Redistribution and use in source and binary forms, with or without
3
+modification, are permitted provided that the following conditions are
4
+met:
5
+
6
+   * Redistributions of source code must retain the above copyright
7
+notice, this list of conditions and the following disclaimer.
8
+   * Redistributions in binary form must reproduce the above
9
+copyright notice, this list of conditions and the following disclaimer
10
+in the documentation and/or other materials provided with the
11
+distribution.
12
+   * Neither the name of Google Inc. nor the names of its
13
+contributors may be used to endorse or promote products derived from
14
+this software without specific prior written permission.
15
+
16
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
0 27
new file mode 100644
... ...
@@ -0,0 +1,342 @@
0
+// Copyright 2009 The Go Authors. All rights reserved.
1
+// Use of this source code is governed by a BSD-style
2
+// license that can be found in the LICENSE file.
3
+
4
+// Package tar implements access to tar archives.
5
+// It aims to cover most of the variations, including those produced
6
+// by GNU and BSD tars.
7
+//
8
+// References:
9
+//   http://www.freebsd.org/cgi/man.cgi?query=tar&sektion=5
10
+//   http://www.gnu.org/software/tar/manual/html_node/Standard.html
11
+//   http://pubs.opengroup.org/onlinepubs/9699919799/utilities/pax.html
12
+package tar
13
+
14
+import (
15
+	"bytes"
16
+	"errors"
17
+	"fmt"
18
+	"os"
19
+	"path"
20
+	"time"
21
+)
22
+
23
+const (
24
+	blockSize = 512
25
+
26
+	// Types
27
+	TypeReg           = '0'    // regular file
28
+	TypeRegA          = '\x00' // regular file
29
+	TypeLink          = '1'    // hard link
30
+	TypeSymlink       = '2'    // symbolic link
31
+	TypeChar          = '3'    // character device node
32
+	TypeBlock         = '4'    // block device node
33
+	TypeDir           = '5'    // directory
34
+	TypeFifo          = '6'    // fifo node
35
+	TypeCont          = '7'    // reserved
36
+	TypeXHeader       = 'x'    // extended header
37
+	TypeXGlobalHeader = 'g'    // global extended header
38
+	TypeGNULongName   = 'L'    // Next file has a long name
39
+	TypeGNULongLink   = 'K'    // Next file symlinks to a file w/ a long name
40
+	TypeGNUSparse     = 'S'    // sparse file
41
+)
42
+
43
+// A Header represents a single header in a tar archive.
44
+// Some fields may not be populated.
45
+type Header struct {
46
+	Name       string    // name of header file entry
47
+	Mode       int64     // permission and mode bits
48
+	Uid        int       // user id of owner
49
+	Gid        int       // group id of owner
50
+	Size       int64     // length in bytes
51
+	ModTime    time.Time // modified time
52
+	Typeflag   byte      // type of header entry
53
+	Linkname   string    // target name of link
54
+	Uname      string    // user name of owner
55
+	Gname      string    // group name of owner
56
+	Devmajor   int64     // major number of character or block device
57
+	Devminor   int64     // minor number of character or block device
58
+	AccessTime time.Time // access time
59
+	ChangeTime time.Time // status change time
60
+	Xattrs     map[string]string
61
+	Winheaders map[string]string
62
+}
63
+
64
+// File name constants from the tar spec.
65
+const (
66
+	fileNameSize       = 100 // Maximum number of bytes in a standard tar name.
67
+	fileNamePrefixSize = 155 // Maximum number of ustar extension bytes.
68
+)
69
+
70
+// FileInfo returns an os.FileInfo for the Header.
71
+func (h *Header) FileInfo() os.FileInfo {
72
+	return headerFileInfo{h}
73
+}
74
+
75
+// headerFileInfo implements os.FileInfo.
76
+type headerFileInfo struct {
77
+	h *Header
78
+}
79
+
80
+func (fi headerFileInfo) Size() int64        { return fi.h.Size }
81
+func (fi headerFileInfo) IsDir() bool        { return fi.Mode().IsDir() }
82
+func (fi headerFileInfo) ModTime() time.Time { return fi.h.ModTime }
83
+func (fi headerFileInfo) Sys() interface{}   { return fi.h }
84
+
85
+// Name returns the base name of the file.
86
+func (fi headerFileInfo) Name() string {
87
+	if fi.IsDir() {
88
+		return path.Base(path.Clean(fi.h.Name))
89
+	}
90
+	return path.Base(fi.h.Name)
91
+}
92
+
93
+// Mode returns the permission and mode bits for the headerFileInfo.
94
+func (fi headerFileInfo) Mode() (mode os.FileMode) {
95
+	// Set file permission bits.
96
+	mode = os.FileMode(fi.h.Mode).Perm()
97
+
98
+	// Set setuid, setgid and sticky bits.
99
+	if fi.h.Mode&c_ISUID != 0 {
100
+		// setuid
101
+		mode |= os.ModeSetuid
102
+	}
103
+	if fi.h.Mode&c_ISGID != 0 {
104
+		// setgid
105
+		mode |= os.ModeSetgid
106
+	}
107
+	if fi.h.Mode&c_ISVTX != 0 {
108
+		// sticky
109
+		mode |= os.ModeSticky
110
+	}
111
+
112
+	// Set file mode bits.
113
+	// clear perm, setuid, setgid and sticky bits.
114
+	m := os.FileMode(fi.h.Mode) &^ 07777
115
+	if m == c_ISDIR {
116
+		// directory
117
+		mode |= os.ModeDir
118
+	}
119
+	if m == c_ISFIFO {
120
+		// named pipe (FIFO)
121
+		mode |= os.ModeNamedPipe
122
+	}
123
+	if m == c_ISLNK {
124
+		// symbolic link
125
+		mode |= os.ModeSymlink
126
+	}
127
+	if m == c_ISBLK {
128
+		// device file
129
+		mode |= os.ModeDevice
130
+	}
131
+	if m == c_ISCHR {
132
+		// Unix character device
133
+		mode |= os.ModeDevice
134
+		mode |= os.ModeCharDevice
135
+	}
136
+	if m == c_ISSOCK {
137
+		// Unix domain socket
138
+		mode |= os.ModeSocket
139
+	}
140
+
141
+	switch fi.h.Typeflag {
142
+	case TypeSymlink:
143
+		// symbolic link
144
+		mode |= os.ModeSymlink
145
+	case TypeChar:
146
+		// character device node
147
+		mode |= os.ModeDevice
148
+		mode |= os.ModeCharDevice
149
+	case TypeBlock:
150
+		// block device node
151
+		mode |= os.ModeDevice
152
+	case TypeDir:
153
+		// directory
154
+		mode |= os.ModeDir
155
+	case TypeFifo:
156
+		// fifo node
157
+		mode |= os.ModeNamedPipe
158
+	}
159
+
160
+	return mode
161
+}
162
+
163
+// sysStat, if non-nil, populates h from system-dependent fields of fi.
164
+var sysStat func(fi os.FileInfo, h *Header) error
165
+
166
+// Mode constants from the tar spec.
167
+const (
168
+	c_ISUID  = 04000   // Set uid
169
+	c_ISGID  = 02000   // Set gid
170
+	c_ISVTX  = 01000   // Save text (sticky bit)
171
+	c_ISDIR  = 040000  // Directory
172
+	c_ISFIFO = 010000  // FIFO
173
+	c_ISREG  = 0100000 // Regular file
174
+	c_ISLNK  = 0120000 // Symbolic link
175
+	c_ISBLK  = 060000  // Block special file
176
+	c_ISCHR  = 020000  // Character special file
177
+	c_ISSOCK = 0140000 // Socket
178
+)
179
+
180
+// Keywords for the PAX Extended Header
181
+const (
182
+	paxAtime    = "atime"
183
+	paxCharset  = "charset"
184
+	paxComment  = "comment"
185
+	paxCtime    = "ctime" // please note that ctime is not a valid pax header.
186
+	paxGid      = "gid"
187
+	paxGname    = "gname"
188
+	paxLinkpath = "linkpath"
189
+	paxMtime    = "mtime"
190
+	paxPath     = "path"
191
+	paxSize     = "size"
192
+	paxUid      = "uid"
193
+	paxUname    = "uname"
194
+	paxXattr    = "SCHILY.xattr."
195
+	paxWindows  = "MSWINDOWS."
196
+	paxNone     = ""
197
+)
198
+
199
+// FileInfoHeader creates a partially-populated Header from fi.
200
+// If fi describes a symlink, FileInfoHeader records link as the link target.
201
+// If fi describes a directory, a slash is appended to the name.
202
+// Because os.FileInfo's Name method returns only the base name of
203
+// the file it describes, it may be necessary to modify the Name field
204
+// of the returned header to provide the full path name of the file.
205
+func FileInfoHeader(fi os.FileInfo, link string) (*Header, error) {
206
+	if fi == nil {
207
+		return nil, errors.New("tar: FileInfo is nil")
208
+	}
209
+	fm := fi.Mode()
210
+	h := &Header{
211
+		Name:    fi.Name(),
212
+		ModTime: fi.ModTime(),
213
+		Mode:    int64(fm.Perm()), // or'd with c_IS* constants later
214
+	}
215
+	switch {
216
+	case fm.IsRegular():
217
+		h.Mode |= c_ISREG
218
+		h.Typeflag = TypeReg
219
+		h.Size = fi.Size()
220
+	case fi.IsDir():
221
+		h.Typeflag = TypeDir
222
+		h.Mode |= c_ISDIR
223
+		h.Name += "/"
224
+	case fm&os.ModeSymlink != 0:
225
+		h.Typeflag = TypeSymlink
226
+		h.Mode |= c_ISLNK
227
+		h.Linkname = link
228
+	case fm&os.ModeDevice != 0:
229
+		if fm&os.ModeCharDevice != 0 {
230
+			h.Mode |= c_ISCHR
231
+			h.Typeflag = TypeChar
232
+		} else {
233
+			h.Mode |= c_ISBLK
234
+			h.Typeflag = TypeBlock
235
+		}
236
+	case fm&os.ModeNamedPipe != 0:
237
+		h.Typeflag = TypeFifo
238
+		h.Mode |= c_ISFIFO
239
+	case fm&os.ModeSocket != 0:
240
+		h.Mode |= c_ISSOCK
241
+	default:
242
+		return nil, fmt.Errorf("archive/tar: unknown file mode %v", fm)
243
+	}
244
+	if fm&os.ModeSetuid != 0 {
245
+		h.Mode |= c_ISUID
246
+	}
247
+	if fm&os.ModeSetgid != 0 {
248
+		h.Mode |= c_ISGID
249
+	}
250
+	if fm&os.ModeSticky != 0 {
251
+		h.Mode |= c_ISVTX
252
+	}
253
+	// If possible, populate additional fields from OS-specific
254
+	// FileInfo fields.
255
+	if sys, ok := fi.Sys().(*Header); ok {
256
+		// This FileInfo came from a Header (not the OS). Use the
257
+		// original Header to populate all remaining fields.
258
+		h.Uid = sys.Uid
259
+		h.Gid = sys.Gid
260
+		h.Uname = sys.Uname
261
+		h.Gname = sys.Gname
262
+		h.AccessTime = sys.AccessTime
263
+		h.ChangeTime = sys.ChangeTime
264
+		if sys.Xattrs != nil {
265
+			h.Xattrs = make(map[string]string)
266
+			for k, v := range sys.Xattrs {
267
+				h.Xattrs[k] = v
268
+			}
269
+		}
270
+		if sys.Typeflag == TypeLink {
271
+			// hard link
272
+			h.Typeflag = TypeLink
273
+			h.Size = 0
274
+			h.Linkname = sys.Linkname
275
+		}
276
+	}
277
+	if sysStat != nil {
278
+		return h, sysStat(fi, h)
279
+	}
280
+	return h, nil
281
+}
282
+
283
+var zeroBlock = make([]byte, blockSize)
284
+
285
+// POSIX specifies a sum of the unsigned byte values, but the Sun tar uses signed byte values.
286
+// We compute and return both.
287
+func checksum(header []byte) (unsigned int64, signed int64) {
288
+	for i := 0; i < len(header); i++ {
289
+		if i == 148 {
290
+			// The chksum field (header[148:156]) is special: it should be treated as space bytes.
291
+			unsigned += ' ' * 8
292
+			signed += ' ' * 8
293
+			i += 7
294
+			continue
295
+		}
296
+		unsigned += int64(header[i])
297
+		signed += int64(int8(header[i]))
298
+	}
299
+	return
300
+}
301
+
302
+type slicer []byte
303
+
304
+func (sp *slicer) next(n int) (b []byte) {
305
+	s := *sp
306
+	b, *sp = s[0:n], s[n:]
307
+	return
308
+}
309
+
310
+func isASCII(s string) bool {
311
+	for _, c := range s {
312
+		if c >= 0x80 {
313
+			return false
314
+		}
315
+	}
316
+	return true
317
+}
318
+
319
+func toASCII(s string) string {
320
+	if isASCII(s) {
321
+		return s
322
+	}
323
+	var buf bytes.Buffer
324
+	for _, c := range s {
325
+		if c < 0x80 {
326
+			buf.WriteByte(byte(c))
327
+		}
328
+	}
329
+	return buf.String()
330
+}
331
+
332
+// isHeaderOnlyType checks if the given type flag is of the type that has no
333
+// data section even if a size is specified.
334
+func isHeaderOnlyType(flag byte) bool {
335
+	switch flag {
336
+	case TypeLink, TypeSymlink, TypeChar, TypeBlock, TypeDir, TypeFifo:
337
+		return true
338
+	default:
339
+		return false
340
+	}
341
+}
0 342
new file mode 100644
... ...
@@ -0,0 +1,996 @@
0
+// Copyright 2009 The Go Authors. All rights reserved.
1
+// Use of this source code is governed by a BSD-style
2
+// license that can be found in the LICENSE file.
3
+
4
+package tar
5
+
6
+// TODO(dsymonds):
7
+//   - pax extensions
8
+
9
+import (
10
+	"bytes"
11
+	"errors"
12
+	"io"
13
+	"io/ioutil"
14
+	"math"
15
+	"os"
16
+	"strconv"
17
+	"strings"
18
+	"time"
19
+)
20
+
21
+var (
22
+	ErrHeader = errors.New("archive/tar: invalid tar header")
23
+)
24
+
25
+const maxNanoSecondIntSize = 9
26
+
27
+// A Reader provides sequential access to the contents of a tar archive.
28
+// A tar archive consists of a sequence of files.
29
+// The Next method advances to the next file in the archive (including the first),
30
+// and then it can be treated as an io.Reader to access the file's data.
31
+type Reader struct {
32
+	r       io.Reader
33
+	err     error
34
+	pad     int64           // amount of padding (ignored) after current file entry
35
+	curr    numBytesReader  // reader for current file entry
36
+	hdrBuff [blockSize]byte // buffer to use in readHeader
37
+}
38
+
39
+type parser struct {
40
+	err error // Last error seen
41
+}
42
+
43
+// A numBytesReader is an io.Reader with a numBytes method, returning the number
44
+// of bytes remaining in the underlying encoded data.
45
+type numBytesReader interface {
46
+	io.Reader
47
+	numBytes() int64
48
+}
49
+
50
+// A regFileReader is a numBytesReader for reading file data from a tar archive.
51
+type regFileReader struct {
52
+	r  io.Reader // underlying reader
53
+	nb int64     // number of unread bytes for current file entry
54
+}
55
+
56
+// A sparseFileReader is a numBytesReader for reading sparse file data from a
57
+// tar archive.
58
+type sparseFileReader struct {
59
+	rfr   numBytesReader // Reads the sparse-encoded file data
60
+	sp    []sparseEntry  // The sparse map for the file
61
+	pos   int64          // Keeps track of file position
62
+	total int64          // Total size of the file
63
+}
64
+
65
+// A sparseEntry holds a single entry in a sparse file's sparse map.
66
+//
67
+// Sparse files are represented using a series of sparseEntrys.
68
+// Despite the name, a sparseEntry represents an actual data fragment that
69
+// references data found in the underlying archive stream. All regions not
70
+// covered by a sparseEntry are logically filled with zeros.
71
+//
72
+// For example, if the underlying raw file contains the 10-byte data:
73
+//	var compactData = "abcdefgh"
74
+//
75
+// And the sparse map has the following entries:
76
+//	var sp = []sparseEntry{
77
+//		{offset: 2,  numBytes: 5} // Data fragment for [2..7]
78
+//		{offset: 18, numBytes: 3} // Data fragment for [18..21]
79
+//	}
80
+//
81
+// Then the content of the resulting sparse file with a "real" size of 25 is:
82
+//	var sparseData = "\x00"*2 + "abcde" + "\x00"*11 + "fgh" + "\x00"*4
83
+type sparseEntry struct {
84
+	offset   int64 // Starting position of the fragment
85
+	numBytes int64 // Length of the fragment
86
+}
87
+
88
+// Keywords for GNU sparse files in a PAX extended header
89
+const (
90
+	paxGNUSparseNumBlocks = "GNU.sparse.numblocks"
91
+	paxGNUSparseOffset    = "GNU.sparse.offset"
92
+	paxGNUSparseNumBytes  = "GNU.sparse.numbytes"
93
+	paxGNUSparseMap       = "GNU.sparse.map"
94
+	paxGNUSparseName      = "GNU.sparse.name"
95
+	paxGNUSparseMajor     = "GNU.sparse.major"
96
+	paxGNUSparseMinor     = "GNU.sparse.minor"
97
+	paxGNUSparseSize      = "GNU.sparse.size"
98
+	paxGNUSparseRealSize  = "GNU.sparse.realsize"
99
+)
100
+
101
+// Keywords for old GNU sparse headers
102
+const (
103
+	oldGNUSparseMainHeaderOffset               = 386
104
+	oldGNUSparseMainHeaderIsExtendedOffset     = 482
105
+	oldGNUSparseMainHeaderNumEntries           = 4
106
+	oldGNUSparseExtendedHeaderIsExtendedOffset = 504
107
+	oldGNUSparseExtendedHeaderNumEntries       = 21
108
+	oldGNUSparseOffsetSize                     = 12
109
+	oldGNUSparseNumBytesSize                   = 12
110
+)
111
+
112
+// NewReader creates a new Reader reading from r.
113
+func NewReader(r io.Reader) *Reader { return &Reader{r: r} }
114
+
115
+// Next advances to the next entry in the tar archive.
116
+//
117
+// io.EOF is returned at the end of the input.
118
+func (tr *Reader) Next() (*Header, error) {
119
+	if tr.err != nil {
120
+		return nil, tr.err
121
+	}
122
+
123
+	var hdr *Header
124
+	var extHdrs map[string]string
125
+
126
+	// Externally, Next iterates through the tar archive as if it is a series of
127
+	// files. Internally, the tar format often uses fake "files" to add meta
128
+	// data that describes the next file. These meta data "files" should not
129
+	// normally be visible to the outside. As such, this loop iterates through
130
+	// one or more "header files" until it finds a "normal file".
131
+loop:
132
+	for {
133
+		tr.err = tr.skipUnread()
134
+		if tr.err != nil {
135
+			return nil, tr.err
136
+		}
137
+
138
+		hdr = tr.readHeader()
139
+		if tr.err != nil {
140
+			return nil, tr.err
141
+		}
142
+
143
+		// Check for PAX/GNU special headers and files.
144
+		switch hdr.Typeflag {
145
+		case TypeXHeader:
146
+			extHdrs, tr.err = parsePAX(tr)
147
+			if tr.err != nil {
148
+				return nil, tr.err
149
+			}
150
+			continue loop // This is a meta header affecting the next header
151
+		case TypeGNULongName, TypeGNULongLink:
152
+			var realname []byte
153
+			realname, tr.err = ioutil.ReadAll(tr)
154
+			if tr.err != nil {
155
+				return nil, tr.err
156
+			}
157
+
158
+			// Convert GNU extensions to use PAX headers.
159
+			if extHdrs == nil {
160
+				extHdrs = make(map[string]string)
161
+			}
162
+			var p parser
163
+			switch hdr.Typeflag {
164
+			case TypeGNULongName:
165
+				extHdrs[paxPath] = p.parseString(realname)
166
+			case TypeGNULongLink:
167
+				extHdrs[paxLinkpath] = p.parseString(realname)
168
+			}
169
+			if p.err != nil {
170
+				tr.err = p.err
171
+				return nil, tr.err
172
+			}
173
+			continue loop // This is a meta header affecting the next header
174
+		default:
175
+			mergePAX(hdr, extHdrs)
176
+
177
+			// Check for a PAX format sparse file
178
+			sp, err := tr.checkForGNUSparsePAXHeaders(hdr, extHdrs)
179
+			if err != nil {
180
+				tr.err = err
181
+				return nil, err
182
+			}
183
+			if sp != nil {
184
+				// Current file is a PAX format GNU sparse file.
185
+				// Set the current file reader to a sparse file reader.
186
+				tr.curr, tr.err = newSparseFileReader(tr.curr, sp, hdr.Size)
187
+				if tr.err != nil {
188
+					return nil, tr.err
189
+				}
190
+			}
191
+			break loop // This is a file, so stop
192
+		}
193
+	}
194
+	return hdr, nil
195
+}
196
+
197
+// checkForGNUSparsePAXHeaders checks the PAX headers for GNU sparse headers. If they are found, then
198
+// this function reads the sparse map and returns it. Unknown sparse formats are ignored, causing the file to
199
+// be treated as a regular file.
200
+func (tr *Reader) checkForGNUSparsePAXHeaders(hdr *Header, headers map[string]string) ([]sparseEntry, error) {
201
+	var sparseFormat string
202
+
203
+	// Check for sparse format indicators
204
+	major, majorOk := headers[paxGNUSparseMajor]
205
+	minor, minorOk := headers[paxGNUSparseMinor]
206
+	sparseName, sparseNameOk := headers[paxGNUSparseName]
207
+	_, sparseMapOk := headers[paxGNUSparseMap]
208
+	sparseSize, sparseSizeOk := headers[paxGNUSparseSize]
209
+	sparseRealSize, sparseRealSizeOk := headers[paxGNUSparseRealSize]
210
+
211
+	// Identify which, if any, sparse format applies from which PAX headers are set
212
+	if majorOk && minorOk {
213
+		sparseFormat = major + "." + minor
214
+	} else if sparseNameOk && sparseMapOk {
215
+		sparseFormat = "0.1"
216
+	} else if sparseSizeOk {
217
+		sparseFormat = "0.0"
218
+	} else {
219
+		// Not a PAX format GNU sparse file.
220
+		return nil, nil
221
+	}
222
+
223
+	// Check for unknown sparse format
224
+	if sparseFormat != "0.0" && sparseFormat != "0.1" && sparseFormat != "1.0" {
225
+		return nil, nil
226
+	}
227
+
228
+	// Update hdr from GNU sparse PAX headers
229
+	if sparseNameOk {
230
+		hdr.Name = sparseName
231
+	}
232
+	if sparseSizeOk {
233
+		realSize, err := strconv.ParseInt(sparseSize, 10, 0)
234
+		if err != nil {
235
+			return nil, ErrHeader
236
+		}
237
+		hdr.Size = realSize
238
+	} else if sparseRealSizeOk {
239
+		realSize, err := strconv.ParseInt(sparseRealSize, 10, 0)
240
+		if err != nil {
241
+			return nil, ErrHeader
242
+		}
243
+		hdr.Size = realSize
244
+	}
245
+
246
+	// Set up the sparse map, according to the particular sparse format in use
247
+	var sp []sparseEntry
248
+	var err error
249
+	switch sparseFormat {
250
+	case "0.0", "0.1":
251
+		sp, err = readGNUSparseMap0x1(headers)
252
+	case "1.0":
253
+		sp, err = readGNUSparseMap1x0(tr.curr)
254
+	}
255
+	return sp, err
256
+}
257
+
258
+// mergePAX merges well known headers according to PAX standard.
259
+// In general headers with the same name as those found
260
+// in the header struct overwrite those found in the header
261
+// struct with higher precision or longer values. Esp. useful
262
+// for name and linkname fields.
263
+func mergePAX(hdr *Header, headers map[string]string) error {
264
+	for k, v := range headers {
265
+		switch k {
266
+		case paxPath:
267
+			hdr.Name = v
268
+		case paxLinkpath:
269
+			hdr.Linkname = v
270
+		case paxGname:
271
+			hdr.Gname = v
272
+		case paxUname:
273
+			hdr.Uname = v
274
+		case paxUid:
275
+			uid, err := strconv.ParseInt(v, 10, 0)
276
+			if err != nil {
277
+				return err
278
+			}
279
+			hdr.Uid = int(uid)
280
+		case paxGid:
281
+			gid, err := strconv.ParseInt(v, 10, 0)
282
+			if err != nil {
283
+				return err
284
+			}
285
+			hdr.Gid = int(gid)
286
+		case paxAtime:
287
+			t, err := parsePAXTime(v)
288
+			if err != nil {
289
+				return err
290
+			}
291
+			hdr.AccessTime = t
292
+		case paxMtime:
293
+			t, err := parsePAXTime(v)
294
+			if err != nil {
295
+				return err
296
+			}
297
+			hdr.ModTime = t
298
+		case paxCtime:
299
+			t, err := parsePAXTime(v)
300
+			if err != nil {
301
+				return err
302
+			}
303
+			hdr.ChangeTime = t
304
+		case paxSize:
305
+			size, err := strconv.ParseInt(v, 10, 0)
306
+			if err != nil {
307
+				return err
308
+			}
309
+			hdr.Size = int64(size)
310
+		default:
311
+			if strings.HasPrefix(k, paxXattr) {
312
+				if hdr.Xattrs == nil {
313
+					hdr.Xattrs = make(map[string]string)
314
+				}
315
+				hdr.Xattrs[k[len(paxXattr):]] = v
316
+			} else if strings.HasPrefix(k, paxWindows) {
317
+				if hdr.Winheaders == nil {
318
+					hdr.Winheaders = make(map[string]string)
319
+				}
320
+				hdr.Winheaders[k[len(paxWindows):]] = v
321
+			}
322
+		}
323
+	}
324
+	return nil
325
+}
326
+
327
+// parsePAXTime takes a string of the form %d.%d as described in
328
+// the PAX specification.
329
+func parsePAXTime(t string) (time.Time, error) {
330
+	buf := []byte(t)
331
+	pos := bytes.IndexByte(buf, '.')
332
+	var seconds, nanoseconds int64
333
+	var err error
334
+	if pos == -1 {
335
+		seconds, err = strconv.ParseInt(t, 10, 0)
336
+		if err != nil {
337
+			return time.Time{}, err
338
+		}
339
+	} else {
340
+		seconds, err = strconv.ParseInt(string(buf[:pos]), 10, 0)
341
+		if err != nil {
342
+			return time.Time{}, err
343
+		}
344
+		nano_buf := string(buf[pos+1:])
345
+		// Pad as needed before converting to a decimal.
346
+		// For example .030 -> .030000000 -> 30000000 nanoseconds
347
+		if len(nano_buf) < maxNanoSecondIntSize {
348
+			// Right pad
349
+			nano_buf += strings.Repeat("0", maxNanoSecondIntSize-len(nano_buf))
350
+		} else if len(nano_buf) > maxNanoSecondIntSize {
351
+			// Right truncate
352
+			nano_buf = nano_buf[:maxNanoSecondIntSize]
353
+		}
354
+		nanoseconds, err = strconv.ParseInt(string(nano_buf), 10, 0)
355
+		if err != nil {
356
+			return time.Time{}, err
357
+		}
358
+	}
359
+	ts := time.Unix(seconds, nanoseconds)
360
+	return ts, nil
361
+}
362
+
363
+// parsePAX parses PAX headers.
364
+// If an extended header (type 'x') is invalid, ErrHeader is returned
365
+func parsePAX(r io.Reader) (map[string]string, error) {
366
+	buf, err := ioutil.ReadAll(r)
367
+	if err != nil {
368
+		return nil, err
369
+	}
370
+	sbuf := string(buf)
371
+
372
+	// For GNU PAX sparse format 0.0 support.
373
+	// This function transforms the sparse format 0.0 headers into sparse format 0.1 headers.
374
+	var sparseMap bytes.Buffer
375
+
376
+	headers := make(map[string]string)
377
+	// Each record is constructed as
378
+	//     "%d %s=%s\n", length, keyword, value
379
+	for len(sbuf) > 0 {
380
+		key, value, residual, err := parsePAXRecord(sbuf)
381
+		if err != nil {
382
+			return nil, ErrHeader
383
+		}
384
+		sbuf = residual
385
+
386
+		keyStr := string(key)
387
+		if keyStr == paxGNUSparseOffset || keyStr == paxGNUSparseNumBytes {
388
+			// GNU sparse format 0.0 special key. Write to sparseMap instead of using the headers map.
389
+			sparseMap.WriteString(value)
390
+			sparseMap.Write([]byte{','})
391
+		} else {
392
+			// Normal key. Set the value in the headers map.
393
+			headers[keyStr] = string(value)
394
+		}
395
+	}
396
+	if sparseMap.Len() != 0 {
397
+		// Add sparse info to headers, chopping off the extra comma
398
+		sparseMap.Truncate(sparseMap.Len() - 1)
399
+		headers[paxGNUSparseMap] = sparseMap.String()
400
+	}
401
+	return headers, nil
402
+}
403
+
404
+// parsePAXRecord parses the input PAX record string into a key-value pair.
405
+// If parsing is successful, it will slice off the currently read record and
406
+// return the remainder as r.
407
+//
408
+// A PAX record is of the following form:
409
+//	"%d %s=%s\n" % (size, key, value)
410
+func parsePAXRecord(s string) (k, v, r string, err error) {
411
+	// The size field ends at the first space.
412
+	sp := strings.IndexByte(s, ' ')
413
+	if sp == -1 {
414
+		return "", "", s, ErrHeader
415
+	}
416
+
417
+	// Parse the first token as a decimal integer.
418
+	n, perr := strconv.ParseInt(s[:sp], 10, 0) // Intentionally parse as native int
419
+	if perr != nil || n < 5 || int64(len(s)) < n {
420
+		return "", "", s, ErrHeader
421
+	}
422
+
423
+	// Extract everything between the space and the final newline.
424
+	rec, nl, rem := s[sp+1:n-1], s[n-1:n], s[n:]
425
+	if nl != "\n" {
426
+		return "", "", s, ErrHeader
427
+	}
428
+
429
+	// The first equals separates the key from the value.
430
+	eq := strings.IndexByte(rec, '=')
431
+	if eq == -1 {
432
+		return "", "", s, ErrHeader
433
+	}
434
+	return rec[:eq], rec[eq+1:], rem, nil
435
+}
436
+
437
+// parseString parses bytes as a NUL-terminated C-style string.
438
+// If a NUL byte is not found then the whole slice is returned as a string.
439
+func (*parser) parseString(b []byte) string {
440
+	n := 0
441
+	for n < len(b) && b[n] != 0 {
442
+		n++
443
+	}
444
+	return string(b[0:n])
445
+}
446
+
447
+// parseNumeric parses the input as being encoded in either base-256 or octal.
448
+// This function may return negative numbers.
449
+// If parsing fails or an integer overflow occurs, err will be set.
450
+func (p *parser) parseNumeric(b []byte) int64 {
451
+	// Check for base-256 (binary) format first.
452
+	// If the first bit is set, then all following bits constitute a two's
453
+	// complement encoded number in big-endian byte order.
454
+	if len(b) > 0 && b[0]&0x80 != 0 {
455
+		// Handling negative numbers relies on the following identity:
456
+		//	-a-1 == ^a
457
+		//
458
+		// If the number is negative, we use an inversion mask to invert the
459
+		// data bytes and treat the value as an unsigned number.
460
+		var inv byte // 0x00 if positive or zero, 0xff if negative
461
+		if b[0]&0x40 != 0 {
462
+			inv = 0xff
463
+		}
464
+
465
+		var x uint64
466
+		for i, c := range b {
467
+			c ^= inv // Inverts c only if inv is 0xff, otherwise does nothing
468
+			if i == 0 {
469
+				c &= 0x7f // Ignore signal bit in first byte
470
+			}
471
+			if (x >> 56) > 0 {
472
+				p.err = ErrHeader // Integer overflow
473
+				return 0
474
+			}
475
+			x = x<<8 | uint64(c)
476
+		}
477
+		if (x >> 63) > 0 {
478
+			p.err = ErrHeader // Integer overflow
479
+			return 0
480
+		}
481
+		if inv == 0xff {
482
+			return ^int64(x)
483
+		}
484
+		return int64(x)
485
+	}
486
+
487
+	// Normal case is base-8 (octal) format.
488
+	return p.parseOctal(b)
489
+}
490
+
491
+func (p *parser) parseOctal(b []byte) int64 {
492
+	// Because unused fields are filled with NULs, we need
493
+	// to skip leading NULs. Fields may also be padded with
494
+	// spaces or NULs.
495
+	// So we remove leading and trailing NULs and spaces to
496
+	// be sure.
497
+	b = bytes.Trim(b, " \x00")
498
+
499
+	if len(b) == 0 {
500
+		return 0
501
+	}
502
+	x, perr := strconv.ParseUint(p.parseString(b), 8, 64)
503
+	if perr != nil {
504
+		p.err = ErrHeader
505
+	}
506
+	return int64(x)
507
+}
508
+
509
+// skipUnread skips any unread bytes in the existing file entry, as well as any
510
+// alignment padding. It returns io.ErrUnexpectedEOF if any io.EOF is
511
+// encountered in the data portion; it is okay to hit io.EOF in the padding.
512
+//
513
+// Note that this function still works properly even when sparse files are being
514
+// used since numBytes returns the bytes remaining in the underlying io.Reader.
515
+func (tr *Reader) skipUnread() error {
516
+	dataSkip := tr.numBytes()      // Number of data bytes to skip
517
+	totalSkip := dataSkip + tr.pad // Total number of bytes to skip
518
+	tr.curr, tr.pad = nil, 0
519
+
520
+	// If possible, Seek to the last byte before the end of the data section.
521
+	// Do this because Seek is often lazy about reporting errors; this will mask
522
+	// the fact that the tar stream may be truncated. We can rely on the
523
+	// io.CopyN done shortly afterwards to trigger any IO errors.
524
+	var seekSkipped int64 // Number of bytes skipped via Seek
525
+	if sr, ok := tr.r.(io.Seeker); ok && dataSkip > 1 {
526
+		// Not all io.Seeker can actually Seek. For example, os.Stdin implements
527
+		// io.Seeker, but calling Seek always returns an error and performs
528
+		// no action. Thus, we try an innocent seek to the current position
529
+		// to see if Seek is really supported.
530
+		pos1, err := sr.Seek(0, os.SEEK_CUR)
531
+		if err == nil {
532
+			// Seek seems supported, so perform the real Seek.
533
+			pos2, err := sr.Seek(dataSkip-1, os.SEEK_CUR)
534
+			if err != nil {
535
+				tr.err = err
536
+				return tr.err
537
+			}
538
+			seekSkipped = pos2 - pos1
539
+		}
540
+	}
541
+
542
+	var copySkipped int64 // Number of bytes skipped via CopyN
543
+	copySkipped, tr.err = io.CopyN(ioutil.Discard, tr.r, totalSkip-seekSkipped)
544
+	if tr.err == io.EOF && seekSkipped+copySkipped < dataSkip {
545
+		tr.err = io.ErrUnexpectedEOF
546
+	}
547
+	return tr.err
548
+}
549
+
550
+func (tr *Reader) verifyChecksum(header []byte) bool {
551
+	if tr.err != nil {
552
+		return false
553
+	}
554
+
555
+	var p parser
556
+	given := p.parseOctal(header[148:156])
557
+	unsigned, signed := checksum(header)
558
+	return p.err == nil && (given == unsigned || given == signed)
559
+}
560
+
561
+// readHeader reads the next block header and assumes that the underlying reader
562
+// is already aligned to a block boundary.
563
+//
564
+// The err will be set to io.EOF only when one of the following occurs:
565
+//	* Exactly 0 bytes are read and EOF is hit.
566
+//	* Exactly 1 block of zeros is read and EOF is hit.
567
+//	* At least 2 blocks of zeros are read.
568
+func (tr *Reader) readHeader() *Header {
569
+	header := tr.hdrBuff[:]
570
+	copy(header, zeroBlock)
571
+
572
+	if _, tr.err = io.ReadFull(tr.r, header); tr.err != nil {
573
+		return nil // io.EOF is okay here
574
+	}
575
+
576
+	// Two blocks of zero bytes marks the end of the archive.
577
+	if bytes.Equal(header, zeroBlock[0:blockSize]) {
578
+		if _, tr.err = io.ReadFull(tr.r, header); tr.err != nil {
579
+			return nil // io.EOF is okay here
580
+		}
581
+		if bytes.Equal(header, zeroBlock[0:blockSize]) {
582
+			tr.err = io.EOF
583
+		} else {
584
+			tr.err = ErrHeader // zero block and then non-zero block
585
+		}
586
+		return nil
587
+	}
588
+
589
+	if !tr.verifyChecksum(header) {
590
+		tr.err = ErrHeader
591
+		return nil
592
+	}
593
+
594
+	// Unpack
595
+	var p parser
596
+	hdr := new(Header)
597
+	s := slicer(header)
598
+
599
+	hdr.Name = p.parseString(s.next(100))
600
+	hdr.Mode = p.parseNumeric(s.next(8))
601
+	hdr.Uid = int(p.parseNumeric(s.next(8)))
602
+	hdr.Gid = int(p.parseNumeric(s.next(8)))
603
+	hdr.Size = p.parseNumeric(s.next(12))
604
+	hdr.ModTime = time.Unix(p.parseNumeric(s.next(12)), 0)
605
+	s.next(8) // chksum
606
+	hdr.Typeflag = s.next(1)[0]
607
+	hdr.Linkname = p.parseString(s.next(100))
608
+
609
+	// The remainder of the header depends on the value of magic.
610
+	// The original (v7) version of tar had no explicit magic field,
611
+	// so its magic bytes, like the rest of the block, are NULs.
612
+	magic := string(s.next(8)) // contains version field as well.
613
+	var format string
614
+	switch {
615
+	case magic[:6] == "ustar\x00": // POSIX tar (1003.1-1988)
616
+		if string(header[508:512]) == "tar\x00" {
617
+			format = "star"
618
+		} else {
619
+			format = "posix"
620
+		}
621
+	case magic == "ustar  \x00": // old GNU tar
622
+		format = "gnu"
623
+	}
624
+
625
+	switch format {
626
+	case "posix", "gnu", "star":
627
+		hdr.Uname = p.parseString(s.next(32))
628
+		hdr.Gname = p.parseString(s.next(32))
629
+		devmajor := s.next(8)
630
+		devminor := s.next(8)
631
+		if hdr.Typeflag == TypeChar || hdr.Typeflag == TypeBlock {
632
+			hdr.Devmajor = p.parseNumeric(devmajor)
633
+			hdr.Devminor = p.parseNumeric(devminor)
634
+		}
635
+		var prefix string
636
+		switch format {
637
+		case "posix", "gnu":
638
+			prefix = p.parseString(s.next(155))
639
+		case "star":
640
+			prefix = p.parseString(s.next(131))
641
+			hdr.AccessTime = time.Unix(p.parseNumeric(s.next(12)), 0)
642
+			hdr.ChangeTime = time.Unix(p.parseNumeric(s.next(12)), 0)
643
+		}
644
+		if len(prefix) > 0 {
645
+			hdr.Name = prefix + "/" + hdr.Name
646
+		}
647
+	}
648
+
649
+	if p.err != nil {
650
+		tr.err = p.err
651
+		return nil
652
+	}
653
+
654
+	nb := hdr.Size
655
+	if isHeaderOnlyType(hdr.Typeflag) {
656
+		nb = 0
657
+	}
658
+	if nb < 0 {
659
+		tr.err = ErrHeader
660
+		return nil
661
+	}
662
+
663
+	// Set the current file reader.
664
+	tr.pad = -nb & (blockSize - 1) // blockSize is a power of two
665
+	tr.curr = &regFileReader{r: tr.r, nb: nb}
666
+
667
+	// Check for old GNU sparse format entry.
668
+	if hdr.Typeflag == TypeGNUSparse {
669
+		// Get the real size of the file.
670
+		hdr.Size = p.parseNumeric(header[483:495])
671
+		if p.err != nil {
672
+			tr.err = p.err
673
+			return nil
674
+		}
675
+
676
+		// Read the sparse map.
677
+		sp := tr.readOldGNUSparseMap(header)
678
+		if tr.err != nil {
679
+			return nil
680
+		}
681
+
682
+		// Current file is a GNU sparse file. Update the current file reader.
683
+		tr.curr, tr.err = newSparseFileReader(tr.curr, sp, hdr.Size)
684
+		if tr.err != nil {
685
+			return nil
686
+		}
687
+	}
688
+
689
+	return hdr
690
+}
691
+
692
+// readOldGNUSparseMap reads the sparse map as stored in the old GNU sparse format.
693
+// The sparse map is stored in the tar header if it's small enough. If it's larger than four entries,
694
+// then one or more extension headers are used to store the rest of the sparse map.
695
+func (tr *Reader) readOldGNUSparseMap(header []byte) []sparseEntry {
696
+	var p parser
697
+	isExtended := header[oldGNUSparseMainHeaderIsExtendedOffset] != 0
698
+	spCap := oldGNUSparseMainHeaderNumEntries
699
+	if isExtended {
700
+		spCap += oldGNUSparseExtendedHeaderNumEntries
701
+	}
702
+	sp := make([]sparseEntry, 0, spCap)
703
+	s := slicer(header[oldGNUSparseMainHeaderOffset:])
704
+
705
+	// Read the four entries from the main tar header
706
+	for i := 0; i < oldGNUSparseMainHeaderNumEntries; i++ {
707
+		offset := p.parseNumeric(s.next(oldGNUSparseOffsetSize))
708
+		numBytes := p.parseNumeric(s.next(oldGNUSparseNumBytesSize))
709
+		if p.err != nil {
710
+			tr.err = p.err
711
+			return nil
712
+		}
713
+		if offset == 0 && numBytes == 0 {
714
+			break
715
+		}
716
+		sp = append(sp, sparseEntry{offset: offset, numBytes: numBytes})
717
+	}
718
+
719
+	for isExtended {
720
+		// There are more entries. Read an extension header and parse its entries.
721
+		sparseHeader := make([]byte, blockSize)
722
+		if _, tr.err = io.ReadFull(tr.r, sparseHeader); tr.err != nil {
723
+			return nil
724
+		}
725
+		isExtended = sparseHeader[oldGNUSparseExtendedHeaderIsExtendedOffset] != 0
726
+		s = slicer(sparseHeader)
727
+		for i := 0; i < oldGNUSparseExtendedHeaderNumEntries; i++ {
728
+			offset := p.parseNumeric(s.next(oldGNUSparseOffsetSize))
729
+			numBytes := p.parseNumeric(s.next(oldGNUSparseNumBytesSize))
730
+			if p.err != nil {
731
+				tr.err = p.err
732
+				return nil
733
+			}
734
+			if offset == 0 && numBytes == 0 {
735
+				break
736
+			}
737
+			sp = append(sp, sparseEntry{offset: offset, numBytes: numBytes})
738
+		}
739
+	}
740
+	return sp
741
+}
742
+
743
+// readGNUSparseMap1x0 reads the sparse map as stored in GNU's PAX sparse format
744
+// version 1.0. The format of the sparse map consists of a series of
745
+// newline-terminated numeric fields. The first field is the number of entries
746
+// and is always present. Following this are the entries, consisting of two
747
+// fields (offset, numBytes). This function must stop reading at the end
748
+// boundary of the block containing the last newline.
749
+//
750
+// Note that the GNU manual says that numeric values should be encoded in octal
751
+// format. However, the GNU tar utility itself outputs these values in decimal.
752
+// As such, this library treats values as being encoded in decimal.
753
+func readGNUSparseMap1x0(r io.Reader) ([]sparseEntry, error) {
754
+	var cntNewline int64
755
+	var buf bytes.Buffer
756
+	var blk = make([]byte, blockSize)
757
+
758
+	// feedTokens copies data in numBlock chunks from r into buf until there are
759
+	// at least cnt newlines in buf. It will not read more blocks than needed.
760
+	var feedTokens = func(cnt int64) error {
761
+		for cntNewline < cnt {
762
+			if _, err := io.ReadFull(r, blk); err != nil {
763
+				if err == io.EOF {
764
+					err = io.ErrUnexpectedEOF
765
+				}
766
+				return err
767
+			}
768
+			buf.Write(blk)
769
+			for _, c := range blk {
770
+				if c == '\n' {
771
+					cntNewline++
772
+				}
773
+			}
774
+		}
775
+		return nil
776
+	}
777
+
778
+	// nextToken gets the next token delimited by a newline. This assumes that
779
+	// at least one newline exists in the buffer.
780
+	var nextToken = func() string {
781
+		cntNewline--
782
+		tok, _ := buf.ReadString('\n')
783
+		return tok[:len(tok)-1] // Cut off newline
784
+	}
785
+
786
+	// Parse for the number of entries.
787
+	// Use integer overflow resistant math to check this.
788
+	if err := feedTokens(1); err != nil {
789
+		return nil, err
790
+	}
791
+	numEntries, err := strconv.ParseInt(nextToken(), 10, 0) // Intentionally parse as native int
792
+	if err != nil || numEntries < 0 || int(2*numEntries) < int(numEntries) {
793
+		return nil, ErrHeader
794
+	}
795
+
796
+	// Parse for all member entries.
797
+	// numEntries is trusted after this since a potential attacker must have
798
+	// committed resources proportional to what this library used.
799
+	if err := feedTokens(2 * numEntries); err != nil {
800
+		return nil, err
801
+	}
802
+	sp := make([]sparseEntry, 0, numEntries)
803
+	for i := int64(0); i < numEntries; i++ {
804
+		offset, err := strconv.ParseInt(nextToken(), 10, 64)
805
+		if err != nil {
806
+			return nil, ErrHeader
807
+		}
808
+		numBytes, err := strconv.ParseInt(nextToken(), 10, 64)
809
+		if err != nil {
810
+			return nil, ErrHeader
811
+		}
812
+		sp = append(sp, sparseEntry{offset: offset, numBytes: numBytes})
813
+	}
814
+	return sp, nil
815
+}
816
+
817
+// readGNUSparseMap0x1 reads the sparse map as stored in GNU's PAX sparse format
818
+// version 0.1. The sparse map is stored in the PAX headers.
819
+func readGNUSparseMap0x1(extHdrs map[string]string) ([]sparseEntry, error) {
820
+	// Get number of entries.
821
+	// Use integer overflow resistant math to check this.
822
+	numEntriesStr := extHdrs[paxGNUSparseNumBlocks]
823
+	numEntries, err := strconv.ParseInt(numEntriesStr, 10, 0) // Intentionally parse as native int
824
+	if err != nil || numEntries < 0 || int(2*numEntries) < int(numEntries) {
825
+		return nil, ErrHeader
826
+	}
827
+
828
+	// There should be two numbers in sparseMap for each entry.
829
+	sparseMap := strings.Split(extHdrs[paxGNUSparseMap], ",")
830
+	if int64(len(sparseMap)) != 2*numEntries {
831
+		return nil, ErrHeader
832
+	}
833
+
834
+	// Loop through the entries in the sparse map.
835
+	// numEntries is trusted now.
836
+	sp := make([]sparseEntry, 0, numEntries)
837
+	for i := int64(0); i < numEntries; i++ {
838
+		offset, err := strconv.ParseInt(sparseMap[2*i], 10, 64)
839
+		if err != nil {
840
+			return nil, ErrHeader
841
+		}
842
+		numBytes, err := strconv.ParseInt(sparseMap[2*i+1], 10, 64)
843
+		if err != nil {
844
+			return nil, ErrHeader
845
+		}
846
+		sp = append(sp, sparseEntry{offset: offset, numBytes: numBytes})
847
+	}
848
+	return sp, nil
849
+}
850
+
851
+// numBytes returns the number of bytes left to read in the current file's entry
852
+// in the tar archive, or 0 if there is no current file.
853
+func (tr *Reader) numBytes() int64 {
854
+	if tr.curr == nil {
855
+		// No current file, so no bytes
856
+		return 0
857
+	}
858
+	return tr.curr.numBytes()
859
+}
860
+
861
+// Read reads from the current entry in the tar archive.
862
+// It returns 0, io.EOF when it reaches the end of that entry,
863
+// until Next is called to advance to the next entry.
864
+//
865
+// Calling Read on special types like TypeLink, TypeSymLink, TypeChar,
866
+// TypeBlock, TypeDir, and TypeFifo returns 0, io.EOF regardless of what
867
+// the Header.Size claims.
868
+func (tr *Reader) Read(b []byte) (n int, err error) {
869
+	if tr.err != nil {
870
+		return 0, tr.err
871
+	}
872
+	if tr.curr == nil {
873
+		return 0, io.EOF
874
+	}
875
+
876
+	n, err = tr.curr.Read(b)
877
+	if err != nil && err != io.EOF {
878
+		tr.err = err
879
+	}
880
+	return
881
+}
882
+
883
+func (rfr *regFileReader) Read(b []byte) (n int, err error) {
884
+	if rfr.nb == 0 {
885
+		// file consumed
886
+		return 0, io.EOF
887
+	}
888
+	if int64(len(b)) > rfr.nb {
889
+		b = b[0:rfr.nb]
890
+	}
891
+	n, err = rfr.r.Read(b)
892
+	rfr.nb -= int64(n)
893
+
894
+	if err == io.EOF && rfr.nb > 0 {
895
+		err = io.ErrUnexpectedEOF
896
+	}
897
+	return
898
+}
899
+
900
+// numBytes returns the number of bytes left to read in the file's data in the tar archive.
901
+func (rfr *regFileReader) numBytes() int64 {
902
+	return rfr.nb
903
+}
904
+
905
+// newSparseFileReader creates a new sparseFileReader, but validates all of the
906
+// sparse entries before doing so.
907
+func newSparseFileReader(rfr numBytesReader, sp []sparseEntry, total int64) (*sparseFileReader, error) {
908
+	if total < 0 {
909
+		return nil, ErrHeader // Total size cannot be negative
910
+	}
911
+
912
+	// Validate all sparse entries. These are the same checks as performed by
913
+	// the BSD tar utility.
914
+	for i, s := range sp {
915
+		switch {
916
+		case s.offset < 0 || s.numBytes < 0:
917
+			return nil, ErrHeader // Negative values are never okay
918
+		case s.offset > math.MaxInt64-s.numBytes:
919
+			return nil, ErrHeader // Integer overflow with large length
920
+		case s.offset+s.numBytes > total:
921
+			return nil, ErrHeader // Region extends beyond the "real" size
922
+		case i > 0 && sp[i-1].offset+sp[i-1].numBytes > s.offset:
923
+			return nil, ErrHeader // Regions can't overlap and must be in order
924
+		}
925
+	}
926
+	return &sparseFileReader{rfr: rfr, sp: sp, total: total}, nil
927
+}
928
+
929
+// readHole reads a sparse hole ending at endOffset.
930
+func (sfr *sparseFileReader) readHole(b []byte, endOffset int64) int {
931
+	n64 := endOffset - sfr.pos
932
+	if n64 > int64(len(b)) {
933
+		n64 = int64(len(b))
934
+	}
935
+	n := int(n64)
936
+	for i := 0; i < n; i++ {
937
+		b[i] = 0
938
+	}
939
+	sfr.pos += n64
940
+	return n
941
+}
942
+
943
+// Read reads the sparse file data in expanded form.
944
+func (sfr *sparseFileReader) Read(b []byte) (n int, err error) {
945
+	// Skip past all empty fragments.
946
+	for len(sfr.sp) > 0 && sfr.sp[0].numBytes == 0 {
947
+		sfr.sp = sfr.sp[1:]
948
+	}
949
+
950
+	// If there are no more fragments, then it is possible that there
951
+	// is one last sparse hole.
952
+	if len(sfr.sp) == 0 {
953
+		// This behavior matches the BSD tar utility.
954
+		// However, GNU tar stops returning data even if sfr.total is unmet.
955
+		if sfr.pos < sfr.total {
956
+			return sfr.readHole(b, sfr.total), nil
957
+		}
958
+		return 0, io.EOF
959
+	}
960
+
961
+	// In front of a data fragment, so read a hole.
962
+	if sfr.pos < sfr.sp[0].offset {
963
+		return sfr.readHole(b, sfr.sp[0].offset), nil
964
+	}
965
+
966
+	// In a data fragment, so read from it.
967
+	// This math is overflow free since we verify that offset and numBytes can
968
+	// be safely added when creating the sparseFileReader.
969
+	endPos := sfr.sp[0].offset + sfr.sp[0].numBytes // End offset of fragment
970
+	bytesLeft := endPos - sfr.pos                   // Bytes left in fragment
971
+	if int64(len(b)) > bytesLeft {
972
+		b = b[:bytesLeft]
973
+	}
974
+
975
+	n, err = sfr.rfr.Read(b)
976
+	sfr.pos += int64(n)
977
+	if err == io.EOF {
978
+		if sfr.pos < endPos {
979
+			err = io.ErrUnexpectedEOF // There was supposed to be more data
980
+		} else if sfr.pos < sfr.total {
981
+			err = nil // There is still an implicit sparse hole at the end
982
+		}
983
+	}
984
+
985
+	if sfr.pos == endPos {
986
+		sfr.sp = sfr.sp[1:] // We are done with this fragment, so pop it
987
+	}
988
+	return n, err
989
+}
990
+
991
+// numBytes returns the number of bytes left to read in the sparse file's
992
+// sparse-encoded data in the tar archive.
993
+func (sfr *sparseFileReader) numBytes() int64 {
994
+	return sfr.rfr.numBytes()
995
+}
0 996
new file mode 100644
... ...
@@ -0,0 +1,20 @@
0
+// Copyright 2012 The Go Authors. All rights reserved.
1
+// Use of this source code is governed by a BSD-style
2
+// license that can be found in the LICENSE file.
3
+
4
+// +build linux dragonfly openbsd solaris
5
+
6
+package tar
7
+
8
+import (
9
+	"syscall"
10
+	"time"
11
+)
12
+
13
+func statAtime(st *syscall.Stat_t) time.Time {
14
+	return time.Unix(st.Atim.Unix())
15
+}
16
+
17
+func statCtime(st *syscall.Stat_t) time.Time {
18
+	return time.Unix(st.Ctim.Unix())
19
+}
0 20
new file mode 100644
... ...
@@ -0,0 +1,20 @@
0
+// Copyright 2012 The Go Authors. All rights reserved.
1
+// Use of this source code is governed by a BSD-style
2
+// license that can be found in the LICENSE file.
3
+
4
+// +build darwin freebsd netbsd
5
+
6
+package tar
7
+
8
+import (
9
+	"syscall"
10
+	"time"
11
+)
12
+
13
+func statAtime(st *syscall.Stat_t) time.Time {
14
+	return time.Unix(st.Atimespec.Unix())
15
+}
16
+
17
+func statCtime(st *syscall.Stat_t) time.Time {
18
+	return time.Unix(st.Ctimespec.Unix())
19
+}
0 20
new file mode 100644
... ...
@@ -0,0 +1,32 @@
0
+// Copyright 2012 The Go Authors. All rights reserved.
1
+// Use of this source code is governed by a BSD-style
2
+// license that can be found in the LICENSE file.
3
+
4
+// +build linux darwin dragonfly freebsd openbsd netbsd solaris
5
+
6
+package tar
7
+
8
+import (
9
+	"os"
10
+	"syscall"
11
+)
12
+
13
+func init() {
14
+	sysStat = statUnix
15
+}
16
+
17
+func statUnix(fi os.FileInfo, h *Header) error {
18
+	sys, ok := fi.Sys().(*syscall.Stat_t)
19
+	if !ok {
20
+		return nil
21
+	}
22
+	h.Uid = int(sys.Uid)
23
+	h.Gid = int(sys.Gid)
24
+	// TODO(bradfitz): populate username & group.  os/user
25
+	// doesn't cache LookupId lookups, and lacks group
26
+	// lookup functions.
27
+	h.AccessTime = statAtime(sys)
28
+	h.ChangeTime = statCtime(sys)
29
+	// TODO(bradfitz): major/minor device numbers?
30
+	return nil
31
+}
0 32
new file mode 100644
... ...
@@ -0,0 +1,419 @@
0
+// Copyright 2009 The Go Authors. All rights reserved.
1
+// Use of this source code is governed by a BSD-style
2
+// license that can be found in the LICENSE file.
3
+
4
+package tar
5
+
6
+// TODO(dsymonds):
7
+// - catch more errors (no first header, etc.)
8
+
9
+import (
10
+	"bytes"
11
+	"errors"
12
+	"fmt"
13
+	"io"
14
+	"path"
15
+	"sort"
16
+	"strconv"
17
+	"strings"
18
+	"time"
19
+)
20
+
21
+var (
22
+	ErrWriteTooLong    = errors.New("archive/tar: write too long")
23
+	ErrFieldTooLong    = errors.New("archive/tar: header field too long")
24
+	ErrWriteAfterClose = errors.New("archive/tar: write after close")
25
+	errInvalidHeader   = errors.New("archive/tar: header field too long or contains invalid values")
26
+)
27
+
28
+// A Writer provides sequential writing of a tar archive in POSIX.1 format.
29
+// A tar archive consists of a sequence of files.
30
+// Call WriteHeader to begin a new file, and then call Write to supply that file's data,
31
+// writing at most hdr.Size bytes in total.
32
+type Writer struct {
33
+	w          io.Writer
34
+	err        error
35
+	nb         int64 // number of unwritten bytes for current file entry
36
+	pad        int64 // amount of padding to write after current file entry
37
+	closed     bool
38
+	usedBinary bool            // whether the binary numeric field extension was used
39
+	preferPax  bool            // use pax header instead of binary numeric header
40
+	hdrBuff    [blockSize]byte // buffer to use in writeHeader when writing a regular header
41
+	paxHdrBuff [blockSize]byte // buffer to use in writeHeader when writing a pax header
42
+}
43
+
44
+type formatter struct {
45
+	err error // Last error seen
46
+}
47
+
48
+// NewWriter creates a new Writer writing to w.
49
+func NewWriter(w io.Writer) *Writer { return &Writer{w: w} }
50
+
51
+// Flush finishes writing the current file (optional).
52
+func (tw *Writer) Flush() error {
53
+	if tw.nb > 0 {
54
+		tw.err = fmt.Errorf("archive/tar: missed writing %d bytes", tw.nb)
55
+		return tw.err
56
+	}
57
+
58
+	n := tw.nb + tw.pad
59
+	for n > 0 && tw.err == nil {
60
+		nr := n
61
+		if nr > blockSize {
62
+			nr = blockSize
63
+		}
64
+		var nw int
65
+		nw, tw.err = tw.w.Write(zeroBlock[0:nr])
66
+		n -= int64(nw)
67
+	}
68
+	tw.nb = 0
69
+	tw.pad = 0
70
+	return tw.err
71
+}
72
+
73
+// Write s into b, terminating it with a NUL if there is room.
74
+func (f *formatter) formatString(b []byte, s string) {
75
+	if len(s) > len(b) {
76
+		f.err = ErrFieldTooLong
77
+		return
78
+	}
79
+	ascii := toASCII(s)
80
+	copy(b, ascii)
81
+	if len(ascii) < len(b) {
82
+		b[len(ascii)] = 0
83
+	}
84
+}
85
+
86
+// Encode x as an octal ASCII string and write it into b with leading zeros.
87
+func (f *formatter) formatOctal(b []byte, x int64) {
88
+	s := strconv.FormatInt(x, 8)
89
+	// leading zeros, but leave room for a NUL.
90
+	for len(s)+1 < len(b) {
91
+		s = "0" + s
92
+	}
93
+	f.formatString(b, s)
94
+}
95
+
96
+// fitsInBase256 reports whether x can be encoded into n bytes using base-256
97
+// encoding. Unlike octal encoding, base-256 encoding does not require that the
98
+// string ends with a NUL character. Thus, all n bytes are available for output.
99
+//
100
+// If operating in binary mode, this assumes strict GNU binary mode; which means
101
+// that the first byte can only be either 0x80 or 0xff. Thus, the first byte is
102
+// equivalent to the sign bit in two's complement form.
103
+func fitsInBase256(n int, x int64) bool {
104
+	var binBits = uint(n-1) * 8
105
+	return n >= 9 || (x >= -1<<binBits && x < 1<<binBits)
106
+}
107
+
108
+// Write x into b, as binary (GNUtar/star extension).
109
+func (f *formatter) formatNumeric(b []byte, x int64) {
110
+	if fitsInBase256(len(b), x) {
111
+		for i := len(b) - 1; i >= 0; i-- {
112
+			b[i] = byte(x)
113
+			x >>= 8
114
+		}
115
+		b[0] |= 0x80 // Highest bit indicates binary format
116
+		return
117
+	}
118
+
119
+	f.formatOctal(b, 0) // Last resort, just write zero
120
+	f.err = ErrFieldTooLong
121
+}
122
+
123
+var (
124
+	minTime = time.Unix(0, 0)
125
+	// There is room for 11 octal digits (33 bits) of mtime.
126
+	maxTime = minTime.Add((1<<33 - 1) * time.Second)
127
+)
128
+
129
+// WriteHeader writes hdr and prepares to accept the file's contents.
130
+// WriteHeader calls Flush if it is not the first header.
131
+// Calling after a Close will return ErrWriteAfterClose.
132
+func (tw *Writer) WriteHeader(hdr *Header) error {
133
+	return tw.writeHeader(hdr, true)
134
+}
135
+
136
+// WriteHeader writes hdr and prepares to accept the file's contents.
137
+// WriteHeader calls Flush if it is not the first header.
138
+// Calling after a Close will return ErrWriteAfterClose.
139
+// As this method is called internally by writePax header to allow it to
140
+// suppress writing the pax header.
141
+func (tw *Writer) writeHeader(hdr *Header, allowPax bool) error {
142
+	if tw.closed {
143
+		return ErrWriteAfterClose
144
+	}
145
+	if tw.err == nil {
146
+		tw.Flush()
147
+	}
148
+	if tw.err != nil {
149
+		return tw.err
150
+	}
151
+
152
+	// a map to hold pax header records, if any are needed
153
+	paxHeaders := make(map[string]string)
154
+
155
+	// TODO(shanemhansen): we might want to use PAX headers for
156
+	// subsecond time resolution, but for now let's just capture
157
+	// too long fields or non ascii characters
158
+
159
+	var f formatter
160
+	var header []byte
161
+
162
+	// We need to select which scratch buffer to use carefully,
163
+	// since this method is called recursively to write PAX headers.
164
+	// If allowPax is true, this is the non-recursive call, and we will use hdrBuff.
165
+	// If allowPax is false, we are being called by writePAXHeader, and hdrBuff is
166
+	// already being used by the non-recursive call, so we must use paxHdrBuff.
167
+	header = tw.hdrBuff[:]
168
+	if !allowPax {
169
+		header = tw.paxHdrBuff[:]
170
+	}
171
+	copy(header, zeroBlock)
172
+	s := slicer(header)
173
+
174
+	// Wrappers around formatter that automatically sets paxHeaders if the
175
+	// argument extends beyond the capacity of the input byte slice.
176
+	var formatString = func(b []byte, s string, paxKeyword string) {
177
+		needsPaxHeader := paxKeyword != paxNone && len(s) > len(b) || !isASCII(s)
178
+		if needsPaxHeader {
179
+			paxHeaders[paxKeyword] = s
180
+			return
181
+		}
182
+		f.formatString(b, s)
183
+	}
184
+	var formatNumeric = func(b []byte, x int64, paxKeyword string) {
185
+		// Try octal first.
186
+		s := strconv.FormatInt(x, 8)
187
+		if len(s) < len(b) {
188
+			f.formatOctal(b, x)
189
+			return
190
+		}
191
+
192
+		// If it is too long for octal, and PAX is preferred, use a PAX header.
193
+		if paxKeyword != paxNone && tw.preferPax {
194
+			f.formatOctal(b, 0)
195
+			s := strconv.FormatInt(x, 10)
196
+			paxHeaders[paxKeyword] = s
197
+			return
198
+		}
199
+
200
+		tw.usedBinary = true
201
+		f.formatNumeric(b, x)
202
+	}
203
+
204
+	// keep a reference to the filename to allow to overwrite it later if we detect that we can use ustar longnames instead of pax
205
+	pathHeaderBytes := s.next(fileNameSize)
206
+
207
+	formatString(pathHeaderBytes, hdr.Name, paxPath)
208
+
209
+	// Handle out of range ModTime carefully.
210
+	var modTime int64
211
+	if !hdr.ModTime.Before(minTime) && !hdr.ModTime.After(maxTime) {
212
+		modTime = hdr.ModTime.Unix()
213
+	}
214
+
215
+	f.formatOctal(s.next(8), hdr.Mode)               // 100:108
216
+	formatNumeric(s.next(8), int64(hdr.Uid), paxUid) // 108:116
217
+	formatNumeric(s.next(8), int64(hdr.Gid), paxGid) // 116:124
218
+	formatNumeric(s.next(12), hdr.Size, paxSize)     // 124:136
219
+	formatNumeric(s.next(12), modTime, paxNone)      // 136:148 --- consider using pax for finer granularity
220
+	s.next(8)                                        // chksum (148:156)
221
+	s.next(1)[0] = hdr.Typeflag                      // 156:157
222
+
223
+	formatString(s.next(100), hdr.Linkname, paxLinkpath)
224
+
225
+	copy(s.next(8), []byte("ustar\x0000"))          // 257:265
226
+	formatString(s.next(32), hdr.Uname, paxUname)   // 265:297
227
+	formatString(s.next(32), hdr.Gname, paxGname)   // 297:329
228
+	formatNumeric(s.next(8), hdr.Devmajor, paxNone) // 329:337
229
+	formatNumeric(s.next(8), hdr.Devminor, paxNone) // 337:345
230
+
231
+	// keep a reference to the prefix to allow to overwrite it later if we detect that we can use ustar longnames instead of pax
232
+	prefixHeaderBytes := s.next(155)
233
+	formatString(prefixHeaderBytes, "", paxNone) // 345:500  prefix
234
+
235
+	// Use the GNU magic instead of POSIX magic if we used any GNU extensions.
236
+	if tw.usedBinary {
237
+		copy(header[257:265], []byte("ustar  \x00"))
238
+	}
239
+
240
+	_, paxPathUsed := paxHeaders[paxPath]
241
+	// try to use a ustar header when only the name is too long
242
+	if !tw.preferPax && len(paxHeaders) == 1 && paxPathUsed {
243
+		prefix, suffix, ok := splitUSTARPath(hdr.Name)
244
+		if ok {
245
+			// Since we can encode in USTAR format, disable PAX header.
246
+			delete(paxHeaders, paxPath)
247
+
248
+			// Update the path fields
249
+			formatString(pathHeaderBytes, suffix, paxNone)
250
+			formatString(prefixHeaderBytes, prefix, paxNone)
251
+		}
252
+	}
253
+
254
+	// The chksum field is terminated by a NUL and a space.
255
+	// This is different from the other octal fields.
256
+	chksum, _ := checksum(header)
257
+	f.formatOctal(header[148:155], chksum) // Never fails
258
+	header[155] = ' '
259
+
260
+	// Check if there were any formatting errors.
261
+	if f.err != nil {
262
+		tw.err = f.err
263
+		return tw.err
264
+	}
265
+
266
+	if allowPax {
267
+		for k, v := range hdr.Xattrs {
268
+			paxHeaders[paxXattr+k] = v
269
+		}
270
+		for k, v := range hdr.Winheaders {
271
+			paxHeaders[paxWindows+k] = v
272
+		}
273
+	}
274
+
275
+	if len(paxHeaders) > 0 {
276
+		if !allowPax {
277
+			return errInvalidHeader
278
+		}
279
+		if err := tw.writePAXHeader(hdr, paxHeaders); err != nil {
280
+			return err
281
+		}
282
+	}
283
+	tw.nb = int64(hdr.Size)
284
+	tw.pad = (blockSize - (tw.nb % blockSize)) % blockSize
285
+
286
+	_, tw.err = tw.w.Write(header)
287
+	return tw.err
288
+}
289
+
290
+// splitUSTARPath splits a path according to USTAR prefix and suffix rules.
291
+// If the path is not splittable, then it will return ("", "", false).
292
+func splitUSTARPath(name string) (prefix, suffix string, ok bool) {
293
+	length := len(name)
294
+	if length <= fileNameSize || !isASCII(name) {
295
+		return "", "", false
296
+	} else if length > fileNamePrefixSize+1 {
297
+		length = fileNamePrefixSize + 1
298
+	} else if name[length-1] == '/' {
299
+		length--
300
+	}
301
+
302
+	i := strings.LastIndex(name[:length], "/")
303
+	nlen := len(name) - i - 1 // nlen is length of suffix
304
+	plen := i                 // plen is length of prefix
305
+	if i <= 0 || nlen > fileNameSize || nlen == 0 || plen > fileNamePrefixSize {
306
+		return "", "", false
307
+	}
308
+	return name[:i], name[i+1:], true
309
+}
310
+
311
+// writePaxHeader writes an extended pax header to the
312
+// archive.
313
+func (tw *Writer) writePAXHeader(hdr *Header, paxHeaders map[string]string) error {
314
+	// Prepare extended header
315
+	ext := new(Header)
316
+	ext.Typeflag = TypeXHeader
317
+	// Setting ModTime is required for reader parsing to
318
+	// succeed, and seems harmless enough.
319
+	ext.ModTime = hdr.ModTime
320
+	// The spec asks that we namespace our pseudo files
321
+	// with the current pid.  However, this results in differing outputs
322
+	// for identical inputs.  As such, the constant 0 is now used instead.
323
+	// golang.org/issue/12358
324
+	dir, file := path.Split(hdr.Name)
325
+	fullName := path.Join(dir, "PaxHeaders.0", file)
326
+
327
+	ascii := toASCII(fullName)
328
+	if len(ascii) > 100 {
329
+		ascii = ascii[:100]
330
+	}
331
+	ext.Name = ascii
332
+	// Construct the body
333
+	var buf bytes.Buffer
334
+
335
+	// Keys are sorted before writing to body to allow deterministic output.
336
+	var keys []string
337
+	for k := range paxHeaders {
338
+		keys = append(keys, k)
339
+	}
340
+	sort.Strings(keys)
341
+
342
+	for _, k := range keys {
343
+		fmt.Fprint(&buf, formatPAXRecord(k, paxHeaders[k]))
344
+	}
345
+
346
+	ext.Size = int64(len(buf.Bytes()))
347
+	if err := tw.writeHeader(ext, false); err != nil {
348
+		return err
349
+	}
350
+	if _, err := tw.Write(buf.Bytes()); err != nil {
351
+		return err
352
+	}
353
+	if err := tw.Flush(); err != nil {
354
+		return err
355
+	}
356
+	return nil
357
+}
358
+
359
+// formatPAXRecord formats a single PAX record, prefixing it with the
360
+// appropriate length.
361
+func formatPAXRecord(k, v string) string {
362
+	const padding = 3 // Extra padding for ' ', '=', and '\n'
363
+	size := len(k) + len(v) + padding
364
+	size += len(strconv.Itoa(size))
365
+	record := fmt.Sprintf("%d %s=%s\n", size, k, v)
366
+
367
+	// Final adjustment if adding size field increased the record size.
368
+	if len(record) != size {
369
+		size = len(record)
370
+		record = fmt.Sprintf("%d %s=%s\n", size, k, v)
371
+	}
372
+	return record
373
+}
374
+
375
+// Write writes to the current entry in the tar archive.
376
+// Write returns the error ErrWriteTooLong if more than
377
+// hdr.Size bytes are written after WriteHeader.
378
+func (tw *Writer) Write(b []byte) (n int, err error) {
379
+	if tw.closed {
380
+		err = ErrWriteAfterClose
381
+		return
382
+	}
383
+	overwrite := false
384
+	if int64(len(b)) > tw.nb {
385
+		b = b[0:tw.nb]
386
+		overwrite = true
387
+	}
388
+	n, err = tw.w.Write(b)
389
+	tw.nb -= int64(n)
390
+	if err == nil && overwrite {
391
+		err = ErrWriteTooLong
392
+		return
393
+	}
394
+	tw.err = err
395
+	return
396
+}
397
+
398
+// Close closes the tar archive, flushing any unwritten
399
+// data to the underlying writer.
400
+func (tw *Writer) Close() error {
401
+	if tw.err != nil || tw.closed {
402
+		return tw.err
403
+	}
404
+	tw.Flush()
405
+	tw.closed = true
406
+	if tw.err != nil {
407
+		return tw.err
408
+	}
409
+
410
+	// trailer: two zero blocks
411
+	for i := 0; i < 2; i++ {
412
+		_, tw.err = tw.w.Write(zeroBlock)
413
+		if tw.err != nil {
414
+			break
415
+		}
416
+	}
417
+	return tw.err
418
+}
0 419
new file mode 100644
... ...
@@ -0,0 +1,241 @@
0
+package winio
1
+
2
+import (
3
+	"encoding/binary"
4
+	"errors"
5
+	"fmt"
6
+	"io"
7
+	"io/ioutil"
8
+	"os"
9
+	"runtime"
10
+	"syscall"
11
+	"unicode/utf16"
12
+)
13
+
14
+//sys backupRead(h syscall.Handle, b []byte, bytesRead *uint32, abort bool, processSecurity bool, context *uintptr) (err error) = BackupRead
15
+//sys backupWrite(h syscall.Handle, b []byte, bytesWritten *uint32, abort bool, processSecurity bool, context *uintptr) (err error) = BackupWrite
16
+
17
+const (
18
+	BackupData = uint32(iota + 1)
19
+	BackupEaData
20
+	BackupSecurity
21
+	BackupAlternateData
22
+	BackupLink
23
+	BackupPropertyData
24
+	BackupObjectId
25
+	BackupReparseData
26
+	BackupSparseBlock
27
+	BackupTxfsData
28
+
29
+	StreamSparseAttributes = uint32(8)
30
+)
31
+
32
+// BackupHeader represents a backup stream of a file.
33
+type BackupHeader struct {
34
+	Id         uint32 // The backup stream ID
35
+	Attributes uint32 // Stream attributes
36
+	Size       int64  // The size of the stream in bytes
37
+	Name       string // The name of the stream (for BackupAlternateData only).
38
+	Offset     int64  // The offset of the stream in the file (for BackupSparseBlock only).
39
+}
40
+
41
+type win32StreamId struct {
42
+	StreamId   uint32
43
+	Attributes uint32
44
+	Size       uint64
45
+	NameSize   uint32
46
+}
47
+
48
+// BackupStreamReader reads from a stream produced by the BackupRead Win32 API and produces a series
49
+// of BackupHeader values.
50
+type BackupStreamReader struct {
51
+	r         io.Reader
52
+	bytesLeft int64
53
+}
54
+
55
+// NewBackupStreamReader produces a BackupStreamReader from any io.Reader.
56
+func NewBackupStreamReader(r io.Reader) *BackupStreamReader {
57
+	return &BackupStreamReader{r, 0}
58
+}
59
+
60
+// Next returns the next backup stream and prepares for calls to Write(). It skips the remainder of the current stream if
61
+// it was not completely read.
62
+func (r *BackupStreamReader) Next() (*BackupHeader, error) {
63
+	if r.bytesLeft > 0 {
64
+		if _, err := io.Copy(ioutil.Discard, r); err != nil {
65
+			return nil, err
66
+		}
67
+	}
68
+	var wsi win32StreamId
69
+	if err := binary.Read(r.r, binary.LittleEndian, &wsi); err != nil {
70
+		return nil, err
71
+	}
72
+	hdr := &BackupHeader{
73
+		Id:         wsi.StreamId,
74
+		Attributes: wsi.Attributes,
75
+		Size:       int64(wsi.Size),
76
+	}
77
+	if wsi.NameSize != 0 {
78
+		name := make([]uint16, int(wsi.NameSize/2))
79
+		if err := binary.Read(r.r, binary.LittleEndian, name); err != nil {
80
+			return nil, err
81
+		}
82
+		hdr.Name = syscall.UTF16ToString(name)
83
+	}
84
+	if wsi.StreamId == BackupSparseBlock {
85
+		if err := binary.Read(r.r, binary.LittleEndian, &hdr.Offset); err != nil {
86
+			return nil, err
87
+		}
88
+		hdr.Size -= 8
89
+	}
90
+	r.bytesLeft = hdr.Size
91
+	return hdr, nil
92
+}
93
+
94
+// Read reads from the current backup stream.
95
+func (r *BackupStreamReader) Read(b []byte) (int, error) {
96
+	if r.bytesLeft == 0 {
97
+		return 0, io.EOF
98
+	}
99
+	if int64(len(b)) > r.bytesLeft {
100
+		b = b[:r.bytesLeft]
101
+	}
102
+	n, err := r.r.Read(b)
103
+	r.bytesLeft -= int64(n)
104
+	if err == io.EOF {
105
+		err = io.ErrUnexpectedEOF
106
+	} else if r.bytesLeft == 0 && err == nil {
107
+		err = io.EOF
108
+	}
109
+	return n, err
110
+}
111
+
112
+// BackupStreamWriter writes a stream compatible with the BackupWrite Win32 API.
113
+type BackupStreamWriter struct {
114
+	w         io.Writer
115
+	bytesLeft int64
116
+}
117
+
118
+// NewBackupStreamWriter produces a BackupStreamWriter on top of an io.Writer.
119
+func NewBackupStreamWriter(w io.Writer) *BackupStreamWriter {
120
+	return &BackupStreamWriter{w, 0}
121
+}
122
+
123
+// WriteHeader writes the next backup stream header and prepares for calls to Write().
124
+func (w *BackupStreamWriter) WriteHeader(hdr *BackupHeader) error {
125
+	if w.bytesLeft != 0 {
126
+		return fmt.Errorf("missing %d bytes", w.bytesLeft)
127
+	}
128
+	name := utf16.Encode([]rune(hdr.Name))
129
+	wsi := win32StreamId{
130
+		StreamId:   hdr.Id,
131
+		Attributes: hdr.Attributes,
132
+		Size:       uint64(hdr.Size),
133
+		NameSize:   uint32(len(name) * 2),
134
+	}
135
+	if hdr.Id == BackupSparseBlock {
136
+		// Include space for the int64 block offset
137
+		wsi.Size += 8
138
+	}
139
+	if err := binary.Write(w.w, binary.LittleEndian, &wsi); err != nil {
140
+		return err
141
+	}
142
+	if len(name) != 0 {
143
+		if err := binary.Write(w.w, binary.LittleEndian, name); err != nil {
144
+			return err
145
+		}
146
+	}
147
+	if hdr.Id == BackupSparseBlock {
148
+		if err := binary.Write(w.w, binary.LittleEndian, hdr.Offset); err != nil {
149
+			return err
150
+		}
151
+	}
152
+	w.bytesLeft = hdr.Size
153
+	return nil
154
+}
155
+
156
+// Write writes to the current backup stream.
157
+func (w *BackupStreamWriter) Write(b []byte) (int, error) {
158
+	if w.bytesLeft < int64(len(b)) {
159
+		return 0, fmt.Errorf("too many bytes by %d", int64(len(b))-w.bytesLeft)
160
+	}
161
+	n, err := w.w.Write(b)
162
+	w.bytesLeft -= int64(n)
163
+	return n, err
164
+}
165
+
166
+// BackupFileReader provides an io.ReadCloser interface on top of the BackupRead Win32 API.
167
+type BackupFileReader struct {
168
+	f               *os.File
169
+	includeSecurity bool
170
+	ctx             uintptr
171
+}
172
+
173
+// NewBackupFileReader returns a new BackupFileReader from a file handle. If includeSecurity is true,
174
+// Read will attempt to read the security descriptor of the file.
175
+func NewBackupFileReader(f *os.File, includeSecurity bool) *BackupFileReader {
176
+	r := &BackupFileReader{f, includeSecurity, 0}
177
+	runtime.SetFinalizer(r, func(r *BackupFileReader) { r.Close() })
178
+	return r
179
+}
180
+
181
+// Read reads a backup stream from the file by calling the Win32 API BackupRead().
182
+func (r *BackupFileReader) Read(b []byte) (int, error) {
183
+	var bytesRead uint32
184
+	err := backupRead(syscall.Handle(r.f.Fd()), b, &bytesRead, false, r.includeSecurity, &r.ctx)
185
+	if err != nil {
186
+		return 0, &os.PathError{"BackupRead", r.f.Name(), err}
187
+	}
188
+	if bytesRead == 0 {
189
+		return 0, io.EOF
190
+	}
191
+	return int(bytesRead), nil
192
+}
193
+
194
+// Close frees Win32 resources associated with the BackupFileReader. It does not close
195
+// the underlying file.
196
+func (r *BackupFileReader) Close() error {
197
+	if r.ctx != 0 {
198
+		backupRead(syscall.Handle(r.f.Fd()), nil, nil, true, false, &r.ctx)
199
+		r.ctx = 0
200
+	}
201
+	return nil
202
+}
203
+
204
+// BackupFileWriter provides an io.WriteCloser interface on top of the BackupWrite Win32 API.
205
+type BackupFileWriter struct {
206
+	f               *os.File
207
+	includeSecurity bool
208
+	ctx             uintptr
209
+}
210
+
211
+// NewBackupFileWrtier returns a new BackupFileWriter from a file handle. If includeSecurity is true,
212
+// Write() will attempt to restore the security descriptor from the stream.
213
+func NewBackupFileWriter(f *os.File, includeSecurity bool) *BackupFileWriter {
214
+	w := &BackupFileWriter{f, includeSecurity, 0}
215
+	runtime.SetFinalizer(w, func(w *BackupFileWriter) { w.Close() })
216
+	return w
217
+}
218
+
219
+// Write restores a portion of the file using the provided backup stream.
220
+func (w *BackupFileWriter) Write(b []byte) (int, error) {
221
+	var bytesWritten uint32
222
+	err := backupWrite(syscall.Handle(w.f.Fd()), b, &bytesWritten, false, w.includeSecurity, &w.ctx)
223
+	if err != nil {
224
+		return 0, &os.PathError{"BackupWrite", w.f.Name(), err}
225
+	}
226
+	if int(bytesWritten) != len(b) {
227
+		return int(bytesWritten), errors.New("not all bytes could be written")
228
+	}
229
+	return len(b), nil
230
+}
231
+
232
+// Close frees Win32 resources associated with the BackupFileWriter. It does not
233
+// close the underlying file.
234
+func (w *BackupFileWriter) Close() error {
235
+	if w.ctx != 0 {
236
+		backupWrite(syscall.Handle(w.f.Fd()), nil, nil, true, false, &w.ctx)
237
+		w.ctx = 0
238
+	}
239
+	return nil
240
+}
0 241
new file mode 100644
... ...
@@ -0,0 +1,362 @@
0
+package backuptar
1
+
2
+import (
3
+	"errors"
4
+	"fmt"
5
+	"io"
6
+	"io/ioutil"
7
+	"path/filepath"
8
+	"strconv"
9
+	"strings"
10
+	"syscall"
11
+	"time"
12
+
13
+	"github.com/Microsoft/go-winio"
14
+	"github.com/Microsoft/go-winio/archive/tar" // until archive/tar supports pax extensions in its interface
15
+)
16
+
17
+const (
18
+	c_ISUID  = 04000   // Set uid
19
+	c_ISGID  = 02000   // Set gid
20
+	c_ISVTX  = 01000   // Save text (sticky bit)
21
+	c_ISDIR  = 040000  // Directory
22
+	c_ISFIFO = 010000  // FIFO
23
+	c_ISREG  = 0100000 // Regular file
24
+	c_ISLNK  = 0120000 // Symbolic link
25
+	c_ISBLK  = 060000  // Block special file
26
+	c_ISCHR  = 020000  // Character special file
27
+	c_ISSOCK = 0140000 // Socket
28
+)
29
+
30
+const (
31
+	hdrFileAttributes     = "fileattr"
32
+	hdrAccessTime         = "accesstime"
33
+	hdrChangeTime         = "changetime"
34
+	hdrCreateTime         = "createtime"
35
+	hdrWriteTime          = "writetime"
36
+	hdrSecurityDescriptor = "sd"
37
+	hdrMountPoint         = "mountpoint"
38
+)
39
+
40
+func writeZeroes(w io.Writer, count int64) error {
41
+	buf := make([]byte, 8192)
42
+	c := len(buf)
43
+	for i := int64(0); i < count; i += int64(c) {
44
+		if int64(c) > count-i {
45
+			c = int(count - i)
46
+		}
47
+		_, err := w.Write(buf[:c])
48
+		if err != nil {
49
+			return err
50
+		}
51
+	}
52
+	return nil
53
+}
54
+
55
+func copySparse(t *tar.Writer, br *winio.BackupStreamReader) error {
56
+	curOffset := int64(0)
57
+	for {
58
+		bhdr, err := br.Next()
59
+		if err == io.EOF {
60
+			err = io.ErrUnexpectedEOF
61
+		}
62
+		if err != nil {
63
+			return err
64
+		}
65
+		if bhdr.Id != winio.BackupSparseBlock {
66
+			return fmt.Errorf("unexpected stream %d", bhdr.Id)
67
+		}
68
+
69
+		// archive/tar does not support writing sparse files
70
+		// so just write zeroes to catch up to the current offset.
71
+		err = writeZeroes(t, bhdr.Offset-curOffset)
72
+		if bhdr.Size == 0 {
73
+			break
74
+		}
75
+		n, err := io.Copy(t, br)
76
+		if err != nil {
77
+			return err
78
+		}
79
+		curOffset = bhdr.Offset + n
80
+	}
81
+	return nil
82
+}
83
+
84
+func win32TimeFromTar(key string, hdrs map[string]string, unixTime time.Time) syscall.Filetime {
85
+	if s, ok := hdrs[key]; ok {
86
+		n, err := strconv.ParseUint(s, 10, 64)
87
+		if err == nil {
88
+			return syscall.Filetime{uint32(n & 0xffffffff), uint32(n >> 32)}
89
+		}
90
+	}
91
+	return syscall.NsecToFiletime(unixTime.UnixNano())
92
+}
93
+
94
+func win32TimeToTar(ft syscall.Filetime) (string, time.Time) {
95
+	return fmt.Sprintf("%d", uint64(ft.LowDateTime)+(uint64(ft.HighDateTime)<<32)), time.Unix(0, ft.Nanoseconds())
96
+}
97
+
98
+// Writes a file to a tar writer using data from a Win32 backup stream.
99
+//
100
+// This encodes Win32 metadata as tar pax vendor extensions starting with MSWINDOWS.
101
+//
102
+// The additional Win32 metadata is:
103
+//
104
+// MSWINDOWS.fileattr: The Win32 file attributes, as a decimal value
105
+//
106
+// MSWINDOWS.accesstime: The last access time, as a Filetime expressed as a 64-bit decimal value.
107
+//
108
+// MSWINDOWS.createtime: The creation time, as a Filetime expressed as a 64-bit decimal value.
109
+//
110
+// MSWINDOWS.changetime: The creation time, as a Filetime expressed as a 64-bit decimal value.
111
+//
112
+// MSWINDOWS.writetime: The creation time, as a Filetime expressed as a 64-bit decimal value.
113
+//
114
+// MSWINDOWS.sd: The Win32 security descriptor, in SDDL (string) format
115
+//
116
+// MSWINDOWS.mountpoint: If present, this is a mount point and not a symlink, even though the type is '2' (symlink)
117
+func WriteTarFileFromBackupStream(t *tar.Writer, r io.Reader, name string, size int64, fileInfo *winio.FileBasicInfo) error {
118
+	name = filepath.ToSlash(name)
119
+	hdr := &tar.Header{
120
+		Name:       name,
121
+		Size:       size,
122
+		Typeflag:   tar.TypeReg,
123
+		Winheaders: make(map[string]string),
124
+	}
125
+	hdr.Winheaders[hdrFileAttributes] = fmt.Sprintf("%d", fileInfo.FileAttributes)
126
+	hdr.Winheaders[hdrAccessTime], hdr.AccessTime = win32TimeToTar(fileInfo.LastAccessTime)
127
+	hdr.Winheaders[hdrChangeTime], hdr.ChangeTime = win32TimeToTar(fileInfo.ChangeTime)
128
+	hdr.Winheaders[hdrCreateTime], _ = win32TimeToTar(fileInfo.CreationTime)
129
+	hdr.Winheaders[hdrWriteTime], hdr.ModTime = win32TimeToTar(fileInfo.LastWriteTime)
130
+
131
+	if (fileInfo.FileAttributes & syscall.FILE_ATTRIBUTE_DIRECTORY) != 0 {
132
+		hdr.Mode |= c_ISDIR
133
+		hdr.Size = 0
134
+		hdr.Typeflag = tar.TypeDir
135
+	}
136
+
137
+	br := winio.NewBackupStreamReader(r)
138
+	var dataHdr *winio.BackupHeader
139
+	for dataHdr == nil {
140
+		bhdr, err := br.Next()
141
+		if err == io.EOF {
142
+			break
143
+		}
144
+		if err != nil {
145
+			return err
146
+		}
147
+		switch bhdr.Id {
148
+		case winio.BackupData:
149
+			hdr.Mode |= c_ISREG
150
+			dataHdr = bhdr
151
+		case winio.BackupSecurity:
152
+			sd, err := ioutil.ReadAll(br)
153
+			if err != nil {
154
+				return err
155
+			}
156
+			sddl, err := winio.SecurityDescriptorToSddl(sd)
157
+			if err != nil {
158
+				return err
159
+			}
160
+			hdr.Winheaders[hdrSecurityDescriptor] = sddl
161
+
162
+		case winio.BackupReparseData:
163
+			hdr.Mode |= c_ISLNK
164
+			hdr.Typeflag = tar.TypeSymlink
165
+			reparseBuffer, err := ioutil.ReadAll(br)
166
+			rp, err := winio.DecodeReparsePoint(reparseBuffer)
167
+			if err != nil {
168
+				return err
169
+			}
170
+			if rp.IsMountPoint {
171
+				hdr.Winheaders[hdrMountPoint] = "1"
172
+			}
173
+			hdr.Linkname = rp.Target
174
+		case winio.BackupEaData, winio.BackupLink, winio.BackupPropertyData, winio.BackupObjectId, winio.BackupTxfsData:
175
+			// ignore these streams
176
+		default:
177
+			return fmt.Errorf("%s: unknown stream ID %d", name, bhdr.Id)
178
+		}
179
+	}
180
+
181
+	err := t.WriteHeader(hdr)
182
+	if err != nil {
183
+		return err
184
+	}
185
+
186
+	if dataHdr != nil {
187
+		// A data stream was found. Copy the data.
188
+		if (dataHdr.Attributes & winio.StreamSparseAttributes) == 0 {
189
+			if size != dataHdr.Size {
190
+				return fmt.Errorf("%s: mismatch between file size %d and header size %d", name, size, dataHdr.Size)
191
+			}
192
+			_, err = io.Copy(t, br)
193
+			if err != nil {
194
+				return err
195
+			}
196
+		} else {
197
+			err = copySparse(t, br)
198
+			if err != nil {
199
+				return err
200
+			}
201
+		}
202
+	}
203
+
204
+	// Look for streams after the data stream. The only ones we handle are alternate data streams.
205
+	// Other streams may have metadata that could be serialized, but the tar header has already
206
+	// been written. In practice, this means that we don't get EA or TXF metadata.
207
+	for {
208
+		bhdr, err := br.Next()
209
+		if err == io.EOF {
210
+			break
211
+		}
212
+		if err != nil {
213
+			return err
214
+		}
215
+		switch bhdr.Id {
216
+		case winio.BackupAlternateData:
217
+			altName := bhdr.Name
218
+			if strings.HasSuffix(altName, ":$DATA") {
219
+				altName = altName[:len(altName)-len(":$DATA")]
220
+			}
221
+			if (bhdr.Attributes & winio.StreamSparseAttributes) == 0 {
222
+				hdr = &tar.Header{
223
+					Name:       name + altName,
224
+					Mode:       hdr.Mode,
225
+					Typeflag:   tar.TypeReg,
226
+					Size:       bhdr.Size,
227
+					ModTime:    hdr.ModTime,
228
+					AccessTime: hdr.AccessTime,
229
+					ChangeTime: hdr.ChangeTime,
230
+				}
231
+				err = t.WriteHeader(hdr)
232
+				if err != nil {
233
+					return err
234
+				}
235
+				_, err = io.Copy(t, br)
236
+				if err != nil {
237
+					return err
238
+				}
239
+
240
+			} else {
241
+				// Unsupported for now, since the size of the alternate stream is not present
242
+				// in the backup stream until after the data has been read.
243
+				return errors.New("tar of sparse alternate data streams is unsupported")
244
+			}
245
+		case winio.BackupEaData, winio.BackupLink, winio.BackupPropertyData, winio.BackupObjectId, winio.BackupTxfsData:
246
+			// ignore these streams
247
+		default:
248
+			return fmt.Errorf("%s: unknown stream ID %d after data", name, bhdr.Id)
249
+		}
250
+	}
251
+	return nil
252
+}
253
+
254
+// Retrieves basic Win32 file information from a tar header, using the additional metadata written by
255
+// WriteTarFileFromBackupStream.
256
+func FileInfoFromHeader(hdr *tar.Header) (name string, size int64, fileInfo *winio.FileBasicInfo, err error) {
257
+	name = hdr.Name
258
+	if hdr.Typeflag == tar.TypeReg || hdr.Typeflag == tar.TypeRegA {
259
+		size = hdr.Size
260
+	}
261
+	fileInfo = &winio.FileBasicInfo{
262
+		LastAccessTime: win32TimeFromTar(hdrAccessTime, hdr.Winheaders, hdr.AccessTime),
263
+		LastWriteTime:  win32TimeFromTar(hdrWriteTime, hdr.Winheaders, hdr.ModTime),
264
+		ChangeTime:     win32TimeFromTar(hdrChangeTime, hdr.Winheaders, hdr.ChangeTime),
265
+		CreationTime:   win32TimeFromTar(hdrCreateTime, hdr.Winheaders, hdr.ModTime),
266
+	}
267
+	if attrStr, ok := hdr.Winheaders[hdrFileAttributes]; ok {
268
+		attr, err := strconv.ParseUint(attrStr, 10, 32)
269
+		if err != nil {
270
+			return "", 0, nil, err
271
+		}
272
+		fileInfo.FileAttributes = uintptr(attr)
273
+	} else {
274
+		if hdr.Typeflag == tar.TypeDir {
275
+			fileInfo.FileAttributes |= syscall.FILE_ATTRIBUTE_DIRECTORY
276
+		}
277
+	}
278
+	return
279
+}
280
+
281
+// Writes a Win32 backup stream from the current tar file. Since this function may process multiple
282
+// tar file entries in order to collect all the alternate data streams for the file, it returns the next
283
+// tar file that was not processed, or io.EOF is there are no more.
284
+func WriteBackupStreamFromTarFile(w io.Writer, t *tar.Reader, hdr *tar.Header) (*tar.Header, error) {
285
+	bw := winio.NewBackupStreamWriter(w)
286
+	if sddl, ok := hdr.Winheaders[hdrSecurityDescriptor]; ok {
287
+		sd, err := winio.SddlToSecurityDescriptor(sddl)
288
+		if err != nil {
289
+			return nil, err
290
+		}
291
+		bhdr := winio.BackupHeader{
292
+			Id:   winio.BackupSecurity,
293
+			Size: int64(len(sd)),
294
+		}
295
+		err = bw.WriteHeader(&bhdr)
296
+		if err != nil {
297
+			return nil, err
298
+		}
299
+		_, err = bw.Write(sd)
300
+		if err != nil {
301
+			return nil, err
302
+		}
303
+	}
304
+	if hdr.Typeflag == tar.TypeSymlink {
305
+		_, isMountPoint := hdr.Winheaders[hdrMountPoint]
306
+		rp := winio.ReparsePoint{
307
+			Target:       hdr.Linkname,
308
+			IsMountPoint: isMountPoint,
309
+		}
310
+		reparse := winio.EncodeReparsePoint(&rp)
311
+		bhdr := winio.BackupHeader{
312
+			Id:   winio.BackupReparseData,
313
+			Size: int64(len(reparse)),
314
+		}
315
+		err := bw.WriteHeader(&bhdr)
316
+		if err != nil {
317
+			return nil, err
318
+		}
319
+		_, err = bw.Write(reparse)
320
+		if err != nil {
321
+			return nil, err
322
+		}
323
+	}
324
+	if hdr.Typeflag == tar.TypeReg || hdr.Typeflag == tar.TypeRegA {
325
+		bhdr := winio.BackupHeader{
326
+			Id:   winio.BackupData,
327
+			Size: hdr.Size,
328
+		}
329
+		err := bw.WriteHeader(&bhdr)
330
+		if err != nil {
331
+			return nil, err
332
+		}
333
+		_, err = io.Copy(bw, t)
334
+		if err != nil {
335
+			return nil, err
336
+		}
337
+	}
338
+	// Copy all the alternate data streams and return the next non-ADS header.
339
+	for {
340
+		ahdr, err := t.Next()
341
+		if err != nil {
342
+			return nil, err
343
+		}
344
+		if ahdr.Typeflag != tar.TypeReg || !strings.HasPrefix(ahdr.Name, hdr.Name+":") {
345
+			return ahdr, nil
346
+		}
347
+		bhdr := winio.BackupHeader{
348
+			Id:   winio.BackupAlternateData,
349
+			Size: ahdr.Size,
350
+			Name: ahdr.Name[len(hdr.Name)+1:] + ":$DATA",
351
+		}
352
+		err = bw.WriteHeader(&bhdr)
353
+		if err != nil {
354
+			return nil, err
355
+		}
356
+		_, err = io.Copy(bw, t)
357
+		if err != nil {
358
+			return nil, err
359
+		}
360
+	}
361
+}
0 362
new file mode 100644
... ...
@@ -0,0 +1,30 @@
0
+package winio
1
+
2
+import (
3
+	"os"
4
+	"syscall"
5
+	"unsafe"
6
+)
7
+
8
+//sys getFileInformationByHandleEx(h syscall.Handle, class uint32, buffer *byte, size uint32) (err error) = GetFileInformationByHandleEx
9
+//sys setFileInformationByHandle(h syscall.Handle, class uint32, buffer *byte, size uint32) (err error) = SetFileInformationByHandle
10
+
11
+type FileBasicInfo struct {
12
+	CreationTime, LastAccessTime, LastWriteTime, ChangeTime syscall.Filetime
13
+	FileAttributes                                          uintptr // includes padding
14
+}
15
+
16
+func GetFileBasicInfo(f *os.File) (*FileBasicInfo, error) {
17
+	bi := &FileBasicInfo{}
18
+	if err := getFileInformationByHandleEx(syscall.Handle(f.Fd()), 0, (*byte)(unsafe.Pointer(bi)), uint32(unsafe.Sizeof(*bi))); err != nil {
19
+		return nil, &os.PathError{"GetFileInformationByHandleEx", f.Name(), err}
20
+	}
21
+	return bi, nil
22
+}
23
+
24
+func SetFileBasicInfo(f *os.File, bi *FileBasicInfo) error {
25
+	if err := setFileInformationByHandle(syscall.Handle(f.Fd()), 0, (*byte)(unsafe.Pointer(bi)), uint32(unsafe.Sizeof(*bi))); err != nil {
26
+		return &os.PathError{"SetFileInformationByHandle", f.Name(), err}
27
+	}
28
+	return nil
29
+}
0 30
deleted file mode 100644
... ...
@@ -1,797 +0,0 @@
1
-// Copyright 2013 The Go Authors. All rights reserved.
2
-// Use of this source code is governed by a BSD-style
3
-// license that can be found in the LICENSE file.
4
-
5
-// +build ignore
6
-
7
-/*
8
-mksyscall_windows generates windows system call bodies
9
-
10
-It parses all files specified on command line containing function
11
-prototypes (like syscall_windows.go) and prints system call bodies
12
-to standard output.
13
-
14
-The prototypes are marked by lines beginning with "//sys" and read
15
-like func declarations if //sys is replaced by func, but:
16
-
17
-* The parameter lists must give a name for each argument. This
18
-  includes return parameters.
19
-
20
-* The parameter lists must give a type for each argument:
21
-  the (x, y, z int) shorthand is not allowed.
22
-
23
-* If the return parameter is an error number, it must be named err.
24
-
25
-* If go func name needs to be different from it's winapi dll name,
26
-  the winapi name could be specified at the end, after "=" sign, like
27
-  //sys LoadLibrary(libname string) (handle uint32, err error) = LoadLibraryA
28
-
29
-* Each function that returns err needs to supply a condition, that
30
-  return value of winapi will be tested against to detect failure.
31
-  This would set err to windows "last-error", otherwise it will be nil.
32
-  The value can be provided at end of //sys declaration, like
33
-  //sys LoadLibrary(libname string) (handle uint32, err error) [failretval==-1] = LoadLibraryA
34
-  and is [failretval==0] by default.
35
-
36
-Usage:
37
-	mksyscall_windows [flags] [path ...]
38
-
39
-The flags are:
40
-	-output
41
-		Specify output file name (outputs to console if blank).
42
-	-trace
43
-		Generate print statement after every syscall.
44
-*/
45
-package main
46
-
47
-import (
48
-	"bufio"
49
-	"bytes"
50
-	"errors"
51
-	"flag"
52
-	"fmt"
53
-	"go/format"
54
-	"go/parser"
55
-	"go/token"
56
-	"io"
57
-	"io/ioutil"
58
-	"log"
59
-	"os"
60
-	"strconv"
61
-	"strings"
62
-	"text/template"
63
-)
64
-
65
-var (
66
-	filename       = flag.String("output", "", "output file name (standard output if omitted)")
67
-	printTraceFlag = flag.Bool("trace", false, "generate print statement after every syscall")
68
-)
69
-
70
-func trim(s string) string {
71
-	return strings.Trim(s, " \t")
72
-}
73
-
74
-var packageName string
75
-
76
-func packagename() string {
77
-	return packageName
78
-}
79
-
80
-func syscalldot() string {
81
-	if packageName == "syscall" {
82
-		return ""
83
-	}
84
-	return "syscall."
85
-}
86
-
87
-// Param is function parameter
88
-type Param struct {
89
-	Name      string
90
-	Type      string
91
-	fn        *Fn
92
-	tmpVarIdx int
93
-}
94
-
95
-// tmpVar returns temp variable name that will be used to represent p during syscall.
96
-func (p *Param) tmpVar() string {
97
-	if p.tmpVarIdx < 0 {
98
-		p.tmpVarIdx = p.fn.curTmpVarIdx
99
-		p.fn.curTmpVarIdx++
100
-	}
101
-	return fmt.Sprintf("_p%d", p.tmpVarIdx)
102
-}
103
-
104
-// BoolTmpVarCode returns source code for bool temp variable.
105
-func (p *Param) BoolTmpVarCode() string {
106
-	const code = `var %s uint32
107
-	if %s {
108
-		%s = 1
109
-	} else {
110
-		%s = 0
111
-	}`
112
-	tmp := p.tmpVar()
113
-	return fmt.Sprintf(code, tmp, p.Name, tmp, tmp)
114
-}
115
-
116
-// SliceTmpVarCode returns source code for slice temp variable.
117
-func (p *Param) SliceTmpVarCode() string {
118
-	const code = `var %s *%s
119
-	if len(%s) > 0 {
120
-		%s = &%s[0]
121
-	}`
122
-	tmp := p.tmpVar()
123
-	return fmt.Sprintf(code, tmp, p.Type[2:], p.Name, tmp, p.Name)
124
-}
125
-
126
-// StringTmpVarCode returns source code for string temp variable.
127
-func (p *Param) StringTmpVarCode() string {
128
-	errvar := p.fn.Rets.ErrorVarName()
129
-	if errvar == "" {
130
-		errvar = "_"
131
-	}
132
-	tmp := p.tmpVar()
133
-	const code = `var %s %s
134
-	%s, %s = %s(%s)`
135
-	s := fmt.Sprintf(code, tmp, p.fn.StrconvType(), tmp, errvar, p.fn.StrconvFunc(), p.Name)
136
-	if errvar == "-" {
137
-		return s
138
-	}
139
-	const morecode = `
140
-	if %s != nil {
141
-		return
142
-	}`
143
-	return s + fmt.Sprintf(morecode, errvar)
144
-}
145
-
146
-// TmpVarCode returns source code for temp variable.
147
-func (p *Param) TmpVarCode() string {
148
-	switch {
149
-	case p.Type == "bool":
150
-		return p.BoolTmpVarCode()
151
-	case strings.HasPrefix(p.Type, "[]"):
152
-		return p.SliceTmpVarCode()
153
-	default:
154
-		return ""
155
-	}
156
-}
157
-
158
-// TmpVarHelperCode returns source code for helper's temp variable.
159
-func (p *Param) TmpVarHelperCode() string {
160
-	if p.Type != "string" {
161
-		return ""
162
-	}
163
-	return p.StringTmpVarCode()
164
-}
165
-
166
-// SyscallArgList returns source code fragments representing p parameter
167
-// in syscall. Slices are translated into 2 syscall parameters: pointer to
168
-// the first element and length.
169
-func (p *Param) SyscallArgList() []string {
170
-	t := p.HelperType()
171
-	var s string
172
-	switch {
173
-	case t[0] == '*':
174
-		s = fmt.Sprintf("unsafe.Pointer(%s)", p.Name)
175
-	case t == "bool":
176
-		s = p.tmpVar()
177
-	case strings.HasPrefix(t, "[]"):
178
-		return []string{
179
-			fmt.Sprintf("uintptr(unsafe.Pointer(%s))", p.tmpVar()),
180
-			fmt.Sprintf("uintptr(len(%s))", p.Name),
181
-		}
182
-	default:
183
-		s = p.Name
184
-	}
185
-	return []string{fmt.Sprintf("uintptr(%s)", s)}
186
-}
187
-
188
-// IsError determines if p parameter is used to return error.
189
-func (p *Param) IsError() bool {
190
-	return p.Name == "err" && p.Type == "error"
191
-}
192
-
193
-// HelperType returns type of parameter p used in helper function.
194
-func (p *Param) HelperType() string {
195
-	if p.Type == "string" {
196
-		return p.fn.StrconvType()
197
-	}
198
-	return p.Type
199
-}
200
-
201
-// join concatenates parameters ps into a string with sep separator.
202
-// Each parameter is converted into string by applying fn to it
203
-// before conversion.
204
-func join(ps []*Param, fn func(*Param) string, sep string) string {
205
-	if len(ps) == 0 {
206
-		return ""
207
-	}
208
-	a := make([]string, 0)
209
-	for _, p := range ps {
210
-		a = append(a, fn(p))
211
-	}
212
-	return strings.Join(a, sep)
213
-}
214
-
215
-// Rets describes function return parameters.
216
-type Rets struct {
217
-	Name         string
218
-	Type         string
219
-	ReturnsError bool
220
-	FailCond     string
221
-}
222
-
223
-// ErrorVarName returns error variable name for r.
224
-func (r *Rets) ErrorVarName() string {
225
-	if r.ReturnsError {
226
-		return "err"
227
-	}
228
-	if r.Type == "error" {
229
-		return r.Name
230
-	}
231
-	return ""
232
-}
233
-
234
-// ToParams converts r into slice of *Param.
235
-func (r *Rets) ToParams() []*Param {
236
-	ps := make([]*Param, 0)
237
-	if len(r.Name) > 0 {
238
-		ps = append(ps, &Param{Name: r.Name, Type: r.Type})
239
-	}
240
-	if r.ReturnsError {
241
-		ps = append(ps, &Param{Name: "err", Type: "error"})
242
-	}
243
-	return ps
244
-}
245
-
246
-// List returns source code of syscall return parameters.
247
-func (r *Rets) List() string {
248
-	s := join(r.ToParams(), func(p *Param) string { return p.Name + " " + p.Type }, ", ")
249
-	if len(s) > 0 {
250
-		s = "(" + s + ")"
251
-	}
252
-	return s
253
-}
254
-
255
-// PrintList returns source code of trace printing part correspondent
256
-// to syscall return values.
257
-func (r *Rets) PrintList() string {
258
-	return join(r.ToParams(), func(p *Param) string { return fmt.Sprintf(`"%s=", %s, `, p.Name, p.Name) }, `", ", `)
259
-}
260
-
261
-// SetReturnValuesCode returns source code that accepts syscall return values.
262
-func (r *Rets) SetReturnValuesCode() string {
263
-	if r.Name == "" && !r.ReturnsError {
264
-		return ""
265
-	}
266
-	retvar := "r0"
267
-	if r.Name == "" {
268
-		retvar = "r1"
269
-	}
270
-	errvar := "_"
271
-	if r.ReturnsError {
272
-		errvar = "e1"
273
-	}
274
-	return fmt.Sprintf("%s, _, %s := ", retvar, errvar)
275
-}
276
-
277
-func (r *Rets) useLongHandleErrorCode(retvar string) string {
278
-	const code = `if %s {
279
-		if e1 != 0 {
280
-			err = error(e1)
281
-		} else {
282
-			err = %sEINVAL
283
-		}
284
-	}`
285
-	cond := retvar + " == 0"
286
-	if r.FailCond != "" {
287
-		cond = strings.Replace(r.FailCond, "failretval", retvar, 1)
288
-	}
289
-	return fmt.Sprintf(code, cond, syscalldot())
290
-}
291
-
292
-// SetErrorCode returns source code that sets return parameters.
293
-func (r *Rets) SetErrorCode() string {
294
-	const code = `if r0 != 0 {
295
-		%s = %sErrno(r0)
296
-	}`
297
-	if r.Name == "" && !r.ReturnsError {
298
-		return ""
299
-	}
300
-	if r.Name == "" {
301
-		return r.useLongHandleErrorCode("r1")
302
-	}
303
-	if r.Type == "error" {
304
-		return fmt.Sprintf(code, r.Name, syscalldot())
305
-	}
306
-	s := ""
307
-	switch {
308
-	case r.Type[0] == '*':
309
-		s = fmt.Sprintf("%s = (%s)(unsafe.Pointer(r0))", r.Name, r.Type)
310
-	case r.Type == "bool":
311
-		s = fmt.Sprintf("%s = r0 != 0", r.Name)
312
-	default:
313
-		s = fmt.Sprintf("%s = %s(r0)", r.Name, r.Type)
314
-	}
315
-	if !r.ReturnsError {
316
-		return s
317
-	}
318
-	return s + "\n\t" + r.useLongHandleErrorCode(r.Name)
319
-}
320
-
321
-// Fn describes syscall function.
322
-type Fn struct {
323
-	Name        string
324
-	Params      []*Param
325
-	Rets        *Rets
326
-	PrintTrace  bool
327
-	confirmproc bool
328
-	dllname     string
329
-	dllfuncname string
330
-	src         string
331
-	// TODO: get rid of this field and just use parameter index instead
332
-	curTmpVarIdx int // insure tmp variables have uniq names
333
-}
334
-
335
-// extractParams parses s to extract function parameters.
336
-func extractParams(s string, f *Fn) ([]*Param, error) {
337
-	s = trim(s)
338
-	if s == "" {
339
-		return nil, nil
340
-	}
341
-	a := strings.Split(s, ",")
342
-	ps := make([]*Param, len(a))
343
-	for i := range ps {
344
-		s2 := trim(a[i])
345
-		b := strings.Split(s2, " ")
346
-		if len(b) != 2 {
347
-			b = strings.Split(s2, "\t")
348
-			if len(b) != 2 {
349
-				return nil, errors.New("Could not extract function parameter from \"" + s2 + "\"")
350
-			}
351
-		}
352
-		ps[i] = &Param{
353
-			Name:      trim(b[0]),
354
-			Type:      trim(b[1]),
355
-			fn:        f,
356
-			tmpVarIdx: -1,
357
-		}
358
-	}
359
-	return ps, nil
360
-}
361
-
362
-// extractSection extracts text out of string s starting after start
363
-// and ending just before end. found return value will indicate success,
364
-// and prefix, body and suffix will contain correspondent parts of string s.
365
-func extractSection(s string, start, end rune) (prefix, body, suffix string, found bool) {
366
-	s = trim(s)
367
-	if strings.HasPrefix(s, string(start)) {
368
-		// no prefix
369
-		body = s[1:]
370
-	} else {
371
-		a := strings.SplitN(s, string(start), 2)
372
-		if len(a) != 2 {
373
-			return "", "", s, false
374
-		}
375
-		prefix = a[0]
376
-		body = a[1]
377
-	}
378
-	a := strings.SplitN(body, string(end), 2)
379
-	if len(a) != 2 {
380
-		return "", "", "", false
381
-	}
382
-	return prefix, a[0], a[1], true
383
-}
384
-
385
-// newFn parses string s and return created function Fn.
386
-func newFn(s string) (*Fn, error) {
387
-	s = trim(s)
388
-	f := &Fn{
389
-		Rets:       &Rets{},
390
-		src:        s,
391
-		PrintTrace: *printTraceFlag,
392
-	}
393
-	// function name and args
394
-	prefix, body, s, found := extractSection(s, '(', ')')
395
-	if !found || prefix == "" {
396
-		return nil, errors.New("Could not extract function name and parameters from \"" + f.src + "\"")
397
-	}
398
-	f.Name = prefix
399
-	var err error
400
-	f.Params, err = extractParams(body, f)
401
-	if err != nil {
402
-		return nil, err
403
-	}
404
-	// return values
405
-	_, body, s, found = extractSection(s, '(', ')')
406
-	if found {
407
-		r, err := extractParams(body, f)
408
-		if err != nil {
409
-			return nil, err
410
-		}
411
-		switch len(r) {
412
-		case 0:
413
-		case 1:
414
-			if r[0].IsError() {
415
-				f.Rets.ReturnsError = true
416
-			} else {
417
-				f.Rets.Name = r[0].Name
418
-				f.Rets.Type = r[0].Type
419
-			}
420
-		case 2:
421
-			if !r[1].IsError() {
422
-				return nil, errors.New("Only last windows error is allowed as second return value in \"" + f.src + "\"")
423
-			}
424
-			f.Rets.ReturnsError = true
425
-			f.Rets.Name = r[0].Name
426
-			f.Rets.Type = r[0].Type
427
-		default:
428
-			return nil, errors.New("Too many return values in \"" + f.src + "\"")
429
-		}
430
-	}
431
-	// fail condition
432
-	_, body, s, found = extractSection(s, '[', ']')
433
-	if found {
434
-		f.Rets.FailCond = body
435
-	}
436
-	// dll and dll function names
437
-	s = trim(s)
438
-	if s == "" {
439
-		return f, nil
440
-	}
441
-	if !strings.HasPrefix(s, "=") {
442
-		return nil, errors.New("Could not extract dll name from \"" + f.src + "\"")
443
-	}
444
-	s = trim(s[1:])
445
-	a := strings.Split(s, ".")
446
-	switch len(a) {
447
-	case 1:
448
-		f.dllfuncname = a[0]
449
-	case 2:
450
-		f.dllname = a[0]
451
-		f.dllfuncname = a[1]
452
-	default:
453
-		return nil, errors.New("Could not extract dll name from \"" + f.src + "\"")
454
-	}
455
-	if f.dllfuncname[len(f.dllfuncname)-1] == '?' {
456
-		f.confirmproc = true
457
-		f.dllfuncname = f.dllfuncname[0 : len(f.dllfuncname)-1]
458
-	}
459
-	return f, nil
460
-}
461
-
462
-// DLLName returns DLL name for function f.
463
-func (f *Fn) DLLName() string {
464
-	if f.dllname == "" {
465
-		return "kernel32"
466
-	}
467
-	return f.dllname
468
-}
469
-
470
-// DLLName returns DLL function name for function f.
471
-func (f *Fn) DLLFuncName() string {
472
-	if f.dllfuncname == "" {
473
-		return f.Name
474
-	}
475
-	return f.dllfuncname
476
-}
477
-
478
-func (f *Fn) ConfirmProc() bool {
479
-	return f.confirmproc
480
-}
481
-
482
-// ParamList returns source code for function f parameters.
483
-func (f *Fn) ParamList() string {
484
-	return join(f.Params, func(p *Param) string { return p.Name + " " + p.Type }, ", ")
485
-}
486
-
487
-// HelperParamList returns source code for helper function f parameters.
488
-func (f *Fn) HelperParamList() string {
489
-	return join(f.Params, func(p *Param) string { return p.Name + " " + p.HelperType() }, ", ")
490
-}
491
-
492
-// ParamPrintList returns source code of trace printing part correspondent
493
-// to syscall input parameters.
494
-func (f *Fn) ParamPrintList() string {
495
-	return join(f.Params, func(p *Param) string { return fmt.Sprintf(`"%s=", %s, `, p.Name, p.Name) }, `", ", `)
496
-}
497
-
498
-// ParamCount return number of syscall parameters for function f.
499
-func (f *Fn) ParamCount() int {
500
-	n := 0
501
-	for _, p := range f.Params {
502
-		n += len(p.SyscallArgList())
503
-	}
504
-	return n
505
-}
506
-
507
-// SyscallParamCount determines which version of Syscall/Syscall6/Syscall9/...
508
-// to use. It returns parameter count for correspondent SyscallX function.
509
-func (f *Fn) SyscallParamCount() int {
510
-	n := f.ParamCount()
511
-	switch {
512
-	case n <= 3:
513
-		return 3
514
-	case n <= 6:
515
-		return 6
516
-	case n <= 9:
517
-		return 9
518
-	case n <= 12:
519
-		return 12
520
-	case n <= 15:
521
-		return 15
522
-	default:
523
-		panic("too many arguments to system call")
524
-	}
525
-}
526
-
527
-// Syscall determines which SyscallX function to use for function f.
528
-func (f *Fn) Syscall() string {
529
-	c := f.SyscallParamCount()
530
-	if c == 3 {
531
-		return syscalldot() + "Syscall"
532
-	}
533
-	return syscalldot() + "Syscall" + strconv.Itoa(c)
534
-}
535
-
536
-// SyscallParamList returns source code for SyscallX parameters for function f.
537
-func (f *Fn) SyscallParamList() string {
538
-	a := make([]string, 0)
539
-	for _, p := range f.Params {
540
-		a = append(a, p.SyscallArgList()...)
541
-	}
542
-	for len(a) < f.SyscallParamCount() {
543
-		a = append(a, "0")
544
-	}
545
-	return strings.Join(a, ", ")
546
-}
547
-
548
-// HelperCallParamList returns source code of call into function f helper.
549
-func (f *Fn) HelperCallParamList() string {
550
-	a := make([]string, 0, len(f.Params))
551
-	for _, p := range f.Params {
552
-		s := p.Name
553
-		if p.Type == "string" {
554
-			s = p.tmpVar()
555
-		}
556
-		a = append(a, s)
557
-	}
558
-	return strings.Join(a, ", ")
559
-}
560
-
561
-// IsUTF16 is true, if f is W (utf16) function. It is false
562
-// for all A (ascii) functions.
563
-func (_ *Fn) IsUTF16() bool {
564
-	return true
565
-}
566
-
567
-// StrconvFunc returns name of Go string to OS string function for f.
568
-func (f *Fn) StrconvFunc() string {
569
-	if f.IsUTF16() {
570
-		return syscalldot() + "UTF16PtrFromString"
571
-	}
572
-	return syscalldot() + "BytePtrFromString"
573
-}
574
-
575
-// StrconvType returns Go type name used for OS string for f.
576
-func (f *Fn) StrconvType() string {
577
-	if f.IsUTF16() {
578
-		return "*uint16"
579
-	}
580
-	return "*byte"
581
-}
582
-
583
-// HasStringParam is true, if f has at least one string parameter.
584
-// Otherwise it is false.
585
-func (f *Fn) HasStringParam() bool {
586
-	for _, p := range f.Params {
587
-		if p.Type == "string" {
588
-			return true
589
-		}
590
-	}
591
-	return false
592
-}
593
-
594
-// HelperName returns name of function f helper.
595
-func (f *Fn) HelperName() string {
596
-	if !f.HasStringParam() {
597
-		return f.Name
598
-	}
599
-	return "_" + f.Name
600
-}
601
-
602
-// Source files and functions.
603
-type Source struct {
604
-	Funcs []*Fn
605
-	Files []string
606
-}
607
-
608
-// ParseFiles parses files listed in fs and extracts all syscall
609
-// functions listed in  sys comments. It returns source files
610
-// and functions collection *Source if successful.
611
-func ParseFiles(fs []string) (*Source, error) {
612
-	src := &Source{
613
-		Funcs: make([]*Fn, 0),
614
-		Files: make([]string, 0),
615
-	}
616
-	for _, file := range fs {
617
-		if err := src.ParseFile(file); err != nil {
618
-			return nil, err
619
-		}
620
-	}
621
-	return src, nil
622
-}
623
-
624
-// DLLs return dll names for a source set src.
625
-func (src *Source) DLLs() []string {
626
-	uniq := make(map[string]bool)
627
-	r := make([]string, 0)
628
-	for _, f := range src.Funcs {
629
-		name := f.DLLName()
630
-		if _, found := uniq[name]; !found {
631
-			uniq[name] = true
632
-			r = append(r, name)
633
-		}
634
-	}
635
-	return r
636
-}
637
-
638
-// ParseFile adds additional file path to a source set src.
639
-func (src *Source) ParseFile(path string) error {
640
-	file, err := os.Open(path)
641
-	if err != nil {
642
-		return err
643
-	}
644
-	defer file.Close()
645
-
646
-	s := bufio.NewScanner(file)
647
-	for s.Scan() {
648
-		t := trim(s.Text())
649
-		if len(t) < 7 {
650
-			continue
651
-		}
652
-		if !strings.HasPrefix(t, "//sys") {
653
-			continue
654
-		}
655
-		t = t[5:]
656
-		if !(t[0] == ' ' || t[0] == '\t') {
657
-			continue
658
-		}
659
-		f, err := newFn(t[1:])
660
-		if err != nil {
661
-			return err
662
-		}
663
-		src.Funcs = append(src.Funcs, f)
664
-	}
665
-	if err := s.Err(); err != nil {
666
-		return err
667
-	}
668
-	src.Files = append(src.Files, path)
669
-
670
-	// get package name
671
-	fset := token.NewFileSet()
672
-	_, err = file.Seek(0, 0)
673
-	if err != nil {
674
-		return err
675
-	}
676
-	pkg, err := parser.ParseFile(fset, "", file, parser.PackageClauseOnly)
677
-	if err != nil {
678
-		return err
679
-	}
680
-	packageName = pkg.Name.Name
681
-
682
-	return nil
683
-}
684
-
685
-// Generate output source file from a source set src.
686
-func (src *Source) Generate(w io.Writer) error {
687
-	funcMap := template.FuncMap{
688
-		"packagename": packagename,
689
-		"syscalldot":  syscalldot,
690
-	}
691
-	t := template.Must(template.New("main").Funcs(funcMap).Parse(srcTemplate))
692
-	err := t.Execute(w, src)
693
-	if err != nil {
694
-		return errors.New("Failed to execute template: " + err.Error())
695
-	}
696
-	return nil
697
-}
698
-
699
-func usage() {
700
-	fmt.Fprintf(os.Stderr, "usage: mksyscall_windows [flags] [path ...]\n")
701
-	flag.PrintDefaults()
702
-	os.Exit(1)
703
-}
704
-
705
-func main() {
706
-	flag.Usage = usage
707
-	flag.Parse()
708
-	if len(flag.Args()) <= 0 {
709
-		fmt.Fprintf(os.Stderr, "no files to parse provided\n")
710
-		usage()
711
-	}
712
-
713
-	src, err := ParseFiles(flag.Args())
714
-	if err != nil {
715
-		log.Fatal(err)
716
-	}
717
-
718
-	var buf bytes.Buffer
719
-	if err := src.Generate(&buf); err != nil {
720
-		log.Fatal(err)
721
-	}
722
-
723
-	data, err := format.Source(buf.Bytes())
724
-	if err != nil {
725
-		log.Fatal(err)
726
-	}
727
-	if *filename == "" {
728
-		_, err = os.Stdout.Write(data)
729
-	} else {
730
-		err = ioutil.WriteFile(*filename, data, 0644)
731
-	}
732
-	if err != nil {
733
-		log.Fatal(err)
734
-	}
735
-}
736
-
737
-// TODO: use println instead to print in the following template
738
-const srcTemplate = `
739
-
740
-{{define "main"}}// MACHINE GENERATED BY 'go generate' COMMAND; DO NOT EDIT
741
-
742
-package {{packagename}}
743
-
744
-import "unsafe"{{if syscalldot}}
745
-import "syscall"{{end}}
746
-
747
-var _ unsafe.Pointer
748
-
749
-var (
750
-{{template "dlls" .}}
751
-{{template "funcnames" .}})
752
-{{range .Funcs}}{{if .HasStringParam}}{{template "helperbody" .}}{{end}}{{template "funcbody" .}}{{end}}
753
-{{end}}
754
-
755
-{{/* help functions */}}
756
-
757
-{{define "dlls"}}{{range .DLLs}}	mod{{.}} = {{syscalldot}}NewLazyDLL("{{.}}.dll")
758
-{{end}}{{end}}
759
-
760
-{{define "funcnames"}}{{range .Funcs}}	proc{{.DLLFuncName}} = mod{{.DLLName}}.NewProc("{{.DLLFuncName}}")
761
-{{end}}{{end}}
762
-
763
-{{define "helperbody"}}
764
-func {{.Name}}({{.ParamList}}) {{template "results" .}}{
765
-{{template "helpertmpvars" .}}	return {{.HelperName}}({{.HelperCallParamList}})
766
-}
767
-{{end}}
768
-
769
-{{define "funcbody"}}
770
-func {{.HelperName}}({{.HelperParamList}}) {{template "results" .}}{
771
-{{template "tmpvars" .}}	{{template "syscallcheck" .}}{{template "syscall" .}}
772
-{{template "seterror" .}}{{template "printtrace" .}}	return
773
-}
774
-{{end}}
775
-
776
-{{define "helpertmpvars"}}{{range .Params}}{{if .TmpVarHelperCode}}	{{.TmpVarHelperCode}}
777
-{{end}}{{end}}{{end}}
778
-
779
-{{define "tmpvars"}}{{range .Params}}{{if .TmpVarCode}}	{{.TmpVarCode}}
780
-{{end}}{{end}}{{end}}
781
-
782
-{{define "results"}}{{if .Rets.List}}{{.Rets.List}} {{end}}{{end}}
783
-
784
-{{define "syscallcheck"}}{{if .ConfirmProc}}if {{.Rets.ErrorVarName}} = proc{{.DLLFuncName}}.Find(); {{.Rets.ErrorVarName}} != nil {
785
-    return
786
-}
787
-{{end}}{{end}}
788
-
789
-{{define "syscall"}}{{.Rets.SetReturnValuesCode}}{{.Syscall}}(proc{{.DLLFuncName}}.Addr(), {{.ParamCount}}, {{.SyscallParamList}}){{end}}
790
-
791
-{{define "seterror"}}{{if .Rets.SetErrorCode}}	{{.Rets.SetErrorCode}}
792
-{{end}}{{end}}
793
-
794
-{{define "printtrace"}}{{if .PrintTrace}}	print("SYSCALL: {{.Name}}(", {{.ParamPrintList}}") (", {{.Rets.PrintList}}")\n")
795
-{{end}}{{end}}
796
-
797
-`
... ...
@@ -213,7 +213,7 @@ func ListenPipe(path, sddl string) (net.Listener, error) {
213 213
 		err error
214 214
 	)
215 215
 	if sddl != "" {
216
-		sd, err = sddlToSecurityDescriptor(sddl)
216
+		sd, err = SddlToSecurityDescriptor(sddl)
217 217
 		if err != nil {
218 218
 			return nil, err
219 219
 		}
220 220
new file mode 100644
... ...
@@ -0,0 +1,147 @@
0
+package winio
1
+
2
+import (
3
+	"bytes"
4
+	"encoding/binary"
5
+	"fmt"
6
+	"runtime"
7
+	"syscall"
8
+	"unicode/utf16"
9
+)
10
+
11
+//sys adjustTokenPrivileges(token syscall.Handle, releaseAll bool, input *byte, outputSize uint32, output *byte, requiredSize *uint32) (err error) = advapi32.AdjustTokenPrivileges
12
+//sys impersonateSelf(level uint32) (err error) = advapi32.ImpersonateSelf
13
+//sys revertToSelf() (err error) = advapi32.RevertToSelf
14
+//sys openThreadToken(thread syscall.Handle, accessMask uint32, openAsSelf bool, token *syscall.Handle) (err error) = advapi32.OpenThreadToken
15
+//sys getCurrentThread() (h syscall.Handle) = GetCurrentThread
16
+//sys lookupPrivilegeValue(systemName string, name string, luid *uint64) (err error) = advapi32.LookupPrivilegeValueW
17
+//sys lookupPrivilegeName(systemName string, luid *uint64, buffer *uint16, size *uint32) (err error) = advapi32.LookupPrivilegeNameW
18
+//sys lookupPrivilegeDisplayName(systemName string, name *uint16, buffer *uint16, size *uint32, languageId *uint32) (err error) = advapi32.LookupPrivilegeDisplayNameW
19
+
20
+const (
21
+	SE_PRIVILEGE_ENABLED = 2
22
+
23
+	SeBackupPrivilege  = "SeBackupPrivilege"
24
+	SeRestorePrivilege = "SeRestorePrivilege"
25
+)
26
+
27
+const (
28
+	securityAnonymous = iota
29
+	securityIdentification
30
+	securityImpersonation
31
+	securityDelegation
32
+)
33
+
34
+type PrivilegeError struct {
35
+	privileges []uint64
36
+}
37
+
38
+func (e *PrivilegeError) Error() string {
39
+	s := ""
40
+	if len(e.privileges) > 1 {
41
+		s = "Could not enable privileges "
42
+	} else {
43
+		s = "Could not enable privilege "
44
+	}
45
+	for i, p := range e.privileges {
46
+		if i != 0 {
47
+			s += ", "
48
+		}
49
+		s += `"`
50
+		s += getPrivilegeName(p)
51
+		s += `"`
52
+	}
53
+	return s
54
+}
55
+
56
+func RunWithPrivilege(name string, fn func() error) error {
57
+	return RunWithPrivileges([]string{name}, fn)
58
+}
59
+
60
+func RunWithPrivileges(names []string, fn func() error) error {
61
+	var privileges []uint64
62
+	for _, name := range names {
63
+		p := uint64(0)
64
+		err := lookupPrivilegeValue("", name, &p)
65
+		if err != nil {
66
+			return err
67
+		}
68
+		privileges = append(privileges, p)
69
+	}
70
+	runtime.LockOSThread()
71
+	defer runtime.UnlockOSThread()
72
+	token, err := newThreadToken()
73
+	if err != nil {
74
+		return err
75
+	}
76
+	defer releaseThreadToken(token)
77
+	err = adjustPrivileges(token, privileges)
78
+	if err != nil {
79
+		return err
80
+	}
81
+	return fn()
82
+}
83
+
84
+func adjustPrivileges(token syscall.Handle, privileges []uint64) error {
85
+	var b bytes.Buffer
86
+	binary.Write(&b, binary.LittleEndian, uint32(len(privileges)))
87
+	for _, p := range privileges {
88
+		binary.Write(&b, binary.LittleEndian, p)
89
+		binary.Write(&b, binary.LittleEndian, uint32(SE_PRIVILEGE_ENABLED))
90
+	}
91
+	prevState := make([]byte, b.Len())
92
+	reqSize := uint32(0)
93
+	if err := adjustTokenPrivileges(token, false, &b.Bytes()[0], uint32(len(prevState)), &prevState[0], &reqSize); err != nil {
94
+		return err
95
+	}
96
+	if int(binary.LittleEndian.Uint32(prevState[0:4])) < len(privileges) {
97
+		return &PrivilegeError{privileges}
98
+	}
99
+	return nil
100
+}
101
+
102
+func getPrivilegeName(luid uint64) string {
103
+	var nameBuffer [256]uint16
104
+	bufSize := uint32(len(nameBuffer))
105
+	err := lookupPrivilegeName("", &luid, &nameBuffer[0], &bufSize)
106
+	if err != nil {
107
+		return fmt.Sprintf("<unknown privilege %d>", luid)
108
+	}
109
+
110
+	var displayNameBuffer [256]uint16
111
+	displayBufSize := uint32(len(displayNameBuffer))
112
+	var langId uint32
113
+	err = lookupPrivilegeDisplayName("", &nameBuffer[0], &displayNameBuffer[0], &displayBufSize, &langId)
114
+	if err != nil {
115
+		return fmt.Sprintf("<unknown privilege %s>", utf16.Decode(nameBuffer[:bufSize]))
116
+	}
117
+
118
+	return string(utf16.Decode(displayNameBuffer[:displayBufSize]))
119
+}
120
+
121
+func newThreadToken() (syscall.Handle, error) {
122
+	err := impersonateSelf(securityImpersonation)
123
+	if err != nil {
124
+		panic(err)
125
+		return 0, err
126
+	}
127
+
128
+	var token syscall.Handle
129
+	err = openThreadToken(getCurrentThread(), syscall.TOKEN_ADJUST_PRIVILEGES|syscall.TOKEN_QUERY, false, &token)
130
+	if err != nil {
131
+		rerr := revertToSelf()
132
+		if rerr != nil {
133
+			panic(rerr)
134
+		}
135
+		return 0, err
136
+	}
137
+	return token, nil
138
+}
139
+
140
+func releaseThreadToken(h syscall.Handle) {
141
+	err := revertToSelf()
142
+	if err != nil {
143
+		panic(err)
144
+	}
145
+	syscall.Close(h)
146
+}
0 147
new file mode 100644
... ...
@@ -0,0 +1,124 @@
0
+package winio
1
+
2
+import (
3
+	"bytes"
4
+	"encoding/binary"
5
+	"fmt"
6
+	"strings"
7
+	"unicode/utf16"
8
+	"unsafe"
9
+)
10
+
11
+const (
12
+	reparseTagMountPoint = 0xA0000003
13
+	reparseTagSymlink    = 0xA000000C
14
+)
15
+
16
+type reparseDataBuffer struct {
17
+	ReparseTag           uint32
18
+	ReparseDataLength    uint16
19
+	Reserved             uint16
20
+	SubstituteNameOffset uint16
21
+	SubstituteNameLength uint16
22
+	PrintNameOffset      uint16
23
+	PrintNameLength      uint16
24
+}
25
+
26
+// ReparsePoint describes a Win32 symlink or mount point.
27
+type ReparsePoint struct {
28
+	Target       string
29
+	IsMountPoint bool
30
+}
31
+
32
+// UnsupportedReparsePointError is returned when trying to decode a non-symlink or
33
+// mount point reparse point.
34
+type UnsupportedReparsePointError struct {
35
+	Tag uint32
36
+}
37
+
38
+func (e *UnsupportedReparsePointError) Error() string {
39
+	return fmt.Sprintf("unsupported reparse point %x", e.Tag)
40
+}
41
+
42
+// DecodeReparsePoint decodes a Win32 REPARSE_DATA_BUFFER structure containing either a symlink
43
+// or a mount point.
44
+func DecodeReparsePoint(b []byte) (*ReparsePoint, error) {
45
+	isMountPoint := false
46
+	tag := binary.LittleEndian.Uint32(b[0:4])
47
+	switch tag {
48
+	case reparseTagMountPoint:
49
+		isMountPoint = true
50
+	case reparseTagSymlink:
51
+	default:
52
+		return nil, &UnsupportedReparsePointError{tag}
53
+	}
54
+	nameOffset := 16 + binary.LittleEndian.Uint16(b[12:14])
55
+	if !isMountPoint {
56
+		nameOffset += 4
57
+	}
58
+	nameLength := binary.LittleEndian.Uint16(b[14:16])
59
+	name := make([]uint16, nameLength/2)
60
+	err := binary.Read(bytes.NewReader(b[nameOffset:nameOffset+nameLength]), binary.LittleEndian, &name)
61
+	if err != nil {
62
+		return nil, err
63
+	}
64
+	return &ReparsePoint{string(utf16.Decode(name)), isMountPoint}, nil
65
+}
66
+
67
+func isDriveLetter(c byte) bool {
68
+	return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')
69
+}
70
+
71
+// EncodeReparsePoint encodes a Win32 REPARSE_DATA_BUFFER structure describing a symlink or
72
+// mount point.
73
+func EncodeReparsePoint(rp *ReparsePoint) []byte {
74
+	// Generate an NT path and determine if this is a relative path.
75
+	var ntTarget string
76
+	relative := false
77
+	if strings.HasPrefix(rp.Target, `\\?\`) {
78
+		ntTarget = rp.Target
79
+	} else if strings.HasPrefix(rp.Target, `\\`) {
80
+		ntTarget = `\??\UNC\` + rp.Target[2:]
81
+	} else if len(rp.Target) >= 2 && isDriveLetter(rp.Target[0]) && rp.Target[1] == ':' {
82
+		ntTarget = `\??\` + rp.Target
83
+	} else {
84
+		ntTarget = rp.Target
85
+		relative = true
86
+	}
87
+
88
+	// The paths must be NUL-terminated even though they are counted strings.
89
+	target16 := utf16.Encode([]rune(rp.Target + "\x00"))
90
+	ntTarget16 := utf16.Encode([]rune(ntTarget + "\x00"))
91
+
92
+	size := int(unsafe.Sizeof(reparseDataBuffer{})) - 8
93
+	size += len(ntTarget16)*2 + len(target16)*2
94
+
95
+	tag := uint32(reparseTagMountPoint)
96
+	if !rp.IsMountPoint {
97
+		tag = reparseTagSymlink
98
+		size += 4 // Add room for symlink flags
99
+	}
100
+
101
+	data := reparseDataBuffer{
102
+		ReparseTag:           tag,
103
+		ReparseDataLength:    uint16(size),
104
+		SubstituteNameOffset: 0,
105
+		SubstituteNameLength: uint16((len(ntTarget16) - 1) * 2),
106
+		PrintNameOffset:      uint16(len(ntTarget16) * 2),
107
+		PrintNameLength:      uint16((len(target16) - 1) * 2),
108
+	}
109
+
110
+	var b bytes.Buffer
111
+	binary.Write(&b, binary.LittleEndian, &data)
112
+	if !rp.IsMountPoint {
113
+		flags := uint32(0)
114
+		if relative {
115
+			flags |= 1
116
+		}
117
+		binary.Write(&b, binary.LittleEndian, flags)
118
+	}
119
+
120
+	binary.Write(&b, binary.LittleEndian, ntTarget16)
121
+	binary.Write(&b, binary.LittleEndian, target16)
122
+	return b.Bytes()
123
+}
... ...
@@ -8,6 +8,7 @@ import (
8 8
 //sys lookupAccountName(systemName *uint16, accountName string, sid *byte, sidSize *uint32, refDomain *uint16, refDomainSize *uint32, sidNameUse *uint32) (err error) = advapi32.LookupAccountNameW
9 9
 //sys convertSidToStringSid(sid *byte, str **uint16) (err error) = advapi32.ConvertSidToStringSidW
10 10
 //sys convertStringSecurityDescriptorToSecurityDescriptor(str string, revision uint32, sd *uintptr, size *uint32) (err error) = advapi32.ConvertStringSecurityDescriptorToSecurityDescriptorW
11
+//sys convertSecurityDescriptorToStringSecurityDescriptor(sd *byte, revision uint32, secInfo uint32, sddl **uint16, sddlSize *uint32) (err error) = advapi32.ConvertSecurityDescriptorToStringSecurityDescriptorW
11 12
 //sys localFree(mem uintptr) = LocalFree
12 13
 //sys getSecurityDescriptorLength(sd uintptr) (len uint32) = advapi32.GetSecurityDescriptorLength
13 14
 
... ...
@@ -70,7 +71,7 @@ func LookupSidByName(name string) (sid string, err error) {
70 70
 	return sid, nil
71 71
 }
72 72
 
73
-func sddlToSecurityDescriptor(sddl string) ([]byte, error) {
73
+func SddlToSecurityDescriptor(sddl string) ([]byte, error) {
74 74
 	var sdBuffer uintptr
75 75
 	err := convertStringSecurityDescriptorToSecurityDescriptor(sddl, 1, &sdBuffer, nil)
76 76
 	if err != nil {
... ...
@@ -78,6 +79,18 @@ func sddlToSecurityDescriptor(sddl string) ([]byte, error) {
78 78
 	}
79 79
 	defer localFree(sdBuffer)
80 80
 	sd := make([]byte, getSecurityDescriptorLength(sdBuffer))
81
-	copy(sd, (*[1 << 30]byte)(unsafe.Pointer(sdBuffer))[:len(sd)])
81
+	copy(sd, (*[0xffff]byte)(unsafe.Pointer(sdBuffer))[:len(sd)])
82 82
 	return sd, nil
83 83
 }
84
+
85
+func SecurityDescriptorToSddl(sd []byte) (string, error) {
86
+	var sddl *uint16
87
+	// The returned string length seems to including an aribtrary number of terminating NULs.
88
+	// Don't use it.
89
+	err := convertSecurityDescriptorToStringSecurityDescriptor(&sd[0], 1, 0xff, &sddl, nil)
90
+	if err != nil {
91
+		return "", err
92
+	}
93
+	defer localFree(uintptr(unsafe.Pointer(sddl)))
94
+	return syscall.UTF16ToString((*[0xffff]uint16)(unsafe.Pointer(sddl))[:]), nil
95
+}
... ...
@@ -1,3 +1,3 @@
1 1
 package winio
2 2
 
3
-//go:generate go run mksyscall_windows.go -output zsyscall.go file.go pipe.go sd.go
3
+//go:generate go run $GOROOT/src/syscall/mksyscall_windows.go -output zsyscall.go file.go pipe.go sd.go fileinfo.go privilege.go backup.go
... ...
@@ -22,8 +22,21 @@ var (
22 22
 	procLookupAccountNameW                                   = modadvapi32.NewProc("LookupAccountNameW")
23 23
 	procConvertSidToStringSidW                               = modadvapi32.NewProc("ConvertSidToStringSidW")
24 24
 	procConvertStringSecurityDescriptorToSecurityDescriptorW = modadvapi32.NewProc("ConvertStringSecurityDescriptorToSecurityDescriptorW")
25
+	procConvertSecurityDescriptorToStringSecurityDescriptorW = modadvapi32.NewProc("ConvertSecurityDescriptorToStringSecurityDescriptorW")
25 26
 	procLocalFree                                            = modkernel32.NewProc("LocalFree")
26 27
 	procGetSecurityDescriptorLength                          = modadvapi32.NewProc("GetSecurityDescriptorLength")
28
+	procGetFileInformationByHandleEx                         = modkernel32.NewProc("GetFileInformationByHandleEx")
29
+	procSetFileInformationByHandle                           = modkernel32.NewProc("SetFileInformationByHandle")
30
+	procAdjustTokenPrivileges                                = modadvapi32.NewProc("AdjustTokenPrivileges")
31
+	procImpersonateSelf                                      = modadvapi32.NewProc("ImpersonateSelf")
32
+	procRevertToSelf                                         = modadvapi32.NewProc("RevertToSelf")
33
+	procOpenThreadToken                                      = modadvapi32.NewProc("OpenThreadToken")
34
+	procGetCurrentThread                                     = modkernel32.NewProc("GetCurrentThread")
35
+	procLookupPrivilegeValueW                                = modadvapi32.NewProc("LookupPrivilegeValueW")
36
+	procLookupPrivilegeNameW                                 = modadvapi32.NewProc("LookupPrivilegeNameW")
37
+	procLookupPrivilegeDisplayNameW                          = modadvapi32.NewProc("LookupPrivilegeDisplayNameW")
38
+	procBackupRead                                           = modkernel32.NewProc("BackupRead")
39
+	procBackupWrite                                          = modkernel32.NewProc("BackupWrite")
27 40
 )
28 41
 
29 42
 func cancelIoEx(file syscall.Handle, o *syscall.Overlapped) (err error) {
... ...
@@ -206,6 +219,18 @@ func _convertStringSecurityDescriptorToSecurityDescriptor(str *uint16, revision
206 206
 	return
207 207
 }
208 208
 
209
+func convertSecurityDescriptorToStringSecurityDescriptor(sd *byte, revision uint32, secInfo uint32, sddl **uint16, sddlSize *uint32) (err error) {
210
+	r1, _, e1 := syscall.Syscall6(procConvertSecurityDescriptorToStringSecurityDescriptorW.Addr(), 5, uintptr(unsafe.Pointer(sd)), uintptr(revision), uintptr(secInfo), uintptr(unsafe.Pointer(sddl)), uintptr(unsafe.Pointer(sddlSize)), 0)
211
+	if r1 == 0 {
212
+		if e1 != 0 {
213
+			err = error(e1)
214
+		} else {
215
+			err = syscall.EINVAL
216
+		}
217
+	}
218
+	return
219
+}
220
+
209 221
 func localFree(mem uintptr) {
210 222
 	syscall.Syscall(procLocalFree.Addr(), 1, uintptr(mem), 0, 0)
211 223
 	return
... ...
@@ -216,3 +241,217 @@ func getSecurityDescriptorLength(sd uintptr) (len uint32) {
216 216
 	len = uint32(r0)
217 217
 	return
218 218
 }
219
+
220
+func getFileInformationByHandleEx(h syscall.Handle, class uint32, buffer *byte, size uint32) (err error) {
221
+	r1, _, e1 := syscall.Syscall6(procGetFileInformationByHandleEx.Addr(), 4, uintptr(h), uintptr(class), uintptr(unsafe.Pointer(buffer)), uintptr(size), 0, 0)
222
+	if r1 == 0 {
223
+		if e1 != 0 {
224
+			err = error(e1)
225
+		} else {
226
+			err = syscall.EINVAL
227
+		}
228
+	}
229
+	return
230
+}
231
+
232
+func setFileInformationByHandle(h syscall.Handle, class uint32, buffer *byte, size uint32) (err error) {
233
+	r1, _, e1 := syscall.Syscall6(procSetFileInformationByHandle.Addr(), 4, uintptr(h), uintptr(class), uintptr(unsafe.Pointer(buffer)), uintptr(size), 0, 0)
234
+	if r1 == 0 {
235
+		if e1 != 0 {
236
+			err = error(e1)
237
+		} else {
238
+			err = syscall.EINVAL
239
+		}
240
+	}
241
+	return
242
+}
243
+
244
+func adjustTokenPrivileges(token syscall.Handle, releaseAll bool, input *byte, outputSize uint32, output *byte, requiredSize *uint32) (err error) {
245
+	var _p0 uint32
246
+	if releaseAll {
247
+		_p0 = 1
248
+	} else {
249
+		_p0 = 0
250
+	}
251
+	r1, _, e1 := syscall.Syscall6(procAdjustTokenPrivileges.Addr(), 6, uintptr(token), uintptr(_p0), uintptr(unsafe.Pointer(input)), uintptr(outputSize), uintptr(unsafe.Pointer(output)), uintptr(unsafe.Pointer(requiredSize)))
252
+	if r1 == 0 {
253
+		if e1 != 0 {
254
+			err = error(e1)
255
+		} else {
256
+			err = syscall.EINVAL
257
+		}
258
+	}
259
+	return
260
+}
261
+
262
+func impersonateSelf(level uint32) (err error) {
263
+	r1, _, e1 := syscall.Syscall(procImpersonateSelf.Addr(), 1, uintptr(level), 0, 0)
264
+	if r1 == 0 {
265
+		if e1 != 0 {
266
+			err = error(e1)
267
+		} else {
268
+			err = syscall.EINVAL
269
+		}
270
+	}
271
+	return
272
+}
273
+
274
+func revertToSelf() (err error) {
275
+	r1, _, e1 := syscall.Syscall(procRevertToSelf.Addr(), 0, 0, 0, 0)
276
+	if r1 == 0 {
277
+		if e1 != 0 {
278
+			err = error(e1)
279
+		} else {
280
+			err = syscall.EINVAL
281
+		}
282
+	}
283
+	return
284
+}
285
+
286
+func openThreadToken(thread syscall.Handle, accessMask uint32, openAsSelf bool, token *syscall.Handle) (err error) {
287
+	var _p0 uint32
288
+	if openAsSelf {
289
+		_p0 = 1
290
+	} else {
291
+		_p0 = 0
292
+	}
293
+	r1, _, e1 := syscall.Syscall6(procOpenThreadToken.Addr(), 4, uintptr(thread), uintptr(accessMask), uintptr(_p0), uintptr(unsafe.Pointer(token)), 0, 0)
294
+	if r1 == 0 {
295
+		if e1 != 0 {
296
+			err = error(e1)
297
+		} else {
298
+			err = syscall.EINVAL
299
+		}
300
+	}
301
+	return
302
+}
303
+
304
+func getCurrentThread() (h syscall.Handle) {
305
+	r0, _, _ := syscall.Syscall(procGetCurrentThread.Addr(), 0, 0, 0, 0)
306
+	h = syscall.Handle(r0)
307
+	return
308
+}
309
+
310
+func lookupPrivilegeValue(systemName string, name string, luid *uint64) (err error) {
311
+	var _p0 *uint16
312
+	_p0, err = syscall.UTF16PtrFromString(systemName)
313
+	if err != nil {
314
+		return
315
+	}
316
+	var _p1 *uint16
317
+	_p1, err = syscall.UTF16PtrFromString(name)
318
+	if err != nil {
319
+		return
320
+	}
321
+	return _lookupPrivilegeValue(_p0, _p1, luid)
322
+}
323
+
324
+func _lookupPrivilegeValue(systemName *uint16, name *uint16, luid *uint64) (err error) {
325
+	r1, _, e1 := syscall.Syscall(procLookupPrivilegeValueW.Addr(), 3, uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(luid)))
326
+	if r1 == 0 {
327
+		if e1 != 0 {
328
+			err = error(e1)
329
+		} else {
330
+			err = syscall.EINVAL
331
+		}
332
+	}
333
+	return
334
+}
335
+
336
+func lookupPrivilegeName(systemName string, luid *uint64, buffer *uint16, size *uint32) (err error) {
337
+	var _p0 *uint16
338
+	_p0, err = syscall.UTF16PtrFromString(systemName)
339
+	if err != nil {
340
+		return
341
+	}
342
+	return _lookupPrivilegeName(_p0, luid, buffer, size)
343
+}
344
+
345
+func _lookupPrivilegeName(systemName *uint16, luid *uint64, buffer *uint16, size *uint32) (err error) {
346
+	r1, _, e1 := syscall.Syscall6(procLookupPrivilegeNameW.Addr(), 4, uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(luid)), uintptr(unsafe.Pointer(buffer)), uintptr(unsafe.Pointer(size)), 0, 0)
347
+	if r1 == 0 {
348
+		if e1 != 0 {
349
+			err = error(e1)
350
+		} else {
351
+			err = syscall.EINVAL
352
+		}
353
+	}
354
+	return
355
+}
356
+
357
+func lookupPrivilegeDisplayName(systemName string, name *uint16, buffer *uint16, size *uint32, languageId *uint32) (err error) {
358
+	var _p0 *uint16
359
+	_p0, err = syscall.UTF16PtrFromString(systemName)
360
+	if err != nil {
361
+		return
362
+	}
363
+	return _lookupPrivilegeDisplayName(_p0, name, buffer, size, languageId)
364
+}
365
+
366
+func _lookupPrivilegeDisplayName(systemName *uint16, name *uint16, buffer *uint16, size *uint32, languageId *uint32) (err error) {
367
+	r1, _, e1 := syscall.Syscall6(procLookupPrivilegeDisplayNameW.Addr(), 5, uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(buffer)), uintptr(unsafe.Pointer(size)), uintptr(unsafe.Pointer(languageId)), 0)
368
+	if r1 == 0 {
369
+		if e1 != 0 {
370
+			err = error(e1)
371
+		} else {
372
+			err = syscall.EINVAL
373
+		}
374
+	}
375
+	return
376
+}
377
+
378
+func backupRead(h syscall.Handle, b []byte, bytesRead *uint32, abort bool, processSecurity bool, context *uintptr) (err error) {
379
+	var _p0 *byte
380
+	if len(b) > 0 {
381
+		_p0 = &b[0]
382
+	}
383
+	var _p1 uint32
384
+	if abort {
385
+		_p1 = 1
386
+	} else {
387
+		_p1 = 0
388
+	}
389
+	var _p2 uint32
390
+	if processSecurity {
391
+		_p2 = 1
392
+	} else {
393
+		_p2 = 0
394
+	}
395
+	r1, _, e1 := syscall.Syscall9(procBackupRead.Addr(), 7, uintptr(h), uintptr(unsafe.Pointer(_p0)), uintptr(len(b)), uintptr(unsafe.Pointer(bytesRead)), uintptr(_p1), uintptr(_p2), uintptr(unsafe.Pointer(context)), 0, 0)
396
+	if r1 == 0 {
397
+		if e1 != 0 {
398
+			err = error(e1)
399
+		} else {
400
+			err = syscall.EINVAL
401
+		}
402
+	}
403
+	return
404
+}
405
+
406
+func backupWrite(h syscall.Handle, b []byte, bytesWritten *uint32, abort bool, processSecurity bool, context *uintptr) (err error) {
407
+	var _p0 *byte
408
+	if len(b) > 0 {
409
+		_p0 = &b[0]
410
+	}
411
+	var _p1 uint32
412
+	if abort {
413
+		_p1 = 1
414
+	} else {
415
+		_p1 = 0
416
+	}
417
+	var _p2 uint32
418
+	if processSecurity {
419
+		_p2 = 1
420
+	} else {
421
+		_p2 = 0
422
+	}
423
+	r1, _, e1 := syscall.Syscall9(procBackupWrite.Addr(), 7, uintptr(h), uintptr(unsafe.Pointer(_p0)), uintptr(len(b)), uintptr(unsafe.Pointer(bytesWritten)), uintptr(_p1), uintptr(_p2), uintptr(unsafe.Pointer(context)), 0, 0)
424
+	if r1 == 0 {
425
+		if e1 != 0 {
426
+			err = error(e1)
427
+		} else {
428
+			err = syscall.EINVAL
429
+		}
430
+	}
431
+	return
432
+}
219 433
deleted file mode 100644
... ...
@@ -1,34 +0,0 @@
1
-package hcsshim
2
-
3
-import "github.com/Sirupsen/logrus"
4
-
5
-// CopyLayer performs a commit of the srcId (which is expected to be a read-write
6
-// layer) into a new read-only layer at dstId.  This requires the full list of
7
-// on-disk paths to parent layers, provided in parentLayerPaths, in order to
8
-// complete the commit.
9
-func CopyLayer(info DriverInfo, srcId, dstId string, parentLayerPaths []string) error {
10
-	title := "hcsshim::CopyLayer "
11
-	logrus.Debugf(title+"srcId %s dstId", srcId, dstId)
12
-
13
-	// Generate layer descriptors
14
-	layers, err := layerPathsToDescriptors(parentLayerPaths)
15
-	if err != nil {
16
-		return err
17
-	}
18
-
19
-	// Convert info to API calling convention
20
-	infop, err := convertDriverInfo(info)
21
-	if err != nil {
22
-		return err
23
-	}
24
-
25
-	err = copyLayer(&infop, srcId, dstId, layers)
26
-	if err != nil {
27
-		err = makeErrorf(err, title, "srcId=%s dstId=%d", srcId, dstId)
28
-		logrus.Error(err)
29
-		return err
30
-	}
31
-
32
-	logrus.Debugf(title+" - succeeded srcId=%s dstId=%s", srcId, dstId)
33
-	return nil
34
-}
... ...
@@ -82,12 +82,12 @@ func CreateProcessInComputeSystem(id string, useStdin bool, useStdout bool, useS
82 82
 	err = createProcessWithStdHandlesInComputeSystem(id, string(paramsJson), &pid, stdinParam, stdoutParam, stderrParam)
83 83
 	if err != nil {
84 84
 		herr := makeErrorf(err, title, "id=%s params=%v", id, params)
85
-		err = herr
86 85
 		// Windows TP4: Hyper-V Containers may return this error with more than one
87 86
 		// concurrent exec. Do not log it as an error
88
-		if herr.Err != WSAEINVAL {
89
-			logrus.Error(err)
87
+		if err != WSAEINVAL {
88
+			logrus.Error(herr)
90 89
 		}
90
+		err = herr
91 91
 		return
92 92
 	}
93 93
 
... ...
@@ -1,6 +1,15 @@
1 1
 package hcsshim
2 2
 
3
-import "github.com/Sirupsen/logrus"
3
+import (
4
+	"io"
5
+	"io/ioutil"
6
+	"os"
7
+	"runtime"
8
+	"syscall"
9
+
10
+	"github.com/Microsoft/go-winio"
11
+	"github.com/Sirupsen/logrus"
12
+)
4 13
 
5 14
 // ExportLayer will create a folder at exportFolderPath and fill that folder with
6 15
 // the transport format version of the layer identified by layerId. This transport
... ...
@@ -34,3 +43,114 @@ func ExportLayer(info DriverInfo, layerId string, exportFolderPath string, paren
34 34
 	logrus.Debugf(title+"succeeded flavour=%d layerId=%s folder=%s", info.Flavour, layerId, exportFolderPath)
35 35
 	return nil
36 36
 }
37
+
38
+type LayerReader interface {
39
+	Next() (string, int64, *winio.FileBasicInfo, error)
40
+	Read(b []byte) (int, error)
41
+	Close() error
42
+}
43
+
44
+// FilterLayerReader provides an interface for extracting the contents of an on-disk layer.
45
+type FilterLayerReader struct {
46
+	context uintptr
47
+}
48
+
49
+// Next reads the next available file from a layer, ensuring that parent directories are always read
50
+// before child files and directories.
51
+//
52
+// Next returns the file's relative path, size, and basic file metadata. Read() should be used to
53
+// extract a Win32 backup stream with the remainder of the metadata and the data.
54
+func (r *FilterLayerReader) Next() (string, int64, *winio.FileBasicInfo, error) {
55
+	var fileNamep *uint16
56
+	fileInfo := &winio.FileBasicInfo{}
57
+	var deleted uint32
58
+	var fileSize int64
59
+	err := exportLayerNext(r.context, &fileNamep, fileInfo, &fileSize, &deleted)
60
+	if err != nil {
61
+		if err == syscall.ERROR_NO_MORE_FILES {
62
+			err = io.EOF
63
+		} else {
64
+			err = makeError(err, "ExportLayerNext", "")
65
+		}
66
+		return "", 0, nil, err
67
+	}
68
+	fileName := convertAndFreeCoTaskMemString(fileNamep)
69
+	if deleted != 0 {
70
+		fileInfo = nil
71
+	}
72
+	if fileName[0] == '\\' {
73
+		fileName = fileName[1:]
74
+	}
75
+	return fileName, fileSize, fileInfo, nil
76
+}
77
+
78
+// Read reads from the current file's Win32 backup stream.
79
+func (r *FilterLayerReader) Read(b []byte) (int, error) {
80
+	var bytesRead uint32
81
+	err := exportLayerRead(r.context, b, &bytesRead)
82
+	if err != nil {
83
+		return 0, makeError(err, "ExportLayerRead", "")
84
+	}
85
+	if bytesRead == 0 {
86
+		return 0, io.EOF
87
+	}
88
+	return int(bytesRead), nil
89
+}
90
+
91
+// Close frees resources associated with the layer reader. It will return an
92
+// error if there was an error while reading the layer or of the layer was not
93
+// completely read.
94
+func (r *FilterLayerReader) Close() (err error) {
95
+	if r.context != 0 {
96
+		err = exportLayerEnd(r.context)
97
+		if err != nil {
98
+			err = makeError(err, "ExportLayerEnd", "")
99
+		}
100
+		r.context = 0
101
+	}
102
+	return
103
+}
104
+
105
+// NewLayerReader returns a new layer reader for reading the contents of an on-disk layer.
106
+func NewLayerReader(info DriverInfo, layerId string, parentLayerPaths []string) (LayerReader, error) {
107
+	if procExportLayerBegin.Find() != nil {
108
+		// The new layer reader is not available on this Windows build. Fall back to the
109
+		// legacy export code path.
110
+		path, err := ioutil.TempDir("", "hcs")
111
+		if err != nil {
112
+			return nil, err
113
+		}
114
+		err = ExportLayer(info, layerId, path, parentLayerPaths)
115
+		if err != nil {
116
+			os.RemoveAll(path)
117
+			return nil, err
118
+		}
119
+		return &legacyLayerReaderWrapper{NewLegacyLayerReader(path)}, nil
120
+	}
121
+
122
+	layers, err := layerPathsToDescriptors(parentLayerPaths)
123
+	if err != nil {
124
+		return nil, err
125
+	}
126
+	infop, err := convertDriverInfo(info)
127
+	if err != nil {
128
+		return nil, err
129
+	}
130
+	r := &FilterLayerReader{}
131
+	err = exportLayerBegin(&infop, layerId, layers, &r.context)
132
+	if err != nil {
133
+		return nil, makeError(err, "ExportLayerBegin", "")
134
+	}
135
+	runtime.SetFinalizer(r, func(r *FilterLayerReader) { r.Close() })
136
+	return r, err
137
+}
138
+
139
+type legacyLayerReaderWrapper struct {
140
+	*LegacyLayerReader
141
+}
142
+
143
+func (r *legacyLayerReaderWrapper) Close() error {
144
+	err := r.LegacyLayerReader.Close()
145
+	os.RemoveAll(r.root)
146
+	return err
147
+}
... ...
@@ -28,6 +28,16 @@ import (
28 28
 //sys prepareLayer(info *driverInfo, id string, descriptors []WC_LAYER_DESCRIPTOR) (hr error) = vmcompute.PrepareLayer?
29 29
 //sys unprepareLayer(info *driverInfo, id string) (hr error) = vmcompute.UnprepareLayer?
30 30
 
31
+//sys importLayerBegin(info *driverInfo, id string, descriptors []WC_LAYER_DESCRIPTOR, context *uintptr) (hr error) = vmcompute.ImportLayerBegin?
32
+//sys importLayerNext(context uintptr, fileName string, fileInfo *winio.FileBasicInfo) (hr error) = vmcompute.ImportLayerNext?
33
+//sys importLayerWrite(context uintptr, buffer []byte) (hr error) = vmcompute.ImportLayerWrite?
34
+//sys importLayerEnd(context uintptr) (hr error) = vmcompute.ImportLayerEnd?
35
+
36
+//sys exportLayerBegin(info *driverInfo, id string, descriptors []WC_LAYER_DESCRIPTOR, context *uintptr) (hr error) = vmcompute.ExportLayerBegin?
37
+//sys exportLayerNext(context uintptr, fileName **uint16, fileInfo *winio.FileBasicInfo, fileSize *int64, deleted *uint32) (hr error) = vmcompute.ExportLayerNext?
38
+//sys exportLayerRead(context uintptr, buffer []byte, bytesRead *uint32) (hr error) = vmcompute.ExportLayerRead?
39
+//sys exportLayerEnd(context uintptr) (hr error) = vmcompute.ExportLayerEnd?
40
+
31 41
 //sys createComputeSystem(id string, configuration string) (hr error) = vmcompute.CreateComputeSystem?
32 42
 //sys createProcessWithStdHandlesInComputeSystem(id string, paramsJson string, pid *uint32, stdin *syscall.Handle, stdout *syscall.Handle, stderr *syscall.Handle) (hr error) = vmcompute.CreateProcessWithStdHandlesInComputeSystem?
33 43
 //sys resizeConsoleInComputeSystem(id string, pid uint32, height uint16, width uint16, flags uint32) (hr error) = vmcompute.ResizeConsoleInComputeSystem?
... ...
@@ -57,16 +67,15 @@ type HcsError struct {
57 57
 	Err   error
58 58
 }
59 59
 
60
-func makeError(err error, title, rest string) *HcsError {
61
-	if hr, ok := err.(syscall.Errno); ok {
62
-		// Convert the HRESULT to a Win32 error code so that it better matches
63
-		// error codes returned from go and other packages.
64
-		err = syscall.Errno(win32FromHresult(uint32(hr)))
60
+func makeError(err error, title, rest string) error {
61
+	// Pass through DLL errors directly since they do not originate from HCS.
62
+	if _, ok := err.(*syscall.DLLError); ok {
63
+		return err
65 64
 	}
66 65
 	return &HcsError{title, rest, err}
67 66
 }
68 67
 
69
-func makeErrorf(err error, title, format string, a ...interface{}) *HcsError {
68
+func makeErrorf(err error, title, format string, a ...interface{}) error {
70 69
 	return makeError(err, title, fmt.Sprintf(format, a...))
71 70
 }
72 71
 
... ...
@@ -75,12 +84,12 @@ func win32FromError(err error) uint32 {
75 75
 		return win32FromError(herr.Err)
76 76
 	}
77 77
 	if code, ok := err.(syscall.Errno); ok {
78
-		return win32FromHresult(uint32(code))
78
+		return uint32(code)
79 79
 	}
80 80
 	return uint32(ERROR_GEN_FAILURE)
81 81
 }
82 82
 
83
-func win32FromHresult(hr uint32) uint32 {
83
+func win32FromHresult(hr uintptr) uintptr {
84 84
 	if hr&0x1fff0000 == 0x00070000 {
85 85
 		return hr & 0xffff
86 86
 	}
... ...
@@ -88,7 +97,18 @@ func win32FromHresult(hr uint32) uint32 {
88 88
 }
89 89
 
90 90
 func (e *HcsError) Error() string {
91
-	return fmt.Sprintf("%s- Win32 API call returned error r1=0x%x err=%s%s", e.title, win32FromError(e.Err), e.Err, e.rest)
91
+	s := e.title
92
+	if len(s) > 0 && s[len(s)-1] != ' ' {
93
+		s += " "
94
+	}
95
+	s += fmt.Sprintf("failed in Win32: %s (0x%x)", e.Err, win32FromError(e.Err))
96
+	if e.rest != "" {
97
+		if e.rest[0] != ' ' {
98
+			s += " "
99
+		}
100
+		s += e.rest
101
+	}
102
+	return s
92 103
 }
93 104
 
94 105
 func convertAndFreeCoTaskMemString(buffer *uint16) string {
... ...
@@ -1,6 +1,13 @@
1 1
 package hcsshim
2 2
 
3
-import "github.com/Sirupsen/logrus"
3
+import (
4
+	"io/ioutil"
5
+	"os"
6
+	"runtime"
7
+
8
+	"github.com/Microsoft/go-winio"
9
+	"github.com/Sirupsen/logrus"
10
+)
4 11
 
5 12
 // ImportLayer will take the contents of the folder at importFolderPath and import
6 13
 // that into a layer with the id layerId.  Note that in order to correctly populate
... ...
@@ -33,3 +40,114 @@ func ImportLayer(info DriverInfo, layerId string, importFolderPath string, paren
33 33
 	logrus.Debugf(title+"succeeded flavour=%d layerId=%s folder=%s", info.Flavour, layerId, importFolderPath)
34 34
 	return nil
35 35
 }
36
+
37
+type LayerWriter interface {
38
+	Add(name string, fileInfo *winio.FileBasicInfo) error
39
+	Remove(name string) error
40
+	Write(b []byte) (int, error)
41
+	Close() error
42
+}
43
+
44
+// FilterLayerWriter provides an interface to write the contents of a layer to the file system.
45
+type FilterLayerWriter struct {
46
+	context uintptr
47
+}
48
+
49
+// Add adds a file or directory to the layer. The file's parent directory must have already been added.
50
+//
51
+// name contains the file's relative path. fileInfo contains file times and file attributes; the rest
52
+// of the file metadata and the file data must be written as a Win32 backup stream to the Write() method.
53
+// winio.BackupStreamWriter can be used to facilitate this.
54
+func (w *FilterLayerWriter) Add(name string, fileInfo *winio.FileBasicInfo) error {
55
+	if name[0] != '\\' {
56
+		name = `\` + name
57
+	}
58
+	err := importLayerNext(w.context, name, fileInfo)
59
+	if err != nil {
60
+		return makeError(err, "ImportLayerNext", "")
61
+	}
62
+	return nil
63
+}
64
+
65
+// Remove removes a file from the layer. The file must have been present in the parent layer.
66
+//
67
+// name contains the file's relative path.
68
+func (w *FilterLayerWriter) Remove(name string) error {
69
+	if name[0] != '\\' {
70
+		name = `\` + name
71
+	}
72
+	err := importLayerNext(w.context, name, nil)
73
+	if err != nil {
74
+		return makeError(err, "ImportLayerNext", "")
75
+	}
76
+	return nil
77
+}
78
+
79
+// Write writes more backup stream data to the current file.
80
+func (w *FilterLayerWriter) Write(b []byte) (int, error) {
81
+	err := importLayerWrite(w.context, b)
82
+	if err != nil {
83
+		err = makeError(err, "ImportLayerWrite", "")
84
+		return 0, err
85
+	}
86
+	return len(b), err
87
+}
88
+
89
+// Close completes the layer write operation. The error must be checked to ensure that the
90
+// operation was successful.
91
+func (w *FilterLayerWriter) Close() (err error) {
92
+	if w.context != 0 {
93
+		err = importLayerEnd(w.context)
94
+		if err != nil {
95
+			err = makeError(err, "ImportLayerEnd", "")
96
+		}
97
+		w.context = 0
98
+	}
99
+	return
100
+}
101
+
102
+type legacyLayerWriterWrapper struct {
103
+	*LegacyLayerWriter
104
+	info             DriverInfo
105
+	layerId          string
106
+	parentLayerPaths []string
107
+}
108
+
109
+func (r *legacyLayerWriterWrapper) Close() error {
110
+	err := r.LegacyLayerWriter.Close()
111
+	if err == nil {
112
+		err = ImportLayer(r.info, r.layerId, r.root, r.parentLayerPaths)
113
+	}
114
+	os.RemoveAll(r.root)
115
+	return err
116
+}
117
+
118
+// NewLayerWriter returns a new layer writer for creating a layer on disk.
119
+func NewLayerWriter(info DriverInfo, layerId string, parentLayerPaths []string) (LayerWriter, error) {
120
+	if procImportLayerBegin.Find() != nil {
121
+		// The new layer reader is not available on this Windows build. Fall back to the
122
+		// legacy export code path.
123
+		path, err := ioutil.TempDir("", "hcs")
124
+		if err != nil {
125
+			return nil, err
126
+		}
127
+		return &legacyLayerWriterWrapper{NewLegacyLayerWriter(path), info, layerId, parentLayerPaths}, nil
128
+	}
129
+	layers, err := layerPathsToDescriptors(parentLayerPaths)
130
+	if err != nil {
131
+		return nil, err
132
+	}
133
+
134
+	infop, err := convertDriverInfo(info)
135
+	if err != nil {
136
+		return nil, err
137
+	}
138
+
139
+	w := &FilterLayerWriter{}
140
+	err = importLayerBegin(&infop, layerId, layers, &w.context)
141
+	if err != nil {
142
+		return nil, makeError(err, "ImportLayerStart", "")
143
+	}
144
+	runtime.SetFinalizer(w, func(w *FilterLayerWriter) { w.Close() })
145
+	return w, nil
146
+}
36 147
new file mode 100644
... ...
@@ -0,0 +1,397 @@
0
+package hcsshim
1
+
2
+import (
3
+	"bufio"
4
+	"encoding/binary"
5
+	"errors"
6
+	"io"
7
+	"os"
8
+	"path/filepath"
9
+	"strings"
10
+	"syscall"
11
+
12
+	"github.com/Microsoft/go-winio"
13
+)
14
+
15
+var errorIterationCanceled = errors.New("")
16
+
17
+func openFileOrDir(path string, mode uint32, createDisposition uint32) (file *os.File, err error) {
18
+	winPath, err := syscall.UTF16FromString(path)
19
+	if err != nil {
20
+		return
21
+	}
22
+	h, err := syscall.CreateFile(&winPath[0], mode, syscall.FILE_SHARE_READ, nil, createDisposition, syscall.FILE_FLAG_BACKUP_SEMANTICS, 0)
23
+	if err != nil {
24
+		err = &os.PathError{"open", path, err}
25
+		return
26
+	}
27
+	file = os.NewFile(uintptr(h), path)
28
+	return
29
+}
30
+
31
+type fileEntry struct {
32
+	path string
33
+	fi   os.FileInfo
34
+	err  error
35
+}
36
+
37
+type LegacyLayerReader struct {
38
+	root         string
39
+	result       chan *fileEntry
40
+	proceed      chan bool
41
+	currentFile  *os.File
42
+	backupReader *winio.BackupFileReader
43
+	isTP4Format  bool
44
+}
45
+
46
+// NewLegacyLayerReader returns a new LayerReader that can read the Windows
47
+// TP4 transport format from disk.
48
+func NewLegacyLayerReader(root string) *LegacyLayerReader {
49
+	r := &LegacyLayerReader{
50
+		root:        root,
51
+		result:      make(chan *fileEntry),
52
+		proceed:     make(chan bool),
53
+		isTP4Format: IsTP4(),
54
+	}
55
+	go r.walk()
56
+	return r
57
+}
58
+
59
+func readTombstones(path string) (map[string]([]string), error) {
60
+	tf, err := os.Open(filepath.Join(path, "tombstones.txt"))
61
+	if err != nil {
62
+		return nil, err
63
+	}
64
+	defer tf.Close()
65
+	s := bufio.NewScanner(tf)
66
+	if !s.Scan() || s.Text() != "\xef\xbb\xbfVersion 1.0" {
67
+		return nil, errors.New("Invalid tombstones file")
68
+	}
69
+
70
+	ts := make(map[string]([]string))
71
+	for s.Scan() {
72
+		t := s.Text()[1:] // skip leading `\`
73
+		dir := filepath.Dir(t)
74
+		ts[dir] = append(ts[dir], t)
75
+	}
76
+	if err = s.Err(); err != nil {
77
+		return nil, err
78
+	}
79
+
80
+	return ts, nil
81
+}
82
+
83
+func (r *LegacyLayerReader) walk() {
84
+	defer close(r.result)
85
+	if !<-r.proceed {
86
+		return
87
+	}
88
+
89
+	ts, err := readTombstones(r.root)
90
+	if err != nil {
91
+		goto ErrorLoop
92
+	}
93
+
94
+	err = filepath.Walk(r.root, func(path string, info os.FileInfo, err error) error {
95
+		if err != nil {
96
+			return err
97
+		}
98
+		if path == r.root || path == filepath.Join(r.root, "tombstones.txt") || strings.HasSuffix(path, ".$wcidirs$") {
99
+			return nil
100
+		}
101
+		r.result <- &fileEntry{path, info, nil}
102
+		if !<-r.proceed {
103
+			return errorIterationCanceled
104
+		}
105
+
106
+		// List all the tombstones.
107
+		if info.IsDir() {
108
+			relPath, err := filepath.Rel(r.root, path)
109
+			if err != nil {
110
+				return err
111
+			}
112
+			if dts, ok := ts[relPath]; ok {
113
+				for _, t := range dts {
114
+					r.result <- &fileEntry{t, nil, nil}
115
+					if !<-r.proceed {
116
+						return errorIterationCanceled
117
+					}
118
+				}
119
+			}
120
+		}
121
+		return nil
122
+	})
123
+	if err == errorIterationCanceled {
124
+		return
125
+	}
126
+	if err == nil {
127
+		err = io.EOF
128
+	}
129
+
130
+ErrorLoop:
131
+	for {
132
+		r.result <- &fileEntry{err: err}
133
+		if !<-r.proceed {
134
+			break
135
+		}
136
+	}
137
+}
138
+
139
+func (r *LegacyLayerReader) reset() {
140
+	if r.backupReader != nil {
141
+		r.backupReader.Close()
142
+		r.backupReader = nil
143
+	}
144
+	if r.currentFile != nil {
145
+		r.currentFile.Close()
146
+		r.currentFile = nil
147
+	}
148
+}
149
+
150
+func findBackupStreamSize(r io.Reader) (int64, error) {
151
+	br := winio.NewBackupStreamReader(r)
152
+	for {
153
+		hdr, err := br.Next()
154
+		if err != nil {
155
+			if err == io.EOF {
156
+				err = nil
157
+			}
158
+			return 0, err
159
+		}
160
+		if hdr.Id == winio.BackupData {
161
+			return hdr.Size, nil
162
+		}
163
+	}
164
+}
165
+
166
+func (r *LegacyLayerReader) Next() (path string, size int64, fileInfo *winio.FileBasicInfo, err error) {
167
+	r.reset()
168
+	r.proceed <- true
169
+	fe := <-r.result
170
+	if fe == nil {
171
+		err = errors.New("LegacyLayerReader closed")
172
+		return
173
+	}
174
+	if fe.err != nil {
175
+		err = fe.err
176
+		return
177
+	}
178
+
179
+	path, err = filepath.Rel(r.root, fe.path)
180
+	if err != nil {
181
+		return
182
+	}
183
+
184
+	if fe.fi == nil {
185
+		// This is a tombstone. Return a nil fileInfo.
186
+		return
187
+	}
188
+
189
+	if fe.fi.IsDir() && strings.HasPrefix(path, `Files\`) {
190
+		fe.path += ".$wcidirs$"
191
+	}
192
+
193
+	f, err := openFileOrDir(fe.path, syscall.GENERIC_READ, syscall.OPEN_EXISTING)
194
+	if err != nil {
195
+		return
196
+	}
197
+	defer func() {
198
+		if f != nil {
199
+			f.Close()
200
+		}
201
+	}()
202
+
203
+	fileInfo, err = winio.GetFileBasicInfo(f)
204
+	if err != nil {
205
+		return
206
+	}
207
+
208
+	if !strings.HasPrefix(path, `Files\`) {
209
+		size = fe.fi.Size()
210
+		r.backupReader = winio.NewBackupFileReader(f, false)
211
+		if path == "Hives" || path == "Files" {
212
+			// The Hives directory has a non-deterministic file time because of the
213
+			// nature of the import process. Use the times from System_Delta.
214
+			var g *os.File
215
+			g, err = os.Open(filepath.Join(r.root, `Hives\System_Delta`))
216
+			if err != nil {
217
+				return
218
+			}
219
+			attr := fileInfo.FileAttributes
220
+			fileInfo, err = winio.GetFileBasicInfo(g)
221
+			g.Close()
222
+			if err != nil {
223
+				return
224
+			}
225
+			fileInfo.FileAttributes = attr
226
+		}
227
+
228
+		// The creation time and access time get reset for files outside of the Files path.
229
+		fileInfo.CreationTime = fileInfo.LastWriteTime
230
+		fileInfo.LastAccessTime = fileInfo.LastWriteTime
231
+
232
+	} else {
233
+		beginning := int64(0)
234
+		if !r.isTP4Format {
235
+			// In TP5, the file attributes were added before the backup stream
236
+			var attr uint32
237
+			err = binary.Read(f, binary.LittleEndian, &attr)
238
+			if err != nil {
239
+				return
240
+			}
241
+			fileInfo.FileAttributes = uintptr(attr)
242
+			beginning = 4
243
+		}
244
+
245
+		// Find the accurate file size.
246
+		if !fe.fi.IsDir() {
247
+			size, err = findBackupStreamSize(f)
248
+			if err != nil {
249
+				err = &os.PathError{"findBackupStreamSize", fe.path, err}
250
+				return
251
+			}
252
+		}
253
+
254
+		// Return back to the beginning of the backup stream.
255
+		_, err = f.Seek(beginning, 0)
256
+		if err != nil {
257
+			return
258
+		}
259
+	}
260
+
261
+	r.currentFile = f
262
+	f = nil
263
+	return
264
+}
265
+
266
+func (r *LegacyLayerReader) Read(b []byte) (int, error) {
267
+	if r.backupReader == nil {
268
+		if r.currentFile == nil {
269
+			return 0, io.EOF
270
+		}
271
+		return r.currentFile.Read(b)
272
+	}
273
+	return r.backupReader.Read(b)
274
+}
275
+
276
+func (r *LegacyLayerReader) Close() error {
277
+	r.proceed <- false
278
+	<-r.result
279
+	r.reset()
280
+	return nil
281
+}
282
+
283
+type LegacyLayerWriter struct {
284
+	root         string
285
+	currentFile  *os.File
286
+	backupWriter *winio.BackupFileWriter
287
+	tombstones   []string
288
+	isTP4Format  bool
289
+}
290
+
291
+// NewLegacyLayerWriter returns a LayerWriter that can write the TP4 transport format
292
+// to disk.
293
+func NewLegacyLayerWriter(root string) *LegacyLayerWriter {
294
+	return &LegacyLayerWriter{
295
+		root:        root,
296
+		isTP4Format: IsTP4(),
297
+	}
298
+}
299
+
300
+func (w *LegacyLayerWriter) reset() {
301
+	if w.backupWriter != nil {
302
+		w.backupWriter.Close()
303
+		w.backupWriter = nil
304
+	}
305
+	if w.currentFile != nil {
306
+		w.currentFile.Close()
307
+		w.currentFile = nil
308
+	}
309
+}
310
+
311
+func (w *LegacyLayerWriter) Add(name string, fileInfo *winio.FileBasicInfo) error {
312
+	w.reset()
313
+	path := filepath.Join(w.root, name)
314
+
315
+	createDisposition := uint32(syscall.CREATE_NEW)
316
+	if (fileInfo.FileAttributes & syscall.FILE_ATTRIBUTE_DIRECTORY) != 0 {
317
+		err := os.Mkdir(path, 0)
318
+		if err != nil {
319
+			return err
320
+		}
321
+		if strings.HasPrefix(name, `Files\`) {
322
+			path += ".$wcidirs$"
323
+		} else {
324
+			createDisposition = syscall.OPEN_EXISTING
325
+		}
326
+	}
327
+
328
+	f, err := openFileOrDir(path, syscall.GENERIC_READ|syscall.GENERIC_WRITE, createDisposition)
329
+	if err != nil {
330
+		return err
331
+	}
332
+	defer func() {
333
+		if f != nil {
334
+			f.Close()
335
+			os.Remove(path)
336
+		}
337
+	}()
338
+
339
+	strippedFi := *fileInfo
340
+	strippedFi.FileAttributes = 0
341
+	err = winio.SetFileBasicInfo(f, &strippedFi)
342
+	if err != nil {
343
+		return err
344
+	}
345
+
346
+	if !strings.HasPrefix(name, `Files\`) {
347
+		w.backupWriter = winio.NewBackupFileWriter(f, false)
348
+	} else {
349
+		if !w.isTP4Format {
350
+			// In TP5, the file attributes were added to the header
351
+			err = binary.Write(f, binary.LittleEndian, uint32(fileInfo.FileAttributes))
352
+			if err != nil {
353
+				return err
354
+			}
355
+		}
356
+	}
357
+
358
+	w.currentFile = f
359
+	f = nil
360
+	return nil
361
+}
362
+
363
+func (w *LegacyLayerWriter) Remove(name string) error {
364
+	w.tombstones = append(w.tombstones, name)
365
+	return nil
366
+}
367
+
368
+func (w *LegacyLayerWriter) Write(b []byte) (int, error) {
369
+	if w.backupWriter == nil {
370
+		if w.currentFile == nil {
371
+			return 0, errors.New("closed")
372
+		}
373
+		return w.currentFile.Write(b)
374
+	}
375
+	return w.backupWriter.Write(b)
376
+}
377
+
378
+func (w *LegacyLayerWriter) Close() error {
379
+	w.reset()
380
+	tf, err := os.Create(filepath.Join(w.root, "tombstones.txt"))
381
+	if err != nil {
382
+		return err
383
+	}
384
+	defer tf.Close()
385
+	_, err = tf.Write([]byte("\xef\xbb\xbfVersion 1.0\n"))
386
+	if err != nil {
387
+		return err
388
+	}
389
+	for _, t := range w.tombstones {
390
+		_, err = tf.Write([]byte(filepath.Join(`\`, t) + "\n"))
391
+		if err != nil {
392
+			return err
393
+		}
394
+	}
395
+	return nil
396
+}
... ...
@@ -294,6 +294,9 @@ func (r *Rets) SetErrorCode() string {
294 294
 	const code = `if r0 != 0 {
295 295
 		%s = %sErrno(r0)
296 296
 	}`
297
+	const hrCode = `if int32(r0) < 0 {
298
+		%s = %sErrno(win32FromHresult(r0))
299
+	}`
297 300
 	if r.Name == "" && !r.ReturnsError {
298 301
 		return ""
299 302
 	}
... ...
@@ -301,7 +304,11 @@ func (r *Rets) SetErrorCode() string {
301 301
 		return r.useLongHandleErrorCode("r1")
302 302
 	}
303 303
 	if r.Type == "error" {
304
-		return fmt.Sprintf(code, r.Name, syscalldot())
304
+		if r.Name == "hr" {
305
+			return fmt.Sprintf(hrCode, r.Name, syscalldot())
306
+		} else {
307
+			return fmt.Sprintf(code, r.Name, syscalldot())
308
+		}
305 309
 	}
306 310
 	s := ""
307 311
 	switch {
308 312
new file mode 100644
... ...
@@ -0,0 +1,7 @@
0
+package hcsshim
1
+
2
+// IsTP4 returns whether the currently running Windows build is at least TP4.
3
+func IsTP4() bool {
4
+	// HNSCall was not present in TP4
5
+	return procHNSCall.Find() != nil
6
+}
... ...
@@ -2,7 +2,11 @@
2 2
 
3 3
 package hcsshim
4 4
 
5
-import "unsafe"
5
+import (
6
+	"unsafe"
7
+
8
+	"github.com/Microsoft/go-winio"
9
+)
6 10
 import "syscall"
7 11
 
8 12
 var _ unsafe.Pointer
... ...
@@ -26,6 +30,14 @@ var (
26 26
 	procNameToGuid                                 = modvmcompute.NewProc("NameToGuid")
27 27
 	procPrepareLayer                               = modvmcompute.NewProc("PrepareLayer")
28 28
 	procUnprepareLayer                             = modvmcompute.NewProc("UnprepareLayer")
29
+	procImportLayerBegin                           = modvmcompute.NewProc("ImportLayerBegin")
30
+	procImportLayerNext                            = modvmcompute.NewProc("ImportLayerNext")
31
+	procImportLayerWrite                           = modvmcompute.NewProc("ImportLayerWrite")
32
+	procImportLayerEnd                             = modvmcompute.NewProc("ImportLayerEnd")
33
+	procExportLayerBegin                           = modvmcompute.NewProc("ExportLayerBegin")
34
+	procExportLayerNext                            = modvmcompute.NewProc("ExportLayerNext")
35
+	procExportLayerRead                            = modvmcompute.NewProc("ExportLayerRead")
36
+	procExportLayerEnd                             = modvmcompute.NewProc("ExportLayerEnd")
29 37
 	procCreateComputeSystem                        = modvmcompute.NewProc("CreateComputeSystem")
30 38
 	procCreateProcessWithStdHandlesInComputeSystem = modvmcompute.NewProc("CreateProcessWithStdHandlesInComputeSystem")
31 39
 	procResizeConsoleInComputeSystem               = modvmcompute.NewProc("ResizeConsoleInComputeSystem")
... ...
@@ -56,8 +68,8 @@ func _activateLayer(info *driverInfo, id *uint16) (hr error) {
56 56
 		return
57 57
 	}
58 58
 	r0, _, _ := syscall.Syscall(procActivateLayer.Addr(), 2, uintptr(unsafe.Pointer(info)), uintptr(unsafe.Pointer(id)), 0)
59
-	if r0 != 0 {
60
-		hr = syscall.Errno(r0)
59
+	if int32(r0) < 0 {
60
+		hr = syscall.Errno(win32FromHresult(r0))
61 61
 	}
62 62
 	return
63 63
 }
... ...
@@ -85,8 +97,8 @@ func _copyLayer(info *driverInfo, srcId *uint16, dstId *uint16, descriptors []WC
85 85
 		return
86 86
 	}
87 87
 	r0, _, _ := syscall.Syscall6(procCopyLayer.Addr(), 5, uintptr(unsafe.Pointer(info)), uintptr(unsafe.Pointer(srcId)), uintptr(unsafe.Pointer(dstId)), uintptr(unsafe.Pointer(_p2)), uintptr(len(descriptors)), 0)
88
-	if r0 != 0 {
89
-		hr = syscall.Errno(r0)
88
+	if int32(r0) < 0 {
89
+		hr = syscall.Errno(win32FromHresult(r0))
90 90
 	}
91 91
 	return
92 92
 }
... ...
@@ -110,8 +122,8 @@ func _createLayer(info *driverInfo, id *uint16, parent *uint16) (hr error) {
110 110
 		return
111 111
 	}
112 112
 	r0, _, _ := syscall.Syscall(procCreateLayer.Addr(), 3, uintptr(unsafe.Pointer(info)), uintptr(unsafe.Pointer(id)), uintptr(unsafe.Pointer(parent)))
113
-	if r0 != 0 {
114
-		hr = syscall.Errno(r0)
113
+	if int32(r0) < 0 {
114
+		hr = syscall.Errno(win32FromHresult(r0))
115 115
 	}
116 116
 	return
117 117
 }
... ...
@@ -139,8 +151,8 @@ func _createSandboxLayer(info *driverInfo, id *uint16, parent *uint16, descripto
139 139
 		return
140 140
 	}
141 141
 	r0, _, _ := syscall.Syscall6(procCreateSandboxLayer.Addr(), 5, uintptr(unsafe.Pointer(info)), uintptr(unsafe.Pointer(id)), uintptr(unsafe.Pointer(parent)), uintptr(unsafe.Pointer(_p2)), uintptr(len(descriptors)), 0)
142
-	if r0 != 0 {
143
-		hr = syscall.Errno(r0)
142
+	if int32(r0) < 0 {
143
+		hr = syscall.Errno(win32FromHresult(r0))
144 144
 	}
145 145
 	return
146 146
 }
... ...
@@ -159,8 +171,8 @@ func _deactivateLayer(info *driverInfo, id *uint16) (hr error) {
159 159
 		return
160 160
 	}
161 161
 	r0, _, _ := syscall.Syscall(procDeactivateLayer.Addr(), 2, uintptr(unsafe.Pointer(info)), uintptr(unsafe.Pointer(id)), 0)
162
-	if r0 != 0 {
163
-		hr = syscall.Errno(r0)
162
+	if int32(r0) < 0 {
163
+		hr = syscall.Errno(win32FromHresult(r0))
164 164
 	}
165 165
 	return
166 166
 }
... ...
@@ -179,8 +191,8 @@ func _destroyLayer(info *driverInfo, id *uint16) (hr error) {
179 179
 		return
180 180
 	}
181 181
 	r0, _, _ := syscall.Syscall(procDestroyLayer.Addr(), 2, uintptr(unsafe.Pointer(info)), uintptr(unsafe.Pointer(id)), 0)
182
-	if r0 != 0 {
183
-		hr = syscall.Errno(r0)
182
+	if int32(r0) < 0 {
183
+		hr = syscall.Errno(win32FromHresult(r0))
184 184
 	}
185 185
 	return
186 186
 }
... ...
@@ -208,8 +220,8 @@ func _exportLayer(info *driverInfo, id *uint16, path *uint16, descriptors []WC_L
208 208
 		return
209 209
 	}
210 210
 	r0, _, _ := syscall.Syscall6(procExportLayer.Addr(), 5, uintptr(unsafe.Pointer(info)), uintptr(unsafe.Pointer(id)), uintptr(unsafe.Pointer(path)), uintptr(unsafe.Pointer(_p2)), uintptr(len(descriptors)), 0)
211
-	if r0 != 0 {
212
-		hr = syscall.Errno(r0)
211
+	if int32(r0) < 0 {
212
+		hr = syscall.Errno(win32FromHresult(r0))
213 213
 	}
214 214
 	return
215 215
 }
... ...
@@ -228,8 +240,8 @@ func _getLayerMountPath(info *driverInfo, id *uint16, length *uintptr, buffer *u
228 228
 		return
229 229
 	}
230 230
 	r0, _, _ := syscall.Syscall6(procGetLayerMountPath.Addr(), 4, uintptr(unsafe.Pointer(info)), uintptr(unsafe.Pointer(id)), uintptr(unsafe.Pointer(length)), uintptr(unsafe.Pointer(buffer)), 0, 0)
231
-	if r0 != 0 {
232
-		hr = syscall.Errno(r0)
231
+	if int32(r0) < 0 {
232
+		hr = syscall.Errno(win32FromHresult(r0))
233 233
 	}
234 234
 	return
235 235
 }
... ...
@@ -239,8 +251,8 @@ func getBaseImages(buffer **uint16) (hr error) {
239 239
 		return
240 240
 	}
241 241
 	r0, _, _ := syscall.Syscall(procGetBaseImages.Addr(), 1, uintptr(unsafe.Pointer(buffer)), 0, 0)
242
-	if r0 != 0 {
243
-		hr = syscall.Errno(r0)
242
+	if int32(r0) < 0 {
243
+		hr = syscall.Errno(win32FromHresult(r0))
244 244
 	}
245 245
 	return
246 246
 }
... ...
@@ -268,8 +280,8 @@ func _importLayer(info *driverInfo, id *uint16, path *uint16, descriptors []WC_L
268 268
 		return
269 269
 	}
270 270
 	r0, _, _ := syscall.Syscall6(procImportLayer.Addr(), 5, uintptr(unsafe.Pointer(info)), uintptr(unsafe.Pointer(id)), uintptr(unsafe.Pointer(path)), uintptr(unsafe.Pointer(_p2)), uintptr(len(descriptors)), 0)
271
-	if r0 != 0 {
272
-		hr = syscall.Errno(r0)
271
+	if int32(r0) < 0 {
272
+		hr = syscall.Errno(win32FromHresult(r0))
273 273
 	}
274 274
 	return
275 275
 }
... ...
@@ -288,8 +300,8 @@ func _layerExists(info *driverInfo, id *uint16, exists *uint32) (hr error) {
288 288
 		return
289 289
 	}
290 290
 	r0, _, _ := syscall.Syscall(procLayerExists.Addr(), 3, uintptr(unsafe.Pointer(info)), uintptr(unsafe.Pointer(id)), uintptr(unsafe.Pointer(exists)))
291
-	if r0 != 0 {
292
-		hr = syscall.Errno(r0)
291
+	if int32(r0) < 0 {
292
+		hr = syscall.Errno(win32FromHresult(r0))
293 293
 	}
294 294
 	return
295 295
 }
... ...
@@ -308,8 +320,8 @@ func _nameToGuid(name *uint16, guid *GUID) (hr error) {
308 308
 		return
309 309
 	}
310 310
 	r0, _, _ := syscall.Syscall(procNameToGuid.Addr(), 2, uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(guid)), 0)
311
-	if r0 != 0 {
312
-		hr = syscall.Errno(r0)
311
+	if int32(r0) < 0 {
312
+		hr = syscall.Errno(win32FromHresult(r0))
313 313
 	}
314 314
 	return
315 315
 }
... ...
@@ -332,8 +344,8 @@ func _prepareLayer(info *driverInfo, id *uint16, descriptors []WC_LAYER_DESCRIPT
332 332
 		return
333 333
 	}
334 334
 	r0, _, _ := syscall.Syscall6(procPrepareLayer.Addr(), 4, uintptr(unsafe.Pointer(info)), uintptr(unsafe.Pointer(id)), uintptr(unsafe.Pointer(_p1)), uintptr(len(descriptors)), 0, 0)
335
-	if r0 != 0 {
336
-		hr = syscall.Errno(r0)
335
+	if int32(r0) < 0 {
336
+		hr = syscall.Errno(win32FromHresult(r0))
337 337
 	}
338 338
 	return
339 339
 }
... ...
@@ -352,8 +364,139 @@ func _unprepareLayer(info *driverInfo, id *uint16) (hr error) {
352 352
 		return
353 353
 	}
354 354
 	r0, _, _ := syscall.Syscall(procUnprepareLayer.Addr(), 2, uintptr(unsafe.Pointer(info)), uintptr(unsafe.Pointer(id)), 0)
355
-	if r0 != 0 {
356
-		hr = syscall.Errno(r0)
355
+	if int32(r0) < 0 {
356
+		hr = syscall.Errno(win32FromHresult(r0))
357
+	}
358
+	return
359
+}
360
+
361
+func importLayerBegin(info *driverInfo, id string, descriptors []WC_LAYER_DESCRIPTOR, context *uintptr) (hr error) {
362
+	var _p0 *uint16
363
+	_p0, hr = syscall.UTF16PtrFromString(id)
364
+	if hr != nil {
365
+		return
366
+	}
367
+	return _importLayerBegin(info, _p0, descriptors, context)
368
+}
369
+
370
+func _importLayerBegin(info *driverInfo, id *uint16, descriptors []WC_LAYER_DESCRIPTOR, context *uintptr) (hr error) {
371
+	var _p1 *WC_LAYER_DESCRIPTOR
372
+	if len(descriptors) > 0 {
373
+		_p1 = &descriptors[0]
374
+	}
375
+	if hr = procImportLayerBegin.Find(); hr != nil {
376
+		return
377
+	}
378
+	r0, _, _ := syscall.Syscall6(procImportLayerBegin.Addr(), 5, uintptr(unsafe.Pointer(info)), uintptr(unsafe.Pointer(id)), uintptr(unsafe.Pointer(_p1)), uintptr(len(descriptors)), uintptr(unsafe.Pointer(context)), 0)
379
+	if int32(r0) < 0 {
380
+		hr = syscall.Errno(win32FromHresult(r0))
381
+	}
382
+	return
383
+}
384
+
385
+func importLayerNext(context uintptr, fileName string, fileInfo *winio.FileBasicInfo) (hr error) {
386
+	var _p0 *uint16
387
+	_p0, hr = syscall.UTF16PtrFromString(fileName)
388
+	if hr != nil {
389
+		return
390
+	}
391
+	return _importLayerNext(context, _p0, fileInfo)
392
+}
393
+
394
+func _importLayerNext(context uintptr, fileName *uint16, fileInfo *winio.FileBasicInfo) (hr error) {
395
+	if hr = procImportLayerNext.Find(); hr != nil {
396
+		return
397
+	}
398
+	r0, _, _ := syscall.Syscall(procImportLayerNext.Addr(), 3, uintptr(context), uintptr(unsafe.Pointer(fileName)), uintptr(unsafe.Pointer(fileInfo)))
399
+	if int32(r0) < 0 {
400
+		hr = syscall.Errno(win32FromHresult(r0))
401
+	}
402
+	return
403
+}
404
+
405
+func importLayerWrite(context uintptr, buffer []byte) (hr error) {
406
+	var _p0 *byte
407
+	if len(buffer) > 0 {
408
+		_p0 = &buffer[0]
409
+	}
410
+	if hr = procImportLayerWrite.Find(); hr != nil {
411
+		return
412
+	}
413
+	r0, _, _ := syscall.Syscall(procImportLayerWrite.Addr(), 3, uintptr(context), uintptr(unsafe.Pointer(_p0)), uintptr(len(buffer)))
414
+	if int32(r0) < 0 {
415
+		hr = syscall.Errno(win32FromHresult(r0))
416
+	}
417
+	return
418
+}
419
+
420
+func importLayerEnd(context uintptr) (hr error) {
421
+	if hr = procImportLayerEnd.Find(); hr != nil {
422
+		return
423
+	}
424
+	r0, _, _ := syscall.Syscall(procImportLayerEnd.Addr(), 1, uintptr(context), 0, 0)
425
+	if int32(r0) < 0 {
426
+		hr = syscall.Errno(win32FromHresult(r0))
427
+	}
428
+	return
429
+}
430
+
431
+func exportLayerBegin(info *driverInfo, id string, descriptors []WC_LAYER_DESCRIPTOR, context *uintptr) (hr error) {
432
+	var _p0 *uint16
433
+	_p0, hr = syscall.UTF16PtrFromString(id)
434
+	if hr != nil {
435
+		return
436
+	}
437
+	return _exportLayerBegin(info, _p0, descriptors, context)
438
+}
439
+
440
+func _exportLayerBegin(info *driverInfo, id *uint16, descriptors []WC_LAYER_DESCRIPTOR, context *uintptr) (hr error) {
441
+	var _p1 *WC_LAYER_DESCRIPTOR
442
+	if len(descriptors) > 0 {
443
+		_p1 = &descriptors[0]
444
+	}
445
+	if hr = procExportLayerBegin.Find(); hr != nil {
446
+		return
447
+	}
448
+	r0, _, _ := syscall.Syscall6(procExportLayerBegin.Addr(), 5, uintptr(unsafe.Pointer(info)), uintptr(unsafe.Pointer(id)), uintptr(unsafe.Pointer(_p1)), uintptr(len(descriptors)), uintptr(unsafe.Pointer(context)), 0)
449
+	if int32(r0) < 0 {
450
+		hr = syscall.Errno(win32FromHresult(r0))
451
+	}
452
+	return
453
+}
454
+
455
+func exportLayerNext(context uintptr, fileName **uint16, fileInfo *winio.FileBasicInfo, fileSize *int64, deleted *uint32) (hr error) {
456
+	if hr = procExportLayerNext.Find(); hr != nil {
457
+		return
458
+	}
459
+	r0, _, _ := syscall.Syscall6(procExportLayerNext.Addr(), 5, uintptr(context), uintptr(unsafe.Pointer(fileName)), uintptr(unsafe.Pointer(fileInfo)), uintptr(unsafe.Pointer(fileSize)), uintptr(unsafe.Pointer(deleted)), 0)
460
+	if int32(r0) < 0 {
461
+		hr = syscall.Errno(win32FromHresult(r0))
462
+	}
463
+	return
464
+}
465
+
466
+func exportLayerRead(context uintptr, buffer []byte, bytesRead *uint32) (hr error) {
467
+	var _p0 *byte
468
+	if len(buffer) > 0 {
469
+		_p0 = &buffer[0]
470
+	}
471
+	if hr = procExportLayerRead.Find(); hr != nil {
472
+		return
473
+	}
474
+	r0, _, _ := syscall.Syscall6(procExportLayerRead.Addr(), 4, uintptr(context), uintptr(unsafe.Pointer(_p0)), uintptr(len(buffer)), uintptr(unsafe.Pointer(bytesRead)), 0, 0)
475
+	if int32(r0) < 0 {
476
+		hr = syscall.Errno(win32FromHresult(r0))
477
+	}
478
+	return
479
+}
480
+
481
+func exportLayerEnd(context uintptr) (hr error) {
482
+	if hr = procExportLayerEnd.Find(); hr != nil {
483
+		return
484
+	}
485
+	r0, _, _ := syscall.Syscall(procExportLayerEnd.Addr(), 1, uintptr(context), 0, 0)
486
+	if int32(r0) < 0 {
487
+		hr = syscall.Errno(win32FromHresult(r0))
357 488
 	}
358 489
 	return
359 490
 }
... ...
@@ -377,8 +520,8 @@ func _createComputeSystem(id *uint16, configuration *uint16) (hr error) {
377 377
 		return
378 378
 	}
379 379
 	r0, _, _ := syscall.Syscall(procCreateComputeSystem.Addr(), 2, uintptr(unsafe.Pointer(id)), uintptr(unsafe.Pointer(configuration)), 0)
380
-	if r0 != 0 {
381
-		hr = syscall.Errno(r0)
380
+	if int32(r0) < 0 {
381
+		hr = syscall.Errno(win32FromHresult(r0))
382 382
 	}
383 383
 	return
384 384
 }
... ...
@@ -402,8 +545,8 @@ func _createProcessWithStdHandlesInComputeSystem(id *uint16, paramsJson *uint16,
402 402
 		return
403 403
 	}
404 404
 	r0, _, _ := syscall.Syscall6(procCreateProcessWithStdHandlesInComputeSystem.Addr(), 6, uintptr(unsafe.Pointer(id)), uintptr(unsafe.Pointer(paramsJson)), uintptr(unsafe.Pointer(pid)), uintptr(unsafe.Pointer(stdin)), uintptr(unsafe.Pointer(stdout)), uintptr(unsafe.Pointer(stderr)))
405
-	if r0 != 0 {
406
-		hr = syscall.Errno(r0)
405
+	if int32(r0) < 0 {
406
+		hr = syscall.Errno(win32FromHresult(r0))
407 407
 	}
408 408
 	return
409 409
 }
... ...
@@ -422,8 +565,8 @@ func _resizeConsoleInComputeSystem(id *uint16, pid uint32, height uint16, width
422 422
 		return
423 423
 	}
424 424
 	r0, _, _ := syscall.Syscall6(procResizeConsoleInComputeSystem.Addr(), 5, uintptr(unsafe.Pointer(id)), uintptr(pid), uintptr(height), uintptr(width), uintptr(flags), 0)
425
-	if r0 != 0 {
426
-		hr = syscall.Errno(r0)
425
+	if int32(r0) < 0 {
426
+		hr = syscall.Errno(win32FromHresult(r0))
427 427
 	}
428 428
 	return
429 429
 }
... ...
@@ -442,8 +585,8 @@ func _shutdownComputeSystem(id *uint16, timeout uint32) (hr error) {
442 442
 		return
443 443
 	}
444 444
 	r0, _, _ := syscall.Syscall(procShutdownComputeSystem.Addr(), 2, uintptr(unsafe.Pointer(id)), uintptr(timeout), 0)
445
-	if r0 != 0 {
446
-		hr = syscall.Errno(r0)
445
+	if int32(r0) < 0 {
446
+		hr = syscall.Errno(win32FromHresult(r0))
447 447
 	}
448 448
 	return
449 449
 }
... ...
@@ -462,8 +605,8 @@ func _startComputeSystem(id *uint16) (hr error) {
462 462
 		return
463 463
 	}
464 464
 	r0, _, _ := syscall.Syscall(procStartComputeSystem.Addr(), 1, uintptr(unsafe.Pointer(id)), 0, 0)
465
-	if r0 != 0 {
466
-		hr = syscall.Errno(r0)
465
+	if int32(r0) < 0 {
466
+		hr = syscall.Errno(win32FromHresult(r0))
467 467
 	}
468 468
 	return
469 469
 }
... ...
@@ -482,8 +625,8 @@ func _terminateComputeSystem(id *uint16) (hr error) {
482 482
 		return
483 483
 	}
484 484
 	r0, _, _ := syscall.Syscall(procTerminateComputeSystem.Addr(), 1, uintptr(unsafe.Pointer(id)), 0, 0)
485
-	if r0 != 0 {
486
-		hr = syscall.Errno(r0)
485
+	if int32(r0) < 0 {
486
+		hr = syscall.Errno(win32FromHresult(r0))
487 487
 	}
488 488
 	return
489 489
 }
... ...
@@ -502,8 +645,8 @@ func _terminateProcessInComputeSystem(id *uint16, pid uint32) (hr error) {
502 502
 		return
503 503
 	}
504 504
 	r0, _, _ := syscall.Syscall(procTerminateProcessInComputeSystem.Addr(), 2, uintptr(unsafe.Pointer(id)), uintptr(pid), 0)
505
-	if r0 != 0 {
506
-		hr = syscall.Errno(r0)
505
+	if int32(r0) < 0 {
506
+		hr = syscall.Errno(win32FromHresult(r0))
507 507
 	}
508 508
 	return
509 509
 }
... ...
@@ -522,8 +665,8 @@ func _waitForProcessInComputeSystem(id *uint16, pid uint32, timeout uint32, exit
522 522
 		return
523 523
 	}
524 524
 	r0, _, _ := syscall.Syscall6(procWaitForProcessInComputeSystem.Addr(), 4, uintptr(unsafe.Pointer(id)), uintptr(pid), uintptr(timeout), uintptr(unsafe.Pointer(exitCode)), 0, 0)
525
-	if r0 != 0 {
526
-		hr = syscall.Errno(r0)
525
+	if int32(r0) < 0 {
526
+		hr = syscall.Errno(win32FromHresult(r0))
527 527
 	}
528 528
 	return
529 529
 }
... ...
@@ -552,8 +695,8 @@ func __hnsCall(method *uint16, path *uint16, object *uint16, response **uint16)
552 552
 		return
553 553
 	}
554 554
 	r0, _, _ := syscall.Syscall6(procHNSCall.Addr(), 4, uintptr(unsafe.Pointer(method)), uintptr(unsafe.Pointer(path)), uintptr(unsafe.Pointer(object)), uintptr(unsafe.Pointer(response)), 0, 0)
555
-	if r0 != 0 {
556
-		hr = syscall.Errno(r0)
555
+	if int32(r0) < 0 {
556
+		hr = syscall.Errno(win32FromHresult(r0))
557 557
 	}
558 558
 	return
559 559
 }