Browse code

Finally remove our copy of "archive/tar" now that Go 1.4 is the minimum!

IT'S ABOUT TIME. :tada:

Signed-off-by: Andrew "Tianon" Page <admwiggin@gmail.com>

Tianon Gravi authored on 2015/05/02 07:01:10
Showing 40 changed files
... ...
@@ -1,6 +1,7 @@
1 1
 package graph
2 2
 
3 3
 import (
4
+	"archive/tar"
4 5
 	"bytes"
5 6
 	"io"
6 7
 	"os"
... ...
@@ -12,7 +13,6 @@ import (
12 12
 	_ "github.com/docker/docker/daemon/graphdriver/vfs" // import the vfs driver so it is used in the tests
13 13
 	"github.com/docker/docker/image"
14 14
 	"github.com/docker/docker/utils"
15
-	"github.com/docker/docker/vendor/src/code.google.com/p/go/src/pkg/archive/tar"
16 15
 )
17 16
 
18 17
 const (
... ...
@@ -59,17 +59,6 @@ clone git github.com/go-fsnotify/fsnotify v1.0.4
59 59
 
60 60
 clone git github.com/go-check/check 64131543e7896d5bcc6bd5a76287eb75ea96c673
61 61
 
62
-# get Go tip's archive/tar, for xattr support and improved performance
63
-# TODO after Go 1.4 drops, bump our minimum supported version and drop this vendored dep
64
-if [ "$1" = '--go' ]; then
65
-	# Go takes forever and a half to clone, so we only redownload it when explicitly requested via the "--go" flag to this script.
66
-	clone hg code.google.com/p/go 1b17b3426e3c
67
-	mv src/code.google.com/p/go/src/pkg/archive/tar tmp-tar
68
-	rm -rf src/code.google.com/p/go
69
-	mkdir -p src/code.google.com/p/go/src/pkg/archive
70
-	mv tmp-tar src/code.google.com/p/go/src/pkg/archive/tar
71
-fi
72
-
73 62
 # get distribution packages
74 63
 clone git github.com/docker/distribution d957768537c5af40e4f4cd96871f7b2bde9e2923
75 64
 mv src/github.com/docker/distribution/digest tmp-digest
... ...
@@ -1,6 +1,7 @@
1 1
 package main
2 2
 
3 3
 import (
4
+	"archive/tar"
4 5
 	"bytes"
5 6
 	"encoding/json"
6 7
 	"io"
... ...
@@ -11,7 +12,6 @@ import (
11 11
 
12 12
 	"github.com/docker/docker/api/types"
13 13
 	"github.com/docker/docker/pkg/stringid"
14
-	"github.com/docker/docker/vendor/src/code.google.com/p/go/src/pkg/archive/tar"
15 14
 	"github.com/go-check/check"
16 15
 )
17 16
 
... ...
@@ -1,6 +1,7 @@
1 1
 package main
2 2
 
3 3
 import (
4
+	"archive/tar"
4 5
 	"fmt"
5 6
 	"io/ioutil"
6 7
 	"os"
... ...
@@ -8,7 +9,6 @@ import (
8 8
 	"strings"
9 9
 	"time"
10 10
 
11
-	"github.com/docker/docker/vendor/src/code.google.com/p/go/src/pkg/archive/tar"
12 11
 	"github.com/go-check/check"
13 12
 )
14 13
 
... ...
@@ -1,6 +1,7 @@
1 1
 package main
2 2
 
3 3
 import (
4
+	"archive/tar"
4 5
 	"bytes"
5 6
 	"encoding/json"
6 7
 	"errors"
... ...
@@ -17,7 +18,6 @@ import (
17 17
 	"time"
18 18
 
19 19
 	"github.com/docker/docker/pkg/stringutils"
20
-	"github.com/docker/docker/vendor/src/code.google.com/p/go/src/pkg/archive/tar"
21 20
 )
22 21
 
23 22
 func getExitCode(err error) (int, error) {
... ...
@@ -1,6 +1,7 @@
1 1
 package archive
2 2
 
3 3
 import (
4
+	"archive/tar"
4 5
 	"bufio"
5 6
 	"bytes"
6 7
 	"compress/bzip2"
... ...
@@ -16,8 +17,6 @@ import (
16 16
 	"strings"
17 17
 	"syscall"
18 18
 
19
-	"github.com/docker/docker/vendor/src/code.google.com/p/go/src/pkg/archive/tar"
20
-
21 19
 	"github.com/Sirupsen/logrus"
22 20
 	"github.com/docker/docker/pkg/fileutils"
23 21
 	"github.com/docker/docker/pkg/pools"
... ...
@@ -1,6 +1,7 @@
1 1
 package archive
2 2
 
3 3
 import (
4
+	"archive/tar"
4 5
 	"bytes"
5 6
 	"fmt"
6 7
 	"io"
... ...
@@ -15,7 +16,6 @@ import (
15 15
 	"time"
16 16
 
17 17
 	"github.com/docker/docker/pkg/system"
18
-	"github.com/docker/docker/vendor/src/code.google.com/p/go/src/pkg/archive/tar"
19 18
 )
20 19
 
21 20
 func TestIsArchiveNilHeader(t *testing.T) {
... ...
@@ -3,11 +3,10 @@
3 3
 package archive
4 4
 
5 5
 import (
6
+	"archive/tar"
6 7
 	"errors"
7 8
 	"os"
8 9
 	"syscall"
9
-
10
-	"github.com/docker/docker/vendor/src/code.google.com/p/go/src/pkg/archive/tar"
11 10
 )
12 11
 
13 12
 // canonicalTarNameForPath returns platform-specific filepath
... ...
@@ -3,11 +3,10 @@
3 3
 package archive
4 4
 
5 5
 import (
6
+	"archive/tar"
6 7
 	"fmt"
7 8
 	"os"
8 9
 	"strings"
9
-
10
-	"github.com/docker/docker/vendor/src/code.google.com/p/go/src/pkg/archive/tar"
11 10
 )
12 11
 
13 12
 // canonicalTarNameForPath returns platform-specific filepath
... ...
@@ -1,6 +1,7 @@
1 1
 package archive
2 2
 
3 3
 import (
4
+	"archive/tar"
4 5
 	"bytes"
5 6
 	"fmt"
6 7
 	"io"
... ...
@@ -11,8 +12,6 @@ import (
11 11
 	"syscall"
12 12
 	"time"
13 13
 
14
-	"github.com/docker/docker/vendor/src/code.google.com/p/go/src/pkg/archive/tar"
15
-
16 14
 	"github.com/Sirupsen/logrus"
17 15
 	"github.com/docker/docker/pkg/pools"
18 16
 	"github.com/docker/docker/pkg/system"
... ...
@@ -1,6 +1,7 @@
1 1
 package archive
2 2
 
3 3
 import (
4
+	"archive/tar"
4 5
 	"fmt"
5 6
 	"io"
6 7
 	"io/ioutil"
... ...
@@ -9,8 +10,6 @@ import (
9 9
 	"strings"
10 10
 	"syscall"
11 11
 
12
-	"github.com/docker/docker/vendor/src/code.google.com/p/go/src/pkg/archive/tar"
13
-
14 12
 	"github.com/docker/docker/pkg/pools"
15 13
 	"github.com/docker/docker/pkg/system"
16 14
 )
... ...
@@ -1,9 +1,8 @@
1 1
 package archive
2 2
 
3 3
 import (
4
+	"archive/tar"
4 5
 	"testing"
5
-
6
-	"github.com/docker/docker/vendor/src/code.google.com/p/go/src/pkg/archive/tar"
7 6
 )
8 7
 
9 8
 func TestApplyLayerInvalidFilenames(t *testing.T) {
... ...
@@ -1,6 +1,7 @@
1 1
 package archive
2 2
 
3 3
 import (
4
+	"archive/tar"
4 5
 	"bytes"
5 6
 	"fmt"
6 7
 	"io"
... ...
@@ -8,8 +9,6 @@ import (
8 8
 	"os"
9 9
 	"path/filepath"
10 10
 	"time"
11
-
12
-	"github.com/docker/docker/vendor/src/code.google.com/p/go/src/pkg/archive/tar"
13 11
 )
14 12
 
15 13
 var testUntarFns = map[string]func(string, io.Reader) error{
... ...
@@ -1,8 +1,8 @@
1 1
 package archive
2 2
 
3 3
 import (
4
+	"archive/tar"
4 5
 	"bytes"
5
-	"github.com/docker/docker/vendor/src/code.google.com/p/go/src/pkg/archive/tar"
6 6
 	"io/ioutil"
7 7
 )
8 8
 
... ...
@@ -1,6 +1,7 @@
1 1
 package tarsum
2 2
 
3 3
 import (
4
+	"archive/tar"
4 5
 	"bytes"
5 6
 	"compress/gzip"
6 7
 	"crypto"
... ...
@@ -11,8 +12,6 @@ import (
11 11
 	"hash"
12 12
 	"io"
13 13
 	"strings"
14
-
15
-	"github.com/docker/docker/vendor/src/code.google.com/p/go/src/pkg/archive/tar"
16 14
 )
17 15
 
18 16
 const (
... ...
@@ -1,6 +1,7 @@
1 1
 package tarsum
2 2
 
3 3
 import (
4
+	"archive/tar"
4 5
 	"bytes"
5 6
 	"compress/gzip"
6 7
 	"crypto/md5"
... ...
@@ -14,8 +15,6 @@ import (
14 14
 	"io/ioutil"
15 15
 	"os"
16 16
 	"testing"
17
-
18
-	"github.com/docker/docker/vendor/src/code.google.com/p/go/src/pkg/archive/tar"
19 17
 )
20 18
 
21 19
 type testLayer struct {
... ...
@@ -1,12 +1,11 @@
1 1
 package tarsum
2 2
 
3 3
 import (
4
+	"archive/tar"
4 5
 	"errors"
5 6
 	"sort"
6 7
 	"strconv"
7 8
 	"strings"
8
-
9
-	"github.com/docker/docker/vendor/src/code.google.com/p/go/src/pkg/archive/tar"
10 9
 )
11 10
 
12 11
 // versioning of the TarSum algorithm
13 12
deleted file mode 100644
... ...
@@ -1,305 +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
-	Xattrs     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 TypeLink, TypeSymlink:
143
-		// hard link, 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
-	paxNone     = ""
196
-)
197
-
198
-// FileInfoHeader creates a partially-populated Header from fi.
199
-// If fi describes a symlink, FileInfoHeader records link as the link target.
200
-// If fi describes a directory, a slash is appended to the name.
201
-// Because os.FileInfo's Name method returns only the base name of
202
-// the file it describes, it may be necessary to modify the Name field
203
-// of the returned header to provide the full path name of the file.
204
-func FileInfoHeader(fi os.FileInfo, link string) (*Header, error) {
205
-	if fi == nil {
206
-		return nil, errors.New("tar: FileInfo is nil")
207
-	}
208
-	fm := fi.Mode()
209
-	h := &Header{
210
-		Name:    fi.Name(),
211
-		ModTime: fi.ModTime(),
212
-		Mode:    int64(fm.Perm()), // or'd with c_IS* constants later
213
-	}
214
-	switch {
215
-	case fm.IsRegular():
216
-		h.Mode |= c_ISREG
217
-		h.Typeflag = TypeReg
218
-		h.Size = fi.Size()
219
-	case fi.IsDir():
220
-		h.Typeflag = TypeDir
221
-		h.Mode |= c_ISDIR
222
-		h.Name += "/"
223
-	case fm&os.ModeSymlink != 0:
224
-		h.Typeflag = TypeSymlink
225
-		h.Mode |= c_ISLNK
226
-		h.Linkname = link
227
-	case fm&os.ModeDevice != 0:
228
-		if fm&os.ModeCharDevice != 0 {
229
-			h.Mode |= c_ISCHR
230
-			h.Typeflag = TypeChar
231
-		} else {
232
-			h.Mode |= c_ISBLK
233
-			h.Typeflag = TypeBlock
234
-		}
235
-	case fm&os.ModeNamedPipe != 0:
236
-		h.Typeflag = TypeFifo
237
-		h.Mode |= c_ISFIFO
238
-	case fm&os.ModeSocket != 0:
239
-		h.Mode |= c_ISSOCK
240
-	default:
241
-		return nil, fmt.Errorf("archive/tar: unknown file mode %v", fm)
242
-	}
243
-	if fm&os.ModeSetuid != 0 {
244
-		h.Mode |= c_ISUID
245
-	}
246
-	if fm&os.ModeSetgid != 0 {
247
-		h.Mode |= c_ISGID
248
-	}
249
-	if fm&os.ModeSticky != 0 {
250
-		h.Mode |= c_ISVTX
251
-	}
252
-	if sysStat != nil {
253
-		return h, sysStat(fi, h)
254
-	}
255
-	return h, nil
256
-}
257
-
258
-var zeroBlock = make([]byte, blockSize)
259
-
260
-// POSIX specifies a sum of the unsigned byte values, but the Sun tar uses signed byte values.
261
-// We compute and return both.
262
-func checksum(header []byte) (unsigned int64, signed int64) {
263
-	for i := 0; i < len(header); i++ {
264
-		if i == 148 {
265
-			// The chksum field (header[148:156]) is special: it should be treated as space bytes.
266
-			unsigned += ' ' * 8
267
-			signed += ' ' * 8
268
-			i += 7
269
-			continue
270
-		}
271
-		unsigned += int64(header[i])
272
-		signed += int64(int8(header[i]))
273
-	}
274
-	return
275
-}
276
-
277
-type slicer []byte
278
-
279
-func (sp *slicer) next(n int) (b []byte) {
280
-	s := *sp
281
-	b, *sp = s[0:n], s[n:]
282
-	return
283
-}
284
-
285
-func isASCII(s string) bool {
286
-	for _, c := range s {
287
-		if c >= 0x80 {
288
-			return false
289
-		}
290
-	}
291
-	return true
292
-}
293
-
294
-func toASCII(s string) string {
295
-	if isASCII(s) {
296
-		return s
297
-	}
298
-	var buf bytes.Buffer
299
-	for _, c := range s {
300
-		if c < 0x80 {
301
-			buf.WriteByte(byte(c))
302
-		}
303
-	}
304
-	return buf.String()
305
-}
306 1
deleted file mode 100644
... ...
@@ -1,79 +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
-package tar_test
6
-
7
-import (
8
-	"archive/tar"
9
-	"bytes"
10
-	"fmt"
11
-	"io"
12
-	"log"
13
-	"os"
14
-)
15
-
16
-func Example() {
17
-	// Create a buffer to write our archive to.
18
-	buf := new(bytes.Buffer)
19
-
20
-	// Create a new tar archive.
21
-	tw := tar.NewWriter(buf)
22
-
23
-	// Add some files to the archive.
24
-	var files = []struct {
25
-		Name, Body string
26
-	}{
27
-		{"readme.txt", "This archive contains some text files."},
28
-		{"gopher.txt", "Gopher names:\nGeorge\nGeoffrey\nGonzo"},
29
-		{"todo.txt", "Get animal handling licence."},
30
-	}
31
-	for _, file := range files {
32
-		hdr := &tar.Header{
33
-			Name: file.Name,
34
-			Size: int64(len(file.Body)),
35
-		}
36
-		if err := tw.WriteHeader(hdr); err != nil {
37
-			log.Fatalln(err)
38
-		}
39
-		if _, err := tw.Write([]byte(file.Body)); err != nil {
40
-			log.Fatalln(err)
41
-		}
42
-	}
43
-	// Make sure to check the error on Close.
44
-	if err := tw.Close(); err != nil {
45
-		log.Fatalln(err)
46
-	}
47
-
48
-	// Open the tar archive for reading.
49
-	r := bytes.NewReader(buf.Bytes())
50
-	tr := tar.NewReader(r)
51
-
52
-	// Iterate through the files in the archive.
53
-	for {
54
-		hdr, err := tr.Next()
55
-		if err == io.EOF {
56
-			// end of tar archive
57
-			break
58
-		}
59
-		if err != nil {
60
-			log.Fatalln(err)
61
-		}
62
-		fmt.Printf("Contents of %s:\n", hdr.Name)
63
-		if _, err := io.Copy(os.Stdout, tr); err != nil {
64
-			log.Fatalln(err)
65
-		}
66
-		fmt.Println()
67
-	}
68
-
69
-	// Output:
70
-	// Contents of readme.txt:
71
-	// This archive contains some text files.
72
-	// Contents of gopher.txt:
73
-	// Gopher names:
74
-	// George
75
-	// Geoffrey
76
-	// Gonzo
77
-	// Contents of todo.txt:
78
-	// Get animal handling licence.
79
-}
80 1
deleted file mode 100644
... ...
@@ -1,820 +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
-	"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
-// A numBytesReader is an io.Reader with a numBytes method, returning the number
40
-// of bytes remaining in the underlying encoded data.
41
-type numBytesReader interface {
42
-	io.Reader
43
-	numBytes() int64
44
-}
45
-
46
-// A regFileReader is a numBytesReader for reading file data from a tar archive.
47
-type regFileReader struct {
48
-	r  io.Reader // underlying reader
49
-	nb int64     // number of unread bytes for current file entry
50
-}
51
-
52
-// A sparseFileReader is a numBytesReader for reading sparse file data from a tar archive.
53
-type sparseFileReader struct {
54
-	rfr *regFileReader // reads the sparse-encoded file data
55
-	sp  []sparseEntry  // the sparse map for the file
56
-	pos int64          // keeps track of file position
57
-	tot int64          // total size of the file
58
-}
59
-
60
-// Keywords for GNU sparse files in a PAX extended header
61
-const (
62
-	paxGNUSparseNumBlocks = "GNU.sparse.numblocks"
63
-	paxGNUSparseOffset    = "GNU.sparse.offset"
64
-	paxGNUSparseNumBytes  = "GNU.sparse.numbytes"
65
-	paxGNUSparseMap       = "GNU.sparse.map"
66
-	paxGNUSparseName      = "GNU.sparse.name"
67
-	paxGNUSparseMajor     = "GNU.sparse.major"
68
-	paxGNUSparseMinor     = "GNU.sparse.minor"
69
-	paxGNUSparseSize      = "GNU.sparse.size"
70
-	paxGNUSparseRealSize  = "GNU.sparse.realsize"
71
-)
72
-
73
-// Keywords for old GNU sparse headers
74
-const (
75
-	oldGNUSparseMainHeaderOffset               = 386
76
-	oldGNUSparseMainHeaderIsExtendedOffset     = 482
77
-	oldGNUSparseMainHeaderNumEntries           = 4
78
-	oldGNUSparseExtendedHeaderIsExtendedOffset = 504
79
-	oldGNUSparseExtendedHeaderNumEntries       = 21
80
-	oldGNUSparseOffsetSize                     = 12
81
-	oldGNUSparseNumBytesSize                   = 12
82
-)
83
-
84
-// NewReader creates a new Reader reading from r.
85
-func NewReader(r io.Reader) *Reader { return &Reader{r: r} }
86
-
87
-// Next advances to the next entry in the tar archive.
88
-func (tr *Reader) Next() (*Header, error) {
89
-	var hdr *Header
90
-	if tr.err == nil {
91
-		tr.skipUnread()
92
-	}
93
-	if tr.err != nil {
94
-		return hdr, tr.err
95
-	}
96
-	hdr = tr.readHeader()
97
-	if hdr == nil {
98
-		return hdr, tr.err
99
-	}
100
-	// Check for PAX/GNU header.
101
-	switch hdr.Typeflag {
102
-	case TypeXHeader:
103
-		//  PAX extended header
104
-		headers, err := parsePAX(tr)
105
-		if err != nil {
106
-			return nil, err
107
-		}
108
-		// We actually read the whole file,
109
-		// but this skips alignment padding
110
-		tr.skipUnread()
111
-		hdr = tr.readHeader()
112
-		mergePAX(hdr, headers)
113
-
114
-		// Check for a PAX format sparse file
115
-		sp, err := tr.checkForGNUSparsePAXHeaders(hdr, headers)
116
-		if err != nil {
117
-			tr.err = err
118
-			return nil, err
119
-		}
120
-		if sp != nil {
121
-			// Current file is a PAX format GNU sparse file.
122
-			// Set the current file reader to a sparse file reader.
123
-			tr.curr = &sparseFileReader{rfr: tr.curr.(*regFileReader), sp: sp, tot: hdr.Size}
124
-		}
125
-		return hdr, nil
126
-	case TypeGNULongName:
127
-		// We have a GNU long name header. Its contents are the real file name.
128
-		realname, err := ioutil.ReadAll(tr)
129
-		if err != nil {
130
-			return nil, err
131
-		}
132
-		hdr, err := tr.Next()
133
-		hdr.Name = cString(realname)
134
-		return hdr, err
135
-	case TypeGNULongLink:
136
-		// We have a GNU long link header.
137
-		realname, err := ioutil.ReadAll(tr)
138
-		if err != nil {
139
-			return nil, err
140
-		}
141
-		hdr, err := tr.Next()
142
-		hdr.Linkname = cString(realname)
143
-		return hdr, err
144
-	}
145
-	return hdr, tr.err
146
-}
147
-
148
-// checkForGNUSparsePAXHeaders checks the PAX headers for GNU sparse headers. If they are found, then
149
-// this function reads the sparse map and returns it. Unknown sparse formats are ignored, causing the file to
150
-// be treated as a regular file.
151
-func (tr *Reader) checkForGNUSparsePAXHeaders(hdr *Header, headers map[string]string) ([]sparseEntry, error) {
152
-	var sparseFormat string
153
-
154
-	// Check for sparse format indicators
155
-	major, majorOk := headers[paxGNUSparseMajor]
156
-	minor, minorOk := headers[paxGNUSparseMinor]
157
-	sparseName, sparseNameOk := headers[paxGNUSparseName]
158
-	_, sparseMapOk := headers[paxGNUSparseMap]
159
-	sparseSize, sparseSizeOk := headers[paxGNUSparseSize]
160
-	sparseRealSize, sparseRealSizeOk := headers[paxGNUSparseRealSize]
161
-
162
-	// Identify which, if any, sparse format applies from which PAX headers are set
163
-	if majorOk && minorOk {
164
-		sparseFormat = major + "." + minor
165
-	} else if sparseNameOk && sparseMapOk {
166
-		sparseFormat = "0.1"
167
-	} else if sparseSizeOk {
168
-		sparseFormat = "0.0"
169
-	} else {
170
-		// Not a PAX format GNU sparse file.
171
-		return nil, nil
172
-	}
173
-
174
-	// Check for unknown sparse format
175
-	if sparseFormat != "0.0" && sparseFormat != "0.1" && sparseFormat != "1.0" {
176
-		return nil, nil
177
-	}
178
-
179
-	// Update hdr from GNU sparse PAX headers
180
-	if sparseNameOk {
181
-		hdr.Name = sparseName
182
-	}
183
-	if sparseSizeOk {
184
-		realSize, err := strconv.ParseInt(sparseSize, 10, 0)
185
-		if err != nil {
186
-			return nil, ErrHeader
187
-		}
188
-		hdr.Size = realSize
189
-	} else if sparseRealSizeOk {
190
-		realSize, err := strconv.ParseInt(sparseRealSize, 10, 0)
191
-		if err != nil {
192
-			return nil, ErrHeader
193
-		}
194
-		hdr.Size = realSize
195
-	}
196
-
197
-	// Set up the sparse map, according to the particular sparse format in use
198
-	var sp []sparseEntry
199
-	var err error
200
-	switch sparseFormat {
201
-	case "0.0", "0.1":
202
-		sp, err = readGNUSparseMap0x1(headers)
203
-	case "1.0":
204
-		sp, err = readGNUSparseMap1x0(tr.curr)
205
-	}
206
-	return sp, err
207
-}
208
-
209
-// mergePAX merges well known headers according to PAX standard.
210
-// In general headers with the same name as those found
211
-// in the header struct overwrite those found in the header
212
-// struct with higher precision or longer values. Esp. useful
213
-// for name and linkname fields.
214
-func mergePAX(hdr *Header, headers map[string]string) error {
215
-	for k, v := range headers {
216
-		switch k {
217
-		case paxPath:
218
-			hdr.Name = v
219
-		case paxLinkpath:
220
-			hdr.Linkname = v
221
-		case paxGname:
222
-			hdr.Gname = v
223
-		case paxUname:
224
-			hdr.Uname = v
225
-		case paxUid:
226
-			uid, err := strconv.ParseInt(v, 10, 0)
227
-			if err != nil {
228
-				return err
229
-			}
230
-			hdr.Uid = int(uid)
231
-		case paxGid:
232
-			gid, err := strconv.ParseInt(v, 10, 0)
233
-			if err != nil {
234
-				return err
235
-			}
236
-			hdr.Gid = int(gid)
237
-		case paxAtime:
238
-			t, err := parsePAXTime(v)
239
-			if err != nil {
240
-				return err
241
-			}
242
-			hdr.AccessTime = t
243
-		case paxMtime:
244
-			t, err := parsePAXTime(v)
245
-			if err != nil {
246
-				return err
247
-			}
248
-			hdr.ModTime = t
249
-		case paxCtime:
250
-			t, err := parsePAXTime(v)
251
-			if err != nil {
252
-				return err
253
-			}
254
-			hdr.ChangeTime = t
255
-		case paxSize:
256
-			size, err := strconv.ParseInt(v, 10, 0)
257
-			if err != nil {
258
-				return err
259
-			}
260
-			hdr.Size = int64(size)
261
-		default:
262
-			if strings.HasPrefix(k, paxXattr) {
263
-				if hdr.Xattrs == nil {
264
-					hdr.Xattrs = make(map[string]string)
265
-				}
266
-				hdr.Xattrs[k[len(paxXattr):]] = v
267
-			}
268
-		}
269
-	}
270
-	return nil
271
-}
272
-
273
-// parsePAXTime takes a string of the form %d.%d as described in
274
-// the PAX specification.
275
-func parsePAXTime(t string) (time.Time, error) {
276
-	buf := []byte(t)
277
-	pos := bytes.IndexByte(buf, '.')
278
-	var seconds, nanoseconds int64
279
-	var err error
280
-	if pos == -1 {
281
-		seconds, err = strconv.ParseInt(t, 10, 0)
282
-		if err != nil {
283
-			return time.Time{}, err
284
-		}
285
-	} else {
286
-		seconds, err = strconv.ParseInt(string(buf[:pos]), 10, 0)
287
-		if err != nil {
288
-			return time.Time{}, err
289
-		}
290
-		nano_buf := string(buf[pos+1:])
291
-		// Pad as needed before converting to a decimal.
292
-		// For example .030 -> .030000000 -> 30000000 nanoseconds
293
-		if len(nano_buf) < maxNanoSecondIntSize {
294
-			// Right pad
295
-			nano_buf += strings.Repeat("0", maxNanoSecondIntSize-len(nano_buf))
296
-		} else if len(nano_buf) > maxNanoSecondIntSize {
297
-			// Right truncate
298
-			nano_buf = nano_buf[:maxNanoSecondIntSize]
299
-		}
300
-		nanoseconds, err = strconv.ParseInt(string(nano_buf), 10, 0)
301
-		if err != nil {
302
-			return time.Time{}, err
303
-		}
304
-	}
305
-	ts := time.Unix(seconds, nanoseconds)
306
-	return ts, nil
307
-}
308
-
309
-// parsePAX parses PAX headers.
310
-// If an extended header (type 'x') is invalid, ErrHeader is returned
311
-func parsePAX(r io.Reader) (map[string]string, error) {
312
-	buf, err := ioutil.ReadAll(r)
313
-	if err != nil {
314
-		return nil, err
315
-	}
316
-
317
-	// For GNU PAX sparse format 0.0 support.
318
-	// This function transforms the sparse format 0.0 headers into sparse format 0.1 headers.
319
-	var sparseMap bytes.Buffer
320
-
321
-	headers := make(map[string]string)
322
-	// Each record is constructed as
323
-	//     "%d %s=%s\n", length, keyword, value
324
-	for len(buf) > 0 {
325
-		// or the header was empty to start with.
326
-		var sp int
327
-		// The size field ends at the first space.
328
-		sp = bytes.IndexByte(buf, ' ')
329
-		if sp == -1 {
330
-			return nil, ErrHeader
331
-		}
332
-		// Parse the first token as a decimal integer.
333
-		n, err := strconv.ParseInt(string(buf[:sp]), 10, 0)
334
-		if err != nil {
335
-			return nil, ErrHeader
336
-		}
337
-		// Extract everything between the decimal and the n -1 on the
338
-		// beginning to eat the ' ', -1 on the end to skip the newline.
339
-		var record []byte
340
-		record, buf = buf[sp+1:n-1], buf[n:]
341
-		// The first equals is guaranteed to mark the end of the key.
342
-		// Everything else is value.
343
-		eq := bytes.IndexByte(record, '=')
344
-		if eq == -1 {
345
-			return nil, ErrHeader
346
-		}
347
-		key, value := record[:eq], record[eq+1:]
348
-
349
-		keyStr := string(key)
350
-		if keyStr == paxGNUSparseOffset || keyStr == paxGNUSparseNumBytes {
351
-			// GNU sparse format 0.0 special key. Write to sparseMap instead of using the headers map.
352
-			sparseMap.Write(value)
353
-			sparseMap.Write([]byte{','})
354
-		} else {
355
-			// Normal key. Set the value in the headers map.
356
-			headers[keyStr] = string(value)
357
-		}
358
-	}
359
-	if sparseMap.Len() != 0 {
360
-		// Add sparse info to headers, chopping off the extra comma
361
-		sparseMap.Truncate(sparseMap.Len() - 1)
362
-		headers[paxGNUSparseMap] = sparseMap.String()
363
-	}
364
-	return headers, nil
365
-}
366
-
367
-// cString parses bytes as a NUL-terminated C-style string.
368
-// If a NUL byte is not found then the whole slice is returned as a string.
369
-func cString(b []byte) string {
370
-	n := 0
371
-	for n < len(b) && b[n] != 0 {
372
-		n++
373
-	}
374
-	return string(b[0:n])
375
-}
376
-
377
-func (tr *Reader) octal(b []byte) int64 {
378
-	// Check for binary format first.
379
-	if len(b) > 0 && b[0]&0x80 != 0 {
380
-		var x int64
381
-		for i, c := range b {
382
-			if i == 0 {
383
-				c &= 0x7f // ignore signal bit in first byte
384
-			}
385
-			x = x<<8 | int64(c)
386
-		}
387
-		return x
388
-	}
389
-
390
-	// Because unused fields are filled with NULs, we need
391
-	// to skip leading NULs. Fields may also be padded with
392
-	// spaces or NULs.
393
-	// So we remove leading and trailing NULs and spaces to
394
-	// be sure.
395
-	b = bytes.Trim(b, " \x00")
396
-
397
-	if len(b) == 0 {
398
-		return 0
399
-	}
400
-	x, err := strconv.ParseUint(cString(b), 8, 64)
401
-	if err != nil {
402
-		tr.err = err
403
-	}
404
-	return int64(x)
405
-}
406
-
407
-// skipUnread skips any unread bytes in the existing file entry, as well as any alignment padding.
408
-func (tr *Reader) skipUnread() {
409
-	nr := tr.numBytes() + tr.pad // number of bytes to skip
410
-	tr.curr, tr.pad = nil, 0
411
-	if sr, ok := tr.r.(io.Seeker); ok {
412
-		if _, err := sr.Seek(nr, os.SEEK_CUR); err == nil {
413
-			return
414
-		}
415
-	}
416
-	_, tr.err = io.CopyN(ioutil.Discard, tr.r, nr)
417
-}
418
-
419
-func (tr *Reader) verifyChecksum(header []byte) bool {
420
-	if tr.err != nil {
421
-		return false
422
-	}
423
-
424
-	given := tr.octal(header[148:156])
425
-	unsigned, signed := checksum(header)
426
-	return given == unsigned || given == signed
427
-}
428
-
429
-func (tr *Reader) readHeader() *Header {
430
-	header := tr.hdrBuff[:]
431
-	copy(header, zeroBlock)
432
-
433
-	if _, tr.err = io.ReadFull(tr.r, header); tr.err != nil {
434
-		return nil
435
-	}
436
-
437
-	// Two blocks of zero bytes marks the end of the archive.
438
-	if bytes.Equal(header, zeroBlock[0:blockSize]) {
439
-		if _, tr.err = io.ReadFull(tr.r, header); tr.err != nil {
440
-			return nil
441
-		}
442
-		if bytes.Equal(header, zeroBlock[0:blockSize]) {
443
-			tr.err = io.EOF
444
-		} else {
445
-			tr.err = ErrHeader // zero block and then non-zero block
446
-		}
447
-		return nil
448
-	}
449
-
450
-	if !tr.verifyChecksum(header) {
451
-		tr.err = ErrHeader
452
-		return nil
453
-	}
454
-
455
-	// Unpack
456
-	hdr := new(Header)
457
-	s := slicer(header)
458
-
459
-	hdr.Name = cString(s.next(100))
460
-	hdr.Mode = tr.octal(s.next(8))
461
-	hdr.Uid = int(tr.octal(s.next(8)))
462
-	hdr.Gid = int(tr.octal(s.next(8)))
463
-	hdr.Size = tr.octal(s.next(12))
464
-	hdr.ModTime = time.Unix(tr.octal(s.next(12)), 0)
465
-	s.next(8) // chksum
466
-	hdr.Typeflag = s.next(1)[0]
467
-	hdr.Linkname = cString(s.next(100))
468
-
469
-	// The remainder of the header depends on the value of magic.
470
-	// The original (v7) version of tar had no explicit magic field,
471
-	// so its magic bytes, like the rest of the block, are NULs.
472
-	magic := string(s.next(8)) // contains version field as well.
473
-	var format string
474
-	switch {
475
-	case magic[:6] == "ustar\x00": // POSIX tar (1003.1-1988)
476
-		if string(header[508:512]) == "tar\x00" {
477
-			format = "star"
478
-		} else {
479
-			format = "posix"
480
-		}
481
-	case magic == "ustar  \x00": // old GNU tar
482
-		format = "gnu"
483
-	}
484
-
485
-	switch format {
486
-	case "posix", "gnu", "star":
487
-		hdr.Uname = cString(s.next(32))
488
-		hdr.Gname = cString(s.next(32))
489
-		devmajor := s.next(8)
490
-		devminor := s.next(8)
491
-		if hdr.Typeflag == TypeChar || hdr.Typeflag == TypeBlock {
492
-			hdr.Devmajor = tr.octal(devmajor)
493
-			hdr.Devminor = tr.octal(devminor)
494
-		}
495
-		var prefix string
496
-		switch format {
497
-		case "posix", "gnu":
498
-			prefix = cString(s.next(155))
499
-		case "star":
500
-			prefix = cString(s.next(131))
501
-			hdr.AccessTime = time.Unix(tr.octal(s.next(12)), 0)
502
-			hdr.ChangeTime = time.Unix(tr.octal(s.next(12)), 0)
503
-		}
504
-		if len(prefix) > 0 {
505
-			hdr.Name = prefix + "/" + hdr.Name
506
-		}
507
-	}
508
-
509
-	if tr.err != nil {
510
-		tr.err = ErrHeader
511
-		return nil
512
-	}
513
-
514
-	// Maximum value of hdr.Size is 64 GB (12 octal digits),
515
-	// so there's no risk of int64 overflowing.
516
-	nb := int64(hdr.Size)
517
-	tr.pad = -nb & (blockSize - 1) // blockSize is a power of two
518
-
519
-	// Set the current file reader.
520
-	tr.curr = &regFileReader{r: tr.r, nb: nb}
521
-
522
-	// Check for old GNU sparse format entry.
523
-	if hdr.Typeflag == TypeGNUSparse {
524
-		// Get the real size of the file.
525
-		hdr.Size = tr.octal(header[483:495])
526
-
527
-		// Read the sparse map.
528
-		sp := tr.readOldGNUSparseMap(header)
529
-		if tr.err != nil {
530
-			return nil
531
-		}
532
-		// Current file is a GNU sparse file. Update the current file reader.
533
-		tr.curr = &sparseFileReader{rfr: tr.curr.(*regFileReader), sp: sp, tot: hdr.Size}
534
-	}
535
-
536
-	return hdr
537
-}
538
-
539
-// A sparseEntry holds a single entry in a sparse file's sparse map.
540
-// A sparse entry indicates the offset and size in a sparse file of a
541
-// block of data.
542
-type sparseEntry struct {
543
-	offset   int64
544
-	numBytes int64
545
-}
546
-
547
-// readOldGNUSparseMap reads the sparse map as stored in the old GNU sparse format.
548
-// The sparse map is stored in the tar header if it's small enough. If it's larger than four entries,
549
-// then one or more extension headers are used to store the rest of the sparse map.
550
-func (tr *Reader) readOldGNUSparseMap(header []byte) []sparseEntry {
551
-	isExtended := header[oldGNUSparseMainHeaderIsExtendedOffset] != 0
552
-	spCap := oldGNUSparseMainHeaderNumEntries
553
-	if isExtended {
554
-		spCap += oldGNUSparseExtendedHeaderNumEntries
555
-	}
556
-	sp := make([]sparseEntry, 0, spCap)
557
-	s := slicer(header[oldGNUSparseMainHeaderOffset:])
558
-
559
-	// Read the four entries from the main tar header
560
-	for i := 0; i < oldGNUSparseMainHeaderNumEntries; i++ {
561
-		offset := tr.octal(s.next(oldGNUSparseOffsetSize))
562
-		numBytes := tr.octal(s.next(oldGNUSparseNumBytesSize))
563
-		if tr.err != nil {
564
-			tr.err = ErrHeader
565
-			return nil
566
-		}
567
-		if offset == 0 && numBytes == 0 {
568
-			break
569
-		}
570
-		sp = append(sp, sparseEntry{offset: offset, numBytes: numBytes})
571
-	}
572
-
573
-	for isExtended {
574
-		// There are more entries. Read an extension header and parse its entries.
575
-		sparseHeader := make([]byte, blockSize)
576
-		if _, tr.err = io.ReadFull(tr.r, sparseHeader); tr.err != nil {
577
-			return nil
578
-		}
579
-		isExtended = sparseHeader[oldGNUSparseExtendedHeaderIsExtendedOffset] != 0
580
-		s = slicer(sparseHeader)
581
-		for i := 0; i < oldGNUSparseExtendedHeaderNumEntries; i++ {
582
-			offset := tr.octal(s.next(oldGNUSparseOffsetSize))
583
-			numBytes := tr.octal(s.next(oldGNUSparseNumBytesSize))
584
-			if tr.err != nil {
585
-				tr.err = ErrHeader
586
-				return nil
587
-			}
588
-			if offset == 0 && numBytes == 0 {
589
-				break
590
-			}
591
-			sp = append(sp, sparseEntry{offset: offset, numBytes: numBytes})
592
-		}
593
-	}
594
-	return sp
595
-}
596
-
597
-// readGNUSparseMap1x0 reads the sparse map as stored in GNU's PAX sparse format version 1.0.
598
-// The sparse map is stored just before the file data and padded out to the nearest block boundary.
599
-func readGNUSparseMap1x0(r io.Reader) ([]sparseEntry, error) {
600
-	buf := make([]byte, 2*blockSize)
601
-	sparseHeader := buf[:blockSize]
602
-
603
-	// readDecimal is a helper function to read a decimal integer from the sparse map
604
-	// while making sure to read from the file in blocks of size blockSize
605
-	readDecimal := func() (int64, error) {
606
-		// Look for newline
607
-		nl := bytes.IndexByte(sparseHeader, '\n')
608
-		if nl == -1 {
609
-			if len(sparseHeader) >= blockSize {
610
-				// This is an error
611
-				return 0, ErrHeader
612
-			}
613
-			oldLen := len(sparseHeader)
614
-			newLen := oldLen + blockSize
615
-			if cap(sparseHeader) < newLen {
616
-				// There's more header, but we need to make room for the next block
617
-				copy(buf, sparseHeader)
618
-				sparseHeader = buf[:newLen]
619
-			} else {
620
-				// There's more header, and we can just reslice
621
-				sparseHeader = sparseHeader[:newLen]
622
-			}
623
-
624
-			// Now that sparseHeader is large enough, read next block
625
-			if _, err := io.ReadFull(r, sparseHeader[oldLen:newLen]); err != nil {
626
-				return 0, err
627
-			}
628
-
629
-			// Look for a newline in the new data
630
-			nl = bytes.IndexByte(sparseHeader[oldLen:newLen], '\n')
631
-			if nl == -1 {
632
-				// This is an error
633
-				return 0, ErrHeader
634
-			}
635
-			nl += oldLen // We want the position from the beginning
636
-		}
637
-		// Now that we've found a newline, read a number
638
-		n, err := strconv.ParseInt(string(sparseHeader[:nl]), 10, 0)
639
-		if err != nil {
640
-			return 0, ErrHeader
641
-		}
642
-
643
-		// Update sparseHeader to consume this number
644
-		sparseHeader = sparseHeader[nl+1:]
645
-		return n, nil
646
-	}
647
-
648
-	// Read the first block
649
-	if _, err := io.ReadFull(r, sparseHeader); err != nil {
650
-		return nil, err
651
-	}
652
-
653
-	// The first line contains the number of entries
654
-	numEntries, err := readDecimal()
655
-	if err != nil {
656
-		return nil, err
657
-	}
658
-
659
-	// Read all the entries
660
-	sp := make([]sparseEntry, 0, numEntries)
661
-	for i := int64(0); i < numEntries; i++ {
662
-		// Read the offset
663
-		offset, err := readDecimal()
664
-		if err != nil {
665
-			return nil, err
666
-		}
667
-		// Read numBytes
668
-		numBytes, err := readDecimal()
669
-		if err != nil {
670
-			return nil, err
671
-		}
672
-
673
-		sp = append(sp, sparseEntry{offset: offset, numBytes: numBytes})
674
-	}
675
-
676
-	return sp, nil
677
-}
678
-
679
-// readGNUSparseMap0x1 reads the sparse map as stored in GNU's PAX sparse format version 0.1.
680
-// The sparse map is stored in the PAX headers.
681
-func readGNUSparseMap0x1(headers map[string]string) ([]sparseEntry, error) {
682
-	// Get number of entries
683
-	numEntriesStr, ok := headers[paxGNUSparseNumBlocks]
684
-	if !ok {
685
-		return nil, ErrHeader
686
-	}
687
-	numEntries, err := strconv.ParseInt(numEntriesStr, 10, 0)
688
-	if err != nil {
689
-		return nil, ErrHeader
690
-	}
691
-
692
-	sparseMap := strings.Split(headers[paxGNUSparseMap], ",")
693
-
694
-	// There should be two numbers in sparseMap for each entry
695
-	if int64(len(sparseMap)) != 2*numEntries {
696
-		return nil, ErrHeader
697
-	}
698
-
699
-	// Loop through the entries in the sparse map
700
-	sp := make([]sparseEntry, 0, numEntries)
701
-	for i := int64(0); i < numEntries; i++ {
702
-		offset, err := strconv.ParseInt(sparseMap[2*i], 10, 0)
703
-		if err != nil {
704
-			return nil, ErrHeader
705
-		}
706
-		numBytes, err := strconv.ParseInt(sparseMap[2*i+1], 10, 0)
707
-		if err != nil {
708
-			return nil, ErrHeader
709
-		}
710
-		sp = append(sp, sparseEntry{offset: offset, numBytes: numBytes})
711
-	}
712
-
713
-	return sp, nil
714
-}
715
-
716
-// numBytes returns the number of bytes left to read in the current file's entry
717
-// in the tar archive, or 0 if there is no current file.
718
-func (tr *Reader) numBytes() int64 {
719
-	if tr.curr == nil {
720
-		// No current file, so no bytes
721
-		return 0
722
-	}
723
-	return tr.curr.numBytes()
724
-}
725
-
726
-// Read reads from the current entry in the tar archive.
727
-// It returns 0, io.EOF when it reaches the end of that entry,
728
-// until Next is called to advance to the next entry.
729
-func (tr *Reader) Read(b []byte) (n int, err error) {
730
-	if tr.curr == nil {
731
-		return 0, io.EOF
732
-	}
733
-	n, err = tr.curr.Read(b)
734
-	if err != nil && err != io.EOF {
735
-		tr.err = err
736
-	}
737
-	return
738
-}
739
-
740
-func (rfr *regFileReader) Read(b []byte) (n int, err error) {
741
-	if rfr.nb == 0 {
742
-		// file consumed
743
-		return 0, io.EOF
744
-	}
745
-	if int64(len(b)) > rfr.nb {
746
-		b = b[0:rfr.nb]
747
-	}
748
-	n, err = rfr.r.Read(b)
749
-	rfr.nb -= int64(n)
750
-
751
-	if err == io.EOF && rfr.nb > 0 {
752
-		err = io.ErrUnexpectedEOF
753
-	}
754
-	return
755
-}
756
-
757
-// numBytes returns the number of bytes left to read in the file's data in the tar archive.
758
-func (rfr *regFileReader) numBytes() int64 {
759
-	return rfr.nb
760
-}
761
-
762
-// readHole reads a sparse file hole ending at offset toOffset
763
-func (sfr *sparseFileReader) readHole(b []byte, toOffset int64) int {
764
-	n64 := toOffset - sfr.pos
765
-	if n64 > int64(len(b)) {
766
-		n64 = int64(len(b))
767
-	}
768
-	n := int(n64)
769
-	for i := 0; i < n; i++ {
770
-		b[i] = 0
771
-	}
772
-	sfr.pos += n64
773
-	return n
774
-}
775
-
776
-// Read reads the sparse file data in expanded form.
777
-func (sfr *sparseFileReader) Read(b []byte) (n int, err error) {
778
-	if len(sfr.sp) == 0 {
779
-		// No more data fragments to read from.
780
-		if sfr.pos < sfr.tot {
781
-			// We're in the last hole
782
-			n = sfr.readHole(b, sfr.tot)
783
-			return
784
-		}
785
-		// Otherwise, we're at the end of the file
786
-		return 0, io.EOF
787
-	}
788
-	if sfr.pos < sfr.sp[0].offset {
789
-		// We're in a hole
790
-		n = sfr.readHole(b, sfr.sp[0].offset)
791
-		return
792
-	}
793
-
794
-	// We're not in a hole, so we'll read from the next data fragment
795
-	posInFragment := sfr.pos - sfr.sp[0].offset
796
-	bytesLeft := sfr.sp[0].numBytes - posInFragment
797
-	if int64(len(b)) > bytesLeft {
798
-		b = b[0:bytesLeft]
799
-	}
800
-
801
-	n, err = sfr.rfr.Read(b)
802
-	sfr.pos += int64(n)
803
-
804
-	if int64(n) == bytesLeft {
805
-		// We're done with this fragment
806
-		sfr.sp = sfr.sp[1:]
807
-	}
808
-
809
-	if err == io.EOF && sfr.pos < sfr.tot {
810
-		// We reached the end of the last fragment's data, but there's a final hole
811
-		err = nil
812
-	}
813
-	return
814
-}
815
-
816
-// numBytes returns the number of bytes left to read in the sparse file's
817
-// sparse-encoded data in the tar archive.
818
-func (sfr *sparseFileReader) numBytes() int64 {
819
-	return sfr.rfr.nb
820
-}
821 1
deleted file mode 100644
... ...
@@ -1,743 +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
-import (
8
-	"bytes"
9
-	"crypto/md5"
10
-	"fmt"
11
-	"io"
12
-	"io/ioutil"
13
-	"os"
14
-	"reflect"
15
-	"strings"
16
-	"testing"
17
-	"time"
18
-)
19
-
20
-type untarTest struct {
21
-	file    string
22
-	headers []*Header
23
-	cksums  []string
24
-}
25
-
26
-var gnuTarTest = &untarTest{
27
-	file: "testdata/gnu.tar",
28
-	headers: []*Header{
29
-		{
30
-			Name:     "small.txt",
31
-			Mode:     0640,
32
-			Uid:      73025,
33
-			Gid:      5000,
34
-			Size:     5,
35
-			ModTime:  time.Unix(1244428340, 0),
36
-			Typeflag: '0',
37
-			Uname:    "dsymonds",
38
-			Gname:    "eng",
39
-		},
40
-		{
41
-			Name:     "small2.txt",
42
-			Mode:     0640,
43
-			Uid:      73025,
44
-			Gid:      5000,
45
-			Size:     11,
46
-			ModTime:  time.Unix(1244436044, 0),
47
-			Typeflag: '0',
48
-			Uname:    "dsymonds",
49
-			Gname:    "eng",
50
-		},
51
-	},
52
-	cksums: []string{
53
-		"e38b27eaccb4391bdec553a7f3ae6b2f",
54
-		"c65bd2e50a56a2138bf1716f2fd56fe9",
55
-	},
56
-}
57
-
58
-var sparseTarTest = &untarTest{
59
-	file: "testdata/sparse-formats.tar",
60
-	headers: []*Header{
61
-		{
62
-			Name:     "sparse-gnu",
63
-			Mode:     420,
64
-			Uid:      1000,
65
-			Gid:      1000,
66
-			Size:     200,
67
-			ModTime:  time.Unix(1392395740, 0),
68
-			Typeflag: 0x53,
69
-			Linkname: "",
70
-			Uname:    "david",
71
-			Gname:    "david",
72
-			Devmajor: 0,
73
-			Devminor: 0,
74
-		},
75
-		{
76
-			Name:     "sparse-posix-0.0",
77
-			Mode:     420,
78
-			Uid:      1000,
79
-			Gid:      1000,
80
-			Size:     200,
81
-			ModTime:  time.Unix(1392342187, 0),
82
-			Typeflag: 0x30,
83
-			Linkname: "",
84
-			Uname:    "david",
85
-			Gname:    "david",
86
-			Devmajor: 0,
87
-			Devminor: 0,
88
-		},
89
-		{
90
-			Name:     "sparse-posix-0.1",
91
-			Mode:     420,
92
-			Uid:      1000,
93
-			Gid:      1000,
94
-			Size:     200,
95
-			ModTime:  time.Unix(1392340456, 0),
96
-			Typeflag: 0x30,
97
-			Linkname: "",
98
-			Uname:    "david",
99
-			Gname:    "david",
100
-			Devmajor: 0,
101
-			Devminor: 0,
102
-		},
103
-		{
104
-			Name:     "sparse-posix-1.0",
105
-			Mode:     420,
106
-			Uid:      1000,
107
-			Gid:      1000,
108
-			Size:     200,
109
-			ModTime:  time.Unix(1392337404, 0),
110
-			Typeflag: 0x30,
111
-			Linkname: "",
112
-			Uname:    "david",
113
-			Gname:    "david",
114
-			Devmajor: 0,
115
-			Devminor: 0,
116
-		},
117
-		{
118
-			Name:     "end",
119
-			Mode:     420,
120
-			Uid:      1000,
121
-			Gid:      1000,
122
-			Size:     4,
123
-			ModTime:  time.Unix(1392398319, 0),
124
-			Typeflag: 0x30,
125
-			Linkname: "",
126
-			Uname:    "david",
127
-			Gname:    "david",
128
-			Devmajor: 0,
129
-			Devminor: 0,
130
-		},
131
-	},
132
-	cksums: []string{
133
-		"6f53234398c2449fe67c1812d993012f",
134
-		"6f53234398c2449fe67c1812d993012f",
135
-		"6f53234398c2449fe67c1812d993012f",
136
-		"6f53234398c2449fe67c1812d993012f",
137
-		"b0061974914468de549a2af8ced10316",
138
-	},
139
-}
140
-
141
-var untarTests = []*untarTest{
142
-	gnuTarTest,
143
-	sparseTarTest,
144
-	{
145
-		file: "testdata/star.tar",
146
-		headers: []*Header{
147
-			{
148
-				Name:       "small.txt",
149
-				Mode:       0640,
150
-				Uid:        73025,
151
-				Gid:        5000,
152
-				Size:       5,
153
-				ModTime:    time.Unix(1244592783, 0),
154
-				Typeflag:   '0',
155
-				Uname:      "dsymonds",
156
-				Gname:      "eng",
157
-				AccessTime: time.Unix(1244592783, 0),
158
-				ChangeTime: time.Unix(1244592783, 0),
159
-			},
160
-			{
161
-				Name:       "small2.txt",
162
-				Mode:       0640,
163
-				Uid:        73025,
164
-				Gid:        5000,
165
-				Size:       11,
166
-				ModTime:    time.Unix(1244592783, 0),
167
-				Typeflag:   '0',
168
-				Uname:      "dsymonds",
169
-				Gname:      "eng",
170
-				AccessTime: time.Unix(1244592783, 0),
171
-				ChangeTime: time.Unix(1244592783, 0),
172
-			},
173
-		},
174
-	},
175
-	{
176
-		file: "testdata/v7.tar",
177
-		headers: []*Header{
178
-			{
179
-				Name:     "small.txt",
180
-				Mode:     0444,
181
-				Uid:      73025,
182
-				Gid:      5000,
183
-				Size:     5,
184
-				ModTime:  time.Unix(1244593104, 0),
185
-				Typeflag: '\x00',
186
-			},
187
-			{
188
-				Name:     "small2.txt",
189
-				Mode:     0444,
190
-				Uid:      73025,
191
-				Gid:      5000,
192
-				Size:     11,
193
-				ModTime:  time.Unix(1244593104, 0),
194
-				Typeflag: '\x00',
195
-			},
196
-		},
197
-	},
198
-	{
199
-		file: "testdata/pax.tar",
200
-		headers: []*Header{
201
-			{
202
-				Name:       "a/123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100",
203
-				Mode:       0664,
204
-				Uid:        1000,
205
-				Gid:        1000,
206
-				Uname:      "shane",
207
-				Gname:      "shane",
208
-				Size:       7,
209
-				ModTime:    time.Unix(1350244992, 23960108),
210
-				ChangeTime: time.Unix(1350244992, 23960108),
211
-				AccessTime: time.Unix(1350244992, 23960108),
212
-				Typeflag:   TypeReg,
213
-			},
214
-			{
215
-				Name:       "a/b",
216
-				Mode:       0777,
217
-				Uid:        1000,
218
-				Gid:        1000,
219
-				Uname:      "shane",
220
-				Gname:      "shane",
221
-				Size:       0,
222
-				ModTime:    time.Unix(1350266320, 910238425),
223
-				ChangeTime: time.Unix(1350266320, 910238425),
224
-				AccessTime: time.Unix(1350266320, 910238425),
225
-				Typeflag:   TypeSymlink,
226
-				Linkname:   "123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100",
227
-			},
228
-		},
229
-	},
230
-	{
231
-		file: "testdata/nil-uid.tar", // golang.org/issue/5290
232
-		headers: []*Header{
233
-			{
234
-				Name:     "P1050238.JPG.log",
235
-				Mode:     0664,
236
-				Uid:      0,
237
-				Gid:      0,
238
-				Size:     14,
239
-				ModTime:  time.Unix(1365454838, 0),
240
-				Typeflag: TypeReg,
241
-				Linkname: "",
242
-				Uname:    "eyefi",
243
-				Gname:    "eyefi",
244
-				Devmajor: 0,
245
-				Devminor: 0,
246
-			},
247
-		},
248
-	},
249
-	{
250
-		file: "testdata/xattrs.tar",
251
-		headers: []*Header{
252
-			{
253
-				Name:       "small.txt",
254
-				Mode:       0644,
255
-				Uid:        1000,
256
-				Gid:        10,
257
-				Size:       5,
258
-				ModTime:    time.Unix(1386065770, 448252320),
259
-				Typeflag:   '0',
260
-				Uname:      "alex",
261
-				Gname:      "wheel",
262
-				AccessTime: time.Unix(1389782991, 419875220),
263
-				ChangeTime: time.Unix(1389782956, 794414986),
264
-				Xattrs: map[string]string{
265
-					"user.key":  "value",
266
-					"user.key2": "value2",
267
-					// Interestingly, selinux encodes the terminating null inside the xattr
268
-					"security.selinux": "unconfined_u:object_r:default_t:s0\x00",
269
-				},
270
-			},
271
-			{
272
-				Name:       "small2.txt",
273
-				Mode:       0644,
274
-				Uid:        1000,
275
-				Gid:        10,
276
-				Size:       11,
277
-				ModTime:    time.Unix(1386065770, 449252304),
278
-				Typeflag:   '0',
279
-				Uname:      "alex",
280
-				Gname:      "wheel",
281
-				AccessTime: time.Unix(1389782991, 419875220),
282
-				ChangeTime: time.Unix(1386065770, 449252304),
283
-				Xattrs: map[string]string{
284
-					"security.selinux": "unconfined_u:object_r:default_t:s0\x00",
285
-				},
286
-			},
287
-		},
288
-	},
289
-}
290
-
291
-func TestReader(t *testing.T) {
292
-testLoop:
293
-	for i, test := range untarTests {
294
-		f, err := os.Open(test.file)
295
-		if err != nil {
296
-			t.Errorf("test %d: Unexpected error: %v", i, err)
297
-			continue
298
-		}
299
-		defer f.Close()
300
-		tr := NewReader(f)
301
-		for j, header := range test.headers {
302
-			hdr, err := tr.Next()
303
-			if err != nil || hdr == nil {
304
-				t.Errorf("test %d, entry %d: Didn't get entry: %v", i, j, err)
305
-				f.Close()
306
-				continue testLoop
307
-			}
308
-			if !reflect.DeepEqual(*hdr, *header) {
309
-				t.Errorf("test %d, entry %d: Incorrect header:\nhave %+v\nwant %+v",
310
-					i, j, *hdr, *header)
311
-			}
312
-		}
313
-		hdr, err := tr.Next()
314
-		if err == io.EOF {
315
-			continue testLoop
316
-		}
317
-		if hdr != nil || err != nil {
318
-			t.Errorf("test %d: Unexpected entry or error: hdr=%v err=%v", i, hdr, err)
319
-		}
320
-	}
321
-}
322
-
323
-func TestPartialRead(t *testing.T) {
324
-	f, err := os.Open("testdata/gnu.tar")
325
-	if err != nil {
326
-		t.Fatalf("Unexpected error: %v", err)
327
-	}
328
-	defer f.Close()
329
-
330
-	tr := NewReader(f)
331
-
332
-	// Read the first four bytes; Next() should skip the last byte.
333
-	hdr, err := tr.Next()
334
-	if err != nil || hdr == nil {
335
-		t.Fatalf("Didn't get first file: %v", err)
336
-	}
337
-	buf := make([]byte, 4)
338
-	if _, err := io.ReadFull(tr, buf); err != nil {
339
-		t.Fatalf("Unexpected error: %v", err)
340
-	}
341
-	if expected := []byte("Kilt"); !bytes.Equal(buf, expected) {
342
-		t.Errorf("Contents = %v, want %v", buf, expected)
343
-	}
344
-
345
-	// Second file
346
-	hdr, err = tr.Next()
347
-	if err != nil || hdr == nil {
348
-		t.Fatalf("Didn't get second file: %v", err)
349
-	}
350
-	buf = make([]byte, 6)
351
-	if _, err := io.ReadFull(tr, buf); err != nil {
352
-		t.Fatalf("Unexpected error: %v", err)
353
-	}
354
-	if expected := []byte("Google"); !bytes.Equal(buf, expected) {
355
-		t.Errorf("Contents = %v, want %v", buf, expected)
356
-	}
357
-}
358
-
359
-func TestIncrementalRead(t *testing.T) {
360
-	test := gnuTarTest
361
-	f, err := os.Open(test.file)
362
-	if err != nil {
363
-		t.Fatalf("Unexpected error: %v", err)
364
-	}
365
-	defer f.Close()
366
-
367
-	tr := NewReader(f)
368
-
369
-	headers := test.headers
370
-	cksums := test.cksums
371
-	nread := 0
372
-
373
-	// loop over all files
374
-	for ; ; nread++ {
375
-		hdr, err := tr.Next()
376
-		if hdr == nil || err == io.EOF {
377
-			break
378
-		}
379
-
380
-		// check the header
381
-		if !reflect.DeepEqual(*hdr, *headers[nread]) {
382
-			t.Errorf("Incorrect header:\nhave %+v\nwant %+v",
383
-				*hdr, headers[nread])
384
-		}
385
-
386
-		// read file contents in little chunks EOF,
387
-		// checksumming all the way
388
-		h := md5.New()
389
-		rdbuf := make([]uint8, 8)
390
-		for {
391
-			nr, err := tr.Read(rdbuf)
392
-			if err == io.EOF {
393
-				break
394
-			}
395
-			if err != nil {
396
-				t.Errorf("Read: unexpected error %v\n", err)
397
-				break
398
-			}
399
-			h.Write(rdbuf[0:nr])
400
-		}
401
-		// verify checksum
402
-		have := fmt.Sprintf("%x", h.Sum(nil))
403
-		want := cksums[nread]
404
-		if want != have {
405
-			t.Errorf("Bad checksum on file %s:\nhave %+v\nwant %+v", hdr.Name, have, want)
406
-		}
407
-	}
408
-	if nread != len(headers) {
409
-		t.Errorf("Didn't process all files\nexpected: %d\nprocessed %d\n", len(headers), nread)
410
-	}
411
-}
412
-
413
-func TestNonSeekable(t *testing.T) {
414
-	test := gnuTarTest
415
-	f, err := os.Open(test.file)
416
-	if err != nil {
417
-		t.Fatalf("Unexpected error: %v", err)
418
-	}
419
-	defer f.Close()
420
-
421
-	type readerOnly struct {
422
-		io.Reader
423
-	}
424
-	tr := NewReader(readerOnly{f})
425
-	nread := 0
426
-
427
-	for ; ; nread++ {
428
-		_, err := tr.Next()
429
-		if err == io.EOF {
430
-			break
431
-		}
432
-		if err != nil {
433
-			t.Fatalf("Unexpected error: %v", err)
434
-		}
435
-	}
436
-
437
-	if nread != len(test.headers) {
438
-		t.Errorf("Didn't process all files\nexpected: %d\nprocessed %d\n", len(test.headers), nread)
439
-	}
440
-}
441
-
442
-func TestParsePAXHeader(t *testing.T) {
443
-	paxTests := [][3]string{
444
-		{"a", "a=name", "10 a=name\n"}, // Test case involving multiple acceptable lengths
445
-		{"a", "a=name", "9 a=name\n"},  // Test case involving multiple acceptable length
446
-		{"mtime", "mtime=1350244992.023960108", "30 mtime=1350244992.023960108\n"}}
447
-	for _, test := range paxTests {
448
-		key, expected, raw := test[0], test[1], test[2]
449
-		reader := bytes.NewReader([]byte(raw))
450
-		headers, err := parsePAX(reader)
451
-		if err != nil {
452
-			t.Errorf("Couldn't parse correctly formatted headers: %v", err)
453
-			continue
454
-		}
455
-		if strings.EqualFold(headers[key], expected) {
456
-			t.Errorf("mtime header incorrectly parsed: got %s, wanted %s", headers[key], expected)
457
-			continue
458
-		}
459
-		trailer := make([]byte, 100)
460
-		n, err := reader.Read(trailer)
461
-		if err != io.EOF || n != 0 {
462
-			t.Error("Buffer wasn't consumed")
463
-		}
464
-	}
465
-	badHeader := bytes.NewReader([]byte("3 somelongkey="))
466
-	if _, err := parsePAX(badHeader); err != ErrHeader {
467
-		t.Fatal("Unexpected success when parsing bad header")
468
-	}
469
-}
470
-
471
-func TestParsePAXTime(t *testing.T) {
472
-	// Some valid PAX time values
473
-	timestamps := map[string]time.Time{
474
-		"1350244992.023960108":  time.Unix(1350244992, 23960108), // The common case
475
-		"1350244992.02396010":   time.Unix(1350244992, 23960100), // Lower precision value
476
-		"1350244992.0239601089": time.Unix(1350244992, 23960108), // Higher precision value
477
-		"1350244992":            time.Unix(1350244992, 0),        // Low precision value
478
-	}
479
-	for input, expected := range timestamps {
480
-		ts, err := parsePAXTime(input)
481
-		if err != nil {
482
-			t.Fatal(err)
483
-		}
484
-		if !ts.Equal(expected) {
485
-			t.Fatalf("Time parsing failure %s %s", ts, expected)
486
-		}
487
-	}
488
-}
489
-
490
-func TestMergePAX(t *testing.T) {
491
-	hdr := new(Header)
492
-	// Test a string, integer, and time based value.
493
-	headers := map[string]string{
494
-		"path":  "a/b/c",
495
-		"uid":   "1000",
496
-		"mtime": "1350244992.023960108",
497
-	}
498
-	err := mergePAX(hdr, headers)
499
-	if err != nil {
500
-		t.Fatal(err)
501
-	}
502
-	want := &Header{
503
-		Name:    "a/b/c",
504
-		Uid:     1000,
505
-		ModTime: time.Unix(1350244992, 23960108),
506
-	}
507
-	if !reflect.DeepEqual(hdr, want) {
508
-		t.Errorf("incorrect merge: got %+v, want %+v", hdr, want)
509
-	}
510
-}
511
-
512
-func TestSparseEndToEnd(t *testing.T) {
513
-	test := sparseTarTest
514
-	f, err := os.Open(test.file)
515
-	if err != nil {
516
-		t.Fatalf("Unexpected error: %v", err)
517
-	}
518
-	defer f.Close()
519
-
520
-	tr := NewReader(f)
521
-
522
-	headers := test.headers
523
-	cksums := test.cksums
524
-	nread := 0
525
-
526
-	// loop over all files
527
-	for ; ; nread++ {
528
-		hdr, err := tr.Next()
529
-		if hdr == nil || err == io.EOF {
530
-			break
531
-		}
532
-
533
-		// check the header
534
-		if !reflect.DeepEqual(*hdr, *headers[nread]) {
535
-			t.Errorf("Incorrect header:\nhave %+v\nwant %+v",
536
-				*hdr, headers[nread])
537
-		}
538
-
539
-		// read and checksum the file data
540
-		h := md5.New()
541
-		_, err = io.Copy(h, tr)
542
-		if err != nil {
543
-			t.Fatalf("Unexpected error: %v", err)
544
-		}
545
-
546
-		// verify checksum
547
-		have := fmt.Sprintf("%x", h.Sum(nil))
548
-		want := cksums[nread]
549
-		if want != have {
550
-			t.Errorf("Bad checksum on file %s:\nhave %+v\nwant %+v", hdr.Name, have, want)
551
-		}
552
-	}
553
-	if nread != len(headers) {
554
-		t.Errorf("Didn't process all files\nexpected: %d\nprocessed %d\n", len(headers), nread)
555
-	}
556
-}
557
-
558
-type sparseFileReadTest struct {
559
-	sparseData []byte
560
-	sparseMap  []sparseEntry
561
-	realSize   int64
562
-	expected   []byte
563
-}
564
-
565
-var sparseFileReadTests = []sparseFileReadTest{
566
-	{
567
-		sparseData: []byte("abcde"),
568
-		sparseMap: []sparseEntry{
569
-			{offset: 0, numBytes: 2},
570
-			{offset: 5, numBytes: 3},
571
-		},
572
-		realSize: 8,
573
-		expected: []byte("ab\x00\x00\x00cde"),
574
-	},
575
-	{
576
-		sparseData: []byte("abcde"),
577
-		sparseMap: []sparseEntry{
578
-			{offset: 0, numBytes: 2},
579
-			{offset: 5, numBytes: 3},
580
-		},
581
-		realSize: 10,
582
-		expected: []byte("ab\x00\x00\x00cde\x00\x00"),
583
-	},
584
-	{
585
-		sparseData: []byte("abcde"),
586
-		sparseMap: []sparseEntry{
587
-			{offset: 1, numBytes: 3},
588
-			{offset: 6, numBytes: 2},
589
-		},
590
-		realSize: 8,
591
-		expected: []byte("\x00abc\x00\x00de"),
592
-	},
593
-	{
594
-		sparseData: []byte("abcde"),
595
-		sparseMap: []sparseEntry{
596
-			{offset: 1, numBytes: 3},
597
-			{offset: 6, numBytes: 2},
598
-		},
599
-		realSize: 10,
600
-		expected: []byte("\x00abc\x00\x00de\x00\x00"),
601
-	},
602
-	{
603
-		sparseData: []byte(""),
604
-		sparseMap:  nil,
605
-		realSize:   2,
606
-		expected:   []byte("\x00\x00"),
607
-	},
608
-}
609
-
610
-func TestSparseFileReader(t *testing.T) {
611
-	for i, test := range sparseFileReadTests {
612
-		r := bytes.NewReader(test.sparseData)
613
-		nb := int64(r.Len())
614
-		sfr := &sparseFileReader{
615
-			rfr: &regFileReader{r: r, nb: nb},
616
-			sp:  test.sparseMap,
617
-			pos: 0,
618
-			tot: test.realSize,
619
-		}
620
-		if sfr.numBytes() != nb {
621
-			t.Errorf("test %d: Before reading, sfr.numBytes() = %d, want %d", i, sfr.numBytes(), nb)
622
-		}
623
-		buf, err := ioutil.ReadAll(sfr)
624
-		if err != nil {
625
-			t.Errorf("test %d: Unexpected error: %v", i, err)
626
-		}
627
-		if e := test.expected; !bytes.Equal(buf, e) {
628
-			t.Errorf("test %d: Contents = %v, want %v", i, buf, e)
629
-		}
630
-		if sfr.numBytes() != 0 {
631
-			t.Errorf("test %d: After draining the reader, numBytes() was nonzero", i)
632
-		}
633
-	}
634
-}
635
-
636
-func TestSparseIncrementalRead(t *testing.T) {
637
-	sparseMap := []sparseEntry{{10, 2}}
638
-	sparseData := []byte("Go")
639
-	expected := "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00Go\x00\x00\x00\x00\x00\x00\x00\x00"
640
-
641
-	r := bytes.NewReader(sparseData)
642
-	nb := int64(r.Len())
643
-	sfr := &sparseFileReader{
644
-		rfr: &regFileReader{r: r, nb: nb},
645
-		sp:  sparseMap,
646
-		pos: 0,
647
-		tot: int64(len(expected)),
648
-	}
649
-
650
-	// We'll read the data 6 bytes at a time, with a hole of size 10 at
651
-	// the beginning and one of size 8 at the end.
652
-	var outputBuf bytes.Buffer
653
-	buf := make([]byte, 6)
654
-	for {
655
-		n, err := sfr.Read(buf)
656
-		if err == io.EOF {
657
-			break
658
-		}
659
-		if err != nil {
660
-			t.Errorf("Read: unexpected error %v\n", err)
661
-		}
662
-		if n > 0 {
663
-			_, err := outputBuf.Write(buf[:n])
664
-			if err != nil {
665
-				t.Errorf("Write: unexpected error %v\n", err)
666
-			}
667
-		}
668
-	}
669
-	got := outputBuf.String()
670
-	if got != expected {
671
-		t.Errorf("Contents = %v, want %v", got, expected)
672
-	}
673
-}
674
-
675
-func TestReadGNUSparseMap0x1(t *testing.T) {
676
-	headers := map[string]string{
677
-		paxGNUSparseNumBlocks: "4",
678
-		paxGNUSparseMap:       "0,5,10,5,20,5,30,5",
679
-	}
680
-	expected := []sparseEntry{
681
-		{offset: 0, numBytes: 5},
682
-		{offset: 10, numBytes: 5},
683
-		{offset: 20, numBytes: 5},
684
-		{offset: 30, numBytes: 5},
685
-	}
686
-
687
-	sp, err := readGNUSparseMap0x1(headers)
688
-	if err != nil {
689
-		t.Errorf("Unexpected error: %v", err)
690
-	}
691
-	if !reflect.DeepEqual(sp, expected) {
692
-		t.Errorf("Incorrect sparse map: got %v, wanted %v", sp, expected)
693
-	}
694
-}
695
-
696
-func TestReadGNUSparseMap1x0(t *testing.T) {
697
-	// This test uses lots of holes so the sparse header takes up more than two blocks
698
-	numEntries := 100
699
-	expected := make([]sparseEntry, 0, numEntries)
700
-	sparseMap := new(bytes.Buffer)
701
-
702
-	fmt.Fprintf(sparseMap, "%d\n", numEntries)
703
-	for i := 0; i < numEntries; i++ {
704
-		offset := int64(2048 * i)
705
-		numBytes := int64(1024)
706
-		expected = append(expected, sparseEntry{offset: offset, numBytes: numBytes})
707
-		fmt.Fprintf(sparseMap, "%d\n%d\n", offset, numBytes)
708
-	}
709
-
710
-	// Make the header the smallest multiple of blockSize that fits the sparseMap
711
-	headerBlocks := (sparseMap.Len() + blockSize - 1) / blockSize
712
-	bufLen := blockSize * headerBlocks
713
-	buf := make([]byte, bufLen)
714
-	copy(buf, sparseMap.Bytes())
715
-
716
-	// Get an reader to read the sparse map
717
-	r := bytes.NewReader(buf)
718
-
719
-	// Read the sparse map
720
-	sp, err := readGNUSparseMap1x0(r)
721
-	if err != nil {
722
-		t.Errorf("Unexpected error: %v", err)
723
-	}
724
-	if !reflect.DeepEqual(sp, expected) {
725
-		t.Errorf("Incorrect sparse map: got %v, wanted %v", sp, expected)
726
-	}
727
-}
728
-
729
-func TestUninitializedRead(t *testing.T) {
730
-	test := gnuTarTest
731
-	f, err := os.Open(test.file)
732
-	if err != nil {
733
-		t.Fatalf("Unexpected error: %v", err)
734
-	}
735
-	defer f.Close()
736
-
737
-	tr := NewReader(f)
738
-	_, err = tr.Read([]byte{})
739
-	if err == nil || err != io.EOF {
740
-		t.Errorf("Unexpected error: %v, wanted %v", err, io.EOF)
741
-	}
742
-
743
-}
744 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,284 +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
-package tar
6
-
7
-import (
8
-	"bytes"
9
-	"io/ioutil"
10
-	"os"
11
-	"path"
12
-	"reflect"
13
-	"strings"
14
-	"testing"
15
-	"time"
16
-)
17
-
18
-func TestFileInfoHeader(t *testing.T) {
19
-	fi, err := os.Stat("testdata/small.txt")
20
-	if err != nil {
21
-		t.Fatal(err)
22
-	}
23
-	h, err := FileInfoHeader(fi, "")
24
-	if err != nil {
25
-		t.Fatalf("FileInfoHeader: %v", err)
26
-	}
27
-	if g, e := h.Name, "small.txt"; g != e {
28
-		t.Errorf("Name = %q; want %q", g, e)
29
-	}
30
-	if g, e := h.Mode, int64(fi.Mode().Perm())|c_ISREG; g != e {
31
-		t.Errorf("Mode = %#o; want %#o", g, e)
32
-	}
33
-	if g, e := h.Size, int64(5); g != e {
34
-		t.Errorf("Size = %v; want %v", g, e)
35
-	}
36
-	if g, e := h.ModTime, fi.ModTime(); !g.Equal(e) {
37
-		t.Errorf("ModTime = %v; want %v", g, e)
38
-	}
39
-	// FileInfoHeader should error when passing nil FileInfo
40
-	if _, err := FileInfoHeader(nil, ""); err == nil {
41
-		t.Fatalf("Expected error when passing nil to FileInfoHeader")
42
-	}
43
-}
44
-
45
-func TestFileInfoHeaderDir(t *testing.T) {
46
-	fi, err := os.Stat("testdata")
47
-	if err != nil {
48
-		t.Fatal(err)
49
-	}
50
-	h, err := FileInfoHeader(fi, "")
51
-	if err != nil {
52
-		t.Fatalf("FileInfoHeader: %v", err)
53
-	}
54
-	if g, e := h.Name, "testdata/"; g != e {
55
-		t.Errorf("Name = %q; want %q", g, e)
56
-	}
57
-	// Ignoring c_ISGID for golang.org/issue/4867
58
-	if g, e := h.Mode&^c_ISGID, int64(fi.Mode().Perm())|c_ISDIR; g != e {
59
-		t.Errorf("Mode = %#o; want %#o", g, e)
60
-	}
61
-	if g, e := h.Size, int64(0); g != e {
62
-		t.Errorf("Size = %v; want %v", g, e)
63
-	}
64
-	if g, e := h.ModTime, fi.ModTime(); !g.Equal(e) {
65
-		t.Errorf("ModTime = %v; want %v", g, e)
66
-	}
67
-}
68
-
69
-func TestFileInfoHeaderSymlink(t *testing.T) {
70
-	h, err := FileInfoHeader(symlink{}, "some-target")
71
-	if err != nil {
72
-		t.Fatal(err)
73
-	}
74
-	if g, e := h.Name, "some-symlink"; g != e {
75
-		t.Errorf("Name = %q; want %q", g, e)
76
-	}
77
-	if g, e := h.Linkname, "some-target"; g != e {
78
-		t.Errorf("Linkname = %q; want %q", g, e)
79
-	}
80
-}
81
-
82
-type symlink struct{}
83
-
84
-func (symlink) Name() string       { return "some-symlink" }
85
-func (symlink) Size() int64        { return 0 }
86
-func (symlink) Mode() os.FileMode  { return os.ModeSymlink }
87
-func (symlink) ModTime() time.Time { return time.Time{} }
88
-func (symlink) IsDir() bool        { return false }
89
-func (symlink) Sys() interface{}   { return nil }
90
-
91
-func TestRoundTrip(t *testing.T) {
92
-	data := []byte("some file contents")
93
-
94
-	var b bytes.Buffer
95
-	tw := NewWriter(&b)
96
-	hdr := &Header{
97
-		Name:    "file.txt",
98
-		Uid:     1 << 21, // too big for 8 octal digits
99
-		Size:    int64(len(data)),
100
-		ModTime: time.Now(),
101
-	}
102
-	// tar only supports second precision.
103
-	hdr.ModTime = hdr.ModTime.Add(-time.Duration(hdr.ModTime.Nanosecond()) * time.Nanosecond)
104
-	if err := tw.WriteHeader(hdr); err != nil {
105
-		t.Fatalf("tw.WriteHeader: %v", err)
106
-	}
107
-	if _, err := tw.Write(data); err != nil {
108
-		t.Fatalf("tw.Write: %v", err)
109
-	}
110
-	if err := tw.Close(); err != nil {
111
-		t.Fatalf("tw.Close: %v", err)
112
-	}
113
-
114
-	// Read it back.
115
-	tr := NewReader(&b)
116
-	rHdr, err := tr.Next()
117
-	if err != nil {
118
-		t.Fatalf("tr.Next: %v", err)
119
-	}
120
-	if !reflect.DeepEqual(rHdr, hdr) {
121
-		t.Errorf("Header mismatch.\n got %+v\nwant %+v", rHdr, hdr)
122
-	}
123
-	rData, err := ioutil.ReadAll(tr)
124
-	if err != nil {
125
-		t.Fatalf("Read: %v", err)
126
-	}
127
-	if !bytes.Equal(rData, data) {
128
-		t.Errorf("Data mismatch.\n got %q\nwant %q", rData, data)
129
-	}
130
-}
131
-
132
-type headerRoundTripTest struct {
133
-	h  *Header
134
-	fm os.FileMode
135
-}
136
-
137
-func TestHeaderRoundTrip(t *testing.T) {
138
-	golden := []headerRoundTripTest{
139
-		// regular file.
140
-		{
141
-			h: &Header{
142
-				Name:     "test.txt",
143
-				Mode:     0644 | c_ISREG,
144
-				Size:     12,
145
-				ModTime:  time.Unix(1360600916, 0),
146
-				Typeflag: TypeReg,
147
-			},
148
-			fm: 0644,
149
-		},
150
-		// hard link.
151
-		{
152
-			h: &Header{
153
-				Name:     "hard.txt",
154
-				Mode:     0644 | c_ISLNK,
155
-				Size:     0,
156
-				ModTime:  time.Unix(1360600916, 0),
157
-				Typeflag: TypeLink,
158
-			},
159
-			fm: 0644 | os.ModeSymlink,
160
-		},
161
-		// symbolic link.
162
-		{
163
-			h: &Header{
164
-				Name:     "link.txt",
165
-				Mode:     0777 | c_ISLNK,
166
-				Size:     0,
167
-				ModTime:  time.Unix(1360600852, 0),
168
-				Typeflag: TypeSymlink,
169
-			},
170
-			fm: 0777 | os.ModeSymlink,
171
-		},
172
-		// character device node.
173
-		{
174
-			h: &Header{
175
-				Name:     "dev/null",
176
-				Mode:     0666 | c_ISCHR,
177
-				Size:     0,
178
-				ModTime:  time.Unix(1360578951, 0),
179
-				Typeflag: TypeChar,
180
-			},
181
-			fm: 0666 | os.ModeDevice | os.ModeCharDevice,
182
-		},
183
-		// block device node.
184
-		{
185
-			h: &Header{
186
-				Name:     "dev/sda",
187
-				Mode:     0660 | c_ISBLK,
188
-				Size:     0,
189
-				ModTime:  time.Unix(1360578954, 0),
190
-				Typeflag: TypeBlock,
191
-			},
192
-			fm: 0660 | os.ModeDevice,
193
-		},
194
-		// directory.
195
-		{
196
-			h: &Header{
197
-				Name:     "dir/",
198
-				Mode:     0755 | c_ISDIR,
199
-				Size:     0,
200
-				ModTime:  time.Unix(1360601116, 0),
201
-				Typeflag: TypeDir,
202
-			},
203
-			fm: 0755 | os.ModeDir,
204
-		},
205
-		// fifo node.
206
-		{
207
-			h: &Header{
208
-				Name:     "dev/initctl",
209
-				Mode:     0600 | c_ISFIFO,
210
-				Size:     0,
211
-				ModTime:  time.Unix(1360578949, 0),
212
-				Typeflag: TypeFifo,
213
-			},
214
-			fm: 0600 | os.ModeNamedPipe,
215
-		},
216
-		// setuid.
217
-		{
218
-			h: &Header{
219
-				Name:     "bin/su",
220
-				Mode:     0755 | c_ISREG | c_ISUID,
221
-				Size:     23232,
222
-				ModTime:  time.Unix(1355405093, 0),
223
-				Typeflag: TypeReg,
224
-			},
225
-			fm: 0755 | os.ModeSetuid,
226
-		},
227
-		// setguid.
228
-		{
229
-			h: &Header{
230
-				Name:     "group.txt",
231
-				Mode:     0750 | c_ISREG | c_ISGID,
232
-				Size:     0,
233
-				ModTime:  time.Unix(1360602346, 0),
234
-				Typeflag: TypeReg,
235
-			},
236
-			fm: 0750 | os.ModeSetgid,
237
-		},
238
-		// sticky.
239
-		{
240
-			h: &Header{
241
-				Name:     "sticky.txt",
242
-				Mode:     0600 | c_ISREG | c_ISVTX,
243
-				Size:     7,
244
-				ModTime:  time.Unix(1360602540, 0),
245
-				Typeflag: TypeReg,
246
-			},
247
-			fm: 0600 | os.ModeSticky,
248
-		},
249
-	}
250
-
251
-	for i, g := range golden {
252
-		fi := g.h.FileInfo()
253
-		h2, err := FileInfoHeader(fi, "")
254
-		if err != nil {
255
-			t.Error(err)
256
-			continue
257
-		}
258
-		if strings.Contains(fi.Name(), "/") {
259
-			t.Errorf("FileInfo of %q contains slash: %q", g.h.Name, fi.Name())
260
-		}
261
-		name := path.Base(g.h.Name)
262
-		if fi.IsDir() {
263
-			name += "/"
264
-		}
265
-		if got, want := h2.Name, name; got != want {
266
-			t.Errorf("i=%d: Name: got %v, want %v", i, got, want)
267
-		}
268
-		if got, want := h2.Size, g.h.Size; got != want {
269
-			t.Errorf("i=%d: Size: got %v, want %v", i, got, want)
270
-		}
271
-		if got, want := h2.Mode, g.h.Mode; got != want {
272
-			t.Errorf("i=%d: Mode: got %o, want %o", i, got, want)
273
-		}
274
-		if got, want := fi.Mode(), g.fm; got != want {
275
-			t.Errorf("i=%d: fi.Mode: got %o, want %o", i, got, want)
276
-		}
277
-		if got, want := h2.ModTime, g.h.ModTime; got != want {
278
-			t.Errorf("i=%d: ModTime: got %v, want %v", i, got, want)
279
-		}
280
-		if sysh, ok := fi.Sys().(*Header); !ok || sysh != g.h {
281
-			t.Errorf("i=%d: Sys didn't return original *Header", i)
282
-		}
283
-	}
284
-}
285 1
deleted file mode 100644
286 2
Binary files a/vendor/src/code.google.com/p/go/src/pkg/archive/tar/testdata/gnu.tar and /dev/null differ
287 3
deleted file mode 100644
288 4
Binary files a/vendor/src/code.google.com/p/go/src/pkg/archive/tar/testdata/nil-uid.tar and /dev/null differ
289 5
deleted file mode 100644
290 6
Binary files a/vendor/src/code.google.com/p/go/src/pkg/archive/tar/testdata/pax.tar and /dev/null differ
291 7
deleted file mode 100644
... ...
@@ -1 +0,0 @@
1
-Kilts
2 1
\ No newline at end of file
3 2
deleted file mode 100644
... ...
@@ -1 +0,0 @@
1
-Google.com
2 1
deleted file mode 100644
3 2
Binary files a/vendor/src/code.google.com/p/go/src/pkg/archive/tar/testdata/sparse-formats.tar and /dev/null differ
4 3
deleted file mode 100644
5 4
Binary files a/vendor/src/code.google.com/p/go/src/pkg/archive/tar/testdata/star.tar and /dev/null differ
6 5
deleted file mode 100644
7 6
Binary files a/vendor/src/code.google.com/p/go/src/pkg/archive/tar/testdata/ustar.tar and /dev/null differ
8 7
deleted file mode 100644
9 8
Binary files a/vendor/src/code.google.com/p/go/src/pkg/archive/tar/testdata/v7.tar and /dev/null differ
10 9
deleted file mode 100644
11 10
Binary files a/vendor/src/code.google.com/p/go/src/pkg/archive/tar/testdata/writer-big-long.tar and /dev/null differ
12 11
deleted file mode 100644
13 12
Binary files a/vendor/src/code.google.com/p/go/src/pkg/archive/tar/testdata/writer-big.tar and /dev/null differ
14 13
deleted file mode 100644
15 14
Binary files a/vendor/src/code.google.com/p/go/src/pkg/archive/tar/testdata/writer.tar and /dev/null differ
16 15
deleted file mode 100644
17 16
Binary files a/vendor/src/code.google.com/p/go/src/pkg/archive/tar/testdata/xattrs.tar and /dev/null differ
18 17
deleted file mode 100644
... ...
@@ -1,396 +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
-	"os"
16
-	"path"
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
-	errNameTooLong     = errors.New("archive/tar: name too long")
27
-	errInvalidHeader   = errors.New("archive/tar: header field too long or contains invalid values")
28
-)
29
-
30
-// A Writer provides sequential writing of a tar archive in POSIX.1 format.
31
-// A tar archive consists of a sequence of files.
32
-// Call WriteHeader to begin a new file, and then call Write to supply that file's data,
33
-// writing at most hdr.Size bytes in total.
34
-type Writer struct {
35
-	w          io.Writer
36
-	err        error
37
-	nb         int64 // number of unwritten bytes for current file entry
38
-	pad        int64 // amount of padding to write after current file entry
39
-	closed     bool
40
-	usedBinary bool            // whether the binary numeric field extension was used
41
-	preferPax  bool            // use pax header instead of binary numeric header
42
-	hdrBuff    [blockSize]byte // buffer to use in writeHeader when writing a regular header
43
-	paxHdrBuff [blockSize]byte // buffer to use in writeHeader when writing a pax header
44
-}
45
-
46
-// NewWriter creates a new Writer writing to w.
47
-func NewWriter(w io.Writer) *Writer { return &Writer{w: w} }
48
-
49
-// Flush finishes writing the current file (optional).
50
-func (tw *Writer) Flush() error {
51
-	if tw.nb > 0 {
52
-		tw.err = fmt.Errorf("archive/tar: missed writing %d bytes", tw.nb)
53
-		return tw.err
54
-	}
55
-
56
-	n := tw.nb + tw.pad
57
-	for n > 0 && tw.err == nil {
58
-		nr := n
59
-		if nr > blockSize {
60
-			nr = blockSize
61
-		}
62
-		var nw int
63
-		nw, tw.err = tw.w.Write(zeroBlock[0:nr])
64
-		n -= int64(nw)
65
-	}
66
-	tw.nb = 0
67
-	tw.pad = 0
68
-	return tw.err
69
-}
70
-
71
-// Write s into b, terminating it with a NUL if there is room.
72
-// If the value is too long for the field and allowPax is true add a paxheader record instead
73
-func (tw *Writer) cString(b []byte, s string, allowPax bool, paxKeyword string, paxHeaders map[string]string) {
74
-	needsPaxHeader := allowPax && len(s) > len(b) || !isASCII(s)
75
-	if needsPaxHeader {
76
-		paxHeaders[paxKeyword] = s
77
-		return
78
-	}
79
-	if len(s) > len(b) {
80
-		if tw.err == nil {
81
-			tw.err = ErrFieldTooLong
82
-		}
83
-		return
84
-	}
85
-	ascii := toASCII(s)
86
-	copy(b, ascii)
87
-	if len(ascii) < len(b) {
88
-		b[len(ascii)] = 0
89
-	}
90
-}
91
-
92
-// Encode x as an octal ASCII string and write it into b with leading zeros.
93
-func (tw *Writer) octal(b []byte, x int64) {
94
-	s := strconv.FormatInt(x, 8)
95
-	// leading zeros, but leave room for a NUL.
96
-	for len(s)+1 < len(b) {
97
-		s = "0" + s
98
-	}
99
-	tw.cString(b, s, false, paxNone, nil)
100
-}
101
-
102
-// Write x into b, either as octal or as binary (GNUtar/star extension).
103
-// If the value is too long for the field and writingPax is enabled both for the field and the add a paxheader record instead
104
-func (tw *Writer) numeric(b []byte, x int64, allowPax bool, paxKeyword string, paxHeaders map[string]string) {
105
-	// Try octal first.
106
-	s := strconv.FormatInt(x, 8)
107
-	if len(s) < len(b) {
108
-		tw.octal(b, x)
109
-		return
110
-	}
111
-
112
-	// If it is too long for octal, and pax is preferred, use a pax header
113
-	if allowPax && tw.preferPax {
114
-		tw.octal(b, 0)
115
-		s := strconv.FormatInt(x, 10)
116
-		paxHeaders[paxKeyword] = s
117
-		return
118
-	}
119
-
120
-	// Too big: use binary (big-endian).
121
-	tw.usedBinary = true
122
-	for i := len(b) - 1; x > 0 && i >= 0; i-- {
123
-		b[i] = byte(x)
124
-		x >>= 8
125
-	}
126
-	b[0] |= 0x80 // highest bit indicates binary format
127
-}
128
-
129
-var (
130
-	minTime = time.Unix(0, 0)
131
-	// There is room for 11 octal digits (33 bits) of mtime.
132
-	maxTime = minTime.Add((1<<33 - 1) * time.Second)
133
-)
134
-
135
-// WriteHeader writes hdr and prepares to accept the file's contents.
136
-// WriteHeader calls Flush if it is not the first header.
137
-// Calling after a Close will return ErrWriteAfterClose.
138
-func (tw *Writer) WriteHeader(hdr *Header) error {
139
-	return tw.writeHeader(hdr, true)
140
-}
141
-
142
-// WriteHeader writes hdr and prepares to accept the file's contents.
143
-// WriteHeader calls Flush if it is not the first header.
144
-// Calling after a Close will return ErrWriteAfterClose.
145
-// As this method is called internally by writePax header to allow it to
146
-// suppress writing the pax header.
147
-func (tw *Writer) writeHeader(hdr *Header, allowPax bool) error {
148
-	if tw.closed {
149
-		return ErrWriteAfterClose
150
-	}
151
-	if tw.err == nil {
152
-		tw.Flush()
153
-	}
154
-	if tw.err != nil {
155
-		return tw.err
156
-	}
157
-
158
-	// a map to hold pax header records, if any are needed
159
-	paxHeaders := make(map[string]string)
160
-
161
-	// TODO(shanemhansen): we might want to use PAX headers for
162
-	// subsecond time resolution, but for now let's just capture
163
-	// too long fields or non ascii characters
164
-
165
-	var header []byte
166
-
167
-	// We need to select which scratch buffer to use carefully,
168
-	// since this method is called recursively to write PAX headers.
169
-	// If allowPax is true, this is the non-recursive call, and we will use hdrBuff.
170
-	// If allowPax is false, we are being called by writePAXHeader, and hdrBuff is
171
-	// already being used by the non-recursive call, so we must use paxHdrBuff.
172
-	header = tw.hdrBuff[:]
173
-	if !allowPax {
174
-		header = tw.paxHdrBuff[:]
175
-	}
176
-	copy(header, zeroBlock)
177
-	s := slicer(header)
178
-
179
-	// keep a reference to the filename to allow to overwrite it later if we detect that we can use ustar longnames instead of pax
180
-	pathHeaderBytes := s.next(fileNameSize)
181
-
182
-	tw.cString(pathHeaderBytes, hdr.Name, true, paxPath, paxHeaders)
183
-
184
-	// Handle out of range ModTime carefully.
185
-	var modTime int64
186
-	if !hdr.ModTime.Before(minTime) && !hdr.ModTime.After(maxTime) {
187
-		modTime = hdr.ModTime.Unix()
188
-	}
189
-
190
-	tw.octal(s.next(8), hdr.Mode)                                   // 100:108
191
-	tw.numeric(s.next(8), int64(hdr.Uid), true, paxUid, paxHeaders) // 108:116
192
-	tw.numeric(s.next(8), int64(hdr.Gid), true, paxGid, paxHeaders) // 116:124
193
-	tw.numeric(s.next(12), hdr.Size, true, paxSize, paxHeaders)     // 124:136
194
-	tw.numeric(s.next(12), modTime, false, paxNone, nil)            // 136:148 --- consider using pax for finer granularity
195
-	s.next(8)                                                       // chksum (148:156)
196
-	s.next(1)[0] = hdr.Typeflag                                     // 156:157
197
-
198
-	tw.cString(s.next(100), hdr.Linkname, true, paxLinkpath, paxHeaders)
199
-
200
-	copy(s.next(8), []byte("ustar\x0000"))                        // 257:265
201
-	tw.cString(s.next(32), hdr.Uname, true, paxUname, paxHeaders) // 265:297
202
-	tw.cString(s.next(32), hdr.Gname, true, paxGname, paxHeaders) // 297:329
203
-	tw.numeric(s.next(8), hdr.Devmajor, false, paxNone, nil)      // 329:337
204
-	tw.numeric(s.next(8), hdr.Devminor, false, paxNone, nil)      // 337:345
205
-
206
-	// keep a reference to the prefix to allow to overwrite it later if we detect that we can use ustar longnames instead of pax
207
-	prefixHeaderBytes := s.next(155)
208
-	tw.cString(prefixHeaderBytes, "", false, paxNone, nil) // 345:500  prefix
209
-
210
-	// Use the GNU magic instead of POSIX magic if we used any GNU extensions.
211
-	if tw.usedBinary {
212
-		copy(header[257:265], []byte("ustar  \x00"))
213
-	}
214
-
215
-	_, paxPathUsed := paxHeaders[paxPath]
216
-	// try to use a ustar header when only the name is too long
217
-	if !tw.preferPax && len(paxHeaders) == 1 && paxPathUsed {
218
-		suffix := hdr.Name
219
-		prefix := ""
220
-		if len(hdr.Name) > fileNameSize && isASCII(hdr.Name) {
221
-			var err error
222
-			prefix, suffix, err = tw.splitUSTARLongName(hdr.Name)
223
-			if err == nil {
224
-				// ok we can use a ustar long name instead of pax, now correct the fields
225
-
226
-				// remove the path field from the pax header. this will suppress the pax header
227
-				delete(paxHeaders, paxPath)
228
-
229
-				// update the path fields
230
-				tw.cString(pathHeaderBytes, suffix, false, paxNone, nil)
231
-				tw.cString(prefixHeaderBytes, prefix, false, paxNone, nil)
232
-
233
-				// Use the ustar magic if we used ustar long names.
234
-				if len(prefix) > 0 && !tw.usedBinary {
235
-					copy(header[257:265], []byte("ustar\x00"))
236
-				}
237
-			}
238
-		}
239
-	}
240
-
241
-	// The chksum field is terminated by a NUL and a space.
242
-	// This is different from the other octal fields.
243
-	chksum, _ := checksum(header)
244
-	tw.octal(header[148:155], chksum)
245
-	header[155] = ' '
246
-
247
-	if tw.err != nil {
248
-		// problem with header; probably integer too big for a field.
249
-		return tw.err
250
-	}
251
-
252
-	if allowPax {
253
-		for k, v := range hdr.Xattrs {
254
-			paxHeaders[paxXattr+k] = v
255
-		}
256
-	}
257
-
258
-	if len(paxHeaders) > 0 {
259
-		if !allowPax {
260
-			return errInvalidHeader
261
-		}
262
-		if err := tw.writePAXHeader(hdr, paxHeaders); err != nil {
263
-			return err
264
-		}
265
-	}
266
-	tw.nb = int64(hdr.Size)
267
-	tw.pad = (blockSize - (tw.nb % blockSize)) % blockSize
268
-
269
-	_, tw.err = tw.w.Write(header)
270
-	return tw.err
271
-}
272
-
273
-// writeUSTARLongName splits a USTAR long name hdr.Name.
274
-// name must be < 256 characters. errNameTooLong is returned
275
-// if hdr.Name can't be split. The splitting heuristic
276
-// is compatible with gnu tar.
277
-func (tw *Writer) splitUSTARLongName(name string) (prefix, suffix string, err error) {
278
-	length := len(name)
279
-	if length > fileNamePrefixSize+1 {
280
-		length = fileNamePrefixSize + 1
281
-	} else if name[length-1] == '/' {
282
-		length--
283
-	}
284
-	i := strings.LastIndex(name[:length], "/")
285
-	// nlen contains the resulting length in the name field.
286
-	// plen contains the resulting length in the prefix field.
287
-	nlen := len(name) - i - 1
288
-	plen := i
289
-	if i <= 0 || nlen > fileNameSize || nlen == 0 || plen > fileNamePrefixSize {
290
-		err = errNameTooLong
291
-		return
292
-	}
293
-	prefix, suffix = name[:i], name[i+1:]
294
-	return
295
-}
296
-
297
-// writePaxHeader writes an extended pax header to the
298
-// archive.
299
-func (tw *Writer) writePAXHeader(hdr *Header, paxHeaders map[string]string) error {
300
-	// Prepare extended header
301
-	ext := new(Header)
302
-	ext.Typeflag = TypeXHeader
303
-	// Setting ModTime is required for reader parsing to
304
-	// succeed, and seems harmless enough.
305
-	ext.ModTime = hdr.ModTime
306
-	// The spec asks that we namespace our pseudo files
307
-	// with the current pid.
308
-	pid := os.Getpid()
309
-	dir, file := path.Split(hdr.Name)
310
-	fullName := path.Join(dir,
311
-		fmt.Sprintf("PaxHeaders.%d", pid), file)
312
-
313
-	ascii := toASCII(fullName)
314
-	if len(ascii) > 100 {
315
-		ascii = ascii[:100]
316
-	}
317
-	ext.Name = ascii
318
-	// Construct the body
319
-	var buf bytes.Buffer
320
-
321
-	for k, v := range paxHeaders {
322
-		fmt.Fprint(&buf, paxHeader(k+"="+v))
323
-	}
324
-
325
-	ext.Size = int64(len(buf.Bytes()))
326
-	if err := tw.writeHeader(ext, false); err != nil {
327
-		return err
328
-	}
329
-	if _, err := tw.Write(buf.Bytes()); err != nil {
330
-		return err
331
-	}
332
-	if err := tw.Flush(); err != nil {
333
-		return err
334
-	}
335
-	return nil
336
-}
337
-
338
-// paxHeader formats a single pax record, prefixing it with the appropriate length
339
-func paxHeader(msg string) string {
340
-	const padding = 2 // Extra padding for space and newline
341
-	size := len(msg) + padding
342
-	size += len(strconv.Itoa(size))
343
-	record := fmt.Sprintf("%d %s\n", size, msg)
344
-	if len(record) != size {
345
-		// Final adjustment if adding size increased
346
-		// the number of digits in size
347
-		size = len(record)
348
-		record = fmt.Sprintf("%d %s\n", size, msg)
349
-	}
350
-	return record
351
-}
352
-
353
-// Write writes to the current entry in the tar archive.
354
-// Write returns the error ErrWriteTooLong if more than
355
-// hdr.Size bytes are written after WriteHeader.
356
-func (tw *Writer) Write(b []byte) (n int, err error) {
357
-	if tw.closed {
358
-		err = ErrWriteTooLong
359
-		return
360
-	}
361
-	overwrite := false
362
-	if int64(len(b)) > tw.nb {
363
-		b = b[0:tw.nb]
364
-		overwrite = true
365
-	}
366
-	n, err = tw.w.Write(b)
367
-	tw.nb -= int64(n)
368
-	if err == nil && overwrite {
369
-		err = ErrWriteTooLong
370
-		return
371
-	}
372
-	tw.err = err
373
-	return
374
-}
375
-
376
-// Close closes the tar archive, flushing any unwritten
377
-// data to the underlying writer.
378
-func (tw *Writer) Close() error {
379
-	if tw.err != nil || tw.closed {
380
-		return tw.err
381
-	}
382
-	tw.Flush()
383
-	tw.closed = true
384
-	if tw.err != nil {
385
-		return tw.err
386
-	}
387
-
388
-	// trailer: two zero blocks
389
-	for i := 0; i < 2; i++ {
390
-		_, tw.err = tw.w.Write(zeroBlock)
391
-		if tw.err != nil {
392
-			break
393
-		}
394
-	}
395
-	return tw.err
396
-}
397 1
deleted file mode 100644
... ...
@@ -1,491 +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
-import (
8
-	"bytes"
9
-	"fmt"
10
-	"io"
11
-	"io/ioutil"
12
-	"os"
13
-	"reflect"
14
-	"strings"
15
-	"testing"
16
-	"testing/iotest"
17
-	"time"
18
-)
19
-
20
-type writerTestEntry struct {
21
-	header   *Header
22
-	contents string
23
-}
24
-
25
-type writerTest struct {
26
-	file    string // filename of expected output
27
-	entries []*writerTestEntry
28
-}
29
-
30
-var writerTests = []*writerTest{
31
-	// The writer test file was produced with this command:
32
-	// tar (GNU tar) 1.26
33
-	//   ln -s small.txt link.txt
34
-	//   tar -b 1 --format=ustar -c -f writer.tar small.txt small2.txt link.txt
35
-	{
36
-		file: "testdata/writer.tar",
37
-		entries: []*writerTestEntry{
38
-			{
39
-				header: &Header{
40
-					Name:     "small.txt",
41
-					Mode:     0640,
42
-					Uid:      73025,
43
-					Gid:      5000,
44
-					Size:     5,
45
-					ModTime:  time.Unix(1246508266, 0),
46
-					Typeflag: '0',
47
-					Uname:    "dsymonds",
48
-					Gname:    "eng",
49
-				},
50
-				contents: "Kilts",
51
-			},
52
-			{
53
-				header: &Header{
54
-					Name:     "small2.txt",
55
-					Mode:     0640,
56
-					Uid:      73025,
57
-					Gid:      5000,
58
-					Size:     11,
59
-					ModTime:  time.Unix(1245217492, 0),
60
-					Typeflag: '0',
61
-					Uname:    "dsymonds",
62
-					Gname:    "eng",
63
-				},
64
-				contents: "Google.com\n",
65
-			},
66
-			{
67
-				header: &Header{
68
-					Name:     "link.txt",
69
-					Mode:     0777,
70
-					Uid:      1000,
71
-					Gid:      1000,
72
-					Size:     0,
73
-					ModTime:  time.Unix(1314603082, 0),
74
-					Typeflag: '2',
75
-					Linkname: "small.txt",
76
-					Uname:    "strings",
77
-					Gname:    "strings",
78
-				},
79
-				// no contents
80
-			},
81
-		},
82
-	},
83
-	// The truncated test file was produced using these commands:
84
-	//   dd if=/dev/zero bs=1048576 count=16384 > /tmp/16gig.txt
85
-	//   tar -b 1 -c -f- /tmp/16gig.txt | dd bs=512 count=8 > writer-big.tar
86
-	{
87
-		file: "testdata/writer-big.tar",
88
-		entries: []*writerTestEntry{
89
-			{
90
-				header: &Header{
91
-					Name:     "tmp/16gig.txt",
92
-					Mode:     0640,
93
-					Uid:      73025,
94
-					Gid:      5000,
95
-					Size:     16 << 30,
96
-					ModTime:  time.Unix(1254699560, 0),
97
-					Typeflag: '0',
98
-					Uname:    "dsymonds",
99
-					Gname:    "eng",
100
-				},
101
-				// fake contents
102
-				contents: strings.Repeat("\x00", 4<<10),
103
-			},
104
-		},
105
-	},
106
-	// The truncated test file was produced using these commands:
107
-	//   dd if=/dev/zero bs=1048576 count=16384 > (longname/)*15 /16gig.txt
108
-	//   tar -b 1 -c -f- (longname/)*15 /16gig.txt | dd bs=512 count=8 > writer-big-long.tar
109
-	{
110
-		file: "testdata/writer-big-long.tar",
111
-		entries: []*writerTestEntry{
112
-			{
113
-				header: &Header{
114
-					Name:     strings.Repeat("longname/", 15) + "16gig.txt",
115
-					Mode:     0644,
116
-					Uid:      1000,
117
-					Gid:      1000,
118
-					Size:     16 << 30,
119
-					ModTime:  time.Unix(1399583047, 0),
120
-					Typeflag: '0',
121
-					Uname:    "guillaume",
122
-					Gname:    "guillaume",
123
-				},
124
-				// fake contents
125
-				contents: strings.Repeat("\x00", 4<<10),
126
-			},
127
-		},
128
-	},
129
-	// This file was produced using gnu tar 1.17
130
-	// gnutar  -b 4 --format=ustar (longname/)*15 + file.txt
131
-	{
132
-		file: "testdata/ustar.tar",
133
-		entries: []*writerTestEntry{
134
-			{
135
-				header: &Header{
136
-					Name:     strings.Repeat("longname/", 15) + "file.txt",
137
-					Mode:     0644,
138
-					Uid:      0765,
139
-					Gid:      024,
140
-					Size:     06,
141
-					ModTime:  time.Unix(1360135598, 0),
142
-					Typeflag: '0',
143
-					Uname:    "shane",
144
-					Gname:    "staff",
145
-				},
146
-				contents: "hello\n",
147
-			},
148
-		},
149
-	},
150
-}
151
-
152
-// Render byte array in a two-character hexadecimal string, spaced for easy visual inspection.
153
-func bytestr(offset int, b []byte) string {
154
-	const rowLen = 32
155
-	s := fmt.Sprintf("%04x ", offset)
156
-	for _, ch := range b {
157
-		switch {
158
-		case '0' <= ch && ch <= '9', 'A' <= ch && ch <= 'Z', 'a' <= ch && ch <= 'z':
159
-			s += fmt.Sprintf("  %c", ch)
160
-		default:
161
-			s += fmt.Sprintf(" %02x", ch)
162
-		}
163
-	}
164
-	return s
165
-}
166
-
167
-// Render a pseudo-diff between two blocks of bytes.
168
-func bytediff(a []byte, b []byte) string {
169
-	const rowLen = 32
170
-	s := fmt.Sprintf("(%d bytes vs. %d bytes)\n", len(a), len(b))
171
-	for offset := 0; len(a)+len(b) > 0; offset += rowLen {
172
-		na, nb := rowLen, rowLen
173
-		if na > len(a) {
174
-			na = len(a)
175
-		}
176
-		if nb > len(b) {
177
-			nb = len(b)
178
-		}
179
-		sa := bytestr(offset, a[0:na])
180
-		sb := bytestr(offset, b[0:nb])
181
-		if sa != sb {
182
-			s += fmt.Sprintf("-%v\n+%v\n", sa, sb)
183
-		}
184
-		a = a[na:]
185
-		b = b[nb:]
186
-	}
187
-	return s
188
-}
189
-
190
-func TestWriter(t *testing.T) {
191
-testLoop:
192
-	for i, test := range writerTests {
193
-		expected, err := ioutil.ReadFile(test.file)
194
-		if err != nil {
195
-			t.Errorf("test %d: Unexpected error: %v", i, err)
196
-			continue
197
-		}
198
-
199
-		buf := new(bytes.Buffer)
200
-		tw := NewWriter(iotest.TruncateWriter(buf, 4<<10)) // only catch the first 4 KB
201
-		big := false
202
-		for j, entry := range test.entries {
203
-			big = big || entry.header.Size > 1<<10
204
-			if err := tw.WriteHeader(entry.header); err != nil {
205
-				t.Errorf("test %d, entry %d: Failed writing header: %v", i, j, err)
206
-				continue testLoop
207
-			}
208
-			if _, err := io.WriteString(tw, entry.contents); err != nil {
209
-				t.Errorf("test %d, entry %d: Failed writing contents: %v", i, j, err)
210
-				continue testLoop
211
-			}
212
-		}
213
-		// Only interested in Close failures for the small tests.
214
-		if err := tw.Close(); err != nil && !big {
215
-			t.Errorf("test %d: Failed closing archive: %v", i, err)
216
-			continue testLoop
217
-		}
218
-
219
-		actual := buf.Bytes()
220
-		if !bytes.Equal(expected, actual) {
221
-			t.Errorf("test %d: Incorrect result: (-=expected, +=actual)\n%v",
222
-				i, bytediff(expected, actual))
223
-		}
224
-		if testing.Short() { // The second test is expensive.
225
-			break
226
-		}
227
-	}
228
-}
229
-
230
-func TestPax(t *testing.T) {
231
-	// Create an archive with a large name
232
-	fileinfo, err := os.Stat("testdata/small.txt")
233
-	if err != nil {
234
-		t.Fatal(err)
235
-	}
236
-	hdr, err := FileInfoHeader(fileinfo, "")
237
-	if err != nil {
238
-		t.Fatalf("os.Stat: %v", err)
239
-	}
240
-	// Force a PAX long name to be written
241
-	longName := strings.Repeat("ab", 100)
242
-	contents := strings.Repeat(" ", int(hdr.Size))
243
-	hdr.Name = longName
244
-	var buf bytes.Buffer
245
-	writer := NewWriter(&buf)
246
-	if err := writer.WriteHeader(hdr); err != nil {
247
-		t.Fatal(err)
248
-	}
249
-	if _, err = writer.Write([]byte(contents)); err != nil {
250
-		t.Fatal(err)
251
-	}
252
-	if err := writer.Close(); err != nil {
253
-		t.Fatal(err)
254
-	}
255
-	// Simple test to make sure PAX extensions are in effect
256
-	if !bytes.Contains(buf.Bytes(), []byte("PaxHeaders.")) {
257
-		t.Fatal("Expected at least one PAX header to be written.")
258
-	}
259
-	// Test that we can get a long name back out of the archive.
260
-	reader := NewReader(&buf)
261
-	hdr, err = reader.Next()
262
-	if err != nil {
263
-		t.Fatal(err)
264
-	}
265
-	if hdr.Name != longName {
266
-		t.Fatal("Couldn't recover long file name")
267
-	}
268
-}
269
-
270
-func TestPaxSymlink(t *testing.T) {
271
-	// Create an archive with a large linkname
272
-	fileinfo, err := os.Stat("testdata/small.txt")
273
-	if err != nil {
274
-		t.Fatal(err)
275
-	}
276
-	hdr, err := FileInfoHeader(fileinfo, "")
277
-	hdr.Typeflag = TypeSymlink
278
-	if err != nil {
279
-		t.Fatalf("os.Stat:1 %v", err)
280
-	}
281
-	// Force a PAX long linkname to be written
282
-	longLinkname := strings.Repeat("1234567890/1234567890", 10)
283
-	hdr.Linkname = longLinkname
284
-
285
-	hdr.Size = 0
286
-	var buf bytes.Buffer
287
-	writer := NewWriter(&buf)
288
-	if err := writer.WriteHeader(hdr); err != nil {
289
-		t.Fatal(err)
290
-	}
291
-	if err := writer.Close(); err != nil {
292
-		t.Fatal(err)
293
-	}
294
-	// Simple test to make sure PAX extensions are in effect
295
-	if !bytes.Contains(buf.Bytes(), []byte("PaxHeaders.")) {
296
-		t.Fatal("Expected at least one PAX header to be written.")
297
-	}
298
-	// Test that we can get a long name back out of the archive.
299
-	reader := NewReader(&buf)
300
-	hdr, err = reader.Next()
301
-	if err != nil {
302
-		t.Fatal(err)
303
-	}
304
-	if hdr.Linkname != longLinkname {
305
-		t.Fatal("Couldn't recover long link name")
306
-	}
307
-}
308
-
309
-func TestPaxNonAscii(t *testing.T) {
310
-	// Create an archive with non ascii. These should trigger a pax header
311
-	// because pax headers have a defined utf-8 encoding.
312
-	fileinfo, err := os.Stat("testdata/small.txt")
313
-	if err != nil {
314
-		t.Fatal(err)
315
-	}
316
-
317
-	hdr, err := FileInfoHeader(fileinfo, "")
318
-	if err != nil {
319
-		t.Fatalf("os.Stat:1 %v", err)
320
-	}
321
-
322
-	// some sample data
323
-	chineseFilename := "文件名"
324
-	chineseGroupname := "組"
325
-	chineseUsername := "用戶名"
326
-
327
-	hdr.Name = chineseFilename
328
-	hdr.Gname = chineseGroupname
329
-	hdr.Uname = chineseUsername
330
-
331
-	contents := strings.Repeat(" ", int(hdr.Size))
332
-
333
-	var buf bytes.Buffer
334
-	writer := NewWriter(&buf)
335
-	if err := writer.WriteHeader(hdr); err != nil {
336
-		t.Fatal(err)
337
-	}
338
-	if _, err = writer.Write([]byte(contents)); err != nil {
339
-		t.Fatal(err)
340
-	}
341
-	if err := writer.Close(); err != nil {
342
-		t.Fatal(err)
343
-	}
344
-	// Simple test to make sure PAX extensions are in effect
345
-	if !bytes.Contains(buf.Bytes(), []byte("PaxHeaders.")) {
346
-		t.Fatal("Expected at least one PAX header to be written.")
347
-	}
348
-	// Test that we can get a long name back out of the archive.
349
-	reader := NewReader(&buf)
350
-	hdr, err = reader.Next()
351
-	if err != nil {
352
-		t.Fatal(err)
353
-	}
354
-	if hdr.Name != chineseFilename {
355
-		t.Fatal("Couldn't recover unicode name")
356
-	}
357
-	if hdr.Gname != chineseGroupname {
358
-		t.Fatal("Couldn't recover unicode group")
359
-	}
360
-	if hdr.Uname != chineseUsername {
361
-		t.Fatal("Couldn't recover unicode user")
362
-	}
363
-}
364
-
365
-func TestPaxXattrs(t *testing.T) {
366
-	xattrs := map[string]string{
367
-		"user.key": "value",
368
-	}
369
-
370
-	// Create an archive with an xattr
371
-	fileinfo, err := os.Stat("testdata/small.txt")
372
-	if err != nil {
373
-		t.Fatal(err)
374
-	}
375
-	hdr, err := FileInfoHeader(fileinfo, "")
376
-	if err != nil {
377
-		t.Fatalf("os.Stat: %v", err)
378
-	}
379
-	contents := "Kilts"
380
-	hdr.Xattrs = xattrs
381
-	var buf bytes.Buffer
382
-	writer := NewWriter(&buf)
383
-	if err := writer.WriteHeader(hdr); err != nil {
384
-		t.Fatal(err)
385
-	}
386
-	if _, err = writer.Write([]byte(contents)); err != nil {
387
-		t.Fatal(err)
388
-	}
389
-	if err := writer.Close(); err != nil {
390
-		t.Fatal(err)
391
-	}
392
-	// Test that we can get the xattrs back out of the archive.
393
-	reader := NewReader(&buf)
394
-	hdr, err = reader.Next()
395
-	if err != nil {
396
-		t.Fatal(err)
397
-	}
398
-	if !reflect.DeepEqual(hdr.Xattrs, xattrs) {
399
-		t.Fatalf("xattrs did not survive round trip: got %+v, want %+v",
400
-			hdr.Xattrs, xattrs)
401
-	}
402
-}
403
-
404
-func TestPAXHeader(t *testing.T) {
405
-	medName := strings.Repeat("CD", 50)
406
-	longName := strings.Repeat("AB", 100)
407
-	paxTests := [][2]string{
408
-		{paxPath + "=/etc/hosts", "19 path=/etc/hosts\n"},
409
-		{"a=b", "6 a=b\n"},          // Single digit length
410
-		{"a=names", "11 a=names\n"}, // Test case involving carries
411
-		{paxPath + "=" + longName, fmt.Sprintf("210 path=%s\n", longName)},
412
-		{paxPath + "=" + medName, fmt.Sprintf("110 path=%s\n", medName)}}
413
-
414
-	for _, test := range paxTests {
415
-		key, expected := test[0], test[1]
416
-		if result := paxHeader(key); result != expected {
417
-			t.Fatalf("paxHeader: got %s, expected %s", result, expected)
418
-		}
419
-	}
420
-}
421
-
422
-func TestUSTARLongName(t *testing.T) {
423
-	// Create an archive with a path that failed to split with USTAR extension in previous versions.
424
-	fileinfo, err := os.Stat("testdata/small.txt")
425
-	if err != nil {
426
-		t.Fatal(err)
427
-	}
428
-	hdr, err := FileInfoHeader(fileinfo, "")
429
-	hdr.Typeflag = TypeDir
430
-	if err != nil {
431
-		t.Fatalf("os.Stat:1 %v", err)
432
-	}
433
-	// Force a PAX long name to be written. The name was taken from a practical example
434
-	// that fails and replaced ever char through numbers to anonymize the sample.
435
-	longName := "/0000_0000000/00000-000000000/0000_0000000/00000-0000000000000/0000_0000000/00000-0000000-00000000/0000_0000000/00000000/0000_0000000/000/0000_0000000/00000000v00/0000_0000000/000000/0000_0000000/0000000/0000_0000000/00000y-00/0000/0000/00000000/0x000000/"
436
-	hdr.Name = longName
437
-
438
-	hdr.Size = 0
439
-	var buf bytes.Buffer
440
-	writer := NewWriter(&buf)
441
-	if err := writer.WriteHeader(hdr); err != nil {
442
-		t.Fatal(err)
443
-	}
444
-	if err := writer.Close(); err != nil {
445
-		t.Fatal(err)
446
-	}
447
-	// Test that we can get a long name back out of the archive.
448
-	reader := NewReader(&buf)
449
-	hdr, err = reader.Next()
450
-	if err != nil {
451
-		t.Fatal(err)
452
-	}
453
-	if hdr.Name != longName {
454
-		t.Fatal("Couldn't recover long name")
455
-	}
456
-}
457
-
458
-func TestValidTypeflagWithPAXHeader(t *testing.T) {
459
-	var buffer bytes.Buffer
460
-	tw := NewWriter(&buffer)
461
-
462
-	fileName := strings.Repeat("ab", 100)
463
-
464
-	hdr := &Header{
465
-		Name:     fileName,
466
-		Size:     4,
467
-		Typeflag: 0,
468
-	}
469
-	if err := tw.WriteHeader(hdr); err != nil {
470
-		t.Fatalf("Failed to write header: %s", err)
471
-	}
472
-	if _, err := tw.Write([]byte("fooo")); err != nil {
473
-		t.Fatalf("Failed to write the file's data: %s", err)
474
-	}
475
-	tw.Close()
476
-
477
-	tr := NewReader(&buffer)
478
-
479
-	for {
480
-		header, err := tr.Next()
481
-		if err == io.EOF {
482
-			break
483
-		}
484
-		if err != nil {
485
-			t.Fatalf("Failed to read header: %s", err)
486
-		}
487
-		if header.Typeflag != 0 {
488
-			t.Fatalf("Typeflag should've been 0, found %d", header.Typeflag)
489
-		}
490
-	}
491
-}