Browse code

Revendor Microsoft/go-winio for 8gB file fix

This pulls in the migration of go-winio/backuptar from the bundled fork
of archive/tar from Go 1.6 to using Go's current archive/tar unmodified.

This fixes the failure to import an OCI layer (tar stream) containing a
file larger than 8gB.

Fixes: #40444

Signed-off-by: Paul "TBBle" Hampson <Paul.Hampson@Pobox.com>

Paul "TBBle" Hampson authored on 2020/09/09 21:49:11
Showing 13 changed files
... ...
@@ -3,6 +3,7 @@
3 3
 package windows // import "github.com/docker/docker/daemon/graphdriver/windows"
4 4
 
5 5
 import (
6
+	"archive/tar"
6 7
 	"bufio"
7 8
 	"bytes"
8 9
 	"encoding/json"
... ...
@@ -20,7 +21,6 @@ import (
20 20
 	"unsafe"
21 21
 
22 22
 	winio "github.com/Microsoft/go-winio"
23
-	"github.com/Microsoft/go-winio/archive/tar"
24 23
 	"github.com/Microsoft/go-winio/backuptar"
25 24
 	"github.com/Microsoft/go-winio/vhd"
26 25
 	"github.com/Microsoft/hcsshim"
... ...
@@ -1,6 +1,6 @@
1 1
 github.com/Azure/go-ansiterm                        d6e3b3328b783f23731bc4d058875b0371ff8109
2 2
 github.com/Microsoft/hcsshim                        9dcb42f100215f8d375b4a9265e5bba009217a85 # moby branch
3
-github.com/Microsoft/go-winio                       6c72808b55902eae4c5943626030429ff20f3b63 # v0.4.14
3
+github.com/Microsoft/go-winio                       5b44b70ab3ab4d291a7c1d28afe7b4afeced0ed4 # v0.4.15-0.20200908182639-5b44b70ab3ab
4 4
 github.com/docker/libtrust                          9cbd2a1374f46905c68a4eb3694a130610adc62a
5 5
 github.com/golang/gddo                              72a348e765d293ed6d1ded7b699591f14d6cd921
6 6
 github.com/google/uuid                              0cd6bf5da1e1c83f8b45653022c74f71af0538a4 # v1.1.1
7 7
deleted file mode 100644
... ...
@@ -1,27 +0,0 @@
1
-Copyright (c) 2012 The Go Authors. All rights reserved.
2
-
3
-Redistribution and use in source and binary forms, with or without
4
-modification, are permitted provided that the following conditions are
5
-met:
6
-
7
-   * Redistributions of source code must retain the above copyright
8
-notice, this list of conditions and the following disclaimer.
9
-   * Redistributions in binary form must reproduce the above
10
-copyright notice, this list of conditions and the following disclaimer
11
-in the documentation and/or other materials provided with the
12
-distribution.
13
-   * Neither the name of Google Inc. nor the names of its
14
-contributors may be used to endorse or promote products derived from
15
-this software without specific prior written permission.
16
-
17
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21
-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 1
deleted file mode 100644
... ...
@@ -1,344 +0,0 @@
1
-// Copyright 2009 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
-// Package tar implements access to tar archives.
6
-// It aims to cover most of the variations, including those produced
7
-// by GNU and BSD tars.
8
-//
9
-// References:
10
-//   http://www.freebsd.org/cgi/man.cgi?query=tar&sektion=5
11
-//   http://www.gnu.org/software/tar/manual/html_node/Standard.html
12
-//   http://pubs.opengroup.org/onlinepubs/9699919799/utilities/pax.html
13
-package tar
14
-
15
-import (
16
-	"bytes"
17
-	"errors"
18
-	"fmt"
19
-	"os"
20
-	"path"
21
-	"time"
22
-)
23
-
24
-const (
25
-	blockSize = 512
26
-
27
-	// Types
28
-	TypeReg           = '0'    // regular file
29
-	TypeRegA          = '\x00' // regular file
30
-	TypeLink          = '1'    // hard link
31
-	TypeSymlink       = '2'    // symbolic link
32
-	TypeChar          = '3'    // character device node
33
-	TypeBlock         = '4'    // block device node
34
-	TypeDir           = '5'    // directory
35
-	TypeFifo          = '6'    // fifo node
36
-	TypeCont          = '7'    // reserved
37
-	TypeXHeader       = 'x'    // extended header
38
-	TypeXGlobalHeader = 'g'    // global extended header
39
-	TypeGNULongName   = 'L'    // Next file has a long name
40
-	TypeGNULongLink   = 'K'    // Next file symlinks to a file w/ a long name
41
-	TypeGNUSparse     = 'S'    // sparse file
42
-)
43
-
44
-// A Header represents a single header in a tar archive.
45
-// Some fields may not be populated.
46
-type Header struct {
47
-	Name         string    // name of header file entry
48
-	Mode         int64     // permission and mode bits
49
-	Uid          int       // user id of owner
50
-	Gid          int       // group id of owner
51
-	Size         int64     // length in bytes
52
-	ModTime      time.Time // modified time
53
-	Typeflag     byte      // type of header entry
54
-	Linkname     string    // target name of link
55
-	Uname        string    // user name of owner
56
-	Gname        string    // group name of owner
57
-	Devmajor     int64     // major number of character or block device
58
-	Devminor     int64     // minor number of character or block device
59
-	AccessTime   time.Time // access time
60
-	ChangeTime   time.Time // status change time
61
-	CreationTime time.Time // creation time
62
-	Xattrs       map[string]string
63
-	Winheaders   map[string]string
64
-}
65
-
66
-// File name constants from the tar spec.
67
-const (
68
-	fileNameSize       = 100 // Maximum number of bytes in a standard tar name.
69
-	fileNamePrefixSize = 155 // Maximum number of ustar extension bytes.
70
-)
71
-
72
-// FileInfo returns an os.FileInfo for the Header.
73
-func (h *Header) FileInfo() os.FileInfo {
74
-	return headerFileInfo{h}
75
-}
76
-
77
-// headerFileInfo implements os.FileInfo.
78
-type headerFileInfo struct {
79
-	h *Header
80
-}
81
-
82
-func (fi headerFileInfo) Size() int64        { return fi.h.Size }
83
-func (fi headerFileInfo) IsDir() bool        { return fi.Mode().IsDir() }
84
-func (fi headerFileInfo) ModTime() time.Time { return fi.h.ModTime }
85
-func (fi headerFileInfo) Sys() interface{}   { return fi.h }
86
-
87
-// Name returns the base name of the file.
88
-func (fi headerFileInfo) Name() string {
89
-	if fi.IsDir() {
90
-		return path.Base(path.Clean(fi.h.Name))
91
-	}
92
-	return path.Base(fi.h.Name)
93
-}
94
-
95
-// Mode returns the permission and mode bits for the headerFileInfo.
96
-func (fi headerFileInfo) Mode() (mode os.FileMode) {
97
-	// Set file permission bits.
98
-	mode = os.FileMode(fi.h.Mode).Perm()
99
-
100
-	// Set setuid, setgid and sticky bits.
101
-	if fi.h.Mode&c_ISUID != 0 {
102
-		// setuid
103
-		mode |= os.ModeSetuid
104
-	}
105
-	if fi.h.Mode&c_ISGID != 0 {
106
-		// setgid
107
-		mode |= os.ModeSetgid
108
-	}
109
-	if fi.h.Mode&c_ISVTX != 0 {
110
-		// sticky
111
-		mode |= os.ModeSticky
112
-	}
113
-
114
-	// Set file mode bits.
115
-	// clear perm, setuid, setgid and sticky bits.
116
-	m := os.FileMode(fi.h.Mode) &^ 07777
117
-	if m == c_ISDIR {
118
-		// directory
119
-		mode |= os.ModeDir
120
-	}
121
-	if m == c_ISFIFO {
122
-		// named pipe (FIFO)
123
-		mode |= os.ModeNamedPipe
124
-	}
125
-	if m == c_ISLNK {
126
-		// symbolic link
127
-		mode |= os.ModeSymlink
128
-	}
129
-	if m == c_ISBLK {
130
-		// device file
131
-		mode |= os.ModeDevice
132
-	}
133
-	if m == c_ISCHR {
134
-		// Unix character device
135
-		mode |= os.ModeDevice
136
-		mode |= os.ModeCharDevice
137
-	}
138
-	if m == c_ISSOCK {
139
-		// Unix domain socket
140
-		mode |= os.ModeSocket
141
-	}
142
-
143
-	switch fi.h.Typeflag {
144
-	case TypeSymlink:
145
-		// symbolic link
146
-		mode |= os.ModeSymlink
147
-	case TypeChar:
148
-		// character device node
149
-		mode |= os.ModeDevice
150
-		mode |= os.ModeCharDevice
151
-	case TypeBlock:
152
-		// block device node
153
-		mode |= os.ModeDevice
154
-	case TypeDir:
155
-		// directory
156
-		mode |= os.ModeDir
157
-	case TypeFifo:
158
-		// fifo node
159
-		mode |= os.ModeNamedPipe
160
-	}
161
-
162
-	return mode
163
-}
164
-
165
-// sysStat, if non-nil, populates h from system-dependent fields of fi.
166
-var sysStat func(fi os.FileInfo, h *Header) error
167
-
168
-// Mode constants from the tar spec.
169
-const (
170
-	c_ISUID  = 04000   // Set uid
171
-	c_ISGID  = 02000   // Set gid
172
-	c_ISVTX  = 01000   // Save text (sticky bit)
173
-	c_ISDIR  = 040000  // Directory
174
-	c_ISFIFO = 010000  // FIFO
175
-	c_ISREG  = 0100000 // Regular file
176
-	c_ISLNK  = 0120000 // Symbolic link
177
-	c_ISBLK  = 060000  // Block special file
178
-	c_ISCHR  = 020000  // Character special file
179
-	c_ISSOCK = 0140000 // Socket
180
-)
181
-
182
-// Keywords for the PAX Extended Header
183
-const (
184
-	paxAtime        = "atime"
185
-	paxCharset      = "charset"
186
-	paxComment      = "comment"
187
-	paxCtime        = "ctime" // please note that ctime is not a valid pax header.
188
-	paxCreationTime = "LIBARCHIVE.creationtime"
189
-	paxGid          = "gid"
190
-	paxGname        = "gname"
191
-	paxLinkpath     = "linkpath"
192
-	paxMtime        = "mtime"
193
-	paxPath         = "path"
194
-	paxSize         = "size"
195
-	paxUid          = "uid"
196
-	paxUname        = "uname"
197
-	paxXattr        = "SCHILY.xattr."
198
-	paxWindows      = "MSWINDOWS."
199
-	paxNone         = ""
200
-)
201
-
202
-// FileInfoHeader creates a partially-populated Header from fi.
203
-// If fi describes a symlink, FileInfoHeader records link as the link target.
204
-// If fi describes a directory, a slash is appended to the name.
205
-// Because os.FileInfo's Name method returns only the base name of
206
-// the file it describes, it may be necessary to modify the Name field
207
-// of the returned header to provide the full path name of the file.
208
-func FileInfoHeader(fi os.FileInfo, link string) (*Header, error) {
209
-	if fi == nil {
210
-		return nil, errors.New("tar: FileInfo is nil")
211
-	}
212
-	fm := fi.Mode()
213
-	h := &Header{
214
-		Name:    fi.Name(),
215
-		ModTime: fi.ModTime(),
216
-		Mode:    int64(fm.Perm()), // or'd with c_IS* constants later
217
-	}
218
-	switch {
219
-	case fm.IsRegular():
220
-		h.Mode |= c_ISREG
221
-		h.Typeflag = TypeReg
222
-		h.Size = fi.Size()
223
-	case fi.IsDir():
224
-		h.Typeflag = TypeDir
225
-		h.Mode |= c_ISDIR
226
-		h.Name += "/"
227
-	case fm&os.ModeSymlink != 0:
228
-		h.Typeflag = TypeSymlink
229
-		h.Mode |= c_ISLNK
230
-		h.Linkname = link
231
-	case fm&os.ModeDevice != 0:
232
-		if fm&os.ModeCharDevice != 0 {
233
-			h.Mode |= c_ISCHR
234
-			h.Typeflag = TypeChar
235
-		} else {
236
-			h.Mode |= c_ISBLK
237
-			h.Typeflag = TypeBlock
238
-		}
239
-	case fm&os.ModeNamedPipe != 0:
240
-		h.Typeflag = TypeFifo
241
-		h.Mode |= c_ISFIFO
242
-	case fm&os.ModeSocket != 0:
243
-		h.Mode |= c_ISSOCK
244
-	default:
245
-		return nil, fmt.Errorf("archive/tar: unknown file mode %v", fm)
246
-	}
247
-	if fm&os.ModeSetuid != 0 {
248
-		h.Mode |= c_ISUID
249
-	}
250
-	if fm&os.ModeSetgid != 0 {
251
-		h.Mode |= c_ISGID
252
-	}
253
-	if fm&os.ModeSticky != 0 {
254
-		h.Mode |= c_ISVTX
255
-	}
256
-	// If possible, populate additional fields from OS-specific
257
-	// FileInfo fields.
258
-	if sys, ok := fi.Sys().(*Header); ok {
259
-		// This FileInfo came from a Header (not the OS). Use the
260
-		// original Header to populate all remaining fields.
261
-		h.Uid = sys.Uid
262
-		h.Gid = sys.Gid
263
-		h.Uname = sys.Uname
264
-		h.Gname = sys.Gname
265
-		h.AccessTime = sys.AccessTime
266
-		h.ChangeTime = sys.ChangeTime
267
-		if sys.Xattrs != nil {
268
-			h.Xattrs = make(map[string]string)
269
-			for k, v := range sys.Xattrs {
270
-				h.Xattrs[k] = v
271
-			}
272
-		}
273
-		if sys.Typeflag == TypeLink {
274
-			// hard link
275
-			h.Typeflag = TypeLink
276
-			h.Size = 0
277
-			h.Linkname = sys.Linkname
278
-		}
279
-	}
280
-	if sysStat != nil {
281
-		return h, sysStat(fi, h)
282
-	}
283
-	return h, nil
284
-}
285
-
286
-var zeroBlock = make([]byte, blockSize)
287
-
288
-// POSIX specifies a sum of the unsigned byte values, but the Sun tar uses signed byte values.
289
-// We compute and return both.
290
-func checksum(header []byte) (unsigned int64, signed int64) {
291
-	for i := 0; i < len(header); i++ {
292
-		if i == 148 {
293
-			// The chksum field (header[148:156]) is special: it should be treated as space bytes.
294
-			unsigned += ' ' * 8
295
-			signed += ' ' * 8
296
-			i += 7
297
-			continue
298
-		}
299
-		unsigned += int64(header[i])
300
-		signed += int64(int8(header[i]))
301
-	}
302
-	return
303
-}
304
-
305
-type slicer []byte
306
-
307
-func (sp *slicer) next(n int) (b []byte) {
308
-	s := *sp
309
-	b, *sp = s[0:n], s[n:]
310
-	return
311
-}
312
-
313
-func isASCII(s string) bool {
314
-	for _, c := range s {
315
-		if c >= 0x80 {
316
-			return false
317
-		}
318
-	}
319
-	return true
320
-}
321
-
322
-func toASCII(s string) string {
323
-	if isASCII(s) {
324
-		return s
325
-	}
326
-	var buf bytes.Buffer
327
-	for _, c := range s {
328
-		if c < 0x80 {
329
-			buf.WriteByte(byte(c))
330
-		}
331
-	}
332
-	return buf.String()
333
-}
334
-
335
-// isHeaderOnlyType checks if the given type flag is of the type that has no
336
-// data section even if a size is specified.
337
-func isHeaderOnlyType(flag byte) bool {
338
-	switch flag {
339
-	case TypeLink, TypeSymlink, TypeChar, TypeBlock, TypeDir, TypeFifo:
340
-		return true
341
-	default:
342
-		return false
343
-	}
344
-}
345 1
deleted file mode 100644
... ...
@@ -1,1002 +0,0 @@
1
-// Copyright 2009 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
-package tar
6
-
7
-// TODO(dsymonds):
8
-//   - pax extensions
9
-
10
-import (
11
-	"bytes"
12
-	"errors"
13
-	"io"
14
-	"io/ioutil"
15
-	"math"
16
-	"os"
17
-	"strconv"
18
-	"strings"
19
-	"time"
20
-)
21
-
22
-var (
23
-	ErrHeader = errors.New("archive/tar: invalid tar header")
24
-)
25
-
26
-const maxNanoSecondIntSize = 9
27
-
28
-// A Reader provides sequential access to the contents of a tar archive.
29
-// A tar archive consists of a sequence of files.
30
-// The Next method advances to the next file in the archive (including the first),
31
-// and then it can be treated as an io.Reader to access the file's data.
32
-type Reader struct {
33
-	r       io.Reader
34
-	err     error
35
-	pad     int64           // amount of padding (ignored) after current file entry
36
-	curr    numBytesReader  // reader for current file entry
37
-	hdrBuff [blockSize]byte // buffer to use in readHeader
38
-}
39
-
40
-type parser struct {
41
-	err error // Last error seen
42
-}
43
-
44
-// A numBytesReader is an io.Reader with a numBytes method, returning the number
45
-// of bytes remaining in the underlying encoded data.
46
-type numBytesReader interface {
47
-	io.Reader
48
-	numBytes() int64
49
-}
50
-
51
-// A regFileReader is a numBytesReader for reading file data from a tar archive.
52
-type regFileReader struct {
53
-	r  io.Reader // underlying reader
54
-	nb int64     // number of unread bytes for current file entry
55
-}
56
-
57
-// A sparseFileReader is a numBytesReader for reading sparse file data from a
58
-// tar archive.
59
-type sparseFileReader struct {
60
-	rfr   numBytesReader // Reads the sparse-encoded file data
61
-	sp    []sparseEntry  // The sparse map for the file
62
-	pos   int64          // Keeps track of file position
63
-	total int64          // Total size of the file
64
-}
65
-
66
-// A sparseEntry holds a single entry in a sparse file's sparse map.
67
-//
68
-// Sparse files are represented using a series of sparseEntrys.
69
-// Despite the name, a sparseEntry represents an actual data fragment that
70
-// references data found in the underlying archive stream. All regions not
71
-// covered by a sparseEntry are logically filled with zeros.
72
-//
73
-// For example, if the underlying raw file contains the 10-byte data:
74
-//	var compactData = "abcdefgh"
75
-//
76
-// And the sparse map has the following entries:
77
-//	var sp = []sparseEntry{
78
-//		{offset: 2,  numBytes: 5} // Data fragment for [2..7]
79
-//		{offset: 18, numBytes: 3} // Data fragment for [18..21]
80
-//	}
81
-//
82
-// Then the content of the resulting sparse file with a "real" size of 25 is:
83
-//	var sparseData = "\x00"*2 + "abcde" + "\x00"*11 + "fgh" + "\x00"*4
84
-type sparseEntry struct {
85
-	offset   int64 // Starting position of the fragment
86
-	numBytes int64 // Length of the fragment
87
-}
88
-
89
-// Keywords for GNU sparse files in a PAX extended header
90
-const (
91
-	paxGNUSparseNumBlocks = "GNU.sparse.numblocks"
92
-	paxGNUSparseOffset    = "GNU.sparse.offset"
93
-	paxGNUSparseNumBytes  = "GNU.sparse.numbytes"
94
-	paxGNUSparseMap       = "GNU.sparse.map"
95
-	paxGNUSparseName      = "GNU.sparse.name"
96
-	paxGNUSparseMajor     = "GNU.sparse.major"
97
-	paxGNUSparseMinor     = "GNU.sparse.minor"
98
-	paxGNUSparseSize      = "GNU.sparse.size"
99
-	paxGNUSparseRealSize  = "GNU.sparse.realsize"
100
-)
101
-
102
-// Keywords for old GNU sparse headers
103
-const (
104
-	oldGNUSparseMainHeaderOffset               = 386
105
-	oldGNUSparseMainHeaderIsExtendedOffset     = 482
106
-	oldGNUSparseMainHeaderNumEntries           = 4
107
-	oldGNUSparseExtendedHeaderIsExtendedOffset = 504
108
-	oldGNUSparseExtendedHeaderNumEntries       = 21
109
-	oldGNUSparseOffsetSize                     = 12
110
-	oldGNUSparseNumBytesSize                   = 12
111
-)
112
-
113
-// NewReader creates a new Reader reading from r.
114
-func NewReader(r io.Reader) *Reader { return &Reader{r: r} }
115
-
116
-// Next advances to the next entry in the tar archive.
117
-//
118
-// io.EOF is returned at the end of the input.
119
-func (tr *Reader) Next() (*Header, error) {
120
-	if tr.err != nil {
121
-		return nil, tr.err
122
-	}
123
-
124
-	var hdr *Header
125
-	var extHdrs map[string]string
126
-
127
-	// Externally, Next iterates through the tar archive as if it is a series of
128
-	// files. Internally, the tar format often uses fake "files" to add meta
129
-	// data that describes the next file. These meta data "files" should not
130
-	// normally be visible to the outside. As such, this loop iterates through
131
-	// one or more "header files" until it finds a "normal file".
132
-loop:
133
-	for {
134
-		tr.err = tr.skipUnread()
135
-		if tr.err != nil {
136
-			return nil, tr.err
137
-		}
138
-
139
-		hdr = tr.readHeader()
140
-		if tr.err != nil {
141
-			return nil, tr.err
142
-		}
143
-
144
-		// Check for PAX/GNU special headers and files.
145
-		switch hdr.Typeflag {
146
-		case TypeXHeader:
147
-			extHdrs, tr.err = parsePAX(tr)
148
-			if tr.err != nil {
149
-				return nil, tr.err
150
-			}
151
-			continue loop // This is a meta header affecting the next header
152
-		case TypeGNULongName, TypeGNULongLink:
153
-			var realname []byte
154
-			realname, tr.err = ioutil.ReadAll(tr)
155
-			if tr.err != nil {
156
-				return nil, tr.err
157
-			}
158
-
159
-			// Convert GNU extensions to use PAX headers.
160
-			if extHdrs == nil {
161
-				extHdrs = make(map[string]string)
162
-			}
163
-			var p parser
164
-			switch hdr.Typeflag {
165
-			case TypeGNULongName:
166
-				extHdrs[paxPath] = p.parseString(realname)
167
-			case TypeGNULongLink:
168
-				extHdrs[paxLinkpath] = p.parseString(realname)
169
-			}
170
-			if p.err != nil {
171
-				tr.err = p.err
172
-				return nil, tr.err
173
-			}
174
-			continue loop // This is a meta header affecting the next header
175
-		default:
176
-			mergePAX(hdr, extHdrs)
177
-
178
-			// Check for a PAX format sparse file
179
-			sp, err := tr.checkForGNUSparsePAXHeaders(hdr, extHdrs)
180
-			if err != nil {
181
-				tr.err = err
182
-				return nil, err
183
-			}
184
-			if sp != nil {
185
-				// Current file is a PAX format GNU sparse file.
186
-				// Set the current file reader to a sparse file reader.
187
-				tr.curr, tr.err = newSparseFileReader(tr.curr, sp, hdr.Size)
188
-				if tr.err != nil {
189
-					return nil, tr.err
190
-				}
191
-			}
192
-			break loop // This is a file, so stop
193
-		}
194
-	}
195
-	return hdr, nil
196
-}
197
-
198
-// checkForGNUSparsePAXHeaders checks the PAX headers for GNU sparse headers. If they are found, then
199
-// this function reads the sparse map and returns it. Unknown sparse formats are ignored, causing the file to
200
-// be treated as a regular file.
201
-func (tr *Reader) checkForGNUSparsePAXHeaders(hdr *Header, headers map[string]string) ([]sparseEntry, error) {
202
-	var sparseFormat string
203
-
204
-	// Check for sparse format indicators
205
-	major, majorOk := headers[paxGNUSparseMajor]
206
-	minor, minorOk := headers[paxGNUSparseMinor]
207
-	sparseName, sparseNameOk := headers[paxGNUSparseName]
208
-	_, sparseMapOk := headers[paxGNUSparseMap]
209
-	sparseSize, sparseSizeOk := headers[paxGNUSparseSize]
210
-	sparseRealSize, sparseRealSizeOk := headers[paxGNUSparseRealSize]
211
-
212
-	// Identify which, if any, sparse format applies from which PAX headers are set
213
-	if majorOk && minorOk {
214
-		sparseFormat = major + "." + minor
215
-	} else if sparseNameOk && sparseMapOk {
216
-		sparseFormat = "0.1"
217
-	} else if sparseSizeOk {
218
-		sparseFormat = "0.0"
219
-	} else {
220
-		// Not a PAX format GNU sparse file.
221
-		return nil, nil
222
-	}
223
-
224
-	// Check for unknown sparse format
225
-	if sparseFormat != "0.0" && sparseFormat != "0.1" && sparseFormat != "1.0" {
226
-		return nil, nil
227
-	}
228
-
229
-	// Update hdr from GNU sparse PAX headers
230
-	if sparseNameOk {
231
-		hdr.Name = sparseName
232
-	}
233
-	if sparseSizeOk {
234
-		realSize, err := strconv.ParseInt(sparseSize, 10, 0)
235
-		if err != nil {
236
-			return nil, ErrHeader
237
-		}
238
-		hdr.Size = realSize
239
-	} else if sparseRealSizeOk {
240
-		realSize, err := strconv.ParseInt(sparseRealSize, 10, 0)
241
-		if err != nil {
242
-			return nil, ErrHeader
243
-		}
244
-		hdr.Size = realSize
245
-	}
246
-
247
-	// Set up the sparse map, according to the particular sparse format in use
248
-	var sp []sparseEntry
249
-	var err error
250
-	switch sparseFormat {
251
-	case "0.0", "0.1":
252
-		sp, err = readGNUSparseMap0x1(headers)
253
-	case "1.0":
254
-		sp, err = readGNUSparseMap1x0(tr.curr)
255
-	}
256
-	return sp, err
257
-}
258
-
259
-// mergePAX merges well known headers according to PAX standard.
260
-// In general headers with the same name as those found
261
-// in the header struct overwrite those found in the header
262
-// struct with higher precision or longer values. Esp. useful
263
-// for name and linkname fields.
264
-func mergePAX(hdr *Header, headers map[string]string) error {
265
-	for k, v := range headers {
266
-		switch k {
267
-		case paxPath:
268
-			hdr.Name = v
269
-		case paxLinkpath:
270
-			hdr.Linkname = v
271
-		case paxGname:
272
-			hdr.Gname = v
273
-		case paxUname:
274
-			hdr.Uname = v
275
-		case paxUid:
276
-			uid, err := strconv.ParseInt(v, 10, 0)
277
-			if err != nil {
278
-				return err
279
-			}
280
-			hdr.Uid = int(uid)
281
-		case paxGid:
282
-			gid, err := strconv.ParseInt(v, 10, 0)
283
-			if err != nil {
284
-				return err
285
-			}
286
-			hdr.Gid = int(gid)
287
-		case paxAtime:
288
-			t, err := parsePAXTime(v)
289
-			if err != nil {
290
-				return err
291
-			}
292
-			hdr.AccessTime = t
293
-		case paxMtime:
294
-			t, err := parsePAXTime(v)
295
-			if err != nil {
296
-				return err
297
-			}
298
-			hdr.ModTime = t
299
-		case paxCtime:
300
-			t, err := parsePAXTime(v)
301
-			if err != nil {
302
-				return err
303
-			}
304
-			hdr.ChangeTime = t
305
-		case paxCreationTime:
306
-			t, err := parsePAXTime(v)
307
-			if err != nil {
308
-				return err
309
-			}
310
-			hdr.CreationTime = t
311
-		case paxSize:
312
-			size, err := strconv.ParseInt(v, 10, 0)
313
-			if err != nil {
314
-				return err
315
-			}
316
-			hdr.Size = int64(size)
317
-		default:
318
-			if strings.HasPrefix(k, paxXattr) {
319
-				if hdr.Xattrs == nil {
320
-					hdr.Xattrs = make(map[string]string)
321
-				}
322
-				hdr.Xattrs[k[len(paxXattr):]] = v
323
-			} else if strings.HasPrefix(k, paxWindows) {
324
-				if hdr.Winheaders == nil {
325
-					hdr.Winheaders = make(map[string]string)
326
-				}
327
-				hdr.Winheaders[k[len(paxWindows):]] = v
328
-			}
329
-		}
330
-	}
331
-	return nil
332
-}
333
-
334
-// parsePAXTime takes a string of the form %d.%d as described in
335
-// the PAX specification.
336
-func parsePAXTime(t string) (time.Time, error) {
337
-	buf := []byte(t)
338
-	pos := bytes.IndexByte(buf, '.')
339
-	var seconds, nanoseconds int64
340
-	var err error
341
-	if pos == -1 {
342
-		seconds, err = strconv.ParseInt(t, 10, 0)
343
-		if err != nil {
344
-			return time.Time{}, err
345
-		}
346
-	} else {
347
-		seconds, err = strconv.ParseInt(string(buf[:pos]), 10, 0)
348
-		if err != nil {
349
-			return time.Time{}, err
350
-		}
351
-		nano_buf := string(buf[pos+1:])
352
-		// Pad as needed before converting to a decimal.
353
-		// For example .030 -> .030000000 -> 30000000 nanoseconds
354
-		if len(nano_buf) < maxNanoSecondIntSize {
355
-			// Right pad
356
-			nano_buf += strings.Repeat("0", maxNanoSecondIntSize-len(nano_buf))
357
-		} else if len(nano_buf) > maxNanoSecondIntSize {
358
-			// Right truncate
359
-			nano_buf = nano_buf[:maxNanoSecondIntSize]
360
-		}
361
-		nanoseconds, err = strconv.ParseInt(string(nano_buf), 10, 0)
362
-		if err != nil {
363
-			return time.Time{}, err
364
-		}
365
-	}
366
-	ts := time.Unix(seconds, nanoseconds)
367
-	return ts, nil
368
-}
369
-
370
-// parsePAX parses PAX headers.
371
-// If an extended header (type 'x') is invalid, ErrHeader is returned
372
-func parsePAX(r io.Reader) (map[string]string, error) {
373
-	buf, err := ioutil.ReadAll(r)
374
-	if err != nil {
375
-		return nil, err
376
-	}
377
-	sbuf := string(buf)
378
-
379
-	// For GNU PAX sparse format 0.0 support.
380
-	// This function transforms the sparse format 0.0 headers into sparse format 0.1 headers.
381
-	var sparseMap bytes.Buffer
382
-
383
-	headers := make(map[string]string)
384
-	// Each record is constructed as
385
-	//     "%d %s=%s\n", length, keyword, value
386
-	for len(sbuf) > 0 {
387
-		key, value, residual, err := parsePAXRecord(sbuf)
388
-		if err != nil {
389
-			return nil, ErrHeader
390
-		}
391
-		sbuf = residual
392
-
393
-		keyStr := string(key)
394
-		if keyStr == paxGNUSparseOffset || keyStr == paxGNUSparseNumBytes {
395
-			// GNU sparse format 0.0 special key. Write to sparseMap instead of using the headers map.
396
-			sparseMap.WriteString(value)
397
-			sparseMap.Write([]byte{','})
398
-		} else {
399
-			// Normal key. Set the value in the headers map.
400
-			headers[keyStr] = string(value)
401
-		}
402
-	}
403
-	if sparseMap.Len() != 0 {
404
-		// Add sparse info to headers, chopping off the extra comma
405
-		sparseMap.Truncate(sparseMap.Len() - 1)
406
-		headers[paxGNUSparseMap] = sparseMap.String()
407
-	}
408
-	return headers, nil
409
-}
410
-
411
-// parsePAXRecord parses the input PAX record string into a key-value pair.
412
-// If parsing is successful, it will slice off the currently read record and
413
-// return the remainder as r.
414
-//
415
-// A PAX record is of the following form:
416
-//	"%d %s=%s\n" % (size, key, value)
417
-func parsePAXRecord(s string) (k, v, r string, err error) {
418
-	// The size field ends at the first space.
419
-	sp := strings.IndexByte(s, ' ')
420
-	if sp == -1 {
421
-		return "", "", s, ErrHeader
422
-	}
423
-
424
-	// Parse the first token as a decimal integer.
425
-	n, perr := strconv.ParseInt(s[:sp], 10, 0) // Intentionally parse as native int
426
-	if perr != nil || n < 5 || int64(len(s)) < n {
427
-		return "", "", s, ErrHeader
428
-	}
429
-
430
-	// Extract everything between the space and the final newline.
431
-	rec, nl, rem := s[sp+1:n-1], s[n-1:n], s[n:]
432
-	if nl != "\n" {
433
-		return "", "", s, ErrHeader
434
-	}
435
-
436
-	// The first equals separates the key from the value.
437
-	eq := strings.IndexByte(rec, '=')
438
-	if eq == -1 {
439
-		return "", "", s, ErrHeader
440
-	}
441
-	return rec[:eq], rec[eq+1:], rem, nil
442
-}
443
-
444
-// parseString parses bytes as a NUL-terminated C-style string.
445
-// If a NUL byte is not found then the whole slice is returned as a string.
446
-func (*parser) parseString(b []byte) string {
447
-	n := 0
448
-	for n < len(b) && b[n] != 0 {
449
-		n++
450
-	}
451
-	return string(b[0:n])
452
-}
453
-
454
-// parseNumeric parses the input as being encoded in either base-256 or octal.
455
-// This function may return negative numbers.
456
-// If parsing fails or an integer overflow occurs, err will be set.
457
-func (p *parser) parseNumeric(b []byte) int64 {
458
-	// Check for base-256 (binary) format first.
459
-	// If the first bit is set, then all following bits constitute a two's
460
-	// complement encoded number in big-endian byte order.
461
-	if len(b) > 0 && b[0]&0x80 != 0 {
462
-		// Handling negative numbers relies on the following identity:
463
-		//	-a-1 == ^a
464
-		//
465
-		// If the number is negative, we use an inversion mask to invert the
466
-		// data bytes and treat the value as an unsigned number.
467
-		var inv byte // 0x00 if positive or zero, 0xff if negative
468
-		if b[0]&0x40 != 0 {
469
-			inv = 0xff
470
-		}
471
-
472
-		var x uint64
473
-		for i, c := range b {
474
-			c ^= inv // Inverts c only if inv is 0xff, otherwise does nothing
475
-			if i == 0 {
476
-				c &= 0x7f // Ignore signal bit in first byte
477
-			}
478
-			if (x >> 56) > 0 {
479
-				p.err = ErrHeader // Integer overflow
480
-				return 0
481
-			}
482
-			x = x<<8 | uint64(c)
483
-		}
484
-		if (x >> 63) > 0 {
485
-			p.err = ErrHeader // Integer overflow
486
-			return 0
487
-		}
488
-		if inv == 0xff {
489
-			return ^int64(x)
490
-		}
491
-		return int64(x)
492
-	}
493
-
494
-	// Normal case is base-8 (octal) format.
495
-	return p.parseOctal(b)
496
-}
497
-
498
-func (p *parser) parseOctal(b []byte) int64 {
499
-	// Because unused fields are filled with NULs, we need
500
-	// to skip leading NULs. Fields may also be padded with
501
-	// spaces or NULs.
502
-	// So we remove leading and trailing NULs and spaces to
503
-	// be sure.
504
-	b = bytes.Trim(b, " \x00")
505
-
506
-	if len(b) == 0 {
507
-		return 0
508
-	}
509
-	x, perr := strconv.ParseUint(p.parseString(b), 8, 64)
510
-	if perr != nil {
511
-		p.err = ErrHeader
512
-	}
513
-	return int64(x)
514
-}
515
-
516
-// skipUnread skips any unread bytes in the existing file entry, as well as any
517
-// alignment padding. It returns io.ErrUnexpectedEOF if any io.EOF is
518
-// encountered in the data portion; it is okay to hit io.EOF in the padding.
519
-//
520
-// Note that this function still works properly even when sparse files are being
521
-// used since numBytes returns the bytes remaining in the underlying io.Reader.
522
-func (tr *Reader) skipUnread() error {
523
-	dataSkip := tr.numBytes()      // Number of data bytes to skip
524
-	totalSkip := dataSkip + tr.pad // Total number of bytes to skip
525
-	tr.curr, tr.pad = nil, 0
526
-
527
-	// If possible, Seek to the last byte before the end of the data section.
528
-	// Do this because Seek is often lazy about reporting errors; this will mask
529
-	// the fact that the tar stream may be truncated. We can rely on the
530
-	// io.CopyN done shortly afterwards to trigger any IO errors.
531
-	var seekSkipped int64 // Number of bytes skipped via Seek
532
-	if sr, ok := tr.r.(io.Seeker); ok && dataSkip > 1 {
533
-		// Not all io.Seeker can actually Seek. For example, os.Stdin implements
534
-		// io.Seeker, but calling Seek always returns an error and performs
535
-		// no action. Thus, we try an innocent seek to the current position
536
-		// to see if Seek is really supported.
537
-		pos1, err := sr.Seek(0, os.SEEK_CUR)
538
-		if err == nil {
539
-			// Seek seems supported, so perform the real Seek.
540
-			pos2, err := sr.Seek(dataSkip-1, os.SEEK_CUR)
541
-			if err != nil {
542
-				tr.err = err
543
-				return tr.err
544
-			}
545
-			seekSkipped = pos2 - pos1
546
-		}
547
-	}
548
-
549
-	var copySkipped int64 // Number of bytes skipped via CopyN
550
-	copySkipped, tr.err = io.CopyN(ioutil.Discard, tr.r, totalSkip-seekSkipped)
551
-	if tr.err == io.EOF && seekSkipped+copySkipped < dataSkip {
552
-		tr.err = io.ErrUnexpectedEOF
553
-	}
554
-	return tr.err
555
-}
556
-
557
-func (tr *Reader) verifyChecksum(header []byte) bool {
558
-	if tr.err != nil {
559
-		return false
560
-	}
561
-
562
-	var p parser
563
-	given := p.parseOctal(header[148:156])
564
-	unsigned, signed := checksum(header)
565
-	return p.err == nil && (given == unsigned || given == signed)
566
-}
567
-
568
-// readHeader reads the next block header and assumes that the underlying reader
569
-// is already aligned to a block boundary.
570
-//
571
-// The err will be set to io.EOF only when one of the following occurs:
572
-//	* Exactly 0 bytes are read and EOF is hit.
573
-//	* Exactly 1 block of zeros is read and EOF is hit.
574
-//	* At least 2 blocks of zeros are read.
575
-func (tr *Reader) readHeader() *Header {
576
-	header := tr.hdrBuff[:]
577
-	copy(header, zeroBlock)
578
-
579
-	if _, tr.err = io.ReadFull(tr.r, header); tr.err != nil {
580
-		return nil // io.EOF is okay here
581
-	}
582
-
583
-	// Two blocks of zero bytes marks the end of the archive.
584
-	if bytes.Equal(header, zeroBlock[0:blockSize]) {
585
-		if _, tr.err = io.ReadFull(tr.r, header); tr.err != nil {
586
-			return nil // io.EOF is okay here
587
-		}
588
-		if bytes.Equal(header, zeroBlock[0:blockSize]) {
589
-			tr.err = io.EOF
590
-		} else {
591
-			tr.err = ErrHeader // zero block and then non-zero block
592
-		}
593
-		return nil
594
-	}
595
-
596
-	if !tr.verifyChecksum(header) {
597
-		tr.err = ErrHeader
598
-		return nil
599
-	}
600
-
601
-	// Unpack
602
-	var p parser
603
-	hdr := new(Header)
604
-	s := slicer(header)
605
-
606
-	hdr.Name = p.parseString(s.next(100))
607
-	hdr.Mode = p.parseNumeric(s.next(8))
608
-	hdr.Uid = int(p.parseNumeric(s.next(8)))
609
-	hdr.Gid = int(p.parseNumeric(s.next(8)))
610
-	hdr.Size = p.parseNumeric(s.next(12))
611
-	hdr.ModTime = time.Unix(p.parseNumeric(s.next(12)), 0)
612
-	s.next(8) // chksum
613
-	hdr.Typeflag = s.next(1)[0]
614
-	hdr.Linkname = p.parseString(s.next(100))
615
-
616
-	// The remainder of the header depends on the value of magic.
617
-	// The original (v7) version of tar had no explicit magic field,
618
-	// so its magic bytes, like the rest of the block, are NULs.
619
-	magic := string(s.next(8)) // contains version field as well.
620
-	var format string
621
-	switch {
622
-	case magic[:6] == "ustar\x00": // POSIX tar (1003.1-1988)
623
-		if string(header[508:512]) == "tar\x00" {
624
-			format = "star"
625
-		} else {
626
-			format = "posix"
627
-		}
628
-	case magic == "ustar  \x00": // old GNU tar
629
-		format = "gnu"
630
-	}
631
-
632
-	switch format {
633
-	case "posix", "gnu", "star":
634
-		hdr.Uname = p.parseString(s.next(32))
635
-		hdr.Gname = p.parseString(s.next(32))
636
-		devmajor := s.next(8)
637
-		devminor := s.next(8)
638
-		if hdr.Typeflag == TypeChar || hdr.Typeflag == TypeBlock {
639
-			hdr.Devmajor = p.parseNumeric(devmajor)
640
-			hdr.Devminor = p.parseNumeric(devminor)
641
-		}
642
-		var prefix string
643
-		switch format {
644
-		case "posix", "gnu":
645
-			prefix = p.parseString(s.next(155))
646
-		case "star":
647
-			prefix = p.parseString(s.next(131))
648
-			hdr.AccessTime = time.Unix(p.parseNumeric(s.next(12)), 0)
649
-			hdr.ChangeTime = time.Unix(p.parseNumeric(s.next(12)), 0)
650
-		}
651
-		if len(prefix) > 0 {
652
-			hdr.Name = prefix + "/" + hdr.Name
653
-		}
654
-	}
655
-
656
-	if p.err != nil {
657
-		tr.err = p.err
658
-		return nil
659
-	}
660
-
661
-	nb := hdr.Size
662
-	if isHeaderOnlyType(hdr.Typeflag) {
663
-		nb = 0
664
-	}
665
-	if nb < 0 {
666
-		tr.err = ErrHeader
667
-		return nil
668
-	}
669
-
670
-	// Set the current file reader.
671
-	tr.pad = -nb & (blockSize - 1) // blockSize is a power of two
672
-	tr.curr = &regFileReader{r: tr.r, nb: nb}
673
-
674
-	// Check for old GNU sparse format entry.
675
-	if hdr.Typeflag == TypeGNUSparse {
676
-		// Get the real size of the file.
677
-		hdr.Size = p.parseNumeric(header[483:495])
678
-		if p.err != nil {
679
-			tr.err = p.err
680
-			return nil
681
-		}
682
-
683
-		// Read the sparse map.
684
-		sp := tr.readOldGNUSparseMap(header)
685
-		if tr.err != nil {
686
-			return nil
687
-		}
688
-
689
-		// Current file is a GNU sparse file. Update the current file reader.
690
-		tr.curr, tr.err = newSparseFileReader(tr.curr, sp, hdr.Size)
691
-		if tr.err != nil {
692
-			return nil
693
-		}
694
-	}
695
-
696
-	return hdr
697
-}
698
-
699
-// readOldGNUSparseMap reads the sparse map as stored in the old GNU sparse format.
700
-// The sparse map is stored in the tar header if it's small enough. If it's larger than four entries,
701
-// then one or more extension headers are used to store the rest of the sparse map.
702
-func (tr *Reader) readOldGNUSparseMap(header []byte) []sparseEntry {
703
-	var p parser
704
-	isExtended := header[oldGNUSparseMainHeaderIsExtendedOffset] != 0
705
-	spCap := oldGNUSparseMainHeaderNumEntries
706
-	if isExtended {
707
-		spCap += oldGNUSparseExtendedHeaderNumEntries
708
-	}
709
-	sp := make([]sparseEntry, 0, spCap)
710
-	s := slicer(header[oldGNUSparseMainHeaderOffset:])
711
-
712
-	// Read the four entries from the main tar header
713
-	for i := 0; i < oldGNUSparseMainHeaderNumEntries; i++ {
714
-		offset := p.parseNumeric(s.next(oldGNUSparseOffsetSize))
715
-		numBytes := p.parseNumeric(s.next(oldGNUSparseNumBytesSize))
716
-		if p.err != nil {
717
-			tr.err = p.err
718
-			return nil
719
-		}
720
-		if offset == 0 && numBytes == 0 {
721
-			break
722
-		}
723
-		sp = append(sp, sparseEntry{offset: offset, numBytes: numBytes})
724
-	}
725
-
726
-	for isExtended {
727
-		// There are more entries. Read an extension header and parse its entries.
728
-		sparseHeader := make([]byte, blockSize)
729
-		if _, tr.err = io.ReadFull(tr.r, sparseHeader); tr.err != nil {
730
-			return nil
731
-		}
732
-		isExtended = sparseHeader[oldGNUSparseExtendedHeaderIsExtendedOffset] != 0
733
-		s = slicer(sparseHeader)
734
-		for i := 0; i < oldGNUSparseExtendedHeaderNumEntries; i++ {
735
-			offset := p.parseNumeric(s.next(oldGNUSparseOffsetSize))
736
-			numBytes := p.parseNumeric(s.next(oldGNUSparseNumBytesSize))
737
-			if p.err != nil {
738
-				tr.err = p.err
739
-				return nil
740
-			}
741
-			if offset == 0 && numBytes == 0 {
742
-				break
743
-			}
744
-			sp = append(sp, sparseEntry{offset: offset, numBytes: numBytes})
745
-		}
746
-	}
747
-	return sp
748
-}
749
-
750
-// readGNUSparseMap1x0 reads the sparse map as stored in GNU's PAX sparse format
751
-// version 1.0. The format of the sparse map consists of a series of
752
-// newline-terminated numeric fields. The first field is the number of entries
753
-// and is always present. Following this are the entries, consisting of two
754
-// fields (offset, numBytes). This function must stop reading at the end
755
-// boundary of the block containing the last newline.
756
-//
757
-// Note that the GNU manual says that numeric values should be encoded in octal
758
-// format. However, the GNU tar utility itself outputs these values in decimal.
759
-// As such, this library treats values as being encoded in decimal.
760
-func readGNUSparseMap1x0(r io.Reader) ([]sparseEntry, error) {
761
-	var cntNewline int64
762
-	var buf bytes.Buffer
763
-	var blk = make([]byte, blockSize)
764
-
765
-	// feedTokens copies data in numBlock chunks from r into buf until there are
766
-	// at least cnt newlines in buf. It will not read more blocks than needed.
767
-	var feedTokens = func(cnt int64) error {
768
-		for cntNewline < cnt {
769
-			if _, err := io.ReadFull(r, blk); err != nil {
770
-				if err == io.EOF {
771
-					err = io.ErrUnexpectedEOF
772
-				}
773
-				return err
774
-			}
775
-			buf.Write(blk)
776
-			for _, c := range blk {
777
-				if c == '\n' {
778
-					cntNewline++
779
-				}
780
-			}
781
-		}
782
-		return nil
783
-	}
784
-
785
-	// nextToken gets the next token delimited by a newline. This assumes that
786
-	// at least one newline exists in the buffer.
787
-	var nextToken = func() string {
788
-		cntNewline--
789
-		tok, _ := buf.ReadString('\n')
790
-		return tok[:len(tok)-1] // Cut off newline
791
-	}
792
-
793
-	// Parse for the number of entries.
794
-	// Use integer overflow resistant math to check this.
795
-	if err := feedTokens(1); err != nil {
796
-		return nil, err
797
-	}
798
-	numEntries, err := strconv.ParseInt(nextToken(), 10, 0) // Intentionally parse as native int
799
-	if err != nil || numEntries < 0 || int(2*numEntries) < int(numEntries) {
800
-		return nil, ErrHeader
801
-	}
802
-
803
-	// Parse for all member entries.
804
-	// numEntries is trusted after this since a potential attacker must have
805
-	// committed resources proportional to what this library used.
806
-	if err := feedTokens(2 * numEntries); err != nil {
807
-		return nil, err
808
-	}
809
-	sp := make([]sparseEntry, 0, numEntries)
810
-	for i := int64(0); i < numEntries; i++ {
811
-		offset, err := strconv.ParseInt(nextToken(), 10, 64)
812
-		if err != nil {
813
-			return nil, ErrHeader
814
-		}
815
-		numBytes, err := strconv.ParseInt(nextToken(), 10, 64)
816
-		if err != nil {
817
-			return nil, ErrHeader
818
-		}
819
-		sp = append(sp, sparseEntry{offset: offset, numBytes: numBytes})
820
-	}
821
-	return sp, nil
822
-}
823
-
824
-// readGNUSparseMap0x1 reads the sparse map as stored in GNU's PAX sparse format
825
-// version 0.1. The sparse map is stored in the PAX headers.
826
-func readGNUSparseMap0x1(extHdrs map[string]string) ([]sparseEntry, error) {
827
-	// Get number of entries.
828
-	// Use integer overflow resistant math to check this.
829
-	numEntriesStr := extHdrs[paxGNUSparseNumBlocks]
830
-	numEntries, err := strconv.ParseInt(numEntriesStr, 10, 0) // Intentionally parse as native int
831
-	if err != nil || numEntries < 0 || int(2*numEntries) < int(numEntries) {
832
-		return nil, ErrHeader
833
-	}
834
-
835
-	// There should be two numbers in sparseMap for each entry.
836
-	sparseMap := strings.Split(extHdrs[paxGNUSparseMap], ",")
837
-	if int64(len(sparseMap)) != 2*numEntries {
838
-		return nil, ErrHeader
839
-	}
840
-
841
-	// Loop through the entries in the sparse map.
842
-	// numEntries is trusted now.
843
-	sp := make([]sparseEntry, 0, numEntries)
844
-	for i := int64(0); i < numEntries; i++ {
845
-		offset, err := strconv.ParseInt(sparseMap[2*i], 10, 64)
846
-		if err != nil {
847
-			return nil, ErrHeader
848
-		}
849
-		numBytes, err := strconv.ParseInt(sparseMap[2*i+1], 10, 64)
850
-		if err != nil {
851
-			return nil, ErrHeader
852
-		}
853
-		sp = append(sp, sparseEntry{offset: offset, numBytes: numBytes})
854
-	}
855
-	return sp, nil
856
-}
857
-
858
-// numBytes returns the number of bytes left to read in the current file's entry
859
-// in the tar archive, or 0 if there is no current file.
860
-func (tr *Reader) numBytes() int64 {
861
-	if tr.curr == nil {
862
-		// No current file, so no bytes
863
-		return 0
864
-	}
865
-	return tr.curr.numBytes()
866
-}
867
-
868
-// Read reads from the current entry in the tar archive.
869
-// It returns 0, io.EOF when it reaches the end of that entry,
870
-// until Next is called to advance to the next entry.
871
-//
872
-// Calling Read on special types like TypeLink, TypeSymLink, TypeChar,
873
-// TypeBlock, TypeDir, and TypeFifo returns 0, io.EOF regardless of what
874
-// the Header.Size claims.
875
-func (tr *Reader) Read(b []byte) (n int, err error) {
876
-	if tr.err != nil {
877
-		return 0, tr.err
878
-	}
879
-	if tr.curr == nil {
880
-		return 0, io.EOF
881
-	}
882
-
883
-	n, err = tr.curr.Read(b)
884
-	if err != nil && err != io.EOF {
885
-		tr.err = err
886
-	}
887
-	return
888
-}
889
-
890
-func (rfr *regFileReader) Read(b []byte) (n int, err error) {
891
-	if rfr.nb == 0 {
892
-		// file consumed
893
-		return 0, io.EOF
894
-	}
895
-	if int64(len(b)) > rfr.nb {
896
-		b = b[0:rfr.nb]
897
-	}
898
-	n, err = rfr.r.Read(b)
899
-	rfr.nb -= int64(n)
900
-
901
-	if err == io.EOF && rfr.nb > 0 {
902
-		err = io.ErrUnexpectedEOF
903
-	}
904
-	return
905
-}
906
-
907
-// numBytes returns the number of bytes left to read in the file's data in the tar archive.
908
-func (rfr *regFileReader) numBytes() int64 {
909
-	return rfr.nb
910
-}
911
-
912
-// newSparseFileReader creates a new sparseFileReader, but validates all of the
913
-// sparse entries before doing so.
914
-func newSparseFileReader(rfr numBytesReader, sp []sparseEntry, total int64) (*sparseFileReader, error) {
915
-	if total < 0 {
916
-		return nil, ErrHeader // Total size cannot be negative
917
-	}
918
-
919
-	// Validate all sparse entries. These are the same checks as performed by
920
-	// the BSD tar utility.
921
-	for i, s := range sp {
922
-		switch {
923
-		case s.offset < 0 || s.numBytes < 0:
924
-			return nil, ErrHeader // Negative values are never okay
925
-		case s.offset > math.MaxInt64-s.numBytes:
926
-			return nil, ErrHeader // Integer overflow with large length
927
-		case s.offset+s.numBytes > total:
928
-			return nil, ErrHeader // Region extends beyond the "real" size
929
-		case i > 0 && sp[i-1].offset+sp[i-1].numBytes > s.offset:
930
-			return nil, ErrHeader // Regions can't overlap and must be in order
931
-		}
932
-	}
933
-	return &sparseFileReader{rfr: rfr, sp: sp, total: total}, nil
934
-}
935
-
936
-// readHole reads a sparse hole ending at endOffset.
937
-func (sfr *sparseFileReader) readHole(b []byte, endOffset int64) int {
938
-	n64 := endOffset - sfr.pos
939
-	if n64 > int64(len(b)) {
940
-		n64 = int64(len(b))
941
-	}
942
-	n := int(n64)
943
-	for i := 0; i < n; i++ {
944
-		b[i] = 0
945
-	}
946
-	sfr.pos += n64
947
-	return n
948
-}
949
-
950
-// Read reads the sparse file data in expanded form.
951
-func (sfr *sparseFileReader) Read(b []byte) (n int, err error) {
952
-	// Skip past all empty fragments.
953
-	for len(sfr.sp) > 0 && sfr.sp[0].numBytes == 0 {
954
-		sfr.sp = sfr.sp[1:]
955
-	}
956
-
957
-	// If there are no more fragments, then it is possible that there
958
-	// is one last sparse hole.
959
-	if len(sfr.sp) == 0 {
960
-		// This behavior matches the BSD tar utility.
961
-		// However, GNU tar stops returning data even if sfr.total is unmet.
962
-		if sfr.pos < sfr.total {
963
-			return sfr.readHole(b, sfr.total), nil
964
-		}
965
-		return 0, io.EOF
966
-	}
967
-
968
-	// In front of a data fragment, so read a hole.
969
-	if sfr.pos < sfr.sp[0].offset {
970
-		return sfr.readHole(b, sfr.sp[0].offset), nil
971
-	}
972
-
973
-	// In a data fragment, so read from it.
974
-	// This math is overflow free since we verify that offset and numBytes can
975
-	// be safely added when creating the sparseFileReader.
976
-	endPos := sfr.sp[0].offset + sfr.sp[0].numBytes // End offset of fragment
977
-	bytesLeft := endPos - sfr.pos                   // Bytes left in fragment
978
-	if int64(len(b)) > bytesLeft {
979
-		b = b[:bytesLeft]
980
-	}
981
-
982
-	n, err = sfr.rfr.Read(b)
983
-	sfr.pos += int64(n)
984
-	if err == io.EOF {
985
-		if sfr.pos < endPos {
986
-			err = io.ErrUnexpectedEOF // There was supposed to be more data
987
-		} else if sfr.pos < sfr.total {
988
-			err = nil // There is still an implicit sparse hole at the end
989
-		}
990
-	}
991
-
992
-	if sfr.pos == endPos {
993
-		sfr.sp = sfr.sp[1:] // We are done with this fragment, so pop it
994
-	}
995
-	return n, err
996
-}
997
-
998
-// numBytes returns the number of bytes left to read in the sparse file's
999
-// sparse-encoded data in the tar archive.
1000
-func (sfr *sparseFileReader) numBytes() int64 {
1001
-	return sfr.rfr.numBytes()
1002
-}
1003 1
deleted file mode 100644
... ...
@@ -1,20 +0,0 @@
1
-// Copyright 2012 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 linux dragonfly openbsd solaris
6
-
7
-package tar
8
-
9
-import (
10
-	"syscall"
11
-	"time"
12
-)
13
-
14
-func statAtime(st *syscall.Stat_t) time.Time {
15
-	return time.Unix(st.Atim.Unix())
16
-}
17
-
18
-func statCtime(st *syscall.Stat_t) time.Time {
19
-	return time.Unix(st.Ctim.Unix())
20
-}
21 1
deleted file mode 100644
... ...
@@ -1,20 +0,0 @@
1
-// Copyright 2012 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 darwin freebsd netbsd
6
-
7
-package tar
8
-
9
-import (
10
-	"syscall"
11
-	"time"
12
-)
13
-
14
-func statAtime(st *syscall.Stat_t) time.Time {
15
-	return time.Unix(st.Atimespec.Unix())
16
-}
17
-
18
-func statCtime(st *syscall.Stat_t) time.Time {
19
-	return time.Unix(st.Ctimespec.Unix())
20
-}
21 1
deleted file mode 100644
... ...
@@ -1,32 +0,0 @@
1
-// Copyright 2012 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 linux darwin dragonfly freebsd openbsd netbsd solaris
6
-
7
-package tar
8
-
9
-import (
10
-	"os"
11
-	"syscall"
12
-)
13
-
14
-func init() {
15
-	sysStat = statUnix
16
-}
17
-
18
-func statUnix(fi os.FileInfo, h *Header) error {
19
-	sys, ok := fi.Sys().(*syscall.Stat_t)
20
-	if !ok {
21
-		return nil
22
-	}
23
-	h.Uid = int(sys.Uid)
24
-	h.Gid = int(sys.Gid)
25
-	// TODO(bradfitz): populate username & group.  os/user
26
-	// doesn't cache LookupId lookups, and lacks group
27
-	// lookup functions.
28
-	h.AccessTime = statAtime(sys)
29
-	h.ChangeTime = statCtime(sys)
30
-	// TODO(bradfitz): major/minor device numbers?
31
-	return nil
32
-}
33 1
deleted file mode 100644
... ...
@@ -1,444 +0,0 @@
1
-// Copyright 2009 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
-package tar
6
-
7
-// TODO(dsymonds):
8
-// - catch more errors (no first header, etc.)
9
-
10
-import (
11
-	"bytes"
12
-	"errors"
13
-	"fmt"
14
-	"io"
15
-	"path"
16
-	"sort"
17
-	"strconv"
18
-	"strings"
19
-	"time"
20
-)
21
-
22
-var (
23
-	ErrWriteTooLong    = errors.New("archive/tar: write too long")
24
-	ErrFieldTooLong    = errors.New("archive/tar: header field too long")
25
-	ErrWriteAfterClose = errors.New("archive/tar: write after close")
26
-	errInvalidHeader   = errors.New("archive/tar: header field too long or contains invalid values")
27
-)
28
-
29
-// A Writer provides sequential writing of a tar archive in POSIX.1 format.
30
-// A tar archive consists of a sequence of files.
31
-// Call WriteHeader to begin a new file, and then call Write to supply that file's data,
32
-// writing at most hdr.Size bytes in total.
33
-type Writer struct {
34
-	w          io.Writer
35
-	err        error
36
-	nb         int64 // number of unwritten bytes for current file entry
37
-	pad        int64 // amount of padding to write after current file entry
38
-	closed     bool
39
-	usedBinary bool            // whether the binary numeric field extension was used
40
-	preferPax  bool            // use pax header instead of binary numeric header
41
-	hdrBuff    [blockSize]byte // buffer to use in writeHeader when writing a regular header
42
-	paxHdrBuff [blockSize]byte // buffer to use in writeHeader when writing a pax header
43
-}
44
-
45
-type formatter struct {
46
-	err error // Last error seen
47
-}
48
-
49
-// NewWriter creates a new Writer writing to w.
50
-func NewWriter(w io.Writer) *Writer { return &Writer{w: w, preferPax: true} }
51
-
52
-// Flush finishes writing the current file (optional).
53
-func (tw *Writer) Flush() error {
54
-	if tw.nb > 0 {
55
-		tw.err = fmt.Errorf("archive/tar: missed writing %d bytes", tw.nb)
56
-		return tw.err
57
-	}
58
-
59
-	n := tw.nb + tw.pad
60
-	for n > 0 && tw.err == nil {
61
-		nr := n
62
-		if nr > blockSize {
63
-			nr = blockSize
64
-		}
65
-		var nw int
66
-		nw, tw.err = tw.w.Write(zeroBlock[0:nr])
67
-		n -= int64(nw)
68
-	}
69
-	tw.nb = 0
70
-	tw.pad = 0
71
-	return tw.err
72
-}
73
-
74
-// Write s into b, terminating it with a NUL if there is room.
75
-func (f *formatter) formatString(b []byte, s string) {
76
-	if len(s) > len(b) {
77
-		f.err = ErrFieldTooLong
78
-		return
79
-	}
80
-	ascii := toASCII(s)
81
-	copy(b, ascii)
82
-	if len(ascii) < len(b) {
83
-		b[len(ascii)] = 0
84
-	}
85
-}
86
-
87
-// Encode x as an octal ASCII string and write it into b with leading zeros.
88
-func (f *formatter) formatOctal(b []byte, x int64) {
89
-	s := strconv.FormatInt(x, 8)
90
-	// leading zeros, but leave room for a NUL.
91
-	for len(s)+1 < len(b) {
92
-		s = "0" + s
93
-	}
94
-	f.formatString(b, s)
95
-}
96
-
97
-// fitsInBase256 reports whether x can be encoded into n bytes using base-256
98
-// encoding. Unlike octal encoding, base-256 encoding does not require that the
99
-// string ends with a NUL character. Thus, all n bytes are available for output.
100
-//
101
-// If operating in binary mode, this assumes strict GNU binary mode; which means
102
-// that the first byte can only be either 0x80 or 0xff. Thus, the first byte is
103
-// equivalent to the sign bit in two's complement form.
104
-func fitsInBase256(n int, x int64) bool {
105
-	var binBits = uint(n-1) * 8
106
-	return n >= 9 || (x >= -1<<binBits && x < 1<<binBits)
107
-}
108
-
109
-// Write x into b, as binary (GNUtar/star extension).
110
-func (f *formatter) formatNumeric(b []byte, x int64) {
111
-	if fitsInBase256(len(b), x) {
112
-		for i := len(b) - 1; i >= 0; i-- {
113
-			b[i] = byte(x)
114
-			x >>= 8
115
-		}
116
-		b[0] |= 0x80 // Highest bit indicates binary format
117
-		return
118
-	}
119
-
120
-	f.formatOctal(b, 0) // Last resort, just write zero
121
-	f.err = ErrFieldTooLong
122
-}
123
-
124
-var (
125
-	minTime = time.Unix(0, 0)
126
-	// There is room for 11 octal digits (33 bits) of mtime.
127
-	maxTime = minTime.Add((1<<33 - 1) * time.Second)
128
-)
129
-
130
-// WriteHeader writes hdr and prepares to accept the file's contents.
131
-// WriteHeader calls Flush if it is not the first header.
132
-// Calling after a Close will return ErrWriteAfterClose.
133
-func (tw *Writer) WriteHeader(hdr *Header) error {
134
-	return tw.writeHeader(hdr, true)
135
-}
136
-
137
-// WriteHeader writes hdr and prepares to accept the file's contents.
138
-// WriteHeader calls Flush if it is not the first header.
139
-// Calling after a Close will return ErrWriteAfterClose.
140
-// As this method is called internally by writePax header to allow it to
141
-// suppress writing the pax header.
142
-func (tw *Writer) writeHeader(hdr *Header, allowPax bool) error {
143
-	if tw.closed {
144
-		return ErrWriteAfterClose
145
-	}
146
-	if tw.err == nil {
147
-		tw.Flush()
148
-	}
149
-	if tw.err != nil {
150
-		return tw.err
151
-	}
152
-
153
-	// a map to hold pax header records, if any are needed
154
-	paxHeaders := make(map[string]string)
155
-
156
-	// TODO(shanemhansen): we might want to use PAX headers for
157
-	// subsecond time resolution, but for now let's just capture
158
-	// too long fields or non ascii characters
159
-
160
-	var f formatter
161
-	var header []byte
162
-
163
-	// We need to select which scratch buffer to use carefully,
164
-	// since this method is called recursively to write PAX headers.
165
-	// If allowPax is true, this is the non-recursive call, and we will use hdrBuff.
166
-	// If allowPax is false, we are being called by writePAXHeader, and hdrBuff is
167
-	// already being used by the non-recursive call, so we must use paxHdrBuff.
168
-	header = tw.hdrBuff[:]
169
-	if !allowPax {
170
-		header = tw.paxHdrBuff[:]
171
-	}
172
-	copy(header, zeroBlock)
173
-	s := slicer(header)
174
-
175
-	// Wrappers around formatter that automatically sets paxHeaders if the
176
-	// argument extends beyond the capacity of the input byte slice.
177
-	var formatString = func(b []byte, s string, paxKeyword string) {
178
-		needsPaxHeader := paxKeyword != paxNone && len(s) > len(b) || !isASCII(s)
179
-		if needsPaxHeader {
180
-			paxHeaders[paxKeyword] = s
181
-			return
182
-		}
183
-		f.formatString(b, s)
184
-	}
185
-	var formatNumeric = func(b []byte, x int64, paxKeyword string) {
186
-		// Try octal first.
187
-		s := strconv.FormatInt(x, 8)
188
-		if len(s) < len(b) {
189
-			f.formatOctal(b, x)
190
-			return
191
-		}
192
-
193
-		// If it is too long for octal, and PAX is preferred, use a PAX header.
194
-		if paxKeyword != paxNone && tw.preferPax {
195
-			f.formatOctal(b, 0)
196
-			s := strconv.FormatInt(x, 10)
197
-			paxHeaders[paxKeyword] = s
198
-			return
199
-		}
200
-
201
-		tw.usedBinary = true
202
-		f.formatNumeric(b, x)
203
-	}
204
-	var formatTime = func(b []byte, t time.Time, paxKeyword string) {
205
-		var unixTime int64
206
-		if !t.Before(minTime) && !t.After(maxTime) {
207
-			unixTime = t.Unix()
208
-		}
209
-		formatNumeric(b, unixTime, paxNone)
210
-
211
-		// Write a PAX header if the time didn't fit precisely.
212
-		if paxKeyword != "" && tw.preferPax && allowPax && (t.Nanosecond() != 0 || !t.Before(minTime) || !t.After(maxTime)) {
213
-			paxHeaders[paxKeyword] = formatPAXTime(t)
214
-		}
215
-	}
216
-
217
-	// keep a reference to the filename to allow to overwrite it later if we detect that we can use ustar longnames instead of pax
218
-	pathHeaderBytes := s.next(fileNameSize)
219
-
220
-	formatString(pathHeaderBytes, hdr.Name, paxPath)
221
-
222
-	f.formatOctal(s.next(8), hdr.Mode)               // 100:108
223
-	formatNumeric(s.next(8), int64(hdr.Uid), paxUid) // 108:116
224
-	formatNumeric(s.next(8), int64(hdr.Gid), paxGid) // 116:124
225
-	formatNumeric(s.next(12), hdr.Size, paxSize)     // 124:136
226
-	formatTime(s.next(12), hdr.ModTime, paxMtime)    // 136:148
227
-	s.next(8)                                        // chksum (148:156)
228
-	s.next(1)[0] = hdr.Typeflag                      // 156:157
229
-
230
-	formatString(s.next(100), hdr.Linkname, paxLinkpath)
231
-
232
-	copy(s.next(8), []byte("ustar\x0000"))          // 257:265
233
-	formatString(s.next(32), hdr.Uname, paxUname)   // 265:297
234
-	formatString(s.next(32), hdr.Gname, paxGname)   // 297:329
235
-	formatNumeric(s.next(8), hdr.Devmajor, paxNone) // 329:337
236
-	formatNumeric(s.next(8), hdr.Devminor, paxNone) // 337:345
237
-
238
-	// keep a reference to the prefix to allow to overwrite it later if we detect that we can use ustar longnames instead of pax
239
-	prefixHeaderBytes := s.next(155)
240
-	formatString(prefixHeaderBytes, "", paxNone) // 345:500  prefix
241
-
242
-	// Use the GNU magic instead of POSIX magic if we used any GNU extensions.
243
-	if tw.usedBinary {
244
-		copy(header[257:265], []byte("ustar  \x00"))
245
-	}
246
-
247
-	_, paxPathUsed := paxHeaders[paxPath]
248
-	// try to use a ustar header when only the name is too long
249
-	if !tw.preferPax && len(paxHeaders) == 1 && paxPathUsed {
250
-		prefix, suffix, ok := splitUSTARPath(hdr.Name)
251
-		if ok {
252
-			// Since we can encode in USTAR format, disable PAX header.
253
-			delete(paxHeaders, paxPath)
254
-
255
-			// Update the path fields
256
-			formatString(pathHeaderBytes, suffix, paxNone)
257
-			formatString(prefixHeaderBytes, prefix, paxNone)
258
-		}
259
-	}
260
-
261
-	// The chksum field is terminated by a NUL and a space.
262
-	// This is different from the other octal fields.
263
-	chksum, _ := checksum(header)
264
-	f.formatOctal(header[148:155], chksum) // Never fails
265
-	header[155] = ' '
266
-
267
-	// Check if there were any formatting errors.
268
-	if f.err != nil {
269
-		tw.err = f.err
270
-		return tw.err
271
-	}
272
-
273
-	if allowPax {
274
-		if !hdr.AccessTime.IsZero() {
275
-			paxHeaders[paxAtime] = formatPAXTime(hdr.AccessTime)
276
-		}
277
-		if !hdr.ChangeTime.IsZero() {
278
-			paxHeaders[paxCtime] = formatPAXTime(hdr.ChangeTime)
279
-		}
280
-		if !hdr.CreationTime.IsZero() {
281
-			paxHeaders[paxCreationTime] = formatPAXTime(hdr.CreationTime)
282
-		}
283
-		for k, v := range hdr.Xattrs {
284
-			paxHeaders[paxXattr+k] = v
285
-		}
286
-		for k, v := range hdr.Winheaders {
287
-			paxHeaders[paxWindows+k] = v
288
-		}
289
-	}
290
-
291
-	if len(paxHeaders) > 0 {
292
-		if !allowPax {
293
-			return errInvalidHeader
294
-		}
295
-		if err := tw.writePAXHeader(hdr, paxHeaders); err != nil {
296
-			return err
297
-		}
298
-	}
299
-	tw.nb = int64(hdr.Size)
300
-	tw.pad = (blockSize - (tw.nb % blockSize)) % blockSize
301
-
302
-	_, tw.err = tw.w.Write(header)
303
-	return tw.err
304
-}
305
-
306
-func formatPAXTime(t time.Time) string {
307
-	sec := t.Unix()
308
-	usec := t.Nanosecond()
309
-	s := strconv.FormatInt(sec, 10)
310
-	if usec != 0 {
311
-		s = fmt.Sprintf("%s.%09d", s, usec)
312
-	}
313
-	return s
314
-}
315
-
316
-// splitUSTARPath splits a path according to USTAR prefix and suffix rules.
317
-// If the path is not splittable, then it will return ("", "", false).
318
-func splitUSTARPath(name string) (prefix, suffix string, ok bool) {
319
-	length := len(name)
320
-	if length <= fileNameSize || !isASCII(name) {
321
-		return "", "", false
322
-	} else if length > fileNamePrefixSize+1 {
323
-		length = fileNamePrefixSize + 1
324
-	} else if name[length-1] == '/' {
325
-		length--
326
-	}
327
-
328
-	i := strings.LastIndex(name[:length], "/")
329
-	nlen := len(name) - i - 1 // nlen is length of suffix
330
-	plen := i                 // plen is length of prefix
331
-	if i <= 0 || nlen > fileNameSize || nlen == 0 || plen > fileNamePrefixSize {
332
-		return "", "", false
333
-	}
334
-	return name[:i], name[i+1:], true
335
-}
336
-
337
-// writePaxHeader writes an extended pax header to the
338
-// archive.
339
-func (tw *Writer) writePAXHeader(hdr *Header, paxHeaders map[string]string) error {
340
-	// Prepare extended header
341
-	ext := new(Header)
342
-	ext.Typeflag = TypeXHeader
343
-	// Setting ModTime is required for reader parsing to
344
-	// succeed, and seems harmless enough.
345
-	ext.ModTime = hdr.ModTime
346
-	// The spec asks that we namespace our pseudo files
347
-	// with the current pid.  However, this results in differing outputs
348
-	// for identical inputs.  As such, the constant 0 is now used instead.
349
-	// golang.org/issue/12358
350
-	dir, file := path.Split(hdr.Name)
351
-	fullName := path.Join(dir, "PaxHeaders.0", file)
352
-
353
-	ascii := toASCII(fullName)
354
-	if len(ascii) > 100 {
355
-		ascii = ascii[:100]
356
-	}
357
-	ext.Name = ascii
358
-	// Construct the body
359
-	var buf bytes.Buffer
360
-
361
-	// Keys are sorted before writing to body to allow deterministic output.
362
-	var keys []string
363
-	for k := range paxHeaders {
364
-		keys = append(keys, k)
365
-	}
366
-	sort.Strings(keys)
367
-
368
-	for _, k := range keys {
369
-		fmt.Fprint(&buf, formatPAXRecord(k, paxHeaders[k]))
370
-	}
371
-
372
-	ext.Size = int64(len(buf.Bytes()))
373
-	if err := tw.writeHeader(ext, false); err != nil {
374
-		return err
375
-	}
376
-	if _, err := tw.Write(buf.Bytes()); err != nil {
377
-		return err
378
-	}
379
-	if err := tw.Flush(); err != nil {
380
-		return err
381
-	}
382
-	return nil
383
-}
384
-
385
-// formatPAXRecord formats a single PAX record, prefixing it with the
386
-// appropriate length.
387
-func formatPAXRecord(k, v string) string {
388
-	const padding = 3 // Extra padding for ' ', '=', and '\n'
389
-	size := len(k) + len(v) + padding
390
-	size += len(strconv.Itoa(size))
391
-	record := fmt.Sprintf("%d %s=%s\n", size, k, v)
392
-
393
-	// Final adjustment if adding size field increased the record size.
394
-	if len(record) != size {
395
-		size = len(record)
396
-		record = fmt.Sprintf("%d %s=%s\n", size, k, v)
397
-	}
398
-	return record
399
-}
400
-
401
-// Write writes to the current entry in the tar archive.
402
-// Write returns the error ErrWriteTooLong if more than
403
-// hdr.Size bytes are written after WriteHeader.
404
-func (tw *Writer) Write(b []byte) (n int, err error) {
405
-	if tw.closed {
406
-		err = ErrWriteAfterClose
407
-		return
408
-	}
409
-	overwrite := false
410
-	if int64(len(b)) > tw.nb {
411
-		b = b[0:tw.nb]
412
-		overwrite = true
413
-	}
414
-	n, err = tw.w.Write(b)
415
-	tw.nb -= int64(n)
416
-	if err == nil && overwrite {
417
-		err = ErrWriteTooLong
418
-		return
419
-	}
420
-	tw.err = err
421
-	return
422
-}
423
-
424
-// Close closes the tar archive, flushing any unwritten
425
-// data to the underlying writer.
426
-func (tw *Writer) Close() error {
427
-	if tw.err != nil || tw.closed {
428
-		return tw.err
429
-	}
430
-	tw.Flush()
431
-	tw.closed = true
432
-	if tw.err != nil {
433
-		return tw.err
434
-	}
435
-
436
-	// trailer: two zero blocks
437
-	for i := 0; i < 2; i++ {
438
-		_, tw.err = tw.w.Write(zeroBlock)
439
-		if tw.err != nil {
440
-			break
441
-		}
442
-	}
443
-	return tw.err
444
-}
445 1
new file mode 100644
... ...
@@ -0,0 +1,68 @@
0
+package backuptar
1
+
2
+import (
3
+	"archive/tar"
4
+	"fmt"
5
+	"strconv"
6
+	"strings"
7
+	"time"
8
+)
9
+
10
+// Functions copied from https://github.com/golang/go/blob/master/src/archive/tar/strconv.go
11
+// as we need to manage the LIBARCHIVE.creationtime PAXRecord manually.
12
+// Idea taken from containerd which did the same thing.
13
+
14
+// parsePAXTime takes a string of the form %d.%d as described in the PAX
15
+// specification. Note that this implementation allows for negative timestamps,
16
+// which is allowed for by the PAX specification, but not always portable.
17
+func parsePAXTime(s string) (time.Time, error) {
18
+	const maxNanoSecondDigits = 9
19
+
20
+	// Split string into seconds and sub-seconds parts.
21
+	ss, sn := s, ""
22
+	if pos := strings.IndexByte(s, '.'); pos >= 0 {
23
+		ss, sn = s[:pos], s[pos+1:]
24
+	}
25
+
26
+	// Parse the seconds.
27
+	secs, err := strconv.ParseInt(ss, 10, 64)
28
+	if err != nil {
29
+		return time.Time{}, tar.ErrHeader
30
+	}
31
+	if len(sn) == 0 {
32
+		return time.Unix(secs, 0), nil // No sub-second values
33
+	}
34
+
35
+	// Parse the nanoseconds.
36
+	if strings.Trim(sn, "0123456789") != "" {
37
+		return time.Time{}, tar.ErrHeader
38
+	}
39
+	if len(sn) < maxNanoSecondDigits {
40
+		sn += strings.Repeat("0", maxNanoSecondDigits-len(sn)) // Right pad
41
+	} else {
42
+		sn = sn[:maxNanoSecondDigits] // Right truncate
43
+	}
44
+	nsecs, _ := strconv.ParseInt(sn, 10, 64) // Must succeed
45
+	if len(ss) > 0 && ss[0] == '-' {
46
+		return time.Unix(secs, -1*nsecs), nil // Negative correction
47
+	}
48
+	return time.Unix(secs, nsecs), nil
49
+}
50
+
51
+// formatPAXTime converts ts into a time of the form %d.%d as described in the
52
+// PAX specification. This function is capable of negative timestamps.
53
+func formatPAXTime(ts time.Time) (s string) {
54
+	secs, nsecs := ts.Unix(), ts.Nanosecond()
55
+	if nsecs == 0 {
56
+		return strconv.FormatInt(secs, 10)
57
+	}
58
+
59
+	// If seconds is negative, then perform correction.
60
+	sign := ""
61
+	if secs < 0 {
62
+		sign = "-"             // Remember sign
63
+		secs = -(secs + 1)     // Add a second to secs
64
+		nsecs = -(nsecs - 1e9) // Take that second away from nsecs
65
+	}
66
+	return strings.TrimRight(fmt.Sprintf("%s%d.%09d", sign, secs, nsecs), "0")
67
+}
... ...
@@ -3,6 +3,7 @@
3 3
 package backuptar
4 4
 
5 5
 import (
6
+	"archive/tar"
6 7
 	"encoding/base64"
7 8
 	"errors"
8 9
 	"fmt"
... ...
@@ -15,7 +16,6 @@ import (
15 15
 	"time"
16 16
 
17 17
 	"github.com/Microsoft/go-winio"
18
-	"github.com/Microsoft/go-winio/archive/tar" // until archive/tar supports pax extensions in its interface
19 18
 )
20 19
 
21 20
 const (
... ...
@@ -32,11 +32,13 @@ const (
32 32
 )
33 33
 
34 34
 const (
35
-	hdrFileAttributes        = "fileattr"
36
-	hdrSecurityDescriptor    = "sd"
37
-	hdrRawSecurityDescriptor = "rawsd"
38
-	hdrMountPoint            = "mountpoint"
39
-	hdrEaPrefix              = "xattr."
35
+	hdrFileAttributes        = "MSWINDOWS.fileattr"
36
+	hdrSecurityDescriptor    = "MSWINDOWS.sd"
37
+	hdrRawSecurityDescriptor = "MSWINDOWS.rawsd"
38
+	hdrMountPoint            = "MSWINDOWS.mountpoint"
39
+	hdrEaPrefix              = "MSWINDOWS.xattr."
40
+
41
+	hdrCreationTime = "LIBARCHIVE.creationtime"
40 42
 )
41 43
 
42 44
 func writeZeroes(w io.Writer, count int64) error {
... ...
@@ -86,16 +88,17 @@ func copySparse(t *tar.Writer, br *winio.BackupStreamReader) error {
86 86
 // BasicInfoHeader creates a tar header from basic file information.
87 87
 func BasicInfoHeader(name string, size int64, fileInfo *winio.FileBasicInfo) *tar.Header {
88 88
 	hdr := &tar.Header{
89
-		Name:         filepath.ToSlash(name),
90
-		Size:         size,
91
-		Typeflag:     tar.TypeReg,
92
-		ModTime:      time.Unix(0, fileInfo.LastWriteTime.Nanoseconds()),
93
-		ChangeTime:   time.Unix(0, fileInfo.ChangeTime.Nanoseconds()),
94
-		AccessTime:   time.Unix(0, fileInfo.LastAccessTime.Nanoseconds()),
95
-		CreationTime: time.Unix(0, fileInfo.CreationTime.Nanoseconds()),
96
-		Winheaders:   make(map[string]string),
89
+		Format:     tar.FormatPAX,
90
+		Name:       filepath.ToSlash(name),
91
+		Size:       size,
92
+		Typeflag:   tar.TypeReg,
93
+		ModTime:    time.Unix(0, fileInfo.LastWriteTime.Nanoseconds()),
94
+		ChangeTime: time.Unix(0, fileInfo.ChangeTime.Nanoseconds()),
95
+		AccessTime: time.Unix(0, fileInfo.LastAccessTime.Nanoseconds()),
96
+		PAXRecords: make(map[string]string),
97 97
 	}
98
-	hdr.Winheaders[hdrFileAttributes] = fmt.Sprintf("%d", fileInfo.FileAttributes)
98
+	hdr.PAXRecords[hdrFileAttributes] = fmt.Sprintf("%d", fileInfo.FileAttributes)
99
+	hdr.PAXRecords[hdrCreationTime] = formatPAXTime(time.Unix(0, fileInfo.CreationTime.Nanoseconds()))
99 100
 
100 101
 	if (fileInfo.FileAttributes & syscall.FILE_ATTRIBUTE_DIRECTORY) != 0 {
101 102
 		hdr.Mode |= c_ISDIR
... ...
@@ -155,7 +158,7 @@ func WriteTarFileFromBackupStream(t *tar.Writer, r io.Reader, name string, size
155 155
 			if err != nil {
156 156
 				return err
157 157
 			}
158
-			hdr.Winheaders[hdrRawSecurityDescriptor] = base64.StdEncoding.EncodeToString(sd)
158
+			hdr.PAXRecords[hdrRawSecurityDescriptor] = base64.StdEncoding.EncodeToString(sd)
159 159
 
160 160
 		case winio.BackupReparseData:
161 161
 			hdr.Mode |= c_ISLNK
... ...
@@ -166,7 +169,7 @@ func WriteTarFileFromBackupStream(t *tar.Writer, r io.Reader, name string, size
166 166
 				return err
167 167
 			}
168 168
 			if rp.IsMountPoint {
169
-				hdr.Winheaders[hdrMountPoint] = "1"
169
+				hdr.PAXRecords[hdrMountPoint] = "1"
170 170
 			}
171 171
 			hdr.Linkname = rp.Target
172 172
 
... ...
@@ -183,7 +186,7 @@ func WriteTarFileFromBackupStream(t *tar.Writer, r io.Reader, name string, size
183 183
 				// Use base64 encoding for the binary value. Note that there
184 184
 				// is no way to encode the EA's flags, since their use doesn't
185 185
 				// make any sense for persisted EAs.
186
-				hdr.Winheaders[hdrEaPrefix+ea.Name] = base64.StdEncoding.EncodeToString(ea.Value)
186
+				hdr.PAXRecords[hdrEaPrefix+ea.Name] = base64.StdEncoding.EncodeToString(ea.Value)
187 187
 			}
188 188
 
189 189
 		case winio.BackupAlternateData, winio.BackupLink, winio.BackupPropertyData, winio.BackupObjectId, winio.BackupTxfsData:
... ...
@@ -254,6 +257,7 @@ func WriteTarFileFromBackupStream(t *tar.Writer, r io.Reader, name string, size
254 254
 			}
255 255
 			if (bhdr.Attributes & winio.StreamSparseAttributes) == 0 {
256 256
 				hdr = &tar.Header{
257
+					Format:     hdr.Format,
257 258
 					Name:       name + altName,
258 259
 					Mode:       hdr.Mode,
259 260
 					Typeflag:   tar.TypeReg,
... ...
@@ -296,9 +300,10 @@ func FileInfoFromHeader(hdr *tar.Header) (name string, size int64, fileInfo *win
296 296
 		LastAccessTime: syscall.NsecToFiletime(hdr.AccessTime.UnixNano()),
297 297
 		LastWriteTime:  syscall.NsecToFiletime(hdr.ModTime.UnixNano()),
298 298
 		ChangeTime:     syscall.NsecToFiletime(hdr.ChangeTime.UnixNano()),
299
-		CreationTime:   syscall.NsecToFiletime(hdr.CreationTime.UnixNano()),
299
+		// Default to ModTime, we'll pull hdrCreationTime below if present
300
+		CreationTime: syscall.NsecToFiletime(hdr.ModTime.UnixNano()),
300 301
 	}
301
-	if attrStr, ok := hdr.Winheaders[hdrFileAttributes]; ok {
302
+	if attrStr, ok := hdr.PAXRecords[hdrFileAttributes]; ok {
302 303
 		attr, err := strconv.ParseUint(attrStr, 10, 32)
303 304
 		if err != nil {
304 305
 			return "", 0, nil, err
... ...
@@ -309,6 +314,13 @@ func FileInfoFromHeader(hdr *tar.Header) (name string, size int64, fileInfo *win
309 309
 			fileInfo.FileAttributes |= syscall.FILE_ATTRIBUTE_DIRECTORY
310 310
 		}
311 311
 	}
312
+	if creationTimeStr, ok := hdr.PAXRecords[hdrCreationTime]; ok {
313
+		creationTime, err := parsePAXTime(creationTimeStr)
314
+		if err != nil {
315
+			return "", 0, nil, err
316
+		}
317
+		fileInfo.CreationTime = syscall.NsecToFiletime(creationTime.UnixNano())
318
+	}
312 319
 	return
313 320
 }
314 321
 
... ...
@@ -321,13 +333,13 @@ func WriteBackupStreamFromTarFile(w io.Writer, t *tar.Reader, hdr *tar.Header) (
321 321
 	var err error
322 322
 	// Maintaining old SDDL-based behavior for backward compatibility.  All new tar headers written
323 323
 	// by this library will have raw binary for the security descriptor.
324
-	if sddl, ok := hdr.Winheaders[hdrSecurityDescriptor]; ok {
324
+	if sddl, ok := hdr.PAXRecords[hdrSecurityDescriptor]; ok {
325 325
 		sd, err = winio.SddlToSecurityDescriptor(sddl)
326 326
 		if err != nil {
327 327
 			return nil, err
328 328
 		}
329 329
 	}
330
-	if sdraw, ok := hdr.Winheaders[hdrRawSecurityDescriptor]; ok {
330
+	if sdraw, ok := hdr.PAXRecords[hdrRawSecurityDescriptor]; ok {
331 331
 		sd, err = base64.StdEncoding.DecodeString(sdraw)
332 332
 		if err != nil {
333 333
 			return nil, err
... ...
@@ -348,7 +360,7 @@ func WriteBackupStreamFromTarFile(w io.Writer, t *tar.Reader, hdr *tar.Header) (
348 348
 		}
349 349
 	}
350 350
 	var eas []winio.ExtendedAttribute
351
-	for k, v := range hdr.Winheaders {
351
+	for k, v := range hdr.PAXRecords {
352 352
 		if !strings.HasPrefix(k, hdrEaPrefix) {
353 353
 			continue
354 354
 		}
... ...
@@ -380,7 +392,7 @@ func WriteBackupStreamFromTarFile(w io.Writer, t *tar.Reader, hdr *tar.Header) (
380 380
 		}
381 381
 	}
382 382
 	if hdr.Typeflag == tar.TypeSymlink {
383
-		_, isMountPoint := hdr.Winheaders[hdrMountPoint]
383
+		_, isMountPoint := hdr.PAXRecords[hdrMountPoint]
384 384
 		rp := winio.ReparsePoint{
385 385
 			Target:       filepath.FromSlash(hdr.Linkname),
386 386
 			IsMountPoint: isMountPoint,
... ...
@@ -5,5 +5,5 @@ go 1.12
5 5
 require (
6 6
 	github.com/pkg/errors v0.8.1
7 7
 	github.com/sirupsen/logrus v1.4.1
8
-	golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b
8
+	golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3
9 9
 )
... ...
@@ -182,13 +182,14 @@ func (s pipeAddress) String() string {
182 182
 }
183 183
 
184 184
 // tryDialPipe attempts to dial the pipe at `path` until `ctx` cancellation or timeout.
185
-func tryDialPipe(ctx context.Context, path *string) (syscall.Handle, error) {
185
+func tryDialPipe(ctx context.Context, path *string, access uint32) (syscall.Handle, error) {
186 186
 	for {
187
+
187 188
 		select {
188 189
 		case <-ctx.Done():
189 190
 			return syscall.Handle(0), ctx.Err()
190 191
 		default:
191
-			h, err := createFile(*path, syscall.GENERIC_READ|syscall.GENERIC_WRITE, 0, nil, syscall.OPEN_EXISTING, syscall.FILE_FLAG_OVERLAPPED|cSECURITY_SQOS_PRESENT|cSECURITY_ANONYMOUS, 0)
192
+			h, err := createFile(*path, access, 0, nil, syscall.OPEN_EXISTING, syscall.FILE_FLAG_OVERLAPPED|cSECURITY_SQOS_PRESENT|cSECURITY_ANONYMOUS, 0)
192 193
 			if err == nil {
193 194
 				return h, nil
194 195
 			}
... ...
@@ -197,7 +198,7 @@ func tryDialPipe(ctx context.Context, path *string) (syscall.Handle, error) {
197 197
 			}
198 198
 			// Wait 10 msec and try again. This is a rather simplistic
199 199
 			// view, as we always try each 10 milliseconds.
200
-			time.Sleep(time.Millisecond * 10)
200
+			time.Sleep(10 * time.Millisecond)
201 201
 		}
202 202
 	}
203 203
 }
... ...
@@ -210,7 +211,7 @@ func DialPipe(path string, timeout *time.Duration) (net.Conn, error) {
210 210
 	if timeout != nil {
211 211
 		absTimeout = time.Now().Add(*timeout)
212 212
 	} else {
213
-		absTimeout = time.Now().Add(time.Second * 2)
213
+		absTimeout = time.Now().Add(2 * time.Second)
214 214
 	}
215 215
 	ctx, _ := context.WithDeadline(context.Background(), absTimeout)
216 216
 	conn, err := DialPipeContext(ctx, path)
... ...
@@ -223,9 +224,15 @@ func DialPipe(path string, timeout *time.Duration) (net.Conn, error) {
223 223
 // DialPipeContext attempts to connect to a named pipe by `path` until `ctx`
224 224
 // cancellation or timeout.
225 225
 func DialPipeContext(ctx context.Context, path string) (net.Conn, error) {
226
+	return DialPipeAccess(ctx, path, syscall.GENERIC_READ|syscall.GENERIC_WRITE)
227
+}
228
+
229
+// DialPipeAccess attempts to connect to a named pipe by `path` with `access` until `ctx`
230
+// cancellation or timeout.
231
+func DialPipeAccess(ctx context.Context, path string, access uint32) (net.Conn, error) {
226 232
 	var err error
227 233
 	var h syscall.Handle
228
-	h, err = tryDialPipe(ctx, &path)
234
+	h, err = tryDialPipe(ctx, &path, access)
229 235
 	if err != nil {
230 236
 		return nil, err
231 237
 	}