Browse code

Remove local fork of archive/tar package

A copy of Go's archive/tar packge was vendored with a patch applied to
mitigate CVE-2019-14271. Vendoring standard library packages is not
supported by Go in module-aware mode, which is getting in the way of
maintenance. A different approach to mitigate the vulnerability is
needed which does not involve vendoring parts of the standard library.

glibc implements name service lookups such as users, groups and DNS
using a scheme known as Name Service Switch. The services are
implemented as modules, shared libraries which glibc dynamically links
into the process the first time a function requiring the module is
called. This is the crux of the vulnerability: if a process linked
against glibc chroots, then calls one of the functions implemented with
NSS for the first time, glibc may load NSS modules out of the chrooted
filesystem.

The API underlying the `docker cp` command is implemented by forking a
new process which chroots into the container's rootfs and writes a tar
stream of files from the container over standard output. It utilizes the
Go standard library's archive/tar package to write the tar stream. It
makes use of the tar.FileInfoHeader function to construct a tar.Header
value from an fs.FileInfo value. In modern versions of Go on *nix
platforms, FileInfoHeader will attempt to resolve the file's UID and GID
to their respective user and group names by calling the os/user
functions LookupId and LookupGroupId. The cgo implementation of os/user
on *nix performs lookups by calling the corresponding libc functions. So
when linked against glibc, calls to tar.FileInfoHeader after the
process has chrooted into the container's rootfs can have the side
effect of loading NSS modules from the container! Without any
mitigations, a malicious container image author can trivially get
arbitrary code execution by leveraging this vulnerability and escape the
chroot (which is not a sandbox) into the host.

Mitigate the vulnerability without patching or forking archive/tar by
hiding the OS-dependent file info from tar.FileInfoHeader which it needs
to perform the lookups. Without that information available it falls back
to populating the tar.Header with only the information obtainable
directly from the FileInfo value without making any calls into os/user.

Fixes #42402

Signed-off-by: Cory Snider <csnider@mirantis.com>

Cory Snider authored on 2022/01/25 05:33:54
Showing 64 changed files
... ...
@@ -2,7 +2,6 @@
2 2
 
3 3
 ARG CROSS="false"
4 4
 ARG SYSTEMD="false"
5
-# IMPORTANT: When updating this please note that stdlib archive/tar pkg is vendored
6 5
 ARG GO_VERSION=1.17.7
7 6
 ARG DEBIAN_FRONTEND=noninteractive
8 7
 ARG VPNKIT_VERSION=0.5.0
... ...
@@ -10,19 +10,5 @@ set -x
10 10
 SCRIPTDIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11 11
 "${SCRIPTDIR}"/go-mod-prepare.sh
12 12
 
13
-if [ $# -eq 0 ] || [ "$1" != "archive/tar" ]; then
14
-	GO111MODULE=auto go mod tidy -modfile 'vendor.mod' -compat 1.17
15
-	GO111MODULE=auto go mod vendor -modfile vendor.mod
16
-fi
17
-
18
-if [ $# -eq 0 ] || [ "$1" = "archive/tar" ]; then
19
-	echo "update vendored copy of archive/tar"
20
-	: "${GO_VERSION:=$(awk -F '[ =]' '$1 == "ARG" && $2 == "GO_VERSION" { print $3; exit }' ./Dockerfile)}"
21
-	rm -rf vendor/archive/tar
22
-	mkdir -p vendor/archive/tar
23
-	echo "downloading: https://golang.org/dl/go${GO_VERSION%.0}.src.tar.gz"
24
-	curl -fsSL "https://golang.org/dl/go${GO_VERSION%.0}.src.tar.gz" \
25
-		| tar --extract --gzip --directory=vendor/archive/tar --strip-components=4 go/src/archive/tar
26
-	patch --strip=4 --directory=vendor/archive/tar --input="$PWD/patches/0001-archive-tar-do-not-populate-user-group-names.patch"
27
-fi
28
-
13
+GO111MODULE=auto go mod tidy -modfile 'vendor.mod' -compat 1.17
14
+GO111MODULE=auto go mod vendor -modfile vendor.mod
29 15
deleted file mode 100644
... ...
@@ -1,75 +0,0 @@
1
-From bc0de86b495ae014b209431ed7cb90fc3d5e4d1f Mon Sep 17 00:00:00 2001
2
-From: Kir Kolyshkin <kolyshkin@gmail.com>
3
-Date: Mon, 9 Apr 2018 15:58:40 -0700
4
-Subject: [PATCH] archive/tar: do not populate user/group names
5
-
6
-This reverts part of commit 29a18899379c ("archive/tar: populate
7
-uname/gname/devmajor/devminor in FileInfoHeader"). The reason is
8
-using os/user functions to resolved uids/gids to names breaks
9
-the static build for Linux/glibc case (the resulting binary panics
10
-on NULL pointer dereference).
11
-
12
-For much more details, see https://github.com/golang/go/issues/23265
13
-
14
-Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
15
- src/archive/tar/stat_unix.go | 26 +++-----------------------
16
- 1 file changed, 3 insertions(+), 23 deletions(-)
17
-
18
-diff --git a/src/archive/tar/stat_unix.go b/src/archive/tar/stat_unix.go
19
-index 868105f338..9640ed4bab 100644
20
-+++ b/src/archive/tar/stat_unix.go
21
-@@ -8,10 +8,7 @@ package tar
22
- 
23
- import (
24
-	"io/fs"
25
--	"os/user"
26
- 	"runtime"
27
--	"strconv"
28
--	"sync"
29
- 	"syscall"
30
- )
31
- 
32
-@@ -19,10 +16,6 @@ func init() {
33
- 	sysStat = statUnix
34
- }
35
- 
36
--// userMap and groupMap caches UID and GID lookups for performance reasons.
37
--// The downside is that renaming uname or gname by the OS never takes effect.
38
--var userMap, groupMap sync.Map // map[int]string
39
--
40
- func statUnix(fi fs.FileInfo, h *Header) error {
41
- 	sys, ok := fi.Sys().(*syscall.Stat_t)
42
- 	if !ok {
43
-@@ -31,22 +24,9 @@ func statUnix(fi os.FileInfo, h *Header) error {
44
- 	h.Uid = int(sys.Uid)
45
- 	h.Gid = int(sys.Gid)
46
- 
47
--	// Best effort at populating Uname and Gname.
48
--	// The os/user functions may fail for any number of reasons
49
--	// (not implemented on that platform, cgo not enabled, etc).
50
--	if u, ok := userMap.Load(h.Uid); ok {
51
--		h.Uname = u.(string)
52
--	} else if u, err := user.LookupId(strconv.Itoa(h.Uid)); err == nil {
53
--		h.Uname = u.Username
54
--		userMap.Store(h.Uid, h.Uname)
55
--	}
56
--	if g, ok := groupMap.Load(h.Gid); ok {
57
--		h.Gname = g.(string)
58
--	} else if g, err := user.LookupGroupId(strconv.Itoa(h.Gid)); err == nil {
59
--		h.Gname = g.Name
60
--		groupMap.Store(h.Gid, h.Gname)
61
--	}
62
--
63
-+	// TODO(bradfitz): populate username & group.  os/user
64
-+	// doesn't cache LookupId lookups, and lacks group
65
-+	// lookup functions.
66
- 	h.AccessTime = statAtime(sys)
67
- 	h.ChangeTime = statCtime(sys)
68
- 
69
-
70
-base-commit: 4af1337d1e9eb9e7b766c9deb787c78413bb25c4
71
-2.24.1
72
-
... ...
@@ -403,12 +403,64 @@ func (compression *Compression) Extension() string {
403 403
 	return ""
404 404
 }
405 405
 
406
+// nosysFileInfo hides the system-dependent info of the wrapped FileInfo to
407
+// prevent tar.FileInfoHeader from introspecting it and potentially calling into
408
+// glibc.
409
+type nosysFileInfo struct {
410
+	os.FileInfo
411
+}
412
+
413
+func (fi nosysFileInfo) Sys() interface{} {
414
+	// A Sys value of type *tar.Header is safe as it is system-independent.
415
+	// The tar.FileInfoHeader function copies the fields into the returned
416
+	// header without performing any OS lookups.
417
+	if sys, ok := fi.FileInfo.Sys().(*tar.Header); ok {
418
+		return sys
419
+	}
420
+	return nil
421
+}
422
+
423
+// sysStat, if non-nil, populates hdr from system-dependent fields of fi.
424
+var sysStat func(fi os.FileInfo, hdr *tar.Header) error
425
+
426
+// FileInfoHeaderNoLookups creates a partially-populated tar.Header from fi.
427
+//
428
+// Compared to the archive/tar.FileInfoHeader function, this function is safe to
429
+// call from a chrooted process as it does not populate fields which would
430
+// require operating system lookups. It behaves identically to
431
+// tar.FileInfoHeader when fi is a FileInfo value returned from
432
+// tar.Header.FileInfo().
433
+//
434
+// When fi is a FileInfo for a native file, such as returned from os.Stat() and
435
+// os.Lstat(), the returned Header value differs from one returned from
436
+// tar.FileInfoHeader in the following ways. The Uname and Gname fields are not
437
+// set as OS lookups would be required to populate them. The AccessTime and
438
+// ChangeTime fields are not currently set (not yet implemented) although that
439
+// is subject to change. Callers which require the AccessTime or ChangeTime
440
+// fields to be zeroed should explicitly zero them out in the returned Header
441
+// value to avoid any compatibility issues in the future.
442
+func FileInfoHeaderNoLookups(fi os.FileInfo, link string) (*tar.Header, error) {
443
+	hdr, err := tar.FileInfoHeader(nosysFileInfo{fi}, link)
444
+	if err != nil {
445
+		return nil, err
446
+	}
447
+	if sysStat != nil {
448
+		return hdr, sysStat(fi, hdr)
449
+	}
450
+	return hdr, nil
451
+}
452
+
406 453
 // FileInfoHeader creates a populated Header from fi.
407
-// Compared to archive pkg this function fills in more information.
408
-// Also, regardless of Go version, this function fills file type bits (e.g. hdr.Mode |= modeISDIR),
409
-// which have been deleted since Go 1.9 archive/tar.
454
+//
455
+// Compared to the archive/tar package, this function fills in less information
456
+// but is safe to call from a chrooted process. The AccessTime and ChangeTime
457
+// fields are not set in the returned header, ModTime is truncated to one-second
458
+// precision, and the Uname and Gname fields are only set when fi is a FileInfo
459
+// value returned from tar.Header.FileInfo(). Also, regardless of Go version,
460
+// this function fills file type bits (e.g. hdr.Mode |= modeISDIR), which have
461
+// been deleted since Go 1.9 archive/tar.
410 462
 func FileInfoHeader(name string, fi os.FileInfo, link string) (*tar.Header, error) {
411
-	hdr, err := tar.FileInfoHeader(fi, link)
463
+	hdr, err := FileInfoHeaderNoLookups(fi, link)
412 464
 	if err != nil {
413 465
 		return nil, err
414 466
 	}
... ...
@@ -418,9 +470,6 @@ func FileInfoHeader(name string, fi os.FileInfo, link string) (*tar.Header, erro
418 418
 	hdr.ChangeTime = time.Time{}
419 419
 	hdr.Mode = fillGo18FileTypeBits(int64(chmodTarEntry(os.FileMode(hdr.Mode))), fi)
420 420
 	hdr.Name = canonicalTarName(name, fi.IsDir())
421
-	if err := setHeaderForSpecialDevice(hdr, name, fi.Sys()); err != nil {
422
-		return nil, err
423
-	}
424 421
 	return hdr, nil
425 422
 }
426 423
 
... ...
@@ -1251,7 +1300,7 @@ func (archiver *Archiver) CopyFileWithTar(src, dst string) (err error) {
1251 1251
 			}
1252 1252
 			defer srcF.Close()
1253 1253
 
1254
-			hdr, err := tar.FileInfoHeader(srcSt, "")
1254
+			hdr, err := FileInfoHeaderNoLookups(srcSt, "")
1255 1255
 			if err != nil {
1256 1256
 				return err
1257 1257
 			}
... ...
@@ -1402,3 +1402,12 @@ func TestPigz(t *testing.T) {
1402 1402
 		assert.Equal(t, reflect.TypeOf(contextReaderCloserWrapper.Reader), reflect.TypeOf(&gzip.Reader{}))
1403 1403
 	}
1404 1404
 }
1405
+
1406
+func TestNosysFileInfo(t *testing.T) {
1407
+	st, err := os.Stat("archive_test.go")
1408
+	assert.NilError(t, err)
1409
+	h, err := tar.FileInfoHeader(nosysFileInfo{st}, "")
1410
+	assert.NilError(t, err)
1411
+	assert.Check(t, h.Uname == "")
1412
+	assert.Check(t, h.Gname == "")
1413
+}
... ...
@@ -17,6 +17,10 @@ import (
17 17
 	"golang.org/x/sys/unix"
18 18
 )
19 19
 
20
+func init() {
21
+	sysStat = statUnix
22
+}
23
+
20 24
 // fixVolumePathPrefix does platform specific processing to ensure that if
21 25
 // the path being passed in is not in a volume path format, convert it to one.
22 26
 func fixVolumePathPrefix(srcPath string) string {
... ...
@@ -45,19 +49,24 @@ func chmodTarEntry(perm os.FileMode) os.FileMode {
45 45
 	return perm // noop for unix as golang APIs provide perm bits correctly
46 46
 }
47 47
 
48
-func setHeaderForSpecialDevice(hdr *tar.Header, name string, stat interface{}) (err error) {
49
-	s, ok := stat.(*syscall.Stat_t)
48
+// statUnix populates hdr from system-dependent fields of fi without performing
49
+// any OS lookups.
50
+func statUnix(fi os.FileInfo, hdr *tar.Header) error {
51
+	s, ok := fi.Sys().(*syscall.Stat_t)
52
+	if !ok {
53
+		return nil
54
+	}
50 55
 
51
-	if ok {
52
-		// Currently go does not fill in the major/minors
53
-		if s.Mode&unix.S_IFBLK != 0 ||
54
-			s.Mode&unix.S_IFCHR != 0 {
55
-			hdr.Devmajor = int64(unix.Major(uint64(s.Rdev))) //nolint: unconvert
56
-			hdr.Devminor = int64(unix.Minor(uint64(s.Rdev))) //nolint: unconvert
57
-		}
56
+	hdr.Uid = int(s.Uid)
57
+	hdr.Gid = int(s.Gid)
58
+
59
+	if s.Mode&unix.S_IFBLK != 0 ||
60
+		s.Mode&unix.S_IFCHR != 0 {
61
+		hdr.Devmajor = int64(unix.Major(uint64(s.Rdev))) //nolint: unconvert
62
+		hdr.Devminor = int64(unix.Minor(uint64(s.Rdev))) //nolint: unconvert
58 63
 	}
59 64
 
60
-	return
65
+	return nil
61 66
 }
62 67
 
63 68
 func getInodeFromStat(stat interface{}) (inode uint64, err error) {
... ...
@@ -137,7 +137,7 @@ func (archiver *Archiver) CopyFileWithTar(src, dst string) (retErr error) {
137 137
 			}
138 138
 			defer srcF.Close()
139 139
 
140
-			hdr, err := tar.FileInfoHeader(srcSt, "")
140
+			hdr, err := archive.FileInfoHeaderNoLookups(srcSt, "")
141 141
 			if err != nil {
142 142
 				return err
143 143
 			}
144 144
deleted file mode 100644
... ...
@@ -1,723 +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
-//
7
-// Tape archives (tar) are a file format for storing a sequence of files that
8
-// can be read and written in a streaming manner.
9
-// This package aims to cover most variations of the format,
10
-// including those produced by GNU and BSD tar tools.
11
-package tar
12
-
13
-import (
14
-	"errors"
15
-	"fmt"
16
-	"io/fs"
17
-	"math"
18
-	"path"
19
-	"reflect"
20
-	"strconv"
21
-	"strings"
22
-	"time"
23
-)
24
-
25
-// BUG: Use of the Uid and Gid fields in Header could overflow on 32-bit
26
-// architectures. If a large value is encountered when decoding, the result
27
-// stored in Header will be the truncated version.
28
-
29
-var (
30
-	ErrHeader          = errors.New("archive/tar: invalid tar header")
31
-	ErrWriteTooLong    = errors.New("archive/tar: write too long")
32
-	ErrFieldTooLong    = errors.New("archive/tar: header field too long")
33
-	ErrWriteAfterClose = errors.New("archive/tar: write after close")
34
-	errMissData        = errors.New("archive/tar: sparse file references non-existent data")
35
-	errUnrefData       = errors.New("archive/tar: sparse file contains unreferenced data")
36
-	errWriteHole       = errors.New("archive/tar: write non-NUL byte in sparse hole")
37
-)
38
-
39
-type headerError []string
40
-
41
-func (he headerError) Error() string {
42
-	const prefix = "archive/tar: cannot encode header"
43
-	var ss []string
44
-	for _, s := range he {
45
-		if s != "" {
46
-			ss = append(ss, s)
47
-		}
48
-	}
49
-	if len(ss) == 0 {
50
-		return prefix
51
-	}
52
-	return fmt.Sprintf("%s: %v", prefix, strings.Join(ss, "; and "))
53
-}
54
-
55
-// Type flags for Header.Typeflag.
56
-const (
57
-	// Type '0' indicates a regular file.
58
-	TypeReg  = '0'
59
-	TypeRegA = '\x00' // Deprecated: Use TypeReg instead.
60
-
61
-	// Type '1' to '6' are header-only flags and may not have a data body.
62
-	TypeLink    = '1' // Hard link
63
-	TypeSymlink = '2' // Symbolic link
64
-	TypeChar    = '3' // Character device node
65
-	TypeBlock   = '4' // Block device node
66
-	TypeDir     = '5' // Directory
67
-	TypeFifo    = '6' // FIFO node
68
-
69
-	// Type '7' is reserved.
70
-	TypeCont = '7'
71
-
72
-	// Type 'x' is used by the PAX format to store key-value records that
73
-	// are only relevant to the next file.
74
-	// This package transparently handles these types.
75
-	TypeXHeader = 'x'
76
-
77
-	// Type 'g' is used by the PAX format to store key-value records that
78
-	// are relevant to all subsequent files.
79
-	// This package only supports parsing and composing such headers,
80
-	// but does not currently support persisting the global state across files.
81
-	TypeXGlobalHeader = 'g'
82
-
83
-	// Type 'S' indicates a sparse file in the GNU format.
84
-	TypeGNUSparse = 'S'
85
-
86
-	// Types 'L' and 'K' are used by the GNU format for a meta file
87
-	// used to store the path or link name for the next file.
88
-	// This package transparently handles these types.
89
-	TypeGNULongName = 'L'
90
-	TypeGNULongLink = 'K'
91
-)
92
-
93
-// Keywords for PAX extended header records.
94
-const (
95
-	paxNone     = "" // Indicates that no PAX key is suitable
96
-	paxPath     = "path"
97
-	paxLinkpath = "linkpath"
98
-	paxSize     = "size"
99
-	paxUid      = "uid"
100
-	paxGid      = "gid"
101
-	paxUname    = "uname"
102
-	paxGname    = "gname"
103
-	paxMtime    = "mtime"
104
-	paxAtime    = "atime"
105
-	paxCtime    = "ctime"   // Removed from later revision of PAX spec, but was valid
106
-	paxCharset  = "charset" // Currently unused
107
-	paxComment  = "comment" // Currently unused
108
-
109
-	paxSchilyXattr = "SCHILY.xattr."
110
-
111
-	// Keywords for GNU sparse files in a PAX extended header.
112
-	paxGNUSparse          = "GNU.sparse."
113
-	paxGNUSparseNumBlocks = "GNU.sparse.numblocks"
114
-	paxGNUSparseOffset    = "GNU.sparse.offset"
115
-	paxGNUSparseNumBytes  = "GNU.sparse.numbytes"
116
-	paxGNUSparseMap       = "GNU.sparse.map"
117
-	paxGNUSparseName      = "GNU.sparse.name"
118
-	paxGNUSparseMajor     = "GNU.sparse.major"
119
-	paxGNUSparseMinor     = "GNU.sparse.minor"
120
-	paxGNUSparseSize      = "GNU.sparse.size"
121
-	paxGNUSparseRealSize  = "GNU.sparse.realsize"
122
-)
123
-
124
-// basicKeys is a set of the PAX keys for which we have built-in support.
125
-// This does not contain "charset" or "comment", which are both PAX-specific,
126
-// so adding them as first-class features of Header is unlikely.
127
-// Users can use the PAXRecords field to set it themselves.
128
-var basicKeys = map[string]bool{
129
-	paxPath: true, paxLinkpath: true, paxSize: true, paxUid: true, paxGid: true,
130
-	paxUname: true, paxGname: true, paxMtime: true, paxAtime: true, paxCtime: true,
131
-}
132
-
133
-// A Header represents a single header in a tar archive.
134
-// Some fields may not be populated.
135
-//
136
-// For forward compatibility, users that retrieve a Header from Reader.Next,
137
-// mutate it in some ways, and then pass it back to Writer.WriteHeader
138
-// should do so by creating a new Header and copying the fields
139
-// that they are interested in preserving.
140
-type Header struct {
141
-	// Typeflag is the type of header entry.
142
-	// The zero value is automatically promoted to either TypeReg or TypeDir
143
-	// depending on the presence of a trailing slash in Name.
144
-	Typeflag byte
145
-
146
-	Name     string // Name of file entry
147
-	Linkname string // Target name of link (valid for TypeLink or TypeSymlink)
148
-
149
-	Size  int64  // Logical file size in bytes
150
-	Mode  int64  // Permission and mode bits
151
-	Uid   int    // User ID of owner
152
-	Gid   int    // Group ID of owner
153
-	Uname string // User name of owner
154
-	Gname string // Group name of owner
155
-
156
-	// If the Format is unspecified, then Writer.WriteHeader rounds ModTime
157
-	// to the nearest second and ignores the AccessTime and ChangeTime fields.
158
-	//
159
-	// To use AccessTime or ChangeTime, specify the Format as PAX or GNU.
160
-	// To use sub-second resolution, specify the Format as PAX.
161
-	ModTime    time.Time // Modification time
162
-	AccessTime time.Time // Access time (requires either PAX or GNU support)
163
-	ChangeTime time.Time // Change time (requires either PAX or GNU support)
164
-
165
-	Devmajor int64 // Major device number (valid for TypeChar or TypeBlock)
166
-	Devminor int64 // Minor device number (valid for TypeChar or TypeBlock)
167
-
168
-	// Xattrs stores extended attributes as PAX records under the
169
-	// "SCHILY.xattr." namespace.
170
-	//
171
-	// The following are semantically equivalent:
172
-	//  h.Xattrs[key] = value
173
-	//  h.PAXRecords["SCHILY.xattr."+key] = value
174
-	//
175
-	// When Writer.WriteHeader is called, the contents of Xattrs will take
176
-	// precedence over those in PAXRecords.
177
-	//
178
-	// Deprecated: Use PAXRecords instead.
179
-	Xattrs map[string]string
180
-
181
-	// PAXRecords is a map of PAX extended header records.
182
-	//
183
-	// User-defined records should have keys of the following form:
184
-	//	VENDOR.keyword
185
-	// Where VENDOR is some namespace in all uppercase, and keyword may
186
-	// not contain the '=' character (e.g., "GOLANG.pkg.version").
187
-	// The key and value should be non-empty UTF-8 strings.
188
-	//
189
-	// When Writer.WriteHeader is called, PAX records derived from the
190
-	// other fields in Header take precedence over PAXRecords.
191
-	PAXRecords map[string]string
192
-
193
-	// Format specifies the format of the tar header.
194
-	//
195
-	// This is set by Reader.Next as a best-effort guess at the format.
196
-	// Since the Reader liberally reads some non-compliant files,
197
-	// it is possible for this to be FormatUnknown.
198
-	//
199
-	// If the format is unspecified when Writer.WriteHeader is called,
200
-	// then it uses the first format (in the order of USTAR, PAX, GNU)
201
-	// capable of encoding this Header (see Format).
202
-	Format Format
203
-}
204
-
205
-// sparseEntry represents a Length-sized fragment at Offset in the file.
206
-type sparseEntry struct{ Offset, Length int64 }
207
-
208
-func (s sparseEntry) endOffset() int64 { return s.Offset + s.Length }
209
-
210
-// A sparse file can be represented as either a sparseDatas or a sparseHoles.
211
-// As long as the total size is known, they are equivalent and one can be
212
-// converted to the other form and back. The various tar formats with sparse
213
-// file support represent sparse files in the sparseDatas form. That is, they
214
-// specify the fragments in the file that has data, and treat everything else as
215
-// having zero bytes. As such, the encoding and decoding logic in this package
216
-// deals with sparseDatas.
217
-//
218
-// However, the external API uses sparseHoles instead of sparseDatas because the
219
-// zero value of sparseHoles logically represents a normal file (i.e., there are
220
-// no holes in it). On the other hand, the zero value of sparseDatas implies
221
-// that the file has no data in it, which is rather odd.
222
-//
223
-// As an example, if the underlying raw file contains the 10-byte data:
224
-//	var compactFile = "abcdefgh"
225
-//
226
-// And the sparse map has the following entries:
227
-//	var spd sparseDatas = []sparseEntry{
228
-//		{Offset: 2,  Length: 5},  // Data fragment for 2..6
229
-//		{Offset: 18, Length: 3},  // Data fragment for 18..20
230
-//	}
231
-//	var sph sparseHoles = []sparseEntry{
232
-//		{Offset: 0,  Length: 2},  // Hole fragment for 0..1
233
-//		{Offset: 7,  Length: 11}, // Hole fragment for 7..17
234
-//		{Offset: 21, Length: 4},  // Hole fragment for 21..24
235
-//	}
236
-//
237
-// Then the content of the resulting sparse file with a Header.Size of 25 is:
238
-//	var sparseFile = "\x00"*2 + "abcde" + "\x00"*11 + "fgh" + "\x00"*4
239
-type (
240
-	sparseDatas []sparseEntry
241
-	sparseHoles []sparseEntry
242
-)
243
-
244
-// validateSparseEntries reports whether sp is a valid sparse map.
245
-// It does not matter whether sp represents data fragments or hole fragments.
246
-func validateSparseEntries(sp []sparseEntry, size int64) bool {
247
-	// Validate all sparse entries. These are the same checks as performed by
248
-	// the BSD tar utility.
249
-	if size < 0 {
250
-		return false
251
-	}
252
-	var pre sparseEntry
253
-	for _, cur := range sp {
254
-		switch {
255
-		case cur.Offset < 0 || cur.Length < 0:
256
-			return false // Negative values are never okay
257
-		case cur.Offset > math.MaxInt64-cur.Length:
258
-			return false // Integer overflow with large length
259
-		case cur.endOffset() > size:
260
-			return false // Region extends beyond the actual size
261
-		case pre.endOffset() > cur.Offset:
262
-			return false // Regions cannot overlap and must be in order
263
-		}
264
-		pre = cur
265
-	}
266
-	return true
267
-}
268
-
269
-// alignSparseEntries mutates src and returns dst where each fragment's
270
-// starting offset is aligned up to the nearest block edge, and each
271
-// ending offset is aligned down to the nearest block edge.
272
-//
273
-// Even though the Go tar Reader and the BSD tar utility can handle entries
274
-// with arbitrary offsets and lengths, the GNU tar utility can only handle
275
-// offsets and lengths that are multiples of blockSize.
276
-func alignSparseEntries(src []sparseEntry, size int64) []sparseEntry {
277
-	dst := src[:0]
278
-	for _, s := range src {
279
-		pos, end := s.Offset, s.endOffset()
280
-		pos += blockPadding(+pos) // Round-up to nearest blockSize
281
-		if end != size {
282
-			end -= blockPadding(-end) // Round-down to nearest blockSize
283
-		}
284
-		if pos < end {
285
-			dst = append(dst, sparseEntry{Offset: pos, Length: end - pos})
286
-		}
287
-	}
288
-	return dst
289
-}
290
-
291
-// invertSparseEntries converts a sparse map from one form to the other.
292
-// If the input is sparseHoles, then it will output sparseDatas and vice-versa.
293
-// The input must have been already validated.
294
-//
295
-// This function mutates src and returns a normalized map where:
296
-//	* adjacent fragments are coalesced together
297
-//	* only the last fragment may be empty
298
-//	* the endOffset of the last fragment is the total size
299
-func invertSparseEntries(src []sparseEntry, size int64) []sparseEntry {
300
-	dst := src[:0]
301
-	var pre sparseEntry
302
-	for _, cur := range src {
303
-		if cur.Length == 0 {
304
-			continue // Skip empty fragments
305
-		}
306
-		pre.Length = cur.Offset - pre.Offset
307
-		if pre.Length > 0 {
308
-			dst = append(dst, pre) // Only add non-empty fragments
309
-		}
310
-		pre.Offset = cur.endOffset()
311
-	}
312
-	pre.Length = size - pre.Offset // Possibly the only empty fragment
313
-	return append(dst, pre)
314
-}
315
-
316
-// fileState tracks the number of logical (includes sparse holes) and physical
317
-// (actual in tar archive) bytes remaining for the current file.
318
-//
319
-// Invariant: LogicalRemaining >= PhysicalRemaining
320
-type fileState interface {
321
-	LogicalRemaining() int64
322
-	PhysicalRemaining() int64
323
-}
324
-
325
-// allowedFormats determines which formats can be used.
326
-// The value returned is the logical OR of multiple possible formats.
327
-// If the value is FormatUnknown, then the input Header cannot be encoded
328
-// and an error is returned explaining why.
329
-//
330
-// As a by-product of checking the fields, this function returns paxHdrs, which
331
-// contain all fields that could not be directly encoded.
332
-// A value receiver ensures that this method does not mutate the source Header.
333
-func (h Header) allowedFormats() (format Format, paxHdrs map[string]string, err error) {
334
-	format = FormatUSTAR | FormatPAX | FormatGNU
335
-	paxHdrs = make(map[string]string)
336
-
337
-	var whyNoUSTAR, whyNoPAX, whyNoGNU string
338
-	var preferPAX bool // Prefer PAX over USTAR
339
-	verifyString := func(s string, size int, name, paxKey string) {
340
-		// NUL-terminator is optional for path and linkpath.
341
-		// Technically, it is required for uname and gname,
342
-		// but neither GNU nor BSD tar checks for it.
343
-		tooLong := len(s) > size
344
-		allowLongGNU := paxKey == paxPath || paxKey == paxLinkpath
345
-		if hasNUL(s) || (tooLong && !allowLongGNU) {
346
-			whyNoGNU = fmt.Sprintf("GNU cannot encode %s=%q", name, s)
347
-			format.mustNotBe(FormatGNU)
348
-		}
349
-		if !isASCII(s) || tooLong {
350
-			canSplitUSTAR := paxKey == paxPath
351
-			if _, _, ok := splitUSTARPath(s); !canSplitUSTAR || !ok {
352
-				whyNoUSTAR = fmt.Sprintf("USTAR cannot encode %s=%q", name, s)
353
-				format.mustNotBe(FormatUSTAR)
354
-			}
355
-			if paxKey == paxNone {
356
-				whyNoPAX = fmt.Sprintf("PAX cannot encode %s=%q", name, s)
357
-				format.mustNotBe(FormatPAX)
358
-			} else {
359
-				paxHdrs[paxKey] = s
360
-			}
361
-		}
362
-		if v, ok := h.PAXRecords[paxKey]; ok && v == s {
363
-			paxHdrs[paxKey] = v
364
-		}
365
-	}
366
-	verifyNumeric := func(n int64, size int, name, paxKey string) {
367
-		if !fitsInBase256(size, n) {
368
-			whyNoGNU = fmt.Sprintf("GNU cannot encode %s=%d", name, n)
369
-			format.mustNotBe(FormatGNU)
370
-		}
371
-		if !fitsInOctal(size, n) {
372
-			whyNoUSTAR = fmt.Sprintf("USTAR cannot encode %s=%d", name, n)
373
-			format.mustNotBe(FormatUSTAR)
374
-			if paxKey == paxNone {
375
-				whyNoPAX = fmt.Sprintf("PAX cannot encode %s=%d", name, n)
376
-				format.mustNotBe(FormatPAX)
377
-			} else {
378
-				paxHdrs[paxKey] = strconv.FormatInt(n, 10)
379
-			}
380
-		}
381
-		if v, ok := h.PAXRecords[paxKey]; ok && v == strconv.FormatInt(n, 10) {
382
-			paxHdrs[paxKey] = v
383
-		}
384
-	}
385
-	verifyTime := func(ts time.Time, size int, name, paxKey string) {
386
-		if ts.IsZero() {
387
-			return // Always okay
388
-		}
389
-		if !fitsInBase256(size, ts.Unix()) {
390
-			whyNoGNU = fmt.Sprintf("GNU cannot encode %s=%v", name, ts)
391
-			format.mustNotBe(FormatGNU)
392
-		}
393
-		isMtime := paxKey == paxMtime
394
-		fitsOctal := fitsInOctal(size, ts.Unix())
395
-		if (isMtime && !fitsOctal) || !isMtime {
396
-			whyNoUSTAR = fmt.Sprintf("USTAR cannot encode %s=%v", name, ts)
397
-			format.mustNotBe(FormatUSTAR)
398
-		}
399
-		needsNano := ts.Nanosecond() != 0
400
-		if !isMtime || !fitsOctal || needsNano {
401
-			preferPAX = true // USTAR may truncate sub-second measurements
402
-			if paxKey == paxNone {
403
-				whyNoPAX = fmt.Sprintf("PAX cannot encode %s=%v", name, ts)
404
-				format.mustNotBe(FormatPAX)
405
-			} else {
406
-				paxHdrs[paxKey] = formatPAXTime(ts)
407
-			}
408
-		}
409
-		if v, ok := h.PAXRecords[paxKey]; ok && v == formatPAXTime(ts) {
410
-			paxHdrs[paxKey] = v
411
-		}
412
-	}
413
-
414
-	// Check basic fields.
415
-	var blk block
416
-	v7 := blk.V7()
417
-	ustar := blk.USTAR()
418
-	gnu := blk.GNU()
419
-	verifyString(h.Name, len(v7.Name()), "Name", paxPath)
420
-	verifyString(h.Linkname, len(v7.LinkName()), "Linkname", paxLinkpath)
421
-	verifyString(h.Uname, len(ustar.UserName()), "Uname", paxUname)
422
-	verifyString(h.Gname, len(ustar.GroupName()), "Gname", paxGname)
423
-	verifyNumeric(h.Mode, len(v7.Mode()), "Mode", paxNone)
424
-	verifyNumeric(int64(h.Uid), len(v7.UID()), "Uid", paxUid)
425
-	verifyNumeric(int64(h.Gid), len(v7.GID()), "Gid", paxGid)
426
-	verifyNumeric(h.Size, len(v7.Size()), "Size", paxSize)
427
-	verifyNumeric(h.Devmajor, len(ustar.DevMajor()), "Devmajor", paxNone)
428
-	verifyNumeric(h.Devminor, len(ustar.DevMinor()), "Devminor", paxNone)
429
-	verifyTime(h.ModTime, len(v7.ModTime()), "ModTime", paxMtime)
430
-	verifyTime(h.AccessTime, len(gnu.AccessTime()), "AccessTime", paxAtime)
431
-	verifyTime(h.ChangeTime, len(gnu.ChangeTime()), "ChangeTime", paxCtime)
432
-
433
-	// Check for header-only types.
434
-	var whyOnlyPAX, whyOnlyGNU string
435
-	switch h.Typeflag {
436
-	case TypeReg, TypeChar, TypeBlock, TypeFifo, TypeGNUSparse:
437
-		// Exclude TypeLink and TypeSymlink, since they may reference directories.
438
-		if strings.HasSuffix(h.Name, "/") {
439
-			return FormatUnknown, nil, headerError{"filename may not have trailing slash"}
440
-		}
441
-	case TypeXHeader, TypeGNULongName, TypeGNULongLink:
442
-		return FormatUnknown, nil, headerError{"cannot manually encode TypeXHeader, TypeGNULongName, or TypeGNULongLink headers"}
443
-	case TypeXGlobalHeader:
444
-		h2 := Header{Name: h.Name, Typeflag: h.Typeflag, Xattrs: h.Xattrs, PAXRecords: h.PAXRecords, Format: h.Format}
445
-		if !reflect.DeepEqual(h, h2) {
446
-			return FormatUnknown, nil, headerError{"only PAXRecords should be set for TypeXGlobalHeader"}
447
-		}
448
-		whyOnlyPAX = "only PAX supports TypeXGlobalHeader"
449
-		format.mayOnlyBe(FormatPAX)
450
-	}
451
-	if !isHeaderOnlyType(h.Typeflag) && h.Size < 0 {
452
-		return FormatUnknown, nil, headerError{"negative size on header-only type"}
453
-	}
454
-
455
-	// Check PAX records.
456
-	if len(h.Xattrs) > 0 {
457
-		for k, v := range h.Xattrs {
458
-			paxHdrs[paxSchilyXattr+k] = v
459
-		}
460
-		whyOnlyPAX = "only PAX supports Xattrs"
461
-		format.mayOnlyBe(FormatPAX)
462
-	}
463
-	if len(h.PAXRecords) > 0 {
464
-		for k, v := range h.PAXRecords {
465
-			switch _, exists := paxHdrs[k]; {
466
-			case exists:
467
-				continue // Do not overwrite existing records
468
-			case h.Typeflag == TypeXGlobalHeader:
469
-				paxHdrs[k] = v // Copy all records
470
-			case !basicKeys[k] && !strings.HasPrefix(k, paxGNUSparse):
471
-				paxHdrs[k] = v // Ignore local records that may conflict
472
-			}
473
-		}
474
-		whyOnlyPAX = "only PAX supports PAXRecords"
475
-		format.mayOnlyBe(FormatPAX)
476
-	}
477
-	for k, v := range paxHdrs {
478
-		if !validPAXRecord(k, v) {
479
-			return FormatUnknown, nil, headerError{fmt.Sprintf("invalid PAX record: %q", k+" = "+v)}
480
-		}
481
-	}
482
-
483
-	// TODO(dsnet): Re-enable this when adding sparse support.
484
-	// See https://golang.org/issue/22735
485
-	/*
486
-		// Check sparse files.
487
-		if len(h.SparseHoles) > 0 || h.Typeflag == TypeGNUSparse {
488
-			if isHeaderOnlyType(h.Typeflag) {
489
-				return FormatUnknown, nil, headerError{"header-only type cannot be sparse"}
490
-			}
491
-			if !validateSparseEntries(h.SparseHoles, h.Size) {
492
-				return FormatUnknown, nil, headerError{"invalid sparse holes"}
493
-			}
494
-			if h.Typeflag == TypeGNUSparse {
495
-				whyOnlyGNU = "only GNU supports TypeGNUSparse"
496
-				format.mayOnlyBe(FormatGNU)
497
-			} else {
498
-				whyNoGNU = "GNU supports sparse files only with TypeGNUSparse"
499
-				format.mustNotBe(FormatGNU)
500
-			}
501
-			whyNoUSTAR = "USTAR does not support sparse files"
502
-			format.mustNotBe(FormatUSTAR)
503
-		}
504
-	*/
505
-
506
-	// Check desired format.
507
-	if wantFormat := h.Format; wantFormat != FormatUnknown {
508
-		if wantFormat.has(FormatPAX) && !preferPAX {
509
-			wantFormat.mayBe(FormatUSTAR) // PAX implies USTAR allowed too
510
-		}
511
-		format.mayOnlyBe(wantFormat) // Set union of formats allowed and format wanted
512
-	}
513
-	if format == FormatUnknown {
514
-		switch h.Format {
515
-		case FormatUSTAR:
516
-			err = headerError{"Format specifies USTAR", whyNoUSTAR, whyOnlyPAX, whyOnlyGNU}
517
-		case FormatPAX:
518
-			err = headerError{"Format specifies PAX", whyNoPAX, whyOnlyGNU}
519
-		case FormatGNU:
520
-			err = headerError{"Format specifies GNU", whyNoGNU, whyOnlyPAX}
521
-		default:
522
-			err = headerError{whyNoUSTAR, whyNoPAX, whyNoGNU, whyOnlyPAX, whyOnlyGNU}
523
-		}
524
-	}
525
-	return format, paxHdrs, err
526
-}
527
-
528
-// FileInfo returns an fs.FileInfo for the Header.
529
-func (h *Header) FileInfo() fs.FileInfo {
530
-	return headerFileInfo{h}
531
-}
532
-
533
-// headerFileInfo implements fs.FileInfo.
534
-type headerFileInfo struct {
535
-	h *Header
536
-}
537
-
538
-func (fi headerFileInfo) Size() int64        { return fi.h.Size }
539
-func (fi headerFileInfo) IsDir() bool        { return fi.Mode().IsDir() }
540
-func (fi headerFileInfo) ModTime() time.Time { return fi.h.ModTime }
541
-func (fi headerFileInfo) Sys() interface{}   { return fi.h }
542
-
543
-// Name returns the base name of the file.
544
-func (fi headerFileInfo) Name() string {
545
-	if fi.IsDir() {
546
-		return path.Base(path.Clean(fi.h.Name))
547
-	}
548
-	return path.Base(fi.h.Name)
549
-}
550
-
551
-// Mode returns the permission and mode bits for the headerFileInfo.
552
-func (fi headerFileInfo) Mode() (mode fs.FileMode) {
553
-	// Set file permission bits.
554
-	mode = fs.FileMode(fi.h.Mode).Perm()
555
-
556
-	// Set setuid, setgid and sticky bits.
557
-	if fi.h.Mode&c_ISUID != 0 {
558
-		mode |= fs.ModeSetuid
559
-	}
560
-	if fi.h.Mode&c_ISGID != 0 {
561
-		mode |= fs.ModeSetgid
562
-	}
563
-	if fi.h.Mode&c_ISVTX != 0 {
564
-		mode |= fs.ModeSticky
565
-	}
566
-
567
-	// Set file mode bits; clear perm, setuid, setgid, and sticky bits.
568
-	switch m := fs.FileMode(fi.h.Mode) &^ 07777; m {
569
-	case c_ISDIR:
570
-		mode |= fs.ModeDir
571
-	case c_ISFIFO:
572
-		mode |= fs.ModeNamedPipe
573
-	case c_ISLNK:
574
-		mode |= fs.ModeSymlink
575
-	case c_ISBLK:
576
-		mode |= fs.ModeDevice
577
-	case c_ISCHR:
578
-		mode |= fs.ModeDevice
579
-		mode |= fs.ModeCharDevice
580
-	case c_ISSOCK:
581
-		mode |= fs.ModeSocket
582
-	}
583
-
584
-	switch fi.h.Typeflag {
585
-	case TypeSymlink:
586
-		mode |= fs.ModeSymlink
587
-	case TypeChar:
588
-		mode |= fs.ModeDevice
589
-		mode |= fs.ModeCharDevice
590
-	case TypeBlock:
591
-		mode |= fs.ModeDevice
592
-	case TypeDir:
593
-		mode |= fs.ModeDir
594
-	case TypeFifo:
595
-		mode |= fs.ModeNamedPipe
596
-	}
597
-
598
-	return mode
599
-}
600
-
601
-// sysStat, if non-nil, populates h from system-dependent fields of fi.
602
-var sysStat func(fi fs.FileInfo, h *Header) error
603
-
604
-const (
605
-	// Mode constants from the USTAR spec:
606
-	// See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/pax.html#tag_20_92_13_06
607
-	c_ISUID = 04000 // Set uid
608
-	c_ISGID = 02000 // Set gid
609
-	c_ISVTX = 01000 // Save text (sticky bit)
610
-
611
-	// Common Unix mode constants; these are not defined in any common tar standard.
612
-	// Header.FileInfo understands these, but FileInfoHeader will never produce these.
613
-	c_ISDIR  = 040000  // Directory
614
-	c_ISFIFO = 010000  // FIFO
615
-	c_ISREG  = 0100000 // Regular file
616
-	c_ISLNK  = 0120000 // Symbolic link
617
-	c_ISBLK  = 060000  // Block special file
618
-	c_ISCHR  = 020000  // Character special file
619
-	c_ISSOCK = 0140000 // Socket
620
-)
621
-
622
-// FileInfoHeader creates a partially-populated Header from fi.
623
-// If fi describes a symlink, FileInfoHeader records link as the link target.
624
-// If fi describes a directory, a slash is appended to the name.
625
-//
626
-// Since fs.FileInfo's Name method only returns the base name of
627
-// the file it describes, it may be necessary to modify Header.Name
628
-// to provide the full path name of the file.
629
-func FileInfoHeader(fi fs.FileInfo, link string) (*Header, error) {
630
-	if fi == nil {
631
-		return nil, errors.New("archive/tar: FileInfo is nil")
632
-	}
633
-	fm := fi.Mode()
634
-	h := &Header{
635
-		Name:    fi.Name(),
636
-		ModTime: fi.ModTime(),
637
-		Mode:    int64(fm.Perm()), // or'd with c_IS* constants later
638
-	}
639
-	switch {
640
-	case fm.IsRegular():
641
-		h.Typeflag = TypeReg
642
-		h.Size = fi.Size()
643
-	case fi.IsDir():
644
-		h.Typeflag = TypeDir
645
-		h.Name += "/"
646
-	case fm&fs.ModeSymlink != 0:
647
-		h.Typeflag = TypeSymlink
648
-		h.Linkname = link
649
-	case fm&fs.ModeDevice != 0:
650
-		if fm&fs.ModeCharDevice != 0 {
651
-			h.Typeflag = TypeChar
652
-		} else {
653
-			h.Typeflag = TypeBlock
654
-		}
655
-	case fm&fs.ModeNamedPipe != 0:
656
-		h.Typeflag = TypeFifo
657
-	case fm&fs.ModeSocket != 0:
658
-		return nil, fmt.Errorf("archive/tar: sockets not supported")
659
-	default:
660
-		return nil, fmt.Errorf("archive/tar: unknown file mode %v", fm)
661
-	}
662
-	if fm&fs.ModeSetuid != 0 {
663
-		h.Mode |= c_ISUID
664
-	}
665
-	if fm&fs.ModeSetgid != 0 {
666
-		h.Mode |= c_ISGID
667
-	}
668
-	if fm&fs.ModeSticky != 0 {
669
-		h.Mode |= c_ISVTX
670
-	}
671
-	// If possible, populate additional fields from OS-specific
672
-	// FileInfo fields.
673
-	if sys, ok := fi.Sys().(*Header); ok {
674
-		// This FileInfo came from a Header (not the OS). Use the
675
-		// original Header to populate all remaining fields.
676
-		h.Uid = sys.Uid
677
-		h.Gid = sys.Gid
678
-		h.Uname = sys.Uname
679
-		h.Gname = sys.Gname
680
-		h.AccessTime = sys.AccessTime
681
-		h.ChangeTime = sys.ChangeTime
682
-		if sys.Xattrs != nil {
683
-			h.Xattrs = make(map[string]string)
684
-			for k, v := range sys.Xattrs {
685
-				h.Xattrs[k] = v
686
-			}
687
-		}
688
-		if sys.Typeflag == TypeLink {
689
-			// hard link
690
-			h.Typeflag = TypeLink
691
-			h.Size = 0
692
-			h.Linkname = sys.Linkname
693
-		}
694
-		if sys.PAXRecords != nil {
695
-			h.PAXRecords = make(map[string]string)
696
-			for k, v := range sys.PAXRecords {
697
-				h.PAXRecords[k] = v
698
-			}
699
-		}
700
-	}
701
-	if sysStat != nil {
702
-		return h, sysStat(fi, h)
703
-	}
704
-	return h, nil
705
-}
706
-
707
-// isHeaderOnlyType checks if the given type flag is of the type that has no
708
-// data section even if a size is specified.
709
-func isHeaderOnlyType(flag byte) bool {
710
-	switch flag {
711
-	case TypeLink, TypeSymlink, TypeChar, TypeBlock, TypeDir, TypeFifo:
712
-		return true
713
-	default:
714
-		return false
715
-	}
716
-}
717
-
718
-func min(a, b int64) int64 {
719
-	if a < b {
720
-		return a
721
-	}
722
-	return b
723
-}
724 1
deleted file mode 100644
... ...
@@ -1,71 +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_minimal() {
17
-	// Create and add some files to the archive.
18
-	var buf bytes.Buffer
19
-	tw := tar.NewWriter(&buf)
20
-	var files = []struct {
21
-		Name, Body string
22
-	}{
23
-		{"readme.txt", "This archive contains some text files."},
24
-		{"gopher.txt", "Gopher names:\nGeorge\nGeoffrey\nGonzo"},
25
-		{"todo.txt", "Get animal handling license."},
26
-	}
27
-	for _, file := range files {
28
-		hdr := &tar.Header{
29
-			Name: file.Name,
30
-			Mode: 0600,
31
-			Size: int64(len(file.Body)),
32
-		}
33
-		if err := tw.WriteHeader(hdr); err != nil {
34
-			log.Fatal(err)
35
-		}
36
-		if _, err := tw.Write([]byte(file.Body)); err != nil {
37
-			log.Fatal(err)
38
-		}
39
-	}
40
-	if err := tw.Close(); err != nil {
41
-		log.Fatal(err)
42
-	}
43
-
44
-	// Open and iterate through the files in the archive.
45
-	tr := tar.NewReader(&buf)
46
-	for {
47
-		hdr, err := tr.Next()
48
-		if err == io.EOF {
49
-			break // End of archive
50
-		}
51
-		if err != nil {
52
-			log.Fatal(err)
53
-		}
54
-		fmt.Printf("Contents of %s:\n", hdr.Name)
55
-		if _, err := io.Copy(os.Stdout, tr); err != nil {
56
-			log.Fatal(err)
57
-		}
58
-		fmt.Println()
59
-	}
60
-
61
-	// Output:
62
-	// Contents of readme.txt:
63
-	// This archive contains some text files.
64
-	// Contents of gopher.txt:
65
-	// Gopher names:
66
-	// George
67
-	// Geoffrey
68
-	// Gonzo
69
-	// Contents of todo.txt:
70
-	// Get animal handling license.
71
-}
72 1
deleted file mode 100644
... ...
@@ -1,303 +0,0 @@
1
-// Copyright 2016 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 "strings"
8
-
9
-// Format represents the tar archive format.
10
-//
11
-// The original tar format was introduced in Unix V7.
12
-// Since then, there have been multiple competing formats attempting to
13
-// standardize or extend the V7 format to overcome its limitations.
14
-// The most common formats are the USTAR, PAX, and GNU formats,
15
-// each with their own advantages and limitations.
16
-//
17
-// The following table captures the capabilities of each format:
18
-//
19
-//	                  |  USTAR |       PAX |       GNU
20
-//	------------------+--------+-----------+----------
21
-//	Name              |   256B | unlimited | unlimited
22
-//	Linkname          |   100B | unlimited | unlimited
23
-//	Size              | uint33 | unlimited |    uint89
24
-//	Mode              | uint21 |    uint21 |    uint57
25
-//	Uid/Gid           | uint21 | unlimited |    uint57
26
-//	Uname/Gname       |    32B | unlimited |       32B
27
-//	ModTime           | uint33 | unlimited |     int89
28
-//	AccessTime        |    n/a | unlimited |     int89
29
-//	ChangeTime        |    n/a | unlimited |     int89
30
-//	Devmajor/Devminor | uint21 |    uint21 |    uint57
31
-//	------------------+--------+-----------+----------
32
-//	string encoding   |  ASCII |     UTF-8 |    binary
33
-//	sub-second times  |     no |       yes |        no
34
-//	sparse files      |     no |       yes |       yes
35
-//
36
-// The table's upper portion shows the Header fields, where each format reports
37
-// the maximum number of bytes allowed for each string field and
38
-// the integer type used to store each numeric field
39
-// (where timestamps are stored as the number of seconds since the Unix epoch).
40
-//
41
-// The table's lower portion shows specialized features of each format,
42
-// such as supported string encodings, support for sub-second timestamps,
43
-// or support for sparse files.
44
-//
45
-// The Writer currently provides no support for sparse files.
46
-type Format int
47
-
48
-// Constants to identify various tar formats.
49
-const (
50
-	// Deliberately hide the meaning of constants from public API.
51
-	_ Format = (1 << iota) / 4 // Sequence of 0, 0, 1, 2, 4, 8, etc...
52
-
53
-	// FormatUnknown indicates that the format is unknown.
54
-	FormatUnknown
55
-
56
-	// The format of the original Unix V7 tar tool prior to standardization.
57
-	formatV7
58
-
59
-	// FormatUSTAR represents the USTAR header format defined in POSIX.1-1988.
60
-	//
61
-	// While this format is compatible with most tar readers,
62
-	// the format has several limitations making it unsuitable for some usages.
63
-	// Most notably, it cannot support sparse files, files larger than 8GiB,
64
-	// filenames larger than 256 characters, and non-ASCII filenames.
65
-	//
66
-	// Reference:
67
-	//	http://pubs.opengroup.org/onlinepubs/9699919799/utilities/pax.html#tag_20_92_13_06
68
-	FormatUSTAR
69
-
70
-	// FormatPAX represents the PAX header format defined in POSIX.1-2001.
71
-	//
72
-	// PAX extends USTAR by writing a special file with Typeflag TypeXHeader
73
-	// preceding the original header. This file contains a set of key-value
74
-	// records, which are used to overcome USTAR's shortcomings, in addition to
75
-	// providing the ability to have sub-second resolution for timestamps.
76
-	//
77
-	// Some newer formats add their own extensions to PAX by defining their
78
-	// own keys and assigning certain semantic meaning to the associated values.
79
-	// For example, sparse file support in PAX is implemented using keys
80
-	// defined by the GNU manual (e.g., "GNU.sparse.map").
81
-	//
82
-	// Reference:
83
-	//	http://pubs.opengroup.org/onlinepubs/009695399/utilities/pax.html
84
-	FormatPAX
85
-
86
-	// FormatGNU represents the GNU header format.
87
-	//
88
-	// The GNU header format is older than the USTAR and PAX standards and
89
-	// is not compatible with them. The GNU format supports
90
-	// arbitrary file sizes, filenames of arbitrary encoding and length,
91
-	// sparse files, and other features.
92
-	//
93
-	// It is recommended that PAX be chosen over GNU unless the target
94
-	// application can only parse GNU formatted archives.
95
-	//
96
-	// Reference:
97
-	//	https://www.gnu.org/software/tar/manual/html_node/Standard.html
98
-	FormatGNU
99
-
100
-	// Schily's tar format, which is incompatible with USTAR.
101
-	// This does not cover STAR extensions to the PAX format; these fall under
102
-	// the PAX format.
103
-	formatSTAR
104
-
105
-	formatMax
106
-)
107
-
108
-func (f Format) has(f2 Format) bool   { return f&f2 != 0 }
109
-func (f *Format) mayBe(f2 Format)     { *f |= f2 }
110
-func (f *Format) mayOnlyBe(f2 Format) { *f &= f2 }
111
-func (f *Format) mustNotBe(f2 Format) { *f &^= f2 }
112
-
113
-var formatNames = map[Format]string{
114
-	formatV7: "V7", FormatUSTAR: "USTAR", FormatPAX: "PAX", FormatGNU: "GNU", formatSTAR: "STAR",
115
-}
116
-
117
-func (f Format) String() string {
118
-	var ss []string
119
-	for f2 := Format(1); f2 < formatMax; f2 <<= 1 {
120
-		if f.has(f2) {
121
-			ss = append(ss, formatNames[f2])
122
-		}
123
-	}
124
-	switch len(ss) {
125
-	case 0:
126
-		return "<unknown>"
127
-	case 1:
128
-		return ss[0]
129
-	default:
130
-		return "(" + strings.Join(ss, " | ") + ")"
131
-	}
132
-}
133
-
134
-// Magics used to identify various formats.
135
-const (
136
-	magicGNU, versionGNU     = "ustar ", " \x00"
137
-	magicUSTAR, versionUSTAR = "ustar\x00", "00"
138
-	trailerSTAR              = "tar\x00"
139
-)
140
-
141
-// Size constants from various tar specifications.
142
-const (
143
-	blockSize  = 512 // Size of each block in a tar stream
144
-	nameSize   = 100 // Max length of the name field in USTAR format
145
-	prefixSize = 155 // Max length of the prefix field in USTAR format
146
-)
147
-
148
-// blockPadding computes the number of bytes needed to pad offset up to the
149
-// nearest block edge where 0 <= n < blockSize.
150
-func blockPadding(offset int64) (n int64) {
151
-	return -offset & (blockSize - 1)
152
-}
153
-
154
-var zeroBlock block
155
-
156
-type block [blockSize]byte
157
-
158
-// Convert block to any number of formats.
159
-func (b *block) V7() *headerV7       { return (*headerV7)(b) }
160
-func (b *block) GNU() *headerGNU     { return (*headerGNU)(b) }
161
-func (b *block) STAR() *headerSTAR   { return (*headerSTAR)(b) }
162
-func (b *block) USTAR() *headerUSTAR { return (*headerUSTAR)(b) }
163
-func (b *block) Sparse() sparseArray { return sparseArray(b[:]) }
164
-
165
-// GetFormat checks that the block is a valid tar header based on the checksum.
166
-// It then attempts to guess the specific format based on magic values.
167
-// If the checksum fails, then FormatUnknown is returned.
168
-func (b *block) GetFormat() Format {
169
-	// Verify checksum.
170
-	var p parser
171
-	value := p.parseOctal(b.V7().Chksum())
172
-	chksum1, chksum2 := b.ComputeChecksum()
173
-	if p.err != nil || (value != chksum1 && value != chksum2) {
174
-		return FormatUnknown
175
-	}
176
-
177
-	// Guess the magic values.
178
-	magic := string(b.USTAR().Magic())
179
-	version := string(b.USTAR().Version())
180
-	trailer := string(b.STAR().Trailer())
181
-	switch {
182
-	case magic == magicUSTAR && trailer == trailerSTAR:
183
-		return formatSTAR
184
-	case magic == magicUSTAR:
185
-		return FormatUSTAR | FormatPAX
186
-	case magic == magicGNU && version == versionGNU:
187
-		return FormatGNU
188
-	default:
189
-		return formatV7
190
-	}
191
-}
192
-
193
-// SetFormat writes the magic values necessary for specified format
194
-// and then updates the checksum accordingly.
195
-func (b *block) SetFormat(format Format) {
196
-	// Set the magic values.
197
-	switch {
198
-	case format.has(formatV7):
199
-		// Do nothing.
200
-	case format.has(FormatGNU):
201
-		copy(b.GNU().Magic(), magicGNU)
202
-		copy(b.GNU().Version(), versionGNU)
203
-	case format.has(formatSTAR):
204
-		copy(b.STAR().Magic(), magicUSTAR)
205
-		copy(b.STAR().Version(), versionUSTAR)
206
-		copy(b.STAR().Trailer(), trailerSTAR)
207
-	case format.has(FormatUSTAR | FormatPAX):
208
-		copy(b.USTAR().Magic(), magicUSTAR)
209
-		copy(b.USTAR().Version(), versionUSTAR)
210
-	default:
211
-		panic("invalid format")
212
-	}
213
-
214
-	// Update checksum.
215
-	// This field is special in that it is terminated by a NULL then space.
216
-	var f formatter
217
-	field := b.V7().Chksum()
218
-	chksum, _ := b.ComputeChecksum() // Possible values are 256..128776
219
-	f.formatOctal(field[:7], chksum) // Never fails since 128776 < 262143
220
-	field[7] = ' '
221
-}
222
-
223
-// ComputeChecksum computes the checksum for the header block.
224
-// POSIX specifies a sum of the unsigned byte values, but the Sun tar used
225
-// signed byte values.
226
-// We compute and return both.
227
-func (b *block) ComputeChecksum() (unsigned, signed int64) {
228
-	for i, c := range b {
229
-		if 148 <= i && i < 156 {
230
-			c = ' ' // Treat the checksum field itself as all spaces.
231
-		}
232
-		unsigned += int64(c)
233
-		signed += int64(int8(c))
234
-	}
235
-	return unsigned, signed
236
-}
237
-
238
-// Reset clears the block with all zeros.
239
-func (b *block) Reset() {
240
-	*b = block{}
241
-}
242
-
243
-type headerV7 [blockSize]byte
244
-
245
-func (h *headerV7) Name() []byte     { return h[000:][:100] }
246
-func (h *headerV7) Mode() []byte     { return h[100:][:8] }
247
-func (h *headerV7) UID() []byte      { return h[108:][:8] }
248
-func (h *headerV7) GID() []byte      { return h[116:][:8] }
249
-func (h *headerV7) Size() []byte     { return h[124:][:12] }
250
-func (h *headerV7) ModTime() []byte  { return h[136:][:12] }
251
-func (h *headerV7) Chksum() []byte   { return h[148:][:8] }
252
-func (h *headerV7) TypeFlag() []byte { return h[156:][:1] }
253
-func (h *headerV7) LinkName() []byte { return h[157:][:100] }
254
-
255
-type headerGNU [blockSize]byte
256
-
257
-func (h *headerGNU) V7() *headerV7       { return (*headerV7)(h) }
258
-func (h *headerGNU) Magic() []byte       { return h[257:][:6] }
259
-func (h *headerGNU) Version() []byte     { return h[263:][:2] }
260
-func (h *headerGNU) UserName() []byte    { return h[265:][:32] }
261
-func (h *headerGNU) GroupName() []byte   { return h[297:][:32] }
262
-func (h *headerGNU) DevMajor() []byte    { return h[329:][:8] }
263
-func (h *headerGNU) DevMinor() []byte    { return h[337:][:8] }
264
-func (h *headerGNU) AccessTime() []byte  { return h[345:][:12] }
265
-func (h *headerGNU) ChangeTime() []byte  { return h[357:][:12] }
266
-func (h *headerGNU) Sparse() sparseArray { return sparseArray(h[386:][:24*4+1]) }
267
-func (h *headerGNU) RealSize() []byte    { return h[483:][:12] }
268
-
269
-type headerSTAR [blockSize]byte
270
-
271
-func (h *headerSTAR) V7() *headerV7      { return (*headerV7)(h) }
272
-func (h *headerSTAR) Magic() []byte      { return h[257:][:6] }
273
-func (h *headerSTAR) Version() []byte    { return h[263:][:2] }
274
-func (h *headerSTAR) UserName() []byte   { return h[265:][:32] }
275
-func (h *headerSTAR) GroupName() []byte  { return h[297:][:32] }
276
-func (h *headerSTAR) DevMajor() []byte   { return h[329:][:8] }
277
-func (h *headerSTAR) DevMinor() []byte   { return h[337:][:8] }
278
-func (h *headerSTAR) Prefix() []byte     { return h[345:][:131] }
279
-func (h *headerSTAR) AccessTime() []byte { return h[476:][:12] }
280
-func (h *headerSTAR) ChangeTime() []byte { return h[488:][:12] }
281
-func (h *headerSTAR) Trailer() []byte    { return h[508:][:4] }
282
-
283
-type headerUSTAR [blockSize]byte
284
-
285
-func (h *headerUSTAR) V7() *headerV7     { return (*headerV7)(h) }
286
-func (h *headerUSTAR) Magic() []byte     { return h[257:][:6] }
287
-func (h *headerUSTAR) Version() []byte   { return h[263:][:2] }
288
-func (h *headerUSTAR) UserName() []byte  { return h[265:][:32] }
289
-func (h *headerUSTAR) GroupName() []byte { return h[297:][:32] }
290
-func (h *headerUSTAR) DevMajor() []byte  { return h[329:][:8] }
291
-func (h *headerUSTAR) DevMinor() []byte  { return h[337:][:8] }
292
-func (h *headerUSTAR) Prefix() []byte    { return h[345:][:155] }
293
-
294
-type sparseArray []byte
295
-
296
-func (s sparseArray) Entry(i int) sparseElem { return sparseElem(s[i*24:]) }
297
-func (s sparseArray) IsExtended() []byte     { return s[24*s.MaxEntries():][:1] }
298
-func (s sparseArray) MaxEntries() int        { return len(s) / 24 }
299
-
300
-type sparseElem []byte
301
-
302
-func (s sparseElem) Offset() []byte { return s[00:][:12] }
303
-func (s sparseElem) Length() []byte { return s[12:][:12] }
304 1
deleted file mode 100644
... ...
@@ -1,857 +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
-	"io"
10
-	"strconv"
11
-	"strings"
12
-	"time"
13
-)
14
-
15
-// Reader provides sequential access to the contents of a tar archive.
16
-// Reader.Next advances to the next file in the archive (including the first),
17
-// and then Reader can be treated as an io.Reader to access the file's data.
18
-type Reader struct {
19
-	r    io.Reader
20
-	pad  int64      // Amount of padding (ignored) after current file entry
21
-	curr fileReader // Reader for current file entry
22
-	blk  block      // Buffer to use as temporary local storage
23
-
24
-	// err is a persistent error.
25
-	// It is only the responsibility of every exported method of Reader to
26
-	// ensure that this error is sticky.
27
-	err error
28
-}
29
-
30
-type fileReader interface {
31
-	io.Reader
32
-	fileState
33
-
34
-	WriteTo(io.Writer) (int64, error)
35
-}
36
-
37
-// NewReader creates a new Reader reading from r.
38
-func NewReader(r io.Reader) *Reader {
39
-	return &Reader{r: r, curr: &regFileReader{r, 0}}
40
-}
41
-
42
-// Next advances to the next entry in the tar archive.
43
-// The Header.Size determines how many bytes can be read for the next file.
44
-// Any remaining data in the current file is automatically discarded.
45
-//
46
-// io.EOF is returned at the end of the input.
47
-func (tr *Reader) Next() (*Header, error) {
48
-	if tr.err != nil {
49
-		return nil, tr.err
50
-	}
51
-	hdr, err := tr.next()
52
-	tr.err = err
53
-	return hdr, err
54
-}
55
-
56
-func (tr *Reader) next() (*Header, error) {
57
-	var paxHdrs map[string]string
58
-	var gnuLongName, gnuLongLink string
59
-
60
-	// Externally, Next iterates through the tar archive as if it is a series of
61
-	// files. Internally, the tar format often uses fake "files" to add meta
62
-	// data that describes the next file. These meta data "files" should not
63
-	// normally be visible to the outside. As such, this loop iterates through
64
-	// one or more "header files" until it finds a "normal file".
65
-	format := FormatUSTAR | FormatPAX | FormatGNU
66
-	for {
67
-		// Discard the remainder of the file and any padding.
68
-		if err := discard(tr.r, tr.curr.PhysicalRemaining()); err != nil {
69
-			return nil, err
70
-		}
71
-		if _, err := tryReadFull(tr.r, tr.blk[:tr.pad]); err != nil {
72
-			return nil, err
73
-		}
74
-		tr.pad = 0
75
-
76
-		hdr, rawHdr, err := tr.readHeader()
77
-		if err != nil {
78
-			return nil, err
79
-		}
80
-		if err := tr.handleRegularFile(hdr); err != nil {
81
-			return nil, err
82
-		}
83
-		format.mayOnlyBe(hdr.Format)
84
-
85
-		// Check for PAX/GNU special headers and files.
86
-		switch hdr.Typeflag {
87
-		case TypeXHeader, TypeXGlobalHeader:
88
-			format.mayOnlyBe(FormatPAX)
89
-			paxHdrs, err = parsePAX(tr)
90
-			if err != nil {
91
-				return nil, err
92
-			}
93
-			if hdr.Typeflag == TypeXGlobalHeader {
94
-				mergePAX(hdr, paxHdrs)
95
-				return &Header{
96
-					Name:       hdr.Name,
97
-					Typeflag:   hdr.Typeflag,
98
-					Xattrs:     hdr.Xattrs,
99
-					PAXRecords: hdr.PAXRecords,
100
-					Format:     format,
101
-				}, nil
102
-			}
103
-			continue // This is a meta header affecting the next header
104
-		case TypeGNULongName, TypeGNULongLink:
105
-			format.mayOnlyBe(FormatGNU)
106
-			realname, err := io.ReadAll(tr)
107
-			if err != nil {
108
-				return nil, err
109
-			}
110
-
111
-			var p parser
112
-			switch hdr.Typeflag {
113
-			case TypeGNULongName:
114
-				gnuLongName = p.parseString(realname)
115
-			case TypeGNULongLink:
116
-				gnuLongLink = p.parseString(realname)
117
-			}
118
-			continue // This is a meta header affecting the next header
119
-		default:
120
-			// The old GNU sparse format is handled here since it is technically
121
-			// just a regular file with additional attributes.
122
-
123
-			if err := mergePAX(hdr, paxHdrs); err != nil {
124
-				return nil, err
125
-			}
126
-			if gnuLongName != "" {
127
-				hdr.Name = gnuLongName
128
-			}
129
-			if gnuLongLink != "" {
130
-				hdr.Linkname = gnuLongLink
131
-			}
132
-			if hdr.Typeflag == TypeRegA {
133
-				if strings.HasSuffix(hdr.Name, "/") {
134
-					hdr.Typeflag = TypeDir // Legacy archives use trailing slash for directories
135
-				} else {
136
-					hdr.Typeflag = TypeReg
137
-				}
138
-			}
139
-
140
-			// The extended headers may have updated the size.
141
-			// Thus, setup the regFileReader again after merging PAX headers.
142
-			if err := tr.handleRegularFile(hdr); err != nil {
143
-				return nil, err
144
-			}
145
-
146
-			// Sparse formats rely on being able to read from the logical data
147
-			// section; there must be a preceding call to handleRegularFile.
148
-			if err := tr.handleSparseFile(hdr, rawHdr); err != nil {
149
-				return nil, err
150
-			}
151
-
152
-			// Set the final guess at the format.
153
-			if format.has(FormatUSTAR) && format.has(FormatPAX) {
154
-				format.mayOnlyBe(FormatUSTAR)
155
-			}
156
-			hdr.Format = format
157
-			return hdr, nil // This is a file, so stop
158
-		}
159
-	}
160
-}
161
-
162
-// handleRegularFile sets up the current file reader and padding such that it
163
-// can only read the following logical data section. It will properly handle
164
-// special headers that contain no data section.
165
-func (tr *Reader) handleRegularFile(hdr *Header) error {
166
-	nb := hdr.Size
167
-	if isHeaderOnlyType(hdr.Typeflag) {
168
-		nb = 0
169
-	}
170
-	if nb < 0 {
171
-		return ErrHeader
172
-	}
173
-
174
-	tr.pad = blockPadding(nb)
175
-	tr.curr = &regFileReader{r: tr.r, nb: nb}
176
-	return nil
177
-}
178
-
179
-// handleSparseFile checks if the current file is a sparse format of any type
180
-// and sets the curr reader appropriately.
181
-func (tr *Reader) handleSparseFile(hdr *Header, rawHdr *block) error {
182
-	var spd sparseDatas
183
-	var err error
184
-	if hdr.Typeflag == TypeGNUSparse {
185
-		spd, err = tr.readOldGNUSparseMap(hdr, rawHdr)
186
-	} else {
187
-		spd, err = tr.readGNUSparsePAXHeaders(hdr)
188
-	}
189
-
190
-	// If sp is non-nil, then this is a sparse file.
191
-	// Note that it is possible for len(sp) == 0.
192
-	if err == nil && spd != nil {
193
-		if isHeaderOnlyType(hdr.Typeflag) || !validateSparseEntries(spd, hdr.Size) {
194
-			return ErrHeader
195
-		}
196
-		sph := invertSparseEntries(spd, hdr.Size)
197
-		tr.curr = &sparseFileReader{tr.curr, sph, 0}
198
-	}
199
-	return err
200
-}
201
-
202
-// readGNUSparsePAXHeaders checks the PAX headers for GNU sparse headers.
203
-// If they are found, then this function reads the sparse map and returns it.
204
-// This assumes that 0.0 headers have already been converted to 0.1 headers
205
-// by the PAX header parsing logic.
206
-func (tr *Reader) readGNUSparsePAXHeaders(hdr *Header) (sparseDatas, error) {
207
-	// Identify the version of GNU headers.
208
-	var is1x0 bool
209
-	major, minor := hdr.PAXRecords[paxGNUSparseMajor], hdr.PAXRecords[paxGNUSparseMinor]
210
-	switch {
211
-	case major == "0" && (minor == "0" || minor == "1"):
212
-		is1x0 = false
213
-	case major == "1" && minor == "0":
214
-		is1x0 = true
215
-	case major != "" || minor != "":
216
-		return nil, nil // Unknown GNU sparse PAX version
217
-	case hdr.PAXRecords[paxGNUSparseMap] != "":
218
-		is1x0 = false // 0.0 and 0.1 did not have explicit version records, so guess
219
-	default:
220
-		return nil, nil // Not a PAX format GNU sparse file.
221
-	}
222
-	hdr.Format.mayOnlyBe(FormatPAX)
223
-
224
-	// Update hdr from GNU sparse PAX headers.
225
-	if name := hdr.PAXRecords[paxGNUSparseName]; name != "" {
226
-		hdr.Name = name
227
-	}
228
-	size := hdr.PAXRecords[paxGNUSparseSize]
229
-	if size == "" {
230
-		size = hdr.PAXRecords[paxGNUSparseRealSize]
231
-	}
232
-	if size != "" {
233
-		n, err := strconv.ParseInt(size, 10, 64)
234
-		if err != nil {
235
-			return nil, ErrHeader
236
-		}
237
-		hdr.Size = n
238
-	}
239
-
240
-	// Read the sparse map according to the appropriate format.
241
-	if is1x0 {
242
-		return readGNUSparseMap1x0(tr.curr)
243
-	}
244
-	return readGNUSparseMap0x1(hdr.PAXRecords)
245
-}
246
-
247
-// mergePAX merges paxHdrs into hdr for all relevant fields of Header.
248
-func mergePAX(hdr *Header, paxHdrs map[string]string) (err error) {
249
-	for k, v := range paxHdrs {
250
-		if v == "" {
251
-			continue // Keep the original USTAR value
252
-		}
253
-		var id64 int64
254
-		switch k {
255
-		case paxPath:
256
-			hdr.Name = v
257
-		case paxLinkpath:
258
-			hdr.Linkname = v
259
-		case paxUname:
260
-			hdr.Uname = v
261
-		case paxGname:
262
-			hdr.Gname = v
263
-		case paxUid:
264
-			id64, err = strconv.ParseInt(v, 10, 64)
265
-			hdr.Uid = int(id64) // Integer overflow possible
266
-		case paxGid:
267
-			id64, err = strconv.ParseInt(v, 10, 64)
268
-			hdr.Gid = int(id64) // Integer overflow possible
269
-		case paxAtime:
270
-			hdr.AccessTime, err = parsePAXTime(v)
271
-		case paxMtime:
272
-			hdr.ModTime, err = parsePAXTime(v)
273
-		case paxCtime:
274
-			hdr.ChangeTime, err = parsePAXTime(v)
275
-		case paxSize:
276
-			hdr.Size, err = strconv.ParseInt(v, 10, 64)
277
-		default:
278
-			if strings.HasPrefix(k, paxSchilyXattr) {
279
-				if hdr.Xattrs == nil {
280
-					hdr.Xattrs = make(map[string]string)
281
-				}
282
-				hdr.Xattrs[k[len(paxSchilyXattr):]] = v
283
-			}
284
-		}
285
-		if err != nil {
286
-			return ErrHeader
287
-		}
288
-	}
289
-	hdr.PAXRecords = paxHdrs
290
-	return nil
291
-}
292
-
293
-// parsePAX parses PAX headers.
294
-// If an extended header (type 'x') is invalid, ErrHeader is returned
295
-func parsePAX(r io.Reader) (map[string]string, error) {
296
-	buf, err := io.ReadAll(r)
297
-	if err != nil {
298
-		return nil, err
299
-	}
300
-	sbuf := string(buf)
301
-
302
-	// For GNU PAX sparse format 0.0 support.
303
-	// This function transforms the sparse format 0.0 headers into format 0.1
304
-	// headers since 0.0 headers were not PAX compliant.
305
-	var sparseMap []string
306
-
307
-	paxHdrs := make(map[string]string)
308
-	for len(sbuf) > 0 {
309
-		key, value, residual, err := parsePAXRecord(sbuf)
310
-		if err != nil {
311
-			return nil, ErrHeader
312
-		}
313
-		sbuf = residual
314
-
315
-		switch key {
316
-		case paxGNUSparseOffset, paxGNUSparseNumBytes:
317
-			// Validate sparse header order and value.
318
-			if (len(sparseMap)%2 == 0 && key != paxGNUSparseOffset) ||
319
-				(len(sparseMap)%2 == 1 && key != paxGNUSparseNumBytes) ||
320
-				strings.Contains(value, ",") {
321
-				return nil, ErrHeader
322
-			}
323
-			sparseMap = append(sparseMap, value)
324
-		default:
325
-			paxHdrs[key] = value
326
-		}
327
-	}
328
-	if len(sparseMap) > 0 {
329
-		paxHdrs[paxGNUSparseMap] = strings.Join(sparseMap, ",")
330
-	}
331
-	return paxHdrs, nil
332
-}
333
-
334
-// readHeader reads the next block header and assumes that the underlying reader
335
-// is already aligned to a block boundary. It returns the raw block of the
336
-// header in case further processing is required.
337
-//
338
-// The err will be set to io.EOF only when one of the following occurs:
339
-//	* Exactly 0 bytes are read and EOF is hit.
340
-//	* Exactly 1 block of zeros is read and EOF is hit.
341
-//	* At least 2 blocks of zeros are read.
342
-func (tr *Reader) readHeader() (*Header, *block, error) {
343
-	// Two blocks of zero bytes marks the end of the archive.
344
-	if _, err := io.ReadFull(tr.r, tr.blk[:]); err != nil {
345
-		return nil, nil, err // EOF is okay here; exactly 0 bytes read
346
-	}
347
-	if bytes.Equal(tr.blk[:], zeroBlock[:]) {
348
-		if _, err := io.ReadFull(tr.r, tr.blk[:]); err != nil {
349
-			return nil, nil, err // EOF is okay here; exactly 1 block of zeros read
350
-		}
351
-		if bytes.Equal(tr.blk[:], zeroBlock[:]) {
352
-			return nil, nil, io.EOF // normal EOF; exactly 2 block of zeros read
353
-		}
354
-		return nil, nil, ErrHeader // Zero block and then non-zero block
355
-	}
356
-
357
-	// Verify the header matches a known format.
358
-	format := tr.blk.GetFormat()
359
-	if format == FormatUnknown {
360
-		return nil, nil, ErrHeader
361
-	}
362
-
363
-	var p parser
364
-	hdr := new(Header)
365
-
366
-	// Unpack the V7 header.
367
-	v7 := tr.blk.V7()
368
-	hdr.Typeflag = v7.TypeFlag()[0]
369
-	hdr.Name = p.parseString(v7.Name())
370
-	hdr.Linkname = p.parseString(v7.LinkName())
371
-	hdr.Size = p.parseNumeric(v7.Size())
372
-	hdr.Mode = p.parseNumeric(v7.Mode())
373
-	hdr.Uid = int(p.parseNumeric(v7.UID()))
374
-	hdr.Gid = int(p.parseNumeric(v7.GID()))
375
-	hdr.ModTime = time.Unix(p.parseNumeric(v7.ModTime()), 0)
376
-
377
-	// Unpack format specific fields.
378
-	if format > formatV7 {
379
-		ustar := tr.blk.USTAR()
380
-		hdr.Uname = p.parseString(ustar.UserName())
381
-		hdr.Gname = p.parseString(ustar.GroupName())
382
-		hdr.Devmajor = p.parseNumeric(ustar.DevMajor())
383
-		hdr.Devminor = p.parseNumeric(ustar.DevMinor())
384
-
385
-		var prefix string
386
-		switch {
387
-		case format.has(FormatUSTAR | FormatPAX):
388
-			hdr.Format = format
389
-			ustar := tr.blk.USTAR()
390
-			prefix = p.parseString(ustar.Prefix())
391
-
392
-			// For Format detection, check if block is properly formatted since
393
-			// the parser is more liberal than what USTAR actually permits.
394
-			notASCII := func(r rune) bool { return r >= 0x80 }
395
-			if bytes.IndexFunc(tr.blk[:], notASCII) >= 0 {
396
-				hdr.Format = FormatUnknown // Non-ASCII characters in block.
397
-			}
398
-			nul := func(b []byte) bool { return int(b[len(b)-1]) == 0 }
399
-			if !(nul(v7.Size()) && nul(v7.Mode()) && nul(v7.UID()) && nul(v7.GID()) &&
400
-				nul(v7.ModTime()) && nul(ustar.DevMajor()) && nul(ustar.DevMinor())) {
401
-				hdr.Format = FormatUnknown // Numeric fields must end in NUL
402
-			}
403
-		case format.has(formatSTAR):
404
-			star := tr.blk.STAR()
405
-			prefix = p.parseString(star.Prefix())
406
-			hdr.AccessTime = time.Unix(p.parseNumeric(star.AccessTime()), 0)
407
-			hdr.ChangeTime = time.Unix(p.parseNumeric(star.ChangeTime()), 0)
408
-		case format.has(FormatGNU):
409
-			hdr.Format = format
410
-			var p2 parser
411
-			gnu := tr.blk.GNU()
412
-			if b := gnu.AccessTime(); b[0] != 0 {
413
-				hdr.AccessTime = time.Unix(p2.parseNumeric(b), 0)
414
-			}
415
-			if b := gnu.ChangeTime(); b[0] != 0 {
416
-				hdr.ChangeTime = time.Unix(p2.parseNumeric(b), 0)
417
-			}
418
-
419
-			// Prior to Go1.8, the Writer had a bug where it would output
420
-			// an invalid tar file in certain rare situations because the logic
421
-			// incorrectly believed that the old GNU format had a prefix field.
422
-			// This is wrong and leads to an output file that mangles the
423
-			// atime and ctime fields, which are often left unused.
424
-			//
425
-			// In order to continue reading tar files created by former, buggy
426
-			// versions of Go, we skeptically parse the atime and ctime fields.
427
-			// If we are unable to parse them and the prefix field looks like
428
-			// an ASCII string, then we fallback on the pre-Go1.8 behavior
429
-			// of treating these fields as the USTAR prefix field.
430
-			//
431
-			// Note that this will not use the fallback logic for all possible
432
-			// files generated by a pre-Go1.8 toolchain. If the generated file
433
-			// happened to have a prefix field that parses as valid
434
-			// atime and ctime fields (e.g., when they are valid octal strings),
435
-			// then it is impossible to distinguish between a valid GNU file
436
-			// and an invalid pre-Go1.8 file.
437
-			//
438
-			// See https://golang.org/issues/12594
439
-			// See https://golang.org/issues/21005
440
-			if p2.err != nil {
441
-				hdr.AccessTime, hdr.ChangeTime = time.Time{}, time.Time{}
442
-				ustar := tr.blk.USTAR()
443
-				if s := p.parseString(ustar.Prefix()); isASCII(s) {
444
-					prefix = s
445
-				}
446
-				hdr.Format = FormatUnknown // Buggy file is not GNU
447
-			}
448
-		}
449
-		if len(prefix) > 0 {
450
-			hdr.Name = prefix + "/" + hdr.Name
451
-		}
452
-	}
453
-	return hdr, &tr.blk, p.err
454
-}
455
-
456
-// readOldGNUSparseMap reads the sparse map from the old GNU sparse format.
457
-// The sparse map is stored in the tar header if it's small enough.
458
-// If it's larger than four entries, then one or more extension headers are used
459
-// to store the rest of the sparse map.
460
-//
461
-// The Header.Size does not reflect the size of any extended headers used.
462
-// Thus, this function will read from the raw io.Reader to fetch extra headers.
463
-// This method mutates blk in the process.
464
-func (tr *Reader) readOldGNUSparseMap(hdr *Header, blk *block) (sparseDatas, error) {
465
-	// Make sure that the input format is GNU.
466
-	// Unfortunately, the STAR format also has a sparse header format that uses
467
-	// the same type flag but has a completely different layout.
468
-	if blk.GetFormat() != FormatGNU {
469
-		return nil, ErrHeader
470
-	}
471
-	hdr.Format.mayOnlyBe(FormatGNU)
472
-
473
-	var p parser
474
-	hdr.Size = p.parseNumeric(blk.GNU().RealSize())
475
-	if p.err != nil {
476
-		return nil, p.err
477
-	}
478
-	s := blk.GNU().Sparse()
479
-	spd := make(sparseDatas, 0, s.MaxEntries())
480
-	for {
481
-		for i := 0; i < s.MaxEntries(); i++ {
482
-			// This termination condition is identical to GNU and BSD tar.
483
-			if s.Entry(i).Offset()[0] == 0x00 {
484
-				break // Don't return, need to process extended headers (even if empty)
485
-			}
486
-			offset := p.parseNumeric(s.Entry(i).Offset())
487
-			length := p.parseNumeric(s.Entry(i).Length())
488
-			if p.err != nil {
489
-				return nil, p.err
490
-			}
491
-			spd = append(spd, sparseEntry{Offset: offset, Length: length})
492
-		}
493
-
494
-		if s.IsExtended()[0] > 0 {
495
-			// There are more entries. Read an extension header and parse its entries.
496
-			if _, err := mustReadFull(tr.r, blk[:]); err != nil {
497
-				return nil, err
498
-			}
499
-			s = blk.Sparse()
500
-			continue
501
-		}
502
-		return spd, nil // Done
503
-	}
504
-}
505
-
506
-// readGNUSparseMap1x0 reads the sparse map as stored in GNU's PAX sparse format
507
-// version 1.0. The format of the sparse map consists of a series of
508
-// newline-terminated numeric fields. The first field is the number of entries
509
-// and is always present. Following this are the entries, consisting of two
510
-// fields (offset, length). This function must stop reading at the end
511
-// boundary of the block containing the last newline.
512
-//
513
-// Note that the GNU manual says that numeric values should be encoded in octal
514
-// format. However, the GNU tar utility itself outputs these values in decimal.
515
-// As such, this library treats values as being encoded in decimal.
516
-func readGNUSparseMap1x0(r io.Reader) (sparseDatas, error) {
517
-	var (
518
-		cntNewline int64
519
-		buf        bytes.Buffer
520
-		blk        block
521
-	)
522
-
523
-	// feedTokens copies data in blocks from r into buf until there are
524
-	// at least cnt newlines in buf. It will not read more blocks than needed.
525
-	feedTokens := func(n int64) error {
526
-		for cntNewline < n {
527
-			if _, err := mustReadFull(r, blk[:]); err != nil {
528
-				return err
529
-			}
530
-			buf.Write(blk[:])
531
-			for _, c := range blk {
532
-				if c == '\n' {
533
-					cntNewline++
534
-				}
535
-			}
536
-		}
537
-		return nil
538
-	}
539
-
540
-	// nextToken gets the next token delimited by a newline. This assumes that
541
-	// at least one newline exists in the buffer.
542
-	nextToken := func() string {
543
-		cntNewline--
544
-		tok, _ := buf.ReadString('\n')
545
-		return strings.TrimRight(tok, "\n")
546
-	}
547
-
548
-	// Parse for the number of entries.
549
-	// Use integer overflow resistant math to check this.
550
-	if err := feedTokens(1); err != nil {
551
-		return nil, err
552
-	}
553
-	numEntries, err := strconv.ParseInt(nextToken(), 10, 0) // Intentionally parse as native int
554
-	if err != nil || numEntries < 0 || int(2*numEntries) < int(numEntries) {
555
-		return nil, ErrHeader
556
-	}
557
-
558
-	// Parse for all member entries.
559
-	// numEntries is trusted after this since a potential attacker must have
560
-	// committed resources proportional to what this library used.
561
-	if err := feedTokens(2 * numEntries); err != nil {
562
-		return nil, err
563
-	}
564
-	spd := make(sparseDatas, 0, numEntries)
565
-	for i := int64(0); i < numEntries; i++ {
566
-		offset, err1 := strconv.ParseInt(nextToken(), 10, 64)
567
-		length, err2 := strconv.ParseInt(nextToken(), 10, 64)
568
-		if err1 != nil || err2 != nil {
569
-			return nil, ErrHeader
570
-		}
571
-		spd = append(spd, sparseEntry{Offset: offset, Length: length})
572
-	}
573
-	return spd, nil
574
-}
575
-
576
-// readGNUSparseMap0x1 reads the sparse map as stored in GNU's PAX sparse format
577
-// version 0.1. The sparse map is stored in the PAX headers.
578
-func readGNUSparseMap0x1(paxHdrs map[string]string) (sparseDatas, error) {
579
-	// Get number of entries.
580
-	// Use integer overflow resistant math to check this.
581
-	numEntriesStr := paxHdrs[paxGNUSparseNumBlocks]
582
-	numEntries, err := strconv.ParseInt(numEntriesStr, 10, 0) // Intentionally parse as native int
583
-	if err != nil || numEntries < 0 || int(2*numEntries) < int(numEntries) {
584
-		return nil, ErrHeader
585
-	}
586
-
587
-	// There should be two numbers in sparseMap for each entry.
588
-	sparseMap := strings.Split(paxHdrs[paxGNUSparseMap], ",")
589
-	if len(sparseMap) == 1 && sparseMap[0] == "" {
590
-		sparseMap = sparseMap[:0]
591
-	}
592
-	if int64(len(sparseMap)) != 2*numEntries {
593
-		return nil, ErrHeader
594
-	}
595
-
596
-	// Loop through the entries in the sparse map.
597
-	// numEntries is trusted now.
598
-	spd := make(sparseDatas, 0, numEntries)
599
-	for len(sparseMap) >= 2 {
600
-		offset, err1 := strconv.ParseInt(sparseMap[0], 10, 64)
601
-		length, err2 := strconv.ParseInt(sparseMap[1], 10, 64)
602
-		if err1 != nil || err2 != nil {
603
-			return nil, ErrHeader
604
-		}
605
-		spd = append(spd, sparseEntry{Offset: offset, Length: length})
606
-		sparseMap = sparseMap[2:]
607
-	}
608
-	return spd, nil
609
-}
610
-
611
-// Read reads from the current file in the tar archive.
612
-// It returns (0, io.EOF) when it reaches the end of that file,
613
-// until Next is called to advance to the next file.
614
-//
615
-// If the current file is sparse, then the regions marked as a hole
616
-// are read back as NUL-bytes.
617
-//
618
-// Calling Read on special types like TypeLink, TypeSymlink, TypeChar,
619
-// TypeBlock, TypeDir, and TypeFifo returns (0, io.EOF) regardless of what
620
-// the Header.Size claims.
621
-func (tr *Reader) Read(b []byte) (int, error) {
622
-	if tr.err != nil {
623
-		return 0, tr.err
624
-	}
625
-	n, err := tr.curr.Read(b)
626
-	if err != nil && err != io.EOF {
627
-		tr.err = err
628
-	}
629
-	return n, err
630
-}
631
-
632
-// writeTo writes the content of the current file to w.
633
-// The bytes written matches the number of remaining bytes in the current file.
634
-//
635
-// If the current file is sparse and w is an io.WriteSeeker,
636
-// then writeTo uses Seek to skip past holes defined in Header.SparseHoles,
637
-// assuming that skipped regions are filled with NULs.
638
-// This always writes the last byte to ensure w is the right size.
639
-//
640
-// TODO(dsnet): Re-export this when adding sparse file support.
641
-// See https://golang.org/issue/22735
642
-func (tr *Reader) writeTo(w io.Writer) (int64, error) {
643
-	if tr.err != nil {
644
-		return 0, tr.err
645
-	}
646
-	n, err := tr.curr.WriteTo(w)
647
-	if err != nil {
648
-		tr.err = err
649
-	}
650
-	return n, err
651
-}
652
-
653
-// regFileReader is a fileReader for reading data from a regular file entry.
654
-type regFileReader struct {
655
-	r  io.Reader // Underlying Reader
656
-	nb int64     // Number of remaining bytes to read
657
-}
658
-
659
-func (fr *regFileReader) Read(b []byte) (n int, err error) {
660
-	if int64(len(b)) > fr.nb {
661
-		b = b[:fr.nb]
662
-	}
663
-	if len(b) > 0 {
664
-		n, err = fr.r.Read(b)
665
-		fr.nb -= int64(n)
666
-	}
667
-	switch {
668
-	case err == io.EOF && fr.nb > 0:
669
-		return n, io.ErrUnexpectedEOF
670
-	case err == nil && fr.nb == 0:
671
-		return n, io.EOF
672
-	default:
673
-		return n, err
674
-	}
675
-}
676
-
677
-func (fr *regFileReader) WriteTo(w io.Writer) (int64, error) {
678
-	return io.Copy(w, struct{ io.Reader }{fr})
679
-}
680
-
681
-func (fr regFileReader) LogicalRemaining() int64 {
682
-	return fr.nb
683
-}
684
-
685
-func (fr regFileReader) PhysicalRemaining() int64 {
686
-	return fr.nb
687
-}
688
-
689
-// sparseFileReader is a fileReader for reading data from a sparse file entry.
690
-type sparseFileReader struct {
691
-	fr  fileReader  // Underlying fileReader
692
-	sp  sparseHoles // Normalized list of sparse holes
693
-	pos int64       // Current position in sparse file
694
-}
695
-
696
-func (sr *sparseFileReader) Read(b []byte) (n int, err error) {
697
-	finished := int64(len(b)) >= sr.LogicalRemaining()
698
-	if finished {
699
-		b = b[:sr.LogicalRemaining()]
700
-	}
701
-
702
-	b0 := b
703
-	endPos := sr.pos + int64(len(b))
704
-	for endPos > sr.pos && err == nil {
705
-		var nf int // Bytes read in fragment
706
-		holeStart, holeEnd := sr.sp[0].Offset, sr.sp[0].endOffset()
707
-		if sr.pos < holeStart { // In a data fragment
708
-			bf := b[:min(int64(len(b)), holeStart-sr.pos)]
709
-			nf, err = tryReadFull(sr.fr, bf)
710
-		} else { // In a hole fragment
711
-			bf := b[:min(int64(len(b)), holeEnd-sr.pos)]
712
-			nf, err = tryReadFull(zeroReader{}, bf)
713
-		}
714
-		b = b[nf:]
715
-		sr.pos += int64(nf)
716
-		if sr.pos >= holeEnd && len(sr.sp) > 1 {
717
-			sr.sp = sr.sp[1:] // Ensure last fragment always remains
718
-		}
719
-	}
720
-
721
-	n = len(b0) - len(b)
722
-	switch {
723
-	case err == io.EOF:
724
-		return n, errMissData // Less data in dense file than sparse file
725
-	case err != nil:
726
-		return n, err
727
-	case sr.LogicalRemaining() == 0 && sr.PhysicalRemaining() > 0:
728
-		return n, errUnrefData // More data in dense file than sparse file
729
-	case finished:
730
-		return n, io.EOF
731
-	default:
732
-		return n, nil
733
-	}
734
-}
735
-
736
-func (sr *sparseFileReader) WriteTo(w io.Writer) (n int64, err error) {
737
-	ws, ok := w.(io.WriteSeeker)
738
-	if ok {
739
-		if _, err := ws.Seek(0, io.SeekCurrent); err != nil {
740
-			ok = false // Not all io.Seeker can really seek
741
-		}
742
-	}
743
-	if !ok {
744
-		return io.Copy(w, struct{ io.Reader }{sr})
745
-	}
746
-
747
-	var writeLastByte bool
748
-	pos0 := sr.pos
749
-	for sr.LogicalRemaining() > 0 && !writeLastByte && err == nil {
750
-		var nf int64 // Size of fragment
751
-		holeStart, holeEnd := sr.sp[0].Offset, sr.sp[0].endOffset()
752
-		if sr.pos < holeStart { // In a data fragment
753
-			nf = holeStart - sr.pos
754
-			nf, err = io.CopyN(ws, sr.fr, nf)
755
-		} else { // In a hole fragment
756
-			nf = holeEnd - sr.pos
757
-			if sr.PhysicalRemaining() == 0 {
758
-				writeLastByte = true
759
-				nf--
760
-			}
761
-			_, err = ws.Seek(nf, io.SeekCurrent)
762
-		}
763
-		sr.pos += nf
764
-		if sr.pos >= holeEnd && len(sr.sp) > 1 {
765
-			sr.sp = sr.sp[1:] // Ensure last fragment always remains
766
-		}
767
-	}
768
-
769
-	// If the last fragment is a hole, then seek to 1-byte before EOF, and
770
-	// write a single byte to ensure the file is the right size.
771
-	if writeLastByte && err == nil {
772
-		_, err = ws.Write([]byte{0})
773
-		sr.pos++
774
-	}
775
-
776
-	n = sr.pos - pos0
777
-	switch {
778
-	case err == io.EOF:
779
-		return n, errMissData // Less data in dense file than sparse file
780
-	case err != nil:
781
-		return n, err
782
-	case sr.LogicalRemaining() == 0 && sr.PhysicalRemaining() > 0:
783
-		return n, errUnrefData // More data in dense file than sparse file
784
-	default:
785
-		return n, nil
786
-	}
787
-}
788
-
789
-func (sr sparseFileReader) LogicalRemaining() int64 {
790
-	return sr.sp[len(sr.sp)-1].endOffset() - sr.pos
791
-}
792
-func (sr sparseFileReader) PhysicalRemaining() int64 {
793
-	return sr.fr.PhysicalRemaining()
794
-}
795
-
796
-type zeroReader struct{}
797
-
798
-func (zeroReader) Read(b []byte) (int, error) {
799
-	for i := range b {
800
-		b[i] = 0
801
-	}
802
-	return len(b), nil
803
-}
804
-
805
-// mustReadFull is like io.ReadFull except it returns
806
-// io.ErrUnexpectedEOF when io.EOF is hit before len(b) bytes are read.
807
-func mustReadFull(r io.Reader, b []byte) (int, error) {
808
-	n, err := tryReadFull(r, b)
809
-	if err == io.EOF {
810
-		err = io.ErrUnexpectedEOF
811
-	}
812
-	return n, err
813
-}
814
-
815
-// tryReadFull is like io.ReadFull except it returns
816
-// io.EOF when it is hit before len(b) bytes are read.
817
-func tryReadFull(r io.Reader, b []byte) (n int, err error) {
818
-	for len(b) > n && err == nil {
819
-		var nn int
820
-		nn, err = r.Read(b[n:])
821
-		n += nn
822
-	}
823
-	if len(b) == n && err == io.EOF {
824
-		err = nil
825
-	}
826
-	return n, err
827
-}
828
-
829
-// discard skips n bytes in r, reporting an error if unable to do so.
830
-func discard(r io.Reader, n int64) error {
831
-	// If possible, Seek to the last byte before the end of the data section.
832
-	// Do this because Seek is often lazy about reporting errors; this will mask
833
-	// the fact that the stream may be truncated. We can rely on the
834
-	// io.CopyN done shortly afterwards to trigger any IO errors.
835
-	var seekSkipped int64 // Number of bytes skipped via Seek
836
-	if sr, ok := r.(io.Seeker); ok && n > 1 {
837
-		// Not all io.Seeker can actually Seek. For example, os.Stdin implements
838
-		// io.Seeker, but calling Seek always returns an error and performs
839
-		// no action. Thus, we try an innocent seek to the current position
840
-		// to see if Seek is really supported.
841
-		pos1, err := sr.Seek(0, io.SeekCurrent)
842
-		if pos1 >= 0 && err == nil {
843
-			// Seek seems supported, so perform the real Seek.
844
-			pos2, err := sr.Seek(n-1, io.SeekCurrent)
845
-			if pos2 < 0 || err != nil {
846
-				return err
847
-			}
848
-			seekSkipped = pos2 - pos1
849
-		}
850
-	}
851
-
852
-	copySkipped, err := io.CopyN(io.Discard, r, n-seekSkipped)
853
-	if err == io.EOF && seekSkipped+copySkipped < n {
854
-		err = io.ErrUnexpectedEOF
855
-	}
856
-	return err
857
-}
858 1
deleted file mode 100644
... ...
@@ -1,1610 +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
-	"errors"
11
-	"fmt"
12
-	"io"
13
-	"math"
14
-	"os"
15
-	"path"
16
-	"reflect"
17
-	"strconv"
18
-	"strings"
19
-	"testing"
20
-	"time"
21
-)
22
-
23
-func TestReader(t *testing.T) {
24
-	vectors := []struct {
25
-		file    string    // Test input file
26
-		headers []*Header // Expected output headers
27
-		chksums []string  // MD5 checksum of files, leave as nil if not checked
28
-		err     error     // Expected error to occur
29
-	}{{
30
-		file: "testdata/gnu.tar",
31
-		headers: []*Header{{
32
-			Name:     "small.txt",
33
-			Mode:     0640,
34
-			Uid:      73025,
35
-			Gid:      5000,
36
-			Size:     5,
37
-			ModTime:  time.Unix(1244428340, 0),
38
-			Typeflag: '0',
39
-			Uname:    "dsymonds",
40
-			Gname:    "eng",
41
-			Format:   FormatGNU,
42
-		}, {
43
-			Name:     "small2.txt",
44
-			Mode:     0640,
45
-			Uid:      73025,
46
-			Gid:      5000,
47
-			Size:     11,
48
-			ModTime:  time.Unix(1244436044, 0),
49
-			Typeflag: '0',
50
-			Uname:    "dsymonds",
51
-			Gname:    "eng",
52
-			Format:   FormatGNU,
53
-		}},
54
-		chksums: []string{
55
-			"e38b27eaccb4391bdec553a7f3ae6b2f",
56
-			"c65bd2e50a56a2138bf1716f2fd56fe9",
57
-		},
58
-	}, {
59
-		file: "testdata/sparse-formats.tar",
60
-		headers: []*Header{{
61
-			Name:     "sparse-gnu",
62
-			Mode:     420,
63
-			Uid:      1000,
64
-			Gid:      1000,
65
-			Size:     200,
66
-			ModTime:  time.Unix(1392395740, 0),
67
-			Typeflag: 0x53,
68
-			Linkname: "",
69
-			Uname:    "david",
70
-			Gname:    "david",
71
-			Devmajor: 0,
72
-			Devminor: 0,
73
-			Format:   FormatGNU,
74
-		}, {
75
-			Name:     "sparse-posix-0.0",
76
-			Mode:     420,
77
-			Uid:      1000,
78
-			Gid:      1000,
79
-			Size:     200,
80
-			ModTime:  time.Unix(1392342187, 0),
81
-			Typeflag: 0x30,
82
-			Linkname: "",
83
-			Uname:    "david",
84
-			Gname:    "david",
85
-			Devmajor: 0,
86
-			Devminor: 0,
87
-			PAXRecords: map[string]string{
88
-				"GNU.sparse.size":      "200",
89
-				"GNU.sparse.numblocks": "95",
90
-				"GNU.sparse.map":       "1,1,3,1,5,1,7,1,9,1,11,1,13,1,15,1,17,1,19,1,21,1,23,1,25,1,27,1,29,1,31,1,33,1,35,1,37,1,39,1,41,1,43,1,45,1,47,1,49,1,51,1,53,1,55,1,57,1,59,1,61,1,63,1,65,1,67,1,69,1,71,1,73,1,75,1,77,1,79,1,81,1,83,1,85,1,87,1,89,1,91,1,93,1,95,1,97,1,99,1,101,1,103,1,105,1,107,1,109,1,111,1,113,1,115,1,117,1,119,1,121,1,123,1,125,1,127,1,129,1,131,1,133,1,135,1,137,1,139,1,141,1,143,1,145,1,147,1,149,1,151,1,153,1,155,1,157,1,159,1,161,1,163,1,165,1,167,1,169,1,171,1,173,1,175,1,177,1,179,1,181,1,183,1,185,1,187,1,189,1",
91
-			},
92
-			Format: FormatPAX,
93
-		}, {
94
-			Name:     "sparse-posix-0.1",
95
-			Mode:     420,
96
-			Uid:      1000,
97
-			Gid:      1000,
98
-			Size:     200,
99
-			ModTime:  time.Unix(1392340456, 0),
100
-			Typeflag: 0x30,
101
-			Linkname: "",
102
-			Uname:    "david",
103
-			Gname:    "david",
104
-			Devmajor: 0,
105
-			Devminor: 0,
106
-			PAXRecords: map[string]string{
107
-				"GNU.sparse.size":      "200",
108
-				"GNU.sparse.numblocks": "95",
109
-				"GNU.sparse.map":       "1,1,3,1,5,1,7,1,9,1,11,1,13,1,15,1,17,1,19,1,21,1,23,1,25,1,27,1,29,1,31,1,33,1,35,1,37,1,39,1,41,1,43,1,45,1,47,1,49,1,51,1,53,1,55,1,57,1,59,1,61,1,63,1,65,1,67,1,69,1,71,1,73,1,75,1,77,1,79,1,81,1,83,1,85,1,87,1,89,1,91,1,93,1,95,1,97,1,99,1,101,1,103,1,105,1,107,1,109,1,111,1,113,1,115,1,117,1,119,1,121,1,123,1,125,1,127,1,129,1,131,1,133,1,135,1,137,1,139,1,141,1,143,1,145,1,147,1,149,1,151,1,153,1,155,1,157,1,159,1,161,1,163,1,165,1,167,1,169,1,171,1,173,1,175,1,177,1,179,1,181,1,183,1,185,1,187,1,189,1",
110
-				"GNU.sparse.name":      "sparse-posix-0.1",
111
-			},
112
-			Format: FormatPAX,
113
-		}, {
114
-			Name:     "sparse-posix-1.0",
115
-			Mode:     420,
116
-			Uid:      1000,
117
-			Gid:      1000,
118
-			Size:     200,
119
-			ModTime:  time.Unix(1392337404, 0),
120
-			Typeflag: 0x30,
121
-			Linkname: "",
122
-			Uname:    "david",
123
-			Gname:    "david",
124
-			Devmajor: 0,
125
-			Devminor: 0,
126
-			PAXRecords: map[string]string{
127
-				"GNU.sparse.major":    "1",
128
-				"GNU.sparse.minor":    "0",
129
-				"GNU.sparse.realsize": "200",
130
-				"GNU.sparse.name":     "sparse-posix-1.0",
131
-			},
132
-			Format: FormatPAX,
133
-		}, {
134
-			Name:     "end",
135
-			Mode:     420,
136
-			Uid:      1000,
137
-			Gid:      1000,
138
-			Size:     4,
139
-			ModTime:  time.Unix(1392398319, 0),
140
-			Typeflag: 0x30,
141
-			Linkname: "",
142
-			Uname:    "david",
143
-			Gname:    "david",
144
-			Devmajor: 0,
145
-			Devminor: 0,
146
-			Format:   FormatGNU,
147
-		}},
148
-		chksums: []string{
149
-			"6f53234398c2449fe67c1812d993012f",
150
-			"6f53234398c2449fe67c1812d993012f",
151
-			"6f53234398c2449fe67c1812d993012f",
152
-			"6f53234398c2449fe67c1812d993012f",
153
-			"b0061974914468de549a2af8ced10316",
154
-		},
155
-	}, {
156
-		file: "testdata/star.tar",
157
-		headers: []*Header{{
158
-			Name:       "small.txt",
159
-			Mode:       0640,
160
-			Uid:        73025,
161
-			Gid:        5000,
162
-			Size:       5,
163
-			ModTime:    time.Unix(1244592783, 0),
164
-			Typeflag:   '0',
165
-			Uname:      "dsymonds",
166
-			Gname:      "eng",
167
-			AccessTime: time.Unix(1244592783, 0),
168
-			ChangeTime: time.Unix(1244592783, 0),
169
-		}, {
170
-			Name:       "small2.txt",
171
-			Mode:       0640,
172
-			Uid:        73025,
173
-			Gid:        5000,
174
-			Size:       11,
175
-			ModTime:    time.Unix(1244592783, 0),
176
-			Typeflag:   '0',
177
-			Uname:      "dsymonds",
178
-			Gname:      "eng",
179
-			AccessTime: time.Unix(1244592783, 0),
180
-			ChangeTime: time.Unix(1244592783, 0),
181
-		}},
182
-	}, {
183
-		file: "testdata/v7.tar",
184
-		headers: []*Header{{
185
-			Name:     "small.txt",
186
-			Mode:     0444,
187
-			Uid:      73025,
188
-			Gid:      5000,
189
-			Size:     5,
190
-			ModTime:  time.Unix(1244593104, 0),
191
-			Typeflag: '0',
192
-		}, {
193
-			Name:     "small2.txt",
194
-			Mode:     0444,
195
-			Uid:      73025,
196
-			Gid:      5000,
197
-			Size:     11,
198
-			ModTime:  time.Unix(1244593104, 0),
199
-			Typeflag: '0',
200
-		}},
201
-	}, {
202
-		file: "testdata/pax.tar",
203
-		headers: []*Header{{
204
-			Name:       "a/123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100",
205
-			Mode:       0664,
206
-			Uid:        1000,
207
-			Gid:        1000,
208
-			Uname:      "shane",
209
-			Gname:      "shane",
210
-			Size:       7,
211
-			ModTime:    time.Unix(1350244992, 23960108),
212
-			ChangeTime: time.Unix(1350244992, 23960108),
213
-			AccessTime: time.Unix(1350244992, 23960108),
214
-			Typeflag:   TypeReg,
215
-			PAXRecords: map[string]string{
216
-				"path":  "a/123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100",
217
-				"mtime": "1350244992.023960108",
218
-				"atime": "1350244992.023960108",
219
-				"ctime": "1350244992.023960108",
220
-			},
221
-			Format: FormatPAX,
222
-		}, {
223
-			Name:       "a/b",
224
-			Mode:       0777,
225
-			Uid:        1000,
226
-			Gid:        1000,
227
-			Uname:      "shane",
228
-			Gname:      "shane",
229
-			Size:       0,
230
-			ModTime:    time.Unix(1350266320, 910238425),
231
-			ChangeTime: time.Unix(1350266320, 910238425),
232
-			AccessTime: time.Unix(1350266320, 910238425),
233
-			Typeflag:   TypeSymlink,
234
-			Linkname:   "123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100",
235
-			PAXRecords: map[string]string{
236
-				"linkpath": "123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100",
237
-				"mtime":    "1350266320.910238425",
238
-				"atime":    "1350266320.910238425",
239
-				"ctime":    "1350266320.910238425",
240
-			},
241
-			Format: FormatPAX,
242
-		}},
243
-	}, {
244
-		file: "testdata/pax-bad-hdr-file.tar",
245
-		err:  ErrHeader,
246
-	}, {
247
-		file: "testdata/pax-bad-mtime-file.tar",
248
-		err:  ErrHeader,
249
-	}, {
250
-		file: "testdata/pax-pos-size-file.tar",
251
-		headers: []*Header{{
252
-			Name:     "foo",
253
-			Mode:     0640,
254
-			Uid:      319973,
255
-			Gid:      5000,
256
-			Size:     999,
257
-			ModTime:  time.Unix(1442282516, 0),
258
-			Typeflag: '0',
259
-			Uname:    "joetsai",
260
-			Gname:    "eng",
261
-			PAXRecords: map[string]string{
262
-				"size": "000000000000000000000999",
263
-			},
264
-			Format: FormatPAX,
265
-		}},
266
-		chksums: []string{
267
-			"0afb597b283fe61b5d4879669a350556",
268
-		},
269
-	}, {
270
-		file: "testdata/pax-records.tar",
271
-		headers: []*Header{{
272
-			Typeflag: TypeReg,
273
-			Name:     "file",
274
-			Uname:    strings.Repeat("long", 10),
275
-			ModTime:  time.Unix(0, 0),
276
-			PAXRecords: map[string]string{
277
-				"GOLANG.pkg": "tar",
278
-				"comment":    "Hello, 世界",
279
-				"uname":      strings.Repeat("long", 10),
280
-			},
281
-			Format: FormatPAX,
282
-		}},
283
-	}, {
284
-		file: "testdata/pax-global-records.tar",
285
-		headers: []*Header{{
286
-			Typeflag:   TypeXGlobalHeader,
287
-			Name:       "global1",
288
-			PAXRecords: map[string]string{"path": "global1", "mtime": "1500000000.0"},
289
-			Format:     FormatPAX,
290
-		}, {
291
-			Typeflag: TypeReg,
292
-			Name:     "file1",
293
-			ModTime:  time.Unix(0, 0),
294
-			Format:   FormatUSTAR,
295
-		}, {
296
-			Typeflag:   TypeReg,
297
-			Name:       "file2",
298
-			PAXRecords: map[string]string{"path": "file2"},
299
-			ModTime:    time.Unix(0, 0),
300
-			Format:     FormatPAX,
301
-		}, {
302
-			Typeflag:   TypeXGlobalHeader,
303
-			Name:       "GlobalHead.0.0",
304
-			PAXRecords: map[string]string{"path": ""},
305
-			Format:     FormatPAX,
306
-		}, {
307
-			Typeflag: TypeReg,
308
-			Name:     "file3",
309
-			ModTime:  time.Unix(0, 0),
310
-			Format:   FormatUSTAR,
311
-		}, {
312
-			Typeflag:   TypeReg,
313
-			Name:       "file4",
314
-			ModTime:    time.Unix(1400000000, 0),
315
-			PAXRecords: map[string]string{"mtime": "1400000000"},
316
-			Format:     FormatPAX,
317
-		}},
318
-	}, {
319
-		file: "testdata/nil-uid.tar", // golang.org/issue/5290
320
-		headers: []*Header{{
321
-			Name:     "P1050238.JPG.log",
322
-			Mode:     0664,
323
-			Uid:      0,
324
-			Gid:      0,
325
-			Size:     14,
326
-			ModTime:  time.Unix(1365454838, 0),
327
-			Typeflag: TypeReg,
328
-			Linkname: "",
329
-			Uname:    "eyefi",
330
-			Gname:    "eyefi",
331
-			Devmajor: 0,
332
-			Devminor: 0,
333
-			Format:   FormatGNU,
334
-		}},
335
-	}, {
336
-		file: "testdata/xattrs.tar",
337
-		headers: []*Header{{
338
-			Name:       "small.txt",
339
-			Mode:       0644,
340
-			Uid:        1000,
341
-			Gid:        10,
342
-			Size:       5,
343
-			ModTime:    time.Unix(1386065770, 448252320),
344
-			Typeflag:   '0',
345
-			Uname:      "alex",
346
-			Gname:      "wheel",
347
-			AccessTime: time.Unix(1389782991, 419875220),
348
-			ChangeTime: time.Unix(1389782956, 794414986),
349
-			Xattrs: map[string]string{
350
-				"user.key":  "value",
351
-				"user.key2": "value2",
352
-				// Interestingly, selinux encodes the terminating null inside the xattr
353
-				"security.selinux": "unconfined_u:object_r:default_t:s0\x00",
354
-			},
355
-			PAXRecords: map[string]string{
356
-				"mtime":                         "1386065770.44825232",
357
-				"atime":                         "1389782991.41987522",
358
-				"ctime":                         "1389782956.794414986",
359
-				"SCHILY.xattr.user.key":         "value",
360
-				"SCHILY.xattr.user.key2":        "value2",
361
-				"SCHILY.xattr.security.selinux": "unconfined_u:object_r:default_t:s0\x00",
362
-			},
363
-			Format: FormatPAX,
364
-		}, {
365
-			Name:       "small2.txt",
366
-			Mode:       0644,
367
-			Uid:        1000,
368
-			Gid:        10,
369
-			Size:       11,
370
-			ModTime:    time.Unix(1386065770, 449252304),
371
-			Typeflag:   '0',
372
-			Uname:      "alex",
373
-			Gname:      "wheel",
374
-			AccessTime: time.Unix(1389782991, 419875220),
375
-			ChangeTime: time.Unix(1386065770, 449252304),
376
-			Xattrs: map[string]string{
377
-				"security.selinux": "unconfined_u:object_r:default_t:s0\x00",
378
-			},
379
-			PAXRecords: map[string]string{
380
-				"mtime":                         "1386065770.449252304",
381
-				"atime":                         "1389782991.41987522",
382
-				"ctime":                         "1386065770.449252304",
383
-				"SCHILY.xattr.security.selinux": "unconfined_u:object_r:default_t:s0\x00",
384
-			},
385
-			Format: FormatPAX,
386
-		}},
387
-	}, {
388
-		// Matches the behavior of GNU, BSD, and STAR tar utilities.
389
-		file: "testdata/gnu-multi-hdrs.tar",
390
-		headers: []*Header{{
391
-			Name:     "GNU2/GNU2/long-path-name",
392
-			Linkname: "GNU4/GNU4/long-linkpath-name",
393
-			ModTime:  time.Unix(0, 0),
394
-			Typeflag: '2',
395
-			Format:   FormatGNU,
396
-		}},
397
-	}, {
398
-		// GNU tar file with atime and ctime fields set.
399
-		// Created with the GNU tar v1.27.1.
400
-		//	tar --incremental -S -cvf gnu-incremental.tar test2
401
-		file: "testdata/gnu-incremental.tar",
402
-		headers: []*Header{{
403
-			Name:       "test2/",
404
-			Mode:       16877,
405
-			Uid:        1000,
406
-			Gid:        1000,
407
-			Size:       14,
408
-			ModTime:    time.Unix(1441973427, 0),
409
-			Typeflag:   'D',
410
-			Uname:      "rawr",
411
-			Gname:      "dsnet",
412
-			AccessTime: time.Unix(1441974501, 0),
413
-			ChangeTime: time.Unix(1441973436, 0),
414
-			Format:     FormatGNU,
415
-		}, {
416
-			Name:       "test2/foo",
417
-			Mode:       33188,
418
-			Uid:        1000,
419
-			Gid:        1000,
420
-			Size:       64,
421
-			ModTime:    time.Unix(1441973363, 0),
422
-			Typeflag:   '0',
423
-			Uname:      "rawr",
424
-			Gname:      "dsnet",
425
-			AccessTime: time.Unix(1441974501, 0),
426
-			ChangeTime: time.Unix(1441973436, 0),
427
-			Format:     FormatGNU,
428
-		}, {
429
-			Name:       "test2/sparse",
430
-			Mode:       33188,
431
-			Uid:        1000,
432
-			Gid:        1000,
433
-			Size:       536870912,
434
-			ModTime:    time.Unix(1441973427, 0),
435
-			Typeflag:   'S',
436
-			Uname:      "rawr",
437
-			Gname:      "dsnet",
438
-			AccessTime: time.Unix(1441991948, 0),
439
-			ChangeTime: time.Unix(1441973436, 0),
440
-			Format:     FormatGNU,
441
-		}},
442
-	}, {
443
-		// Matches the behavior of GNU and BSD tar utilities.
444
-		file: "testdata/pax-multi-hdrs.tar",
445
-		headers: []*Header{{
446
-			Name:     "bar",
447
-			Linkname: "PAX4/PAX4/long-linkpath-name",
448
-			ModTime:  time.Unix(0, 0),
449
-			Typeflag: '2',
450
-			PAXRecords: map[string]string{
451
-				"linkpath": "PAX4/PAX4/long-linkpath-name",
452
-			},
453
-			Format: FormatPAX,
454
-		}},
455
-	}, {
456
-		// Both BSD and GNU tar truncate long names at first NUL even
457
-		// if there is data following that NUL character.
458
-		// This is reasonable as GNU long names are C-strings.
459
-		file: "testdata/gnu-long-nul.tar",
460
-		headers: []*Header{{
461
-			Name:     "0123456789",
462
-			Mode:     0644,
463
-			Uid:      1000,
464
-			Gid:      1000,
465
-			ModTime:  time.Unix(1486082191, 0),
466
-			Typeflag: '0',
467
-			Uname:    "rawr",
468
-			Gname:    "dsnet",
469
-			Format:   FormatGNU,
470
-		}},
471
-	}, {
472
-		// This archive was generated by Writer but is readable by both
473
-		// GNU and BSD tar utilities.
474
-		// The archive generated by GNU is nearly byte-for-byte identical
475
-		// to the Go version except the Go version sets a negative Devminor
476
-		// just to force the GNU format.
477
-		file: "testdata/gnu-utf8.tar",
478
-		headers: []*Header{{
479
-			Name: "☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹",
480
-			Mode: 0644,
481
-			Uid:  1000, Gid: 1000,
482
-			ModTime:  time.Unix(0, 0),
483
-			Typeflag: '0',
484
-			Uname:    "☺",
485
-			Gname:    "⚹",
486
-			Format:   FormatGNU,
487
-		}},
488
-	}, {
489
-		// This archive was generated by Writer but is readable by both
490
-		// GNU and BSD tar utilities.
491
-		// The archive generated by GNU is nearly byte-for-byte identical
492
-		// to the Go version except the Go version sets a negative Devminor
493
-		// just to force the GNU format.
494
-		file: "testdata/gnu-not-utf8.tar",
495
-		headers: []*Header{{
496
-			Name:     "hi\x80\x81\x82\x83bye",
497
-			Mode:     0644,
498
-			Uid:      1000,
499
-			Gid:      1000,
500
-			ModTime:  time.Unix(0, 0),
501
-			Typeflag: '0',
502
-			Uname:    "rawr",
503
-			Gname:    "dsnet",
504
-			Format:   FormatGNU,
505
-		}},
506
-	}, {
507
-		// BSD tar v3.1.2 and GNU tar v1.27.1 both rejects PAX records
508
-		// with NULs in the key.
509
-		file: "testdata/pax-nul-xattrs.tar",
510
-		err:  ErrHeader,
511
-	}, {
512
-		// BSD tar v3.1.2 rejects a PAX path with NUL in the value, while
513
-		// GNU tar v1.27.1 simply truncates at first NUL.
514
-		// We emulate the behavior of BSD since it is strange doing NUL
515
-		// truncations since PAX records are length-prefix strings instead
516
-		// of NUL-terminated C-strings.
517
-		file: "testdata/pax-nul-path.tar",
518
-		err:  ErrHeader,
519
-	}, {
520
-		file: "testdata/neg-size.tar",
521
-		err:  ErrHeader,
522
-	}, {
523
-		file: "testdata/issue10968.tar",
524
-		err:  ErrHeader,
525
-	}, {
526
-		file: "testdata/issue11169.tar",
527
-		err:  ErrHeader,
528
-	}, {
529
-		file: "testdata/issue12435.tar",
530
-		err:  ErrHeader,
531
-	}, {
532
-		// Ensure that we can read back the original Header as written with
533
-		// a buggy pre-Go1.8 tar.Writer.
534
-		file: "testdata/invalid-go17.tar",
535
-		headers: []*Header{{
536
-			Name:     "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/foo",
537
-			Uid:      010000000,
538
-			ModTime:  time.Unix(0, 0),
539
-			Typeflag: '0',
540
-		}},
541
-	}, {
542
-		// USTAR archive with a regular entry with non-zero device numbers.
543
-		file: "testdata/ustar-file-devs.tar",
544
-		headers: []*Header{{
545
-			Name:     "file",
546
-			Mode:     0644,
547
-			Typeflag: '0',
548
-			ModTime:  time.Unix(0, 0),
549
-			Devmajor: 1,
550
-			Devminor: 1,
551
-			Format:   FormatUSTAR,
552
-		}},
553
-	}, {
554
-		// Generated by Go, works on BSD tar v3.1.2 and GNU tar v.1.27.1.
555
-		file: "testdata/gnu-nil-sparse-data.tar",
556
-		headers: []*Header{{
557
-			Name:     "sparse.db",
558
-			Typeflag: TypeGNUSparse,
559
-			Size:     1000,
560
-			ModTime:  time.Unix(0, 0),
561
-			Format:   FormatGNU,
562
-		}},
563
-	}, {
564
-		// Generated by Go, works on BSD tar v3.1.2 and GNU tar v.1.27.1.
565
-		file: "testdata/gnu-nil-sparse-hole.tar",
566
-		headers: []*Header{{
567
-			Name:     "sparse.db",
568
-			Typeflag: TypeGNUSparse,
569
-			Size:     1000,
570
-			ModTime:  time.Unix(0, 0),
571
-			Format:   FormatGNU,
572
-		}},
573
-	}, {
574
-		// Generated by Go, works on BSD tar v3.1.2 and GNU tar v.1.27.1.
575
-		file: "testdata/pax-nil-sparse-data.tar",
576
-		headers: []*Header{{
577
-			Name:     "sparse.db",
578
-			Typeflag: TypeReg,
579
-			Size:     1000,
580
-			ModTime:  time.Unix(0, 0),
581
-			PAXRecords: map[string]string{
582
-				"size":                "1512",
583
-				"GNU.sparse.major":    "1",
584
-				"GNU.sparse.minor":    "0",
585
-				"GNU.sparse.realsize": "1000",
586
-				"GNU.sparse.name":     "sparse.db",
587
-			},
588
-			Format: FormatPAX,
589
-		}},
590
-	}, {
591
-		// Generated by Go, works on BSD tar v3.1.2 and GNU tar v.1.27.1.
592
-		file: "testdata/pax-nil-sparse-hole.tar",
593
-		headers: []*Header{{
594
-			Name:     "sparse.db",
595
-			Typeflag: TypeReg,
596
-			Size:     1000,
597
-			ModTime:  time.Unix(0, 0),
598
-			PAXRecords: map[string]string{
599
-				"size":                "512",
600
-				"GNU.sparse.major":    "1",
601
-				"GNU.sparse.minor":    "0",
602
-				"GNU.sparse.realsize": "1000",
603
-				"GNU.sparse.name":     "sparse.db",
604
-			},
605
-			Format: FormatPAX,
606
-		}},
607
-	}, {
608
-		file: "testdata/trailing-slash.tar",
609
-		headers: []*Header{{
610
-			Typeflag: TypeDir,
611
-			Name:     strings.Repeat("123456789/", 30),
612
-			ModTime:  time.Unix(0, 0),
613
-			PAXRecords: map[string]string{
614
-				"path": strings.Repeat("123456789/", 30),
615
-			},
616
-			Format: FormatPAX,
617
-		}},
618
-	}}
619
-
620
-	for _, v := range vectors {
621
-		t.Run(path.Base(v.file), func(t *testing.T) {
622
-			f, err := os.Open(v.file)
623
-			if err != nil {
624
-				t.Fatalf("unexpected error: %v", err)
625
-			}
626
-			defer f.Close()
627
-
628
-			// Capture all headers and checksums.
629
-			var (
630
-				tr      = NewReader(f)
631
-				hdrs    []*Header
632
-				chksums []string
633
-				rdbuf   = make([]byte, 8)
634
-			)
635
-			for {
636
-				var hdr *Header
637
-				hdr, err = tr.Next()
638
-				if err != nil {
639
-					if err == io.EOF {
640
-						err = nil // Expected error
641
-					}
642
-					break
643
-				}
644
-				hdrs = append(hdrs, hdr)
645
-
646
-				if v.chksums == nil {
647
-					continue
648
-				}
649
-				h := md5.New()
650
-				_, err = io.CopyBuffer(h, tr, rdbuf) // Effectively an incremental read
651
-				if err != nil {
652
-					break
653
-				}
654
-				chksums = append(chksums, fmt.Sprintf("%x", h.Sum(nil)))
655
-			}
656
-
657
-			for i, hdr := range hdrs {
658
-				if i >= len(v.headers) {
659
-					t.Fatalf("entry %d: unexpected header:\ngot %+v", i, *hdr)
660
-					continue
661
-				}
662
-				if !reflect.DeepEqual(*hdr, *v.headers[i]) {
663
-					t.Fatalf("entry %d: incorrect header:\ngot  %+v\nwant %+v", i, *hdr, *v.headers[i])
664
-				}
665
-			}
666
-			if len(hdrs) != len(v.headers) {
667
-				t.Fatalf("got %d headers, want %d headers", len(hdrs), len(v.headers))
668
-			}
669
-
670
-			for i, sum := range chksums {
671
-				if i >= len(v.chksums) {
672
-					t.Fatalf("entry %d: unexpected sum: got %s", i, sum)
673
-					continue
674
-				}
675
-				if sum != v.chksums[i] {
676
-					t.Fatalf("entry %d: incorrect checksum: got %s, want %s", i, sum, v.chksums[i])
677
-				}
678
-			}
679
-
680
-			if err != v.err {
681
-				t.Fatalf("unexpected error: got %v, want %v", err, v.err)
682
-			}
683
-			f.Close()
684
-		})
685
-	}
686
-}
687
-
688
-func TestPartialRead(t *testing.T) {
689
-	type testCase struct {
690
-		cnt    int    // Number of bytes to read
691
-		output string // Expected value of string read
692
-	}
693
-	vectors := []struct {
694
-		file  string
695
-		cases []testCase
696
-	}{{
697
-		file: "testdata/gnu.tar",
698
-		cases: []testCase{
699
-			{4, "Kilt"},
700
-			{6, "Google"},
701
-		},
702
-	}, {
703
-		file: "testdata/sparse-formats.tar",
704
-		cases: []testCase{
705
-			{2, "\x00G"},
706
-			{4, "\x00G\x00o"},
707
-			{6, "\x00G\x00o\x00G"},
708
-			{8, "\x00G\x00o\x00G\x00o"},
709
-			{4, "end\n"},
710
-		},
711
-	}}
712
-
713
-	for _, v := range vectors {
714
-		t.Run(path.Base(v.file), func(t *testing.T) {
715
-			f, err := os.Open(v.file)
716
-			if err != nil {
717
-				t.Fatalf("Open() error: %v", err)
718
-			}
719
-			defer f.Close()
720
-
721
-			tr := NewReader(f)
722
-			for i, tc := range v.cases {
723
-				hdr, err := tr.Next()
724
-				if err != nil || hdr == nil {
725
-					t.Fatalf("entry %d, Next(): got %v, want %v", i, err, nil)
726
-				}
727
-				buf := make([]byte, tc.cnt)
728
-				if _, err := io.ReadFull(tr, buf); err != nil {
729
-					t.Fatalf("entry %d, ReadFull(): got %v, want %v", i, err, nil)
730
-				}
731
-				if string(buf) != tc.output {
732
-					t.Fatalf("entry %d, ReadFull(): got %q, want %q", i, string(buf), tc.output)
733
-				}
734
-			}
735
-
736
-			if _, err := tr.Next(); err != io.EOF {
737
-				t.Fatalf("Next(): got %v, want EOF", err)
738
-			}
739
-		})
740
-	}
741
-}
742
-
743
-func TestUninitializedRead(t *testing.T) {
744
-	f, err := os.Open("testdata/gnu.tar")
745
-	if err != nil {
746
-		t.Fatalf("Unexpected error: %v", err)
747
-	}
748
-	defer f.Close()
749
-
750
-	tr := NewReader(f)
751
-	_, err = tr.Read([]byte{})
752
-	if err == nil || err != io.EOF {
753
-		t.Errorf("Unexpected error: %v, wanted %v", err, io.EOF)
754
-	}
755
-
756
-}
757
-
758
-type reader struct{ io.Reader }
759
-type readSeeker struct{ io.ReadSeeker }
760
-type readBadSeeker struct{ io.ReadSeeker }
761
-
762
-func (rbs *readBadSeeker) Seek(int64, int) (int64, error) { return 0, fmt.Errorf("illegal seek") }
763
-
764
-// TestReadTruncation test the ending condition on various truncated files and
765
-// that truncated files are still detected even if the underlying io.Reader
766
-// satisfies io.Seeker.
767
-func TestReadTruncation(t *testing.T) {
768
-	var ss []string
769
-	for _, p := range []string{
770
-		"testdata/gnu.tar",
771
-		"testdata/ustar-file-reg.tar",
772
-		"testdata/pax-path-hdr.tar",
773
-		"testdata/sparse-formats.tar",
774
-	} {
775
-		buf, err := os.ReadFile(p)
776
-		if err != nil {
777
-			t.Fatalf("unexpected error: %v", err)
778
-		}
779
-		ss = append(ss, string(buf))
780
-	}
781
-
782
-	data1, data2, pax, sparse := ss[0], ss[1], ss[2], ss[3]
783
-	data2 += strings.Repeat("\x00", 10*512)
784
-	trash := strings.Repeat("garbage ", 64) // Exactly 512 bytes
785
-
786
-	vectors := []struct {
787
-		input string // Input stream
788
-		cnt   int    // Expected number of headers read
789
-		err   error  // Expected error outcome
790
-	}{
791
-		{"", 0, io.EOF}, // Empty file is a "valid" tar file
792
-		{data1[:511], 0, io.ErrUnexpectedEOF},
793
-		{data1[:512], 1, io.ErrUnexpectedEOF},
794
-		{data1[:1024], 1, io.EOF},
795
-		{data1[:1536], 2, io.ErrUnexpectedEOF},
796
-		{data1[:2048], 2, io.EOF},
797
-		{data1, 2, io.EOF},
798
-		{data1[:2048] + data2[:1536], 3, io.EOF},
799
-		{data2[:511], 0, io.ErrUnexpectedEOF},
800
-		{data2[:512], 1, io.ErrUnexpectedEOF},
801
-		{data2[:1195], 1, io.ErrUnexpectedEOF},
802
-		{data2[:1196], 1, io.EOF}, // Exact end of data and start of padding
803
-		{data2[:1200], 1, io.EOF},
804
-		{data2[:1535], 1, io.EOF},
805
-		{data2[:1536], 1, io.EOF}, // Exact end of padding
806
-		{data2[:1536] + trash[:1], 1, io.ErrUnexpectedEOF},
807
-		{data2[:1536] + trash[:511], 1, io.ErrUnexpectedEOF},
808
-		{data2[:1536] + trash, 1, ErrHeader},
809
-		{data2[:2048], 1, io.EOF}, // Exactly 1 empty block
810
-		{data2[:2048] + trash[:1], 1, io.ErrUnexpectedEOF},
811
-		{data2[:2048] + trash[:511], 1, io.ErrUnexpectedEOF},
812
-		{data2[:2048] + trash, 1, ErrHeader},
813
-		{data2[:2560], 1, io.EOF}, // Exactly 2 empty blocks (normal end-of-stream)
814
-		{data2[:2560] + trash[:1], 1, io.EOF},
815
-		{data2[:2560] + trash[:511], 1, io.EOF},
816
-		{data2[:2560] + trash, 1, io.EOF},
817
-		{data2[:3072], 1, io.EOF},
818
-		{pax, 0, io.EOF}, // PAX header without data is a "valid" tar file
819
-		{pax + trash[:1], 0, io.ErrUnexpectedEOF},
820
-		{pax + trash[:511], 0, io.ErrUnexpectedEOF},
821
-		{sparse[:511], 0, io.ErrUnexpectedEOF},
822
-		{sparse[:512], 0, io.ErrUnexpectedEOF},
823
-		{sparse[:3584], 1, io.EOF},
824
-		{sparse[:9200], 1, io.EOF}, // Terminate in padding of sparse header
825
-		{sparse[:9216], 1, io.EOF},
826
-		{sparse[:9728], 2, io.ErrUnexpectedEOF},
827
-		{sparse[:10240], 2, io.EOF},
828
-		{sparse[:11264], 2, io.ErrUnexpectedEOF},
829
-		{sparse, 5, io.EOF},
830
-		{sparse + trash, 5, io.EOF},
831
-	}
832
-
833
-	for i, v := range vectors {
834
-		for j := 0; j < 6; j++ {
835
-			var tr *Reader
836
-			var s1, s2 string
837
-
838
-			switch j {
839
-			case 0:
840
-				tr = NewReader(&reader{strings.NewReader(v.input)})
841
-				s1, s2 = "io.Reader", "auto"
842
-			case 1:
843
-				tr = NewReader(&reader{strings.NewReader(v.input)})
844
-				s1, s2 = "io.Reader", "manual"
845
-			case 2:
846
-				tr = NewReader(&readSeeker{strings.NewReader(v.input)})
847
-				s1, s2 = "io.ReadSeeker", "auto"
848
-			case 3:
849
-				tr = NewReader(&readSeeker{strings.NewReader(v.input)})
850
-				s1, s2 = "io.ReadSeeker", "manual"
851
-			case 4:
852
-				tr = NewReader(&readBadSeeker{strings.NewReader(v.input)})
853
-				s1, s2 = "ReadBadSeeker", "auto"
854
-			case 5:
855
-				tr = NewReader(&readBadSeeker{strings.NewReader(v.input)})
856
-				s1, s2 = "ReadBadSeeker", "manual"
857
-			}
858
-
859
-			var cnt int
860
-			var err error
861
-			for {
862
-				if _, err = tr.Next(); err != nil {
863
-					break
864
-				}
865
-				cnt++
866
-				if s2 == "manual" {
867
-					if _, err = tr.writeTo(io.Discard); err != nil {
868
-						break
869
-					}
870
-				}
871
-			}
872
-			if err != v.err {
873
-				t.Errorf("test %d, NewReader(%s) with %s discard: got %v, want %v",
874
-					i, s1, s2, err, v.err)
875
-			}
876
-			if cnt != v.cnt {
877
-				t.Errorf("test %d, NewReader(%s) with %s discard: got %d headers, want %d headers",
878
-					i, s1, s2, cnt, v.cnt)
879
-			}
880
-		}
881
-	}
882
-}
883
-
884
-// TestReadHeaderOnly tests that Reader does not attempt to read special
885
-// header-only files.
886
-func TestReadHeaderOnly(t *testing.T) {
887
-	f, err := os.Open("testdata/hdr-only.tar")
888
-	if err != nil {
889
-		t.Fatalf("unexpected error: %v", err)
890
-	}
891
-	defer f.Close()
892
-
893
-	var hdrs []*Header
894
-	tr := NewReader(f)
895
-	for {
896
-		hdr, err := tr.Next()
897
-		if err == io.EOF {
898
-			break
899
-		}
900
-		if err != nil {
901
-			t.Errorf("Next(): got %v, want %v", err, nil)
902
-			continue
903
-		}
904
-		hdrs = append(hdrs, hdr)
905
-
906
-		// If a special flag, we should read nothing.
907
-		cnt, _ := io.ReadFull(tr, []byte{0})
908
-		if cnt > 0 && hdr.Typeflag != TypeReg {
909
-			t.Errorf("ReadFull(...): got %d bytes, want 0 bytes", cnt)
910
-		}
911
-	}
912
-
913
-	// File is crafted with 16 entries. The later 8 are identical to the first
914
-	// 8 except that the size is set.
915
-	if len(hdrs) != 16 {
916
-		t.Fatalf("len(hdrs): got %d, want %d", len(hdrs), 16)
917
-	}
918
-	for i := 0; i < 8; i++ {
919
-		hdr1, hdr2 := hdrs[i+0], hdrs[i+8]
920
-		hdr1.Size, hdr2.Size = 0, 0
921
-		if !reflect.DeepEqual(*hdr1, *hdr2) {
922
-			t.Errorf("incorrect header:\ngot  %+v\nwant %+v", *hdr1, *hdr2)
923
-		}
924
-	}
925
-}
926
-
927
-func TestMergePAX(t *testing.T) {
928
-	vectors := []struct {
929
-		in   map[string]string
930
-		want *Header
931
-		ok   bool
932
-	}{{
933
-		in: map[string]string{
934
-			"path":  "a/b/c",
935
-			"uid":   "1000",
936
-			"mtime": "1350244992.023960108",
937
-		},
938
-		want: &Header{
939
-			Name:    "a/b/c",
940
-			Uid:     1000,
941
-			ModTime: time.Unix(1350244992, 23960108),
942
-			PAXRecords: map[string]string{
943
-				"path":  "a/b/c",
944
-				"uid":   "1000",
945
-				"mtime": "1350244992.023960108",
946
-			},
947
-		},
948
-		ok: true,
949
-	}, {
950
-		in: map[string]string{
951
-			"gid": "gtgergergersagersgers",
952
-		},
953
-		ok: false,
954
-	}, {
955
-		in: map[string]string{
956
-			"missing":          "missing",
957
-			"SCHILY.xattr.key": "value",
958
-		},
959
-		want: &Header{
960
-			Xattrs: map[string]string{"key": "value"},
961
-			PAXRecords: map[string]string{
962
-				"missing":          "missing",
963
-				"SCHILY.xattr.key": "value",
964
-			},
965
-		},
966
-		ok: true,
967
-	}}
968
-
969
-	for i, v := range vectors {
970
-		got := new(Header)
971
-		err := mergePAX(got, v.in)
972
-		if v.ok && !reflect.DeepEqual(*got, *v.want) {
973
-			t.Errorf("test %d, mergePAX(...):\ngot  %+v\nwant %+v", i, *got, *v.want)
974
-		}
975
-		if ok := err == nil; ok != v.ok {
976
-			t.Errorf("test %d, mergePAX(...): got %v, want %v", i, ok, v.ok)
977
-		}
978
-	}
979
-}
980
-
981
-func TestParsePAX(t *testing.T) {
982
-	vectors := []struct {
983
-		in   string
984
-		want map[string]string
985
-		ok   bool
986
-	}{
987
-		{"", nil, true},
988
-		{"6 k=1\n", map[string]string{"k": "1"}, true},
989
-		{"10 a=name\n", map[string]string{"a": "name"}, true},
990
-		{"9 a=name\n", map[string]string{"a": "name"}, true},
991
-		{"30 mtime=1350244992.023960108\n", map[string]string{"mtime": "1350244992.023960108"}, true},
992
-		{"3 somelongkey=\n", nil, false},
993
-		{"50 tooshort=\n", nil, false},
994
-		{"13 key1=haha\n13 key2=nana\n13 key3=kaka\n",
995
-			map[string]string{"key1": "haha", "key2": "nana", "key3": "kaka"}, true},
996
-		{"13 key1=val1\n13 key2=val2\n8 key1=\n",
997
-			map[string]string{"key1": "", "key2": "val2"}, true},
998
-		{"22 GNU.sparse.size=10\n26 GNU.sparse.numblocks=2\n" +
999
-			"23 GNU.sparse.offset=1\n25 GNU.sparse.numbytes=2\n" +
1000
-			"23 GNU.sparse.offset=3\n25 GNU.sparse.numbytes=4\n",
1001
-			map[string]string{paxGNUSparseSize: "10", paxGNUSparseNumBlocks: "2", paxGNUSparseMap: "1,2,3,4"}, true},
1002
-		{"22 GNU.sparse.size=10\n26 GNU.sparse.numblocks=1\n" +
1003
-			"25 GNU.sparse.numbytes=2\n23 GNU.sparse.offset=1\n",
1004
-			nil, false},
1005
-		{"22 GNU.sparse.size=10\n26 GNU.sparse.numblocks=1\n" +
1006
-			"25 GNU.sparse.offset=1,2\n25 GNU.sparse.numbytes=2\n",
1007
-			nil, false},
1008
-	}
1009
-
1010
-	for i, v := range vectors {
1011
-		r := strings.NewReader(v.in)
1012
-		got, err := parsePAX(r)
1013
-		if !reflect.DeepEqual(got, v.want) && !(len(got) == 0 && len(v.want) == 0) {
1014
-			t.Errorf("test %d, parsePAX():\ngot  %v\nwant %v", i, got, v.want)
1015
-		}
1016
-		if ok := err == nil; ok != v.ok {
1017
-			t.Errorf("test %d, parsePAX(): got %v, want %v", i, ok, v.ok)
1018
-		}
1019
-	}
1020
-}
1021
-
1022
-func TestReadOldGNUSparseMap(t *testing.T) {
1023
-	populateSparseMap := func(sa sparseArray, sps []string) []string {
1024
-		for i := 0; len(sps) > 0 && i < sa.MaxEntries(); i++ {
1025
-			copy(sa.Entry(i), sps[0])
1026
-			sps = sps[1:]
1027
-		}
1028
-		if len(sps) > 0 {
1029
-			copy(sa.IsExtended(), "\x80")
1030
-		}
1031
-		return sps
1032
-	}
1033
-
1034
-	makeInput := func(format Format, size string, sps ...string) (out []byte) {
1035
-		// Write the initial GNU header.
1036
-		var blk block
1037
-		gnu := blk.GNU()
1038
-		sparse := gnu.Sparse()
1039
-		copy(gnu.RealSize(), size)
1040
-		sps = populateSparseMap(sparse, sps)
1041
-		if format != FormatUnknown {
1042
-			blk.SetFormat(format)
1043
-		}
1044
-		out = append(out, blk[:]...)
1045
-
1046
-		// Write extended sparse blocks.
1047
-		for len(sps) > 0 {
1048
-			var blk block
1049
-			sps = populateSparseMap(blk.Sparse(), sps)
1050
-			out = append(out, blk[:]...)
1051
-		}
1052
-		return out
1053
-	}
1054
-
1055
-	makeSparseStrings := func(sp []sparseEntry) (out []string) {
1056
-		var f formatter
1057
-		for _, s := range sp {
1058
-			var b [24]byte
1059
-			f.formatNumeric(b[:12], s.Offset)
1060
-			f.formatNumeric(b[12:], s.Length)
1061
-			out = append(out, string(b[:]))
1062
-		}
1063
-		return out
1064
-	}
1065
-
1066
-	vectors := []struct {
1067
-		input    []byte
1068
-		wantMap  sparseDatas
1069
-		wantSize int64
1070
-		wantErr  error
1071
-	}{{
1072
-		input:   makeInput(FormatUnknown, ""),
1073
-		wantErr: ErrHeader,
1074
-	}, {
1075
-		input:    makeInput(FormatGNU, "1234", "fewa"),
1076
-		wantSize: 01234,
1077
-		wantErr:  ErrHeader,
1078
-	}, {
1079
-		input:    makeInput(FormatGNU, "0031"),
1080
-		wantSize: 031,
1081
-	}, {
1082
-		input:   makeInput(FormatGNU, "80"),
1083
-		wantErr: ErrHeader,
1084
-	}, {
1085
-		input: makeInput(FormatGNU, "1234",
1086
-			makeSparseStrings(sparseDatas{{0, 0}, {1, 1}})...),
1087
-		wantMap:  sparseDatas{{0, 0}, {1, 1}},
1088
-		wantSize: 01234,
1089
-	}, {
1090
-		input: makeInput(FormatGNU, "1234",
1091
-			append(makeSparseStrings(sparseDatas{{0, 0}, {1, 1}}), []string{"", "blah"}...)...),
1092
-		wantMap:  sparseDatas{{0, 0}, {1, 1}},
1093
-		wantSize: 01234,
1094
-	}, {
1095
-		input: makeInput(FormatGNU, "3333",
1096
-			makeSparseStrings(sparseDatas{{0, 1}, {2, 1}, {4, 1}, {6, 1}})...),
1097
-		wantMap:  sparseDatas{{0, 1}, {2, 1}, {4, 1}, {6, 1}},
1098
-		wantSize: 03333,
1099
-	}, {
1100
-		input: makeInput(FormatGNU, "",
1101
-			append(append(
1102
-				makeSparseStrings(sparseDatas{{0, 1}, {2, 1}}),
1103
-				[]string{"", ""}...),
1104
-				makeSparseStrings(sparseDatas{{4, 1}, {6, 1}})...)...),
1105
-		wantMap: sparseDatas{{0, 1}, {2, 1}, {4, 1}, {6, 1}},
1106
-	}, {
1107
-		input: makeInput(FormatGNU, "",
1108
-			makeSparseStrings(sparseDatas{{0, 1}, {2, 1}, {4, 1}, {6, 1}, {8, 1}, {10, 1}})...)[:blockSize],
1109
-		wantErr: io.ErrUnexpectedEOF,
1110
-	}, {
1111
-		input: makeInput(FormatGNU, "",
1112
-			makeSparseStrings(sparseDatas{{0, 1}, {2, 1}, {4, 1}, {6, 1}, {8, 1}, {10, 1}})...)[:3*blockSize/2],
1113
-		wantErr: io.ErrUnexpectedEOF,
1114
-	}, {
1115
-		input: makeInput(FormatGNU, "",
1116
-			makeSparseStrings(sparseDatas{{0, 1}, {2, 1}, {4, 1}, {6, 1}, {8, 1}, {10, 1}})...),
1117
-		wantMap: sparseDatas{{0, 1}, {2, 1}, {4, 1}, {6, 1}, {8, 1}, {10, 1}},
1118
-	}, {
1119
-		input: makeInput(FormatGNU, "",
1120
-			makeSparseStrings(sparseDatas{{10 << 30, 512}, {20 << 30, 512}})...),
1121
-		wantMap: sparseDatas{{10 << 30, 512}, {20 << 30, 512}},
1122
-	}}
1123
-
1124
-	for i, v := range vectors {
1125
-		var blk block
1126
-		var hdr Header
1127
-		v.input = v.input[copy(blk[:], v.input):]
1128
-		tr := Reader{r: bytes.NewReader(v.input)}
1129
-		got, err := tr.readOldGNUSparseMap(&hdr, &blk)
1130
-		if !equalSparseEntries(got, v.wantMap) {
1131
-			t.Errorf("test %d, readOldGNUSparseMap(): got %v, want %v", i, got, v.wantMap)
1132
-		}
1133
-		if err != v.wantErr {
1134
-			t.Errorf("test %d, readOldGNUSparseMap() = %v, want %v", i, err, v.wantErr)
1135
-		}
1136
-		if hdr.Size != v.wantSize {
1137
-			t.Errorf("test %d, Header.Size = %d, want %d", i, hdr.Size, v.wantSize)
1138
-		}
1139
-	}
1140
-}
1141
-
1142
-func TestReadGNUSparsePAXHeaders(t *testing.T) {
1143
-	padInput := func(s string) string {
1144
-		return s + string(zeroBlock[:blockPadding(int64(len(s)))])
1145
-	}
1146
-
1147
-	vectors := []struct {
1148
-		inputData string
1149
-		inputHdrs map[string]string
1150
-		wantMap   sparseDatas
1151
-		wantSize  int64
1152
-		wantName  string
1153
-		wantErr   error
1154
-	}{{
1155
-		inputHdrs: nil,
1156
-		wantErr:   nil,
1157
-	}, {
1158
-		inputHdrs: map[string]string{
1159
-			paxGNUSparseNumBlocks: strconv.FormatInt(math.MaxInt64, 10),
1160
-			paxGNUSparseMap:       "0,1,2,3",
1161
-		},
1162
-		wantErr: ErrHeader,
1163
-	}, {
1164
-		inputHdrs: map[string]string{
1165
-			paxGNUSparseNumBlocks: "4\x00",
1166
-			paxGNUSparseMap:       "0,1,2,3",
1167
-		},
1168
-		wantErr: ErrHeader,
1169
-	}, {
1170
-		inputHdrs: map[string]string{
1171
-			paxGNUSparseNumBlocks: "4",
1172
-			paxGNUSparseMap:       "0,1,2,3",
1173
-		},
1174
-		wantErr: ErrHeader,
1175
-	}, {
1176
-		inputHdrs: map[string]string{
1177
-			paxGNUSparseNumBlocks: "2",
1178
-			paxGNUSparseMap:       "0,1,2,3",
1179
-		},
1180
-		wantMap: sparseDatas{{0, 1}, {2, 3}},
1181
-	}, {
1182
-		inputHdrs: map[string]string{
1183
-			paxGNUSparseNumBlocks: "2",
1184
-			paxGNUSparseMap:       "0, 1,2,3",
1185
-		},
1186
-		wantErr: ErrHeader,
1187
-	}, {
1188
-		inputHdrs: map[string]string{
1189
-			paxGNUSparseNumBlocks: "2",
1190
-			paxGNUSparseMap:       "0,1,02,3",
1191
-			paxGNUSparseRealSize:  "4321",
1192
-		},
1193
-		wantMap:  sparseDatas{{0, 1}, {2, 3}},
1194
-		wantSize: 4321,
1195
-	}, {
1196
-		inputHdrs: map[string]string{
1197
-			paxGNUSparseNumBlocks: "2",
1198
-			paxGNUSparseMap:       "0,one1,2,3",
1199
-		},
1200
-		wantErr: ErrHeader,
1201
-	}, {
1202
-		inputHdrs: map[string]string{
1203
-			paxGNUSparseMajor:     "0",
1204
-			paxGNUSparseMinor:     "0",
1205
-			paxGNUSparseNumBlocks: "2",
1206
-			paxGNUSparseMap:       "0,1,2,3",
1207
-			paxGNUSparseSize:      "1234",
1208
-			paxGNUSparseRealSize:  "4321",
1209
-			paxGNUSparseName:      "realname",
1210
-		},
1211
-		wantMap:  sparseDatas{{0, 1}, {2, 3}},
1212
-		wantSize: 1234,
1213
-		wantName: "realname",
1214
-	}, {
1215
-		inputHdrs: map[string]string{
1216
-			paxGNUSparseMajor:     "0",
1217
-			paxGNUSparseMinor:     "0",
1218
-			paxGNUSparseNumBlocks: "1",
1219
-			paxGNUSparseMap:       "10737418240,512",
1220
-			paxGNUSparseSize:      "10737418240",
1221
-			paxGNUSparseName:      "realname",
1222
-		},
1223
-		wantMap:  sparseDatas{{10737418240, 512}},
1224
-		wantSize: 10737418240,
1225
-		wantName: "realname",
1226
-	}, {
1227
-		inputHdrs: map[string]string{
1228
-			paxGNUSparseMajor:     "0",
1229
-			paxGNUSparseMinor:     "0",
1230
-			paxGNUSparseNumBlocks: "0",
1231
-			paxGNUSparseMap:       "",
1232
-		},
1233
-		wantMap: sparseDatas{},
1234
-	}, {
1235
-		inputHdrs: map[string]string{
1236
-			paxGNUSparseMajor:     "0",
1237
-			paxGNUSparseMinor:     "1",
1238
-			paxGNUSparseNumBlocks: "4",
1239
-			paxGNUSparseMap:       "0,5,10,5,20,5,30,5",
1240
-		},
1241
-		wantMap: sparseDatas{{0, 5}, {10, 5}, {20, 5}, {30, 5}},
1242
-	}, {
1243
-		inputHdrs: map[string]string{
1244
-			paxGNUSparseMajor:     "1",
1245
-			paxGNUSparseMinor:     "0",
1246
-			paxGNUSparseNumBlocks: "4",
1247
-			paxGNUSparseMap:       "0,5,10,5,20,5,30,5",
1248
-		},
1249
-		wantErr: io.ErrUnexpectedEOF,
1250
-	}, {
1251
-		inputData: padInput("0\n"),
1252
-		inputHdrs: map[string]string{paxGNUSparseMajor: "1", paxGNUSparseMinor: "0"},
1253
-		wantMap:   sparseDatas{},
1254
-	}, {
1255
-		inputData: padInput("0\n")[:blockSize-1] + "#",
1256
-		inputHdrs: map[string]string{paxGNUSparseMajor: "1", paxGNUSparseMinor: "0"},
1257
-		wantMap:   sparseDatas{},
1258
-	}, {
1259
-		inputData: padInput("0"),
1260
-		inputHdrs: map[string]string{paxGNUSparseMajor: "1", paxGNUSparseMinor: "0"},
1261
-		wantErr:   io.ErrUnexpectedEOF,
1262
-	}, {
1263
-		inputData: padInput("ab\n"),
1264
-		inputHdrs: map[string]string{paxGNUSparseMajor: "1", paxGNUSparseMinor: "0"},
1265
-		wantErr:   ErrHeader,
1266
-	}, {
1267
-		inputData: padInput("1\n2\n3\n"),
1268
-		inputHdrs: map[string]string{paxGNUSparseMajor: "1", paxGNUSparseMinor: "0"},
1269
-		wantMap:   sparseDatas{{2, 3}},
1270
-	}, {
1271
-		inputData: padInput("1\n2\n"),
1272
-		inputHdrs: map[string]string{paxGNUSparseMajor: "1", paxGNUSparseMinor: "0"},
1273
-		wantErr:   io.ErrUnexpectedEOF,
1274
-	}, {
1275
-		inputData: padInput("1\n2\n\n"),
1276
-		inputHdrs: map[string]string{paxGNUSparseMajor: "1", paxGNUSparseMinor: "0"},
1277
-		wantErr:   ErrHeader,
1278
-	}, {
1279
-		inputData: string(zeroBlock[:]) + padInput("0\n"),
1280
-		inputHdrs: map[string]string{paxGNUSparseMajor: "1", paxGNUSparseMinor: "0"},
1281
-		wantErr:   ErrHeader,
1282
-	}, {
1283
-		inputData: strings.Repeat("0", blockSize) + padInput("1\n5\n1\n"),
1284
-		inputHdrs: map[string]string{paxGNUSparseMajor: "1", paxGNUSparseMinor: "0"},
1285
-		wantMap:   sparseDatas{{5, 1}},
1286
-	}, {
1287
-		inputData: padInput(fmt.Sprintf("%d\n", int64(math.MaxInt64))),
1288
-		inputHdrs: map[string]string{paxGNUSparseMajor: "1", paxGNUSparseMinor: "0"},
1289
-		wantErr:   ErrHeader,
1290
-	}, {
1291
-		inputData: padInput(strings.Repeat("0", 300) + "1\n" + strings.Repeat("0", 1000) + "5\n" + strings.Repeat("0", 800) + "2\n"),
1292
-		inputHdrs: map[string]string{paxGNUSparseMajor: "1", paxGNUSparseMinor: "0"},
1293
-		wantMap:   sparseDatas{{5, 2}},
1294
-	}, {
1295
-		inputData: padInput("2\n10737418240\n512\n21474836480\n512\n"),
1296
-		inputHdrs: map[string]string{paxGNUSparseMajor: "1", paxGNUSparseMinor: "0"},
1297
-		wantMap:   sparseDatas{{10737418240, 512}, {21474836480, 512}},
1298
-	}, {
1299
-		inputData: padInput("100\n" + func() string {
1300
-			var ss []string
1301
-			for i := 0; i < 100; i++ {
1302
-				ss = append(ss, fmt.Sprintf("%d\n%d\n", int64(i)<<30, 512))
1303
-			}
1304
-			return strings.Join(ss, "")
1305
-		}()),
1306
-		inputHdrs: map[string]string{paxGNUSparseMajor: "1", paxGNUSparseMinor: "0"},
1307
-		wantMap: func() (spd sparseDatas) {
1308
-			for i := 0; i < 100; i++ {
1309
-				spd = append(spd, sparseEntry{int64(i) << 30, 512})
1310
-			}
1311
-			return spd
1312
-		}(),
1313
-	}}
1314
-
1315
-	for i, v := range vectors {
1316
-		var hdr Header
1317
-		hdr.PAXRecords = v.inputHdrs
1318
-		r := strings.NewReader(v.inputData + "#") // Add canary byte
1319
-		tr := Reader{curr: &regFileReader{r, int64(r.Len())}}
1320
-		got, err := tr.readGNUSparsePAXHeaders(&hdr)
1321
-		if !equalSparseEntries(got, v.wantMap) {
1322
-			t.Errorf("test %d, readGNUSparsePAXHeaders(): got %v, want %v", i, got, v.wantMap)
1323
-		}
1324
-		if err != v.wantErr {
1325
-			t.Errorf("test %d, readGNUSparsePAXHeaders() = %v, want %v", i, err, v.wantErr)
1326
-		}
1327
-		if hdr.Size != v.wantSize {
1328
-			t.Errorf("test %d, Header.Size = %d, want %d", i, hdr.Size, v.wantSize)
1329
-		}
1330
-		if hdr.Name != v.wantName {
1331
-			t.Errorf("test %d, Header.Name = %s, want %s", i, hdr.Name, v.wantName)
1332
-		}
1333
-		if v.wantErr == nil && r.Len() == 0 {
1334
-			t.Errorf("test %d, canary byte unexpectedly consumed", i)
1335
-		}
1336
-	}
1337
-}
1338
-
1339
-// testNonEmptyReader wraps an io.Reader and ensures that
1340
-// Read is never called with an empty buffer.
1341
-type testNonEmptyReader struct{ io.Reader }
1342
-
1343
-func (r testNonEmptyReader) Read(b []byte) (int, error) {
1344
-	if len(b) == 0 {
1345
-		return 0, errors.New("unexpected empty Read call")
1346
-	}
1347
-	return r.Reader.Read(b)
1348
-}
1349
-
1350
-func TestFileReader(t *testing.T) {
1351
-	type (
1352
-		testRead struct { // Read(cnt) == (wantStr, wantErr)
1353
-			cnt     int
1354
-			wantStr string
1355
-			wantErr error
1356
-		}
1357
-		testWriteTo struct { // WriteTo(testFile{ops}) == (wantCnt, wantErr)
1358
-			ops     fileOps
1359
-			wantCnt int64
1360
-			wantErr error
1361
-		}
1362
-		testRemaining struct { // LogicalRemaining() == wantLCnt, PhysicalRemaining() == wantPCnt
1363
-			wantLCnt int64
1364
-			wantPCnt int64
1365
-		}
1366
-		testFnc interface{} // testRead | testWriteTo | testRemaining
1367
-	)
1368
-
1369
-	type (
1370
-		makeReg struct {
1371
-			str  string
1372
-			size int64
1373
-		}
1374
-		makeSparse struct {
1375
-			makeReg makeReg
1376
-			spd     sparseDatas
1377
-			size    int64
1378
-		}
1379
-		fileMaker interface{} // makeReg | makeSparse
1380
-	)
1381
-
1382
-	vectors := []struct {
1383
-		maker fileMaker
1384
-		tests []testFnc
1385
-	}{{
1386
-		maker: makeReg{"", 0},
1387
-		tests: []testFnc{
1388
-			testRemaining{0, 0},
1389
-			testRead{0, "", io.EOF},
1390
-			testRead{1, "", io.EOF},
1391
-			testWriteTo{nil, 0, nil},
1392
-			testRemaining{0, 0},
1393
-		},
1394
-	}, {
1395
-		maker: makeReg{"", 1},
1396
-		tests: []testFnc{
1397
-			testRemaining{1, 1},
1398
-			testRead{5, "", io.ErrUnexpectedEOF},
1399
-			testWriteTo{nil, 0, io.ErrUnexpectedEOF},
1400
-			testRemaining{1, 1},
1401
-		},
1402
-	}, {
1403
-		maker: makeReg{"hello", 5},
1404
-		tests: []testFnc{
1405
-			testRemaining{5, 5},
1406
-			testRead{5, "hello", io.EOF},
1407
-			testRemaining{0, 0},
1408
-		},
1409
-	}, {
1410
-		maker: makeReg{"hello, world", 50},
1411
-		tests: []testFnc{
1412
-			testRemaining{50, 50},
1413
-			testRead{7, "hello, ", nil},
1414
-			testRemaining{43, 43},
1415
-			testRead{5, "world", nil},
1416
-			testRemaining{38, 38},
1417
-			testWriteTo{nil, 0, io.ErrUnexpectedEOF},
1418
-			testRead{1, "", io.ErrUnexpectedEOF},
1419
-			testRemaining{38, 38},
1420
-		},
1421
-	}, {
1422
-		maker: makeReg{"hello, world", 5},
1423
-		tests: []testFnc{
1424
-			testRemaining{5, 5},
1425
-			testRead{0, "", nil},
1426
-			testRead{4, "hell", nil},
1427
-			testRemaining{1, 1},
1428
-			testWriteTo{fileOps{"o"}, 1, nil},
1429
-			testRemaining{0, 0},
1430
-			testWriteTo{nil, 0, nil},
1431
-			testRead{0, "", io.EOF},
1432
-		},
1433
-	}, {
1434
-		maker: makeSparse{makeReg{"abcde", 5}, sparseDatas{{0, 2}, {5, 3}}, 8},
1435
-		tests: []testFnc{
1436
-			testRemaining{8, 5},
1437
-			testRead{3, "ab\x00", nil},
1438
-			testRead{10, "\x00\x00cde", io.EOF},
1439
-			testRemaining{0, 0},
1440
-		},
1441
-	}, {
1442
-		maker: makeSparse{makeReg{"abcde", 5}, sparseDatas{{0, 2}, {5, 3}}, 8},
1443
-		tests: []testFnc{
1444
-			testRemaining{8, 5},
1445
-			testWriteTo{fileOps{"ab", int64(3), "cde"}, 8, nil},
1446
-			testRemaining{0, 0},
1447
-		},
1448
-	}, {
1449
-		maker: makeSparse{makeReg{"abcde", 5}, sparseDatas{{0, 2}, {5, 3}}, 10},
1450
-		tests: []testFnc{
1451
-			testRemaining{10, 5},
1452
-			testRead{100, "ab\x00\x00\x00cde\x00\x00", io.EOF},
1453
-			testRemaining{0, 0},
1454
-		},
1455
-	}, {
1456
-		maker: makeSparse{makeReg{"abc", 5}, sparseDatas{{0, 2}, {5, 3}}, 10},
1457
-		tests: []testFnc{
1458
-			testRemaining{10, 5},
1459
-			testRead{100, "ab\x00\x00\x00c", io.ErrUnexpectedEOF},
1460
-			testRemaining{4, 2},
1461
-		},
1462
-	}, {
1463
-		maker: makeSparse{makeReg{"abcde", 5}, sparseDatas{{1, 3}, {6, 2}}, 8},
1464
-		tests: []testFnc{
1465
-			testRemaining{8, 5},
1466
-			testRead{8, "\x00abc\x00\x00de", io.EOF},
1467
-			testRemaining{0, 0},
1468
-		},
1469
-	}, {
1470
-		maker: makeSparse{makeReg{"abcde", 5}, sparseDatas{{1, 3}, {6, 0}, {6, 0}, {6, 2}}, 8},
1471
-		tests: []testFnc{
1472
-			testRemaining{8, 5},
1473
-			testRead{8, "\x00abc\x00\x00de", io.EOF},
1474
-			testRemaining{0, 0},
1475
-		},
1476
-	}, {
1477
-		maker: makeSparse{makeReg{"abcde", 5}, sparseDatas{{1, 3}, {6, 0}, {6, 0}, {6, 2}}, 8},
1478
-		tests: []testFnc{
1479
-			testRemaining{8, 5},
1480
-			testWriteTo{fileOps{int64(1), "abc", int64(2), "de"}, 8, nil},
1481
-			testRemaining{0, 0},
1482
-		},
1483
-	}, {
1484
-		maker: makeSparse{makeReg{"abcde", 5}, sparseDatas{{1, 3}, {6, 2}}, 10},
1485
-		tests: []testFnc{
1486
-			testRead{100, "\x00abc\x00\x00de\x00\x00", io.EOF},
1487
-		},
1488
-	}, {
1489
-		maker: makeSparse{makeReg{"abcde", 5}, sparseDatas{{1, 3}, {6, 2}}, 10},
1490
-		tests: []testFnc{
1491
-			testWriteTo{fileOps{int64(1), "abc", int64(2), "de", int64(1), "\x00"}, 10, nil},
1492
-		},
1493
-	}, {
1494
-		maker: makeSparse{makeReg{"abcde", 5}, sparseDatas{{1, 3}, {6, 2}, {8, 0}, {8, 0}, {8, 0}, {8, 0}}, 10},
1495
-		tests: []testFnc{
1496
-			testRead{100, "\x00abc\x00\x00de\x00\x00", io.EOF},
1497
-		},
1498
-	}, {
1499
-		maker: makeSparse{makeReg{"", 0}, sparseDatas{}, 2},
1500
-		tests: []testFnc{
1501
-			testRead{100, "\x00\x00", io.EOF},
1502
-		},
1503
-	}, {
1504
-		maker: makeSparse{makeReg{"", 8}, sparseDatas{{1, 3}, {6, 5}}, 15},
1505
-		tests: []testFnc{
1506
-			testRead{100, "\x00", io.ErrUnexpectedEOF},
1507
-		},
1508
-	}, {
1509
-		maker: makeSparse{makeReg{"ab", 2}, sparseDatas{{1, 3}, {6, 5}}, 15},
1510
-		tests: []testFnc{
1511
-			testRead{100, "\x00ab", errMissData},
1512
-		},
1513
-	}, {
1514
-		maker: makeSparse{makeReg{"ab", 8}, sparseDatas{{1, 3}, {6, 5}}, 15},
1515
-		tests: []testFnc{
1516
-			testRead{100, "\x00ab", io.ErrUnexpectedEOF},
1517
-		},
1518
-	}, {
1519
-		maker: makeSparse{makeReg{"abc", 3}, sparseDatas{{1, 3}, {6, 5}}, 15},
1520
-		tests: []testFnc{
1521
-			testRead{100, "\x00abc\x00\x00", errMissData},
1522
-		},
1523
-	}, {
1524
-		maker: makeSparse{makeReg{"abc", 8}, sparseDatas{{1, 3}, {6, 5}}, 15},
1525
-		tests: []testFnc{
1526
-			testRead{100, "\x00abc\x00\x00", io.ErrUnexpectedEOF},
1527
-		},
1528
-	}, {
1529
-		maker: makeSparse{makeReg{"abcde", 5}, sparseDatas{{1, 3}, {6, 5}}, 15},
1530
-		tests: []testFnc{
1531
-			testRead{100, "\x00abc\x00\x00de", errMissData},
1532
-		},
1533
-	}, {
1534
-		maker: makeSparse{makeReg{"abcde", 5}, sparseDatas{{1, 3}, {6, 5}}, 15},
1535
-		tests: []testFnc{
1536
-			testWriteTo{fileOps{int64(1), "abc", int64(2), "de"}, 8, errMissData},
1537
-		},
1538
-	}, {
1539
-		maker: makeSparse{makeReg{"abcde", 8}, sparseDatas{{1, 3}, {6, 5}}, 15},
1540
-		tests: []testFnc{
1541
-			testRead{100, "\x00abc\x00\x00de", io.ErrUnexpectedEOF},
1542
-		},
1543
-	}, {
1544
-		maker: makeSparse{makeReg{"abcdefghEXTRA", 13}, sparseDatas{{1, 3}, {6, 5}}, 15},
1545
-		tests: []testFnc{
1546
-			testRemaining{15, 13},
1547
-			testRead{100, "\x00abc\x00\x00defgh\x00\x00\x00\x00", errUnrefData},
1548
-			testWriteTo{nil, 0, errUnrefData},
1549
-			testRemaining{0, 5},
1550
-		},
1551
-	}, {
1552
-		maker: makeSparse{makeReg{"abcdefghEXTRA", 13}, sparseDatas{{1, 3}, {6, 5}}, 15},
1553
-		tests: []testFnc{
1554
-			testRemaining{15, 13},
1555
-			testWriteTo{fileOps{int64(1), "abc", int64(2), "defgh", int64(4)}, 15, errUnrefData},
1556
-			testRead{100, "", errUnrefData},
1557
-			testRemaining{0, 5},
1558
-		},
1559
-	}}
1560
-
1561
-	for i, v := range vectors {
1562
-		var fr fileReader
1563
-		switch maker := v.maker.(type) {
1564
-		case makeReg:
1565
-			r := testNonEmptyReader{strings.NewReader(maker.str)}
1566
-			fr = &regFileReader{r, maker.size}
1567
-		case makeSparse:
1568
-			if !validateSparseEntries(maker.spd, maker.size) {
1569
-				t.Fatalf("invalid sparse map: %v", maker.spd)
1570
-			}
1571
-			sph := invertSparseEntries(maker.spd, maker.size)
1572
-			r := testNonEmptyReader{strings.NewReader(maker.makeReg.str)}
1573
-			fr = &regFileReader{r, maker.makeReg.size}
1574
-			fr = &sparseFileReader{fr, sph, 0}
1575
-		default:
1576
-			t.Fatalf("test %d, unknown make operation: %T", i, maker)
1577
-		}
1578
-
1579
-		for j, tf := range v.tests {
1580
-			switch tf := tf.(type) {
1581
-			case testRead:
1582
-				b := make([]byte, tf.cnt)
1583
-				n, err := fr.Read(b)
1584
-				if got := string(b[:n]); got != tf.wantStr || err != tf.wantErr {
1585
-					t.Errorf("test %d.%d, Read(%d):\ngot  (%q, %v)\nwant (%q, %v)", i, j, tf.cnt, got, err, tf.wantStr, tf.wantErr)
1586
-				}
1587
-			case testWriteTo:
1588
-				f := &testFile{ops: tf.ops}
1589
-				got, err := fr.WriteTo(f)
1590
-				if _, ok := err.(testError); ok {
1591
-					t.Errorf("test %d.%d, WriteTo(): %v", i, j, err)
1592
-				} else if got != tf.wantCnt || err != tf.wantErr {
1593
-					t.Errorf("test %d.%d, WriteTo() = (%d, %v), want (%d, %v)", i, j, got, err, tf.wantCnt, tf.wantErr)
1594
-				}
1595
-				if len(f.ops) > 0 {
1596
-					t.Errorf("test %d.%d, expected %d more operations", i, j, len(f.ops))
1597
-				}
1598
-			case testRemaining:
1599
-				if got := fr.LogicalRemaining(); got != tf.wantLCnt {
1600
-					t.Errorf("test %d.%d, LogicalRemaining() = %d, want %d", i, j, got, tf.wantLCnt)
1601
-				}
1602
-				if got := fr.PhysicalRemaining(); got != tf.wantPCnt {
1603
-					t.Errorf("test %d.%d, PhysicalRemaining() = %d, want %d", i, j, got, tf.wantPCnt)
1604
-				}
1605
-			default:
1606
-				t.Fatalf("test %d.%d, unknown test operation: %T", i, j, tf)
1607
-			}
1608
-		}
1609
-	}
1610
-}
1611 1
deleted file mode 100644
... ...
@@ -1,21 +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
-//go:build aix || linux || dragonfly || openbsd || solaris
6
-// +build aix linux dragonfly openbsd solaris
7
-
8
-package tar
9
-
10
-import (
11
-	"syscall"
12
-	"time"
13
-)
14
-
15
-func statAtime(st *syscall.Stat_t) time.Time {
16
-	return time.Unix(st.Atim.Unix())
17
-}
18
-
19
-func statCtime(st *syscall.Stat_t) time.Time {
20
-	return time.Unix(st.Ctim.Unix())
21
-}
22 1
deleted file mode 100644
... ...
@@ -1,21 +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
-//go:build darwin || freebsd || netbsd
6
-// +build darwin freebsd netbsd
7
-
8
-package tar
9
-
10
-import (
11
-	"syscall"
12
-	"time"
13
-)
14
-
15
-func statAtime(st *syscall.Stat_t) time.Time {
16
-	return time.Unix(st.Atimespec.Unix())
17
-}
18
-
19
-func statCtime(st *syscall.Stat_t) time.Time {
20
-	return time.Unix(st.Ctimespec.Unix())
21
-}
22 1
deleted file mode 100644
... ...
@@ -1,82 +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
-//go:build aix || linux || darwin || dragonfly || freebsd || openbsd || netbsd || solaris
6
-// +build aix linux darwin dragonfly freebsd openbsd netbsd solaris
7
-
8
-package tar
9
-
10
-import (
11
-	"io/fs"
12
-	"runtime"
13
-	"syscall"
14
-)
15
-
16
-func init() {
17
-	sysStat = statUnix
18
-}
19
-
20
-func statUnix(fi fs.FileInfo, h *Header) error {
21
-	sys, ok := fi.Sys().(*syscall.Stat_t)
22
-	if !ok {
23
-		return nil
24
-	}
25
-	h.Uid = int(sys.Uid)
26
-	h.Gid = int(sys.Gid)
27
-
28
-	// TODO(bradfitz): populate username & group.  os/user
29
-	// doesn't cache LookupId lookups, and lacks group
30
-	// lookup functions.
31
-	h.AccessTime = statAtime(sys)
32
-	h.ChangeTime = statCtime(sys)
33
-
34
-	// Best effort at populating Devmajor and Devminor.
35
-	if h.Typeflag == TypeChar || h.Typeflag == TypeBlock {
36
-		dev := uint64(sys.Rdev) // May be int32 or uint32
37
-		switch runtime.GOOS {
38
-		case "aix":
39
-			var major, minor uint32
40
-			major = uint32((dev & 0x3fffffff00000000) >> 32)
41
-			minor = uint32((dev & 0x00000000ffffffff) >> 0)
42
-			h.Devmajor, h.Devminor = int64(major), int64(minor)
43
-		case "linux":
44
-			// Copied from golang.org/x/sys/unix/dev_linux.go.
45
-			major := uint32((dev & 0x00000000000fff00) >> 8)
46
-			major |= uint32((dev & 0xfffff00000000000) >> 32)
47
-			minor := uint32((dev & 0x00000000000000ff) >> 0)
48
-			minor |= uint32((dev & 0x00000ffffff00000) >> 12)
49
-			h.Devmajor, h.Devminor = int64(major), int64(minor)
50
-		case "darwin", "ios":
51
-			// Copied from golang.org/x/sys/unix/dev_darwin.go.
52
-			major := uint32((dev >> 24) & 0xff)
53
-			minor := uint32(dev & 0xffffff)
54
-			h.Devmajor, h.Devminor = int64(major), int64(minor)
55
-		case "dragonfly":
56
-			// Copied from golang.org/x/sys/unix/dev_dragonfly.go.
57
-			major := uint32((dev >> 8) & 0xff)
58
-			minor := uint32(dev & 0xffff00ff)
59
-			h.Devmajor, h.Devminor = int64(major), int64(minor)
60
-		case "freebsd":
61
-			// Copied from golang.org/x/sys/unix/dev_freebsd.go.
62
-			major := uint32((dev >> 8) & 0xff)
63
-			minor := uint32(dev & 0xffff00ff)
64
-			h.Devmajor, h.Devminor = int64(major), int64(minor)
65
-		case "netbsd":
66
-			// Copied from golang.org/x/sys/unix/dev_netbsd.go.
67
-			major := uint32((dev & 0x000fff00) >> 8)
68
-			minor := uint32((dev & 0x000000ff) >> 0)
69
-			minor |= uint32((dev & 0xfff00000) >> 12)
70
-			h.Devmajor, h.Devminor = int64(major), int64(minor)
71
-		case "openbsd":
72
-			// Copied from golang.org/x/sys/unix/dev_openbsd.go.
73
-			major := uint32((dev & 0x0000ff00) >> 8)
74
-			minor := uint32((dev & 0x000000ff) >> 0)
75
-			minor |= uint32((dev & 0xffff0000) >> 8)
76
-			h.Devmajor, h.Devminor = int64(major), int64(minor)
77
-		default:
78
-			// TODO: Implement solaris (see https://golang.org/issue/8106)
79
-		}
80
-	}
81
-	return nil
82
-}
83 1
deleted file mode 100644
... ...
@@ -1,345 +0,0 @@
1
-// Copyright 2016 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
-	"strconv"
11
-	"strings"
12
-	"time"
13
-)
14
-
15
-// hasNUL reports whether the NUL character exists within s.
16
-func hasNUL(s string) bool {
17
-	return strings.IndexByte(s, 0) >= 0
18
-}
19
-
20
-// isASCII reports whether the input is an ASCII C-style string.
21
-func isASCII(s string) bool {
22
-	for _, c := range s {
23
-		if c >= 0x80 || c == 0x00 {
24
-			return false
25
-		}
26
-	}
27
-	return true
28
-}
29
-
30
-// toASCII converts the input to an ASCII C-style string.
31
-// This is a best effort conversion, so invalid characters are dropped.
32
-func toASCII(s string) string {
33
-	if isASCII(s) {
34
-		return s
35
-	}
36
-	b := make([]byte, 0, len(s))
37
-	for _, c := range s {
38
-		if c < 0x80 && c != 0x00 {
39
-			b = append(b, byte(c))
40
-		}
41
-	}
42
-	return string(b)
43
-}
44
-
45
-type parser struct {
46
-	err error // Last error seen
47
-}
48
-
49
-type formatter struct {
50
-	err error // Last error seen
51
-}
52
-
53
-// parseString parses bytes as a NUL-terminated C-style string.
54
-// If a NUL byte is not found then the whole slice is returned as a string.
55
-func (*parser) parseString(b []byte) string {
56
-	if i := bytes.IndexByte(b, 0); i >= 0 {
57
-		return string(b[:i])
58
-	}
59
-	return string(b)
60
-}
61
-
62
-// formatString copies s into b, NUL-terminating if possible.
63
-func (f *formatter) formatString(b []byte, s string) {
64
-	if len(s) > len(b) {
65
-		f.err = ErrFieldTooLong
66
-	}
67
-	copy(b, s)
68
-	if len(s) < len(b) {
69
-		b[len(s)] = 0
70
-	}
71
-
72
-	// Some buggy readers treat regular files with a trailing slash
73
-	// in the V7 path field as a directory even though the full path
74
-	// recorded elsewhere (e.g., via PAX record) contains no trailing slash.
75
-	if len(s) > len(b) && b[len(b)-1] == '/' {
76
-		n := len(strings.TrimRight(s[:len(b)], "/"))
77
-		b[n] = 0 // Replace trailing slash with NUL terminator
78
-	}
79
-}
80
-
81
-// fitsInBase256 reports whether x can be encoded into n bytes using base-256
82
-// encoding. Unlike octal encoding, base-256 encoding does not require that the
83
-// string ends with a NUL character. Thus, all n bytes are available for output.
84
-//
85
-// If operating in binary mode, this assumes strict GNU binary mode; which means
86
-// that the first byte can only be either 0x80 or 0xff. Thus, the first byte is
87
-// equivalent to the sign bit in two's complement form.
88
-func fitsInBase256(n int, x int64) bool {
89
-	binBits := uint(n-1) * 8
90
-	return n >= 9 || (x >= -1<<binBits && x < 1<<binBits)
91
-}
92
-
93
-// parseNumeric parses the input as being encoded in either base-256 or octal.
94
-// This function may return negative numbers.
95
-// If parsing fails or an integer overflow occurs, err will be set.
96
-func (p *parser) parseNumeric(b []byte) int64 {
97
-	// Check for base-256 (binary) format first.
98
-	// If the first bit is set, then all following bits constitute a two's
99
-	// complement encoded number in big-endian byte order.
100
-	if len(b) > 0 && b[0]&0x80 != 0 {
101
-		// Handling negative numbers relies on the following identity:
102
-		//	-a-1 == ^a
103
-		//
104
-		// If the number is negative, we use an inversion mask to invert the
105
-		// data bytes and treat the value as an unsigned number.
106
-		var inv byte // 0x00 if positive or zero, 0xff if negative
107
-		if b[0]&0x40 != 0 {
108
-			inv = 0xff
109
-		}
110
-
111
-		var x uint64
112
-		for i, c := range b {
113
-			c ^= inv // Inverts c only if inv is 0xff, otherwise does nothing
114
-			if i == 0 {
115
-				c &= 0x7f // Ignore signal bit in first byte
116
-			}
117
-			if (x >> 56) > 0 {
118
-				p.err = ErrHeader // Integer overflow
119
-				return 0
120
-			}
121
-			x = x<<8 | uint64(c)
122
-		}
123
-		if (x >> 63) > 0 {
124
-			p.err = ErrHeader // Integer overflow
125
-			return 0
126
-		}
127
-		if inv == 0xff {
128
-			return ^int64(x)
129
-		}
130
-		return int64(x)
131
-	}
132
-
133
-	// Normal case is base-8 (octal) format.
134
-	return p.parseOctal(b)
135
-}
136
-
137
-// formatNumeric encodes x into b using base-8 (octal) encoding if possible.
138
-// Otherwise it will attempt to use base-256 (binary) encoding.
139
-func (f *formatter) formatNumeric(b []byte, x int64) {
140
-	if fitsInOctal(len(b), x) {
141
-		f.formatOctal(b, x)
142
-		return
143
-	}
144
-
145
-	if fitsInBase256(len(b), x) {
146
-		for i := len(b) - 1; i >= 0; i-- {
147
-			b[i] = byte(x)
148
-			x >>= 8
149
-		}
150
-		b[0] |= 0x80 // Highest bit indicates binary format
151
-		return
152
-	}
153
-
154
-	f.formatOctal(b, 0) // Last resort, just write zero
155
-	f.err = ErrFieldTooLong
156
-}
157
-
158
-func (p *parser) parseOctal(b []byte) int64 {
159
-	// Because unused fields are filled with NULs, we need
160
-	// to skip leading NULs. Fields may also be padded with
161
-	// spaces or NULs.
162
-	// So we remove leading and trailing NULs and spaces to
163
-	// be sure.
164
-	b = bytes.Trim(b, " \x00")
165
-
166
-	if len(b) == 0 {
167
-		return 0
168
-	}
169
-	x, perr := strconv.ParseUint(p.parseString(b), 8, 64)
170
-	if perr != nil {
171
-		p.err = ErrHeader
172
-	}
173
-	return int64(x)
174
-}
175
-
176
-func (f *formatter) formatOctal(b []byte, x int64) {
177
-	if !fitsInOctal(len(b), x) {
178
-		x = 0 // Last resort, just write zero
179
-		f.err = ErrFieldTooLong
180
-	}
181
-
182
-	s := strconv.FormatInt(x, 8)
183
-	// Add leading zeros, but leave room for a NUL.
184
-	if n := len(b) - len(s) - 1; n > 0 {
185
-		s = strings.Repeat("0", n) + s
186
-	}
187
-	f.formatString(b, s)
188
-}
189
-
190
-// fitsInOctal reports whether the integer x fits in a field n-bytes long
191
-// using octal encoding with the appropriate NUL terminator.
192
-func fitsInOctal(n int, x int64) bool {
193
-	octBits := uint(n-1) * 3
194
-	return x >= 0 && (n >= 22 || x < 1<<octBits)
195
-}
196
-
197
-// parsePAXTime takes a string of the form %d.%d as described in the PAX
198
-// specification. Note that this implementation allows for negative timestamps,
199
-// which is allowed for by the PAX specification, but not always portable.
200
-func parsePAXTime(s string) (time.Time, error) {
201
-	const maxNanoSecondDigits = 9
202
-
203
-	// Split string into seconds and sub-seconds parts.
204
-	ss, sn := s, ""
205
-	if pos := strings.IndexByte(s, '.'); pos >= 0 {
206
-		ss, sn = s[:pos], s[pos+1:]
207
-	}
208
-
209
-	// Parse the seconds.
210
-	secs, err := strconv.ParseInt(ss, 10, 64)
211
-	if err != nil {
212
-		return time.Time{}, ErrHeader
213
-	}
214
-	if len(sn) == 0 {
215
-		return time.Unix(secs, 0), nil // No sub-second values
216
-	}
217
-
218
-	// Parse the nanoseconds.
219
-	if strings.Trim(sn, "0123456789") != "" {
220
-		return time.Time{}, ErrHeader
221
-	}
222
-	if len(sn) < maxNanoSecondDigits {
223
-		sn += strings.Repeat("0", maxNanoSecondDigits-len(sn)) // Right pad
224
-	} else {
225
-		sn = sn[:maxNanoSecondDigits] // Right truncate
226
-	}
227
-	nsecs, _ := strconv.ParseInt(sn, 10, 64) // Must succeed
228
-	if len(ss) > 0 && ss[0] == '-' {
229
-		return time.Unix(secs, -1*nsecs), nil // Negative correction
230
-	}
231
-	return time.Unix(secs, nsecs), nil
232
-}
233
-
234
-// formatPAXTime converts ts into a time of the form %d.%d as described in the
235
-// PAX specification. This function is capable of negative timestamps.
236
-func formatPAXTime(ts time.Time) (s string) {
237
-	secs, nsecs := ts.Unix(), ts.Nanosecond()
238
-	if nsecs == 0 {
239
-		return strconv.FormatInt(secs, 10)
240
-	}
241
-
242
-	// If seconds is negative, then perform correction.
243
-	sign := ""
244
-	if secs < 0 {
245
-		sign = "-"             // Remember sign
246
-		secs = -(secs + 1)     // Add a second to secs
247
-		nsecs = -(nsecs - 1e9) // Take that second away from nsecs
248
-	}
249
-	return strings.TrimRight(fmt.Sprintf("%s%d.%09d", sign, secs, nsecs), "0")
250
-}
251
-
252
-// parsePAXRecord parses the input PAX record string into a key-value pair.
253
-// If parsing is successful, it will slice off the currently read record and
254
-// return the remainder as r.
255
-func parsePAXRecord(s string) (k, v, r string, err error) {
256
-	// The size field ends at the first space.
257
-	sp := strings.IndexByte(s, ' ')
258
-	if sp == -1 {
259
-		return "", "", s, ErrHeader
260
-	}
261
-
262
-	// Parse the first token as a decimal integer.
263
-	n, perr := strconv.ParseInt(s[:sp], 10, 0) // Intentionally parse as native int
264
-	if perr != nil || n < 5 || int64(len(s)) < n {
265
-		return "", "", s, ErrHeader
266
-	}
267
-
268
-	afterSpace := int64(sp + 1)
269
-	beforeLastNewLine := n - 1
270
-	// In some cases, "length" was perhaps padded/malformed, and
271
-	// trying to index past where the space supposedly is goes past
272
-	// the end of the actual record.
273
-	// For example:
274
-	//    "0000000000000000000000000000000030 mtime=1432668921.098285006\n30 ctime=2147483649.15163319"
275
-	//                                  ^     ^
276
-	//                                  |     |
277
-	//                                  |  afterSpace=35
278
-	//                                  |
279
-	//                          beforeLastNewLine=29
280
-	// yet indexOf(firstSpace) MUST BE before endOfRecord.
281
-	//
282
-	// See https://golang.org/issues/40196.
283
-	if afterSpace >= beforeLastNewLine {
284
-		return "", "", s, ErrHeader
285
-	}
286
-
287
-	// Extract everything between the space and the final newline.
288
-	rec, nl, rem := s[afterSpace:beforeLastNewLine], s[beforeLastNewLine:n], s[n:]
289
-	if nl != "\n" {
290
-		return "", "", s, ErrHeader
291
-	}
292
-
293
-	// The first equals separates the key from the value.
294
-	eq := strings.IndexByte(rec, '=')
295
-	if eq == -1 {
296
-		return "", "", s, ErrHeader
297
-	}
298
-	k, v = rec[:eq], rec[eq+1:]
299
-
300
-	if !validPAXRecord(k, v) {
301
-		return "", "", s, ErrHeader
302
-	}
303
-	return k, v, rem, nil
304
-}
305
-
306
-// formatPAXRecord formats a single PAX record, prefixing it with the
307
-// appropriate length.
308
-func formatPAXRecord(k, v string) (string, error) {
309
-	if !validPAXRecord(k, v) {
310
-		return "", ErrHeader
311
-	}
312
-
313
-	const padding = 3 // Extra padding for ' ', '=', and '\n'
314
-	size := len(k) + len(v) + padding
315
-	size += len(strconv.Itoa(size))
316
-	record := strconv.Itoa(size) + " " + k + "=" + v + "\n"
317
-
318
-	// Final adjustment if adding size field increased the record size.
319
-	if len(record) != size {
320
-		size = len(record)
321
-		record = strconv.Itoa(size) + " " + k + "=" + v + "\n"
322
-	}
323
-	return record, nil
324
-}
325
-
326
-// validPAXRecord reports whether the key-value pair is valid where each
327
-// record is formatted as:
328
-//	"%d %s=%s\n" % (size, key, value)
329
-//
330
-// Keys and values should be UTF-8, but the number of bad writers out there
331
-// forces us to be a more liberal.
332
-// Thus, we only reject all keys with NUL, and only reject NULs in values
333
-// for the PAX version of the USTAR string fields.
334
-// The key must not contain an '=' character.
335
-func validPAXRecord(k, v string) bool {
336
-	if k == "" || strings.IndexByte(k, '=') >= 0 {
337
-		return false
338
-	}
339
-	switch k {
340
-	case paxPath, paxLinkpath, paxUname, paxGname:
341
-		return !hasNUL(v)
342
-	default:
343
-		return !hasNUL(k)
344
-	}
345
-}
346 1
deleted file mode 100644
... ...
@@ -1,441 +0,0 @@
1
-// Copyright 2016 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
-	"math"
9
-	"strings"
10
-	"testing"
11
-	"time"
12
-)
13
-
14
-func TestFitsInBase256(t *testing.T) {
15
-	vectors := []struct {
16
-		in    int64
17
-		width int
18
-		ok    bool
19
-	}{
20
-		{+1, 8, true},
21
-		{0, 8, true},
22
-		{-1, 8, true},
23
-		{1 << 56, 8, false},
24
-		{(1 << 56) - 1, 8, true},
25
-		{-1 << 56, 8, true},
26
-		{(-1 << 56) - 1, 8, false},
27
-		{121654, 8, true},
28
-		{-9849849, 8, true},
29
-		{math.MaxInt64, 9, true},
30
-		{0, 9, true},
31
-		{math.MinInt64, 9, true},
32
-		{math.MaxInt64, 12, true},
33
-		{0, 12, true},
34
-		{math.MinInt64, 12, true},
35
-	}
36
-
37
-	for _, v := range vectors {
38
-		ok := fitsInBase256(v.width, v.in)
39
-		if ok != v.ok {
40
-			t.Errorf("fitsInBase256(%d, %d): got %v, want %v", v.in, v.width, ok, v.ok)
41
-		}
42
-	}
43
-}
44
-
45
-func TestParseNumeric(t *testing.T) {
46
-	vectors := []struct {
47
-		in   string
48
-		want int64
49
-		ok   bool
50
-	}{
51
-		// Test base-256 (binary) encoded values.
52
-		{"", 0, true},
53
-		{"\x80", 0, true},
54
-		{"\x80\x00", 0, true},
55
-		{"\x80\x00\x00", 0, true},
56
-		{"\xbf", (1 << 6) - 1, true},
57
-		{"\xbf\xff", (1 << 14) - 1, true},
58
-		{"\xbf\xff\xff", (1 << 22) - 1, true},
59
-		{"\xff", -1, true},
60
-		{"\xff\xff", -1, true},
61
-		{"\xff\xff\xff", -1, true},
62
-		{"\xc0", -1 * (1 << 6), true},
63
-		{"\xc0\x00", -1 * (1 << 14), true},
64
-		{"\xc0\x00\x00", -1 * (1 << 22), true},
65
-		{"\x87\x76\xa2\x22\xeb\x8a\x72\x61", 537795476381659745, true},
66
-		{"\x80\x00\x00\x00\x07\x76\xa2\x22\xeb\x8a\x72\x61", 537795476381659745, true},
67
-		{"\xf7\x76\xa2\x22\xeb\x8a\x72\x61", -615126028225187231, true},
68
-		{"\xff\xff\xff\xff\xf7\x76\xa2\x22\xeb\x8a\x72\x61", -615126028225187231, true},
69
-		{"\x80\x7f\xff\xff\xff\xff\xff\xff\xff", math.MaxInt64, true},
70
-		{"\x80\x80\x00\x00\x00\x00\x00\x00\x00", 0, false},
71
-		{"\xff\x80\x00\x00\x00\x00\x00\x00\x00", math.MinInt64, true},
72
-		{"\xff\x7f\xff\xff\xff\xff\xff\xff\xff", 0, false},
73
-		{"\xf5\xec\xd1\xc7\x7e\x5f\x26\x48\x81\x9f\x8f\x9b", 0, false},
74
-
75
-		// Test base-8 (octal) encoded values.
76
-		{"0000000\x00", 0, true},
77
-		{" \x0000000\x00", 0, true},
78
-		{" \x0000003\x00", 3, true},
79
-		{"00000000227\x00", 0227, true},
80
-		{"032033\x00 ", 032033, true},
81
-		{"320330\x00 ", 0320330, true},
82
-		{"0000660\x00 ", 0660, true},
83
-		{"\x00 0000660\x00 ", 0660, true},
84
-		{"0123456789abcdef", 0, false},
85
-		{"0123456789\x00abcdef", 0, false},
86
-		{"01234567\x0089abcdef", 342391, true},
87
-		{"0123\x7e\x5f\x264123", 0, false},
88
-	}
89
-
90
-	for _, v := range vectors {
91
-		var p parser
92
-		got := p.parseNumeric([]byte(v.in))
93
-		ok := (p.err == nil)
94
-		if ok != v.ok {
95
-			if v.ok {
96
-				t.Errorf("parseNumeric(%q): got parsing failure, want success", v.in)
97
-			} else {
98
-				t.Errorf("parseNumeric(%q): got parsing success, want failure", v.in)
99
-			}
100
-		}
101
-		if ok && got != v.want {
102
-			t.Errorf("parseNumeric(%q): got %d, want %d", v.in, got, v.want)
103
-		}
104
-	}
105
-}
106
-
107
-func TestFormatNumeric(t *testing.T) {
108
-	vectors := []struct {
109
-		in   int64
110
-		want string
111
-		ok   bool
112
-	}{
113
-		// Test base-8 (octal) encoded values.
114
-		{0, "0\x00", true},
115
-		{7, "7\x00", true},
116
-		{8, "\x80\x08", true},
117
-		{077, "77\x00", true},
118
-		{0100, "\x80\x00\x40", true},
119
-		{0, "0000000\x00", true},
120
-		{0123, "0000123\x00", true},
121
-		{07654321, "7654321\x00", true},
122
-		{07777777, "7777777\x00", true},
123
-		{010000000, "\x80\x00\x00\x00\x00\x20\x00\x00", true},
124
-		{0, "00000000000\x00", true},
125
-		{000001234567, "00001234567\x00", true},
126
-		{076543210321, "76543210321\x00", true},
127
-		{012345670123, "12345670123\x00", true},
128
-		{077777777777, "77777777777\x00", true},
129
-		{0100000000000, "\x80\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00", true},
130
-		{math.MaxInt64, "777777777777777777777\x00", true},
131
-
132
-		// Test base-256 (binary) encoded values.
133
-		{-1, "\xff", true},
134
-		{-1, "\xff\xff", true},
135
-		{-1, "\xff\xff\xff", true},
136
-		{(1 << 0), "0", false},
137
-		{(1 << 8) - 1, "\x80\xff", true},
138
-		{(1 << 8), "0\x00", false},
139
-		{(1 << 16) - 1, "\x80\xff\xff", true},
140
-		{(1 << 16), "00\x00", false},
141
-		{-1 * (1 << 0), "\xff", true},
142
-		{-1*(1<<0) - 1, "0", false},
143
-		{-1 * (1 << 8), "\xff\x00", true},
144
-		{-1*(1<<8) - 1, "0\x00", false},
145
-		{-1 * (1 << 16), "\xff\x00\x00", true},
146
-		{-1*(1<<16) - 1, "00\x00", false},
147
-		{537795476381659745, "0000000\x00", false},
148
-		{537795476381659745, "\x80\x00\x00\x00\x07\x76\xa2\x22\xeb\x8a\x72\x61", true},
149
-		{-615126028225187231, "0000000\x00", false},
150
-		{-615126028225187231, "\xff\xff\xff\xff\xf7\x76\xa2\x22\xeb\x8a\x72\x61", true},
151
-		{math.MaxInt64, "0000000\x00", false},
152
-		{math.MaxInt64, "\x80\x00\x00\x00\x7f\xff\xff\xff\xff\xff\xff\xff", true},
153
-		{math.MinInt64, "0000000\x00", false},
154
-		{math.MinInt64, "\xff\xff\xff\xff\x80\x00\x00\x00\x00\x00\x00\x00", true},
155
-		{math.MaxInt64, "\x80\x7f\xff\xff\xff\xff\xff\xff\xff", true},
156
-		{math.MinInt64, "\xff\x80\x00\x00\x00\x00\x00\x00\x00", true},
157
-	}
158
-
159
-	for _, v := range vectors {
160
-		var f formatter
161
-		got := make([]byte, len(v.want))
162
-		f.formatNumeric(got, v.in)
163
-		ok := (f.err == nil)
164
-		if ok != v.ok {
165
-			if v.ok {
166
-				t.Errorf("formatNumeric(%d): got formatting failure, want success", v.in)
167
-			} else {
168
-				t.Errorf("formatNumeric(%d): got formatting success, want failure", v.in)
169
-			}
170
-		}
171
-		if string(got) != v.want {
172
-			t.Errorf("formatNumeric(%d): got %q, want %q", v.in, got, v.want)
173
-		}
174
-	}
175
-}
176
-
177
-func TestFitsInOctal(t *testing.T) {
178
-	vectors := []struct {
179
-		input int64
180
-		width int
181
-		ok    bool
182
-	}{
183
-		{-1, 1, false},
184
-		{-1, 2, false},
185
-		{-1, 3, false},
186
-		{0, 1, true},
187
-		{0 + 1, 1, false},
188
-		{0, 2, true},
189
-		{07, 2, true},
190
-		{07 + 1, 2, false},
191
-		{0, 4, true},
192
-		{0777, 4, true},
193
-		{0777 + 1, 4, false},
194
-		{0, 8, true},
195
-		{07777777, 8, true},
196
-		{07777777 + 1, 8, false},
197
-		{0, 12, true},
198
-		{077777777777, 12, true},
199
-		{077777777777 + 1, 12, false},
200
-		{math.MaxInt64, 22, true},
201
-		{012345670123, 12, true},
202
-		{01564164, 12, true},
203
-		{-012345670123, 12, false},
204
-		{-01564164, 12, false},
205
-		{-1564164, 30, false},
206
-	}
207
-
208
-	for _, v := range vectors {
209
-		ok := fitsInOctal(v.width, v.input)
210
-		if ok != v.ok {
211
-			t.Errorf("checkOctal(%d, %d): got %v, want %v", v.input, v.width, ok, v.ok)
212
-		}
213
-	}
214
-}
215
-
216
-func TestParsePAXTime(t *testing.T) {
217
-	vectors := []struct {
218
-		in   string
219
-		want time.Time
220
-		ok   bool
221
-	}{
222
-		{"1350244992.023960108", time.Unix(1350244992, 23960108), true},
223
-		{"1350244992.02396010", time.Unix(1350244992, 23960100), true},
224
-		{"1350244992.0239601089", time.Unix(1350244992, 23960108), true},
225
-		{"1350244992.3", time.Unix(1350244992, 300000000), true},
226
-		{"1350244992", time.Unix(1350244992, 0), true},
227
-		{"-1.000000001", time.Unix(-1, -1e0+0e0), true},
228
-		{"-1.000001", time.Unix(-1, -1e3+0e0), true},
229
-		{"-1.001000", time.Unix(-1, -1e6+0e0), true},
230
-		{"-1", time.Unix(-1, -0e0+0e0), true},
231
-		{"-1.999000", time.Unix(-1, -1e9+1e6), true},
232
-		{"-1.999999", time.Unix(-1, -1e9+1e3), true},
233
-		{"-1.999999999", time.Unix(-1, -1e9+1e0), true},
234
-		{"0.000000001", time.Unix(0, 1e0+0e0), true},
235
-		{"0.000001", time.Unix(0, 1e3+0e0), true},
236
-		{"0.001000", time.Unix(0, 1e6+0e0), true},
237
-		{"0", time.Unix(0, 0e0), true},
238
-		{"0.999000", time.Unix(0, 1e9-1e6), true},
239
-		{"0.999999", time.Unix(0, 1e9-1e3), true},
240
-		{"0.999999999", time.Unix(0, 1e9-1e0), true},
241
-		{"1.000000001", time.Unix(+1, +1e0-0e0), true},
242
-		{"1.000001", time.Unix(+1, +1e3-0e0), true},
243
-		{"1.001000", time.Unix(+1, +1e6-0e0), true},
244
-		{"1", time.Unix(+1, +0e0-0e0), true},
245
-		{"1.999000", time.Unix(+1, +1e9-1e6), true},
246
-		{"1.999999", time.Unix(+1, +1e9-1e3), true},
247
-		{"1.999999999", time.Unix(+1, +1e9-1e0), true},
248
-		{"-1350244992.023960108", time.Unix(-1350244992, -23960108), true},
249
-		{"-1350244992.02396010", time.Unix(-1350244992, -23960100), true},
250
-		{"-1350244992.0239601089", time.Unix(-1350244992, -23960108), true},
251
-		{"-1350244992.3", time.Unix(-1350244992, -300000000), true},
252
-		{"-1350244992", time.Unix(-1350244992, 0), true},
253
-		{"", time.Time{}, false},
254
-		{"0", time.Unix(0, 0), true},
255
-		{"1.", time.Unix(1, 0), true},
256
-		{"0.0", time.Unix(0, 0), true},
257
-		{".5", time.Time{}, false},
258
-		{"-1.3", time.Unix(-1, -3e8), true},
259
-		{"-1.0", time.Unix(-1, -0e0), true},
260
-		{"-0.0", time.Unix(-0, -0e0), true},
261
-		{"-0.1", time.Unix(-0, -1e8), true},
262
-		{"-0.01", time.Unix(-0, -1e7), true},
263
-		{"-0.99", time.Unix(-0, -99e7), true},
264
-		{"-0.98", time.Unix(-0, -98e7), true},
265
-		{"-1.1", time.Unix(-1, -1e8), true},
266
-		{"-1.01", time.Unix(-1, -1e7), true},
267
-		{"-2.99", time.Unix(-2, -99e7), true},
268
-		{"-5.98", time.Unix(-5, -98e7), true},
269
-		{"-", time.Time{}, false},
270
-		{"+", time.Time{}, false},
271
-		{"-1.-1", time.Time{}, false},
272
-		{"99999999999999999999999999999999999999999999999", time.Time{}, false},
273
-		{"0.123456789abcdef", time.Time{}, false},
274
-		{"foo", time.Time{}, false},
275
-		{"\x00", time.Time{}, false},
276
-		{"𝟵𝟴𝟳𝟲𝟱.𝟰𝟯𝟮𝟭𝟬", time.Time{}, false}, // Unicode numbers (U+1D7EC to U+1D7F5)
277
-		{"98765﹒43210", time.Time{}, false}, // Unicode period (U+FE52)
278
-	}
279
-
280
-	for _, v := range vectors {
281
-		ts, err := parsePAXTime(v.in)
282
-		ok := (err == nil)
283
-		if v.ok != ok {
284
-			if v.ok {
285
-				t.Errorf("parsePAXTime(%q): got parsing failure, want success", v.in)
286
-			} else {
287
-				t.Errorf("parsePAXTime(%q): got parsing success, want failure", v.in)
288
-			}
289
-		}
290
-		if ok && !ts.Equal(v.want) {
291
-			t.Errorf("parsePAXTime(%q): got (%ds %dns), want (%ds %dns)",
292
-				v.in, ts.Unix(), ts.Nanosecond(), v.want.Unix(), v.want.Nanosecond())
293
-		}
294
-	}
295
-}
296
-
297
-func TestFormatPAXTime(t *testing.T) {
298
-	vectors := []struct {
299
-		sec, nsec int64
300
-		want      string
301
-	}{
302
-		{1350244992, 0, "1350244992"},
303
-		{1350244992, 300000000, "1350244992.3"},
304
-		{1350244992, 23960100, "1350244992.0239601"},
305
-		{1350244992, 23960108, "1350244992.023960108"},
306
-		{+1, +1e9 - 1e0, "1.999999999"},
307
-		{+1, +1e9 - 1e3, "1.999999"},
308
-		{+1, +1e9 - 1e6, "1.999"},
309
-		{+1, +0e0 - 0e0, "1"},
310
-		{+1, +1e6 - 0e0, "1.001"},
311
-		{+1, +1e3 - 0e0, "1.000001"},
312
-		{+1, +1e0 - 0e0, "1.000000001"},
313
-		{0, 1e9 - 1e0, "0.999999999"},
314
-		{0, 1e9 - 1e3, "0.999999"},
315
-		{0, 1e9 - 1e6, "0.999"},
316
-		{0, 0e0, "0"},
317
-		{0, 1e6 + 0e0, "0.001"},
318
-		{0, 1e3 + 0e0, "0.000001"},
319
-		{0, 1e0 + 0e0, "0.000000001"},
320
-		{-1, -1e9 + 1e0, "-1.999999999"},
321
-		{-1, -1e9 + 1e3, "-1.999999"},
322
-		{-1, -1e9 + 1e6, "-1.999"},
323
-		{-1, -0e0 + 0e0, "-1"},
324
-		{-1, -1e6 + 0e0, "-1.001"},
325
-		{-1, -1e3 + 0e0, "-1.000001"},
326
-		{-1, -1e0 + 0e0, "-1.000000001"},
327
-		{-1350244992, 0, "-1350244992"},
328
-		{-1350244992, -300000000, "-1350244992.3"},
329
-		{-1350244992, -23960100, "-1350244992.0239601"},
330
-		{-1350244992, -23960108, "-1350244992.023960108"},
331
-	}
332
-
333
-	for _, v := range vectors {
334
-		got := formatPAXTime(time.Unix(v.sec, v.nsec))
335
-		if got != v.want {
336
-			t.Errorf("formatPAXTime(%ds, %dns): got %q, want %q",
337
-				v.sec, v.nsec, got, v.want)
338
-		}
339
-	}
340
-}
341
-
342
-func TestParsePAXRecord(t *testing.T) {
343
-	medName := strings.Repeat("CD", 50)
344
-	longName := strings.Repeat("AB", 100)
345
-
346
-	vectors := []struct {
347
-		in      string
348
-		wantRes string
349
-		wantKey string
350
-		wantVal string
351
-		ok      bool
352
-	}{
353
-		{"6 k=v\n\n", "\n", "k", "v", true},
354
-		{"19 path=/etc/hosts\n", "", "path", "/etc/hosts", true},
355
-		{"210 path=" + longName + "\nabc", "abc", "path", longName, true},
356
-		{"110 path=" + medName + "\n", "", "path", medName, true},
357
-		{"9 foo=ba\n", "", "foo", "ba", true},
358
-		{"11 foo=bar\n\x00", "\x00", "foo", "bar", true},
359
-		{"18 foo=b=\nar=\n==\x00\n", "", "foo", "b=\nar=\n==\x00", true},
360
-		{"27 foo=hello9 foo=ba\nworld\n", "", "foo", "hello9 foo=ba\nworld", true},
361
-		{"27 ☺☻☹=日a本b語ç\nmeow mix", "meow mix", "☺☻☹", "日a本b語ç", true},
362
-		{"17 \x00hello=\x00world\n", "17 \x00hello=\x00world\n", "", "", false},
363
-		{"1 k=1\n", "1 k=1\n", "", "", false},
364
-		{"6 k~1\n", "6 k~1\n", "", "", false},
365
-		{"6_k=1\n", "6_k=1\n", "", "", false},
366
-		{"6 k=1 ", "6 k=1 ", "", "", false},
367
-		{"632 k=1\n", "632 k=1\n", "", "", false},
368
-		{"16 longkeyname=hahaha\n", "16 longkeyname=hahaha\n", "", "", false},
369
-		{"3 somelongkey=\n", "3 somelongkey=\n", "", "", false},
370
-		{"50 tooshort=\n", "50 tooshort=\n", "", "", false},
371
-		{"0000000000000000000000000000000030 mtime=1432668921.098285006\n30 ctime=2147483649.15163319", "0000000000000000000000000000000030 mtime=1432668921.098285006\n30 ctime=2147483649.15163319", "mtime", "1432668921.098285006", false},
372
-		{"06 k=v\n", "06 k=v\n", "", "", false},
373
-		{"00006 k=v\n", "00006 k=v\n", "", "", false},
374
-		{"000006 k=v\n", "000006 k=v\n", "", "", false},
375
-		{"000000 k=v\n", "000000 k=v\n", "", "", false},
376
-		{"0 k=v\n", "0 k=v\n", "", "", false},
377
-		{"+0000005 x=\n", "+0000005 x=\n", "", "", false},
378
-	}
379
-
380
-	for _, v := range vectors {
381
-		key, val, res, err := parsePAXRecord(v.in)
382
-		ok := (err == nil)
383
-		if ok != v.ok {
384
-			if v.ok {
385
-				t.Errorf("parsePAXRecord(%q): got parsing failure, want success", v.in)
386
-			} else {
387
-				t.Errorf("parsePAXRecord(%q): got parsing success, want failure", v.in)
388
-			}
389
-		}
390
-		if v.ok && (key != v.wantKey || val != v.wantVal) {
391
-			t.Errorf("parsePAXRecord(%q): got (%q: %q), want (%q: %q)",
392
-				v.in, key, val, v.wantKey, v.wantVal)
393
-		}
394
-		if res != v.wantRes {
395
-			t.Errorf("parsePAXRecord(%q): got residual %q, want residual %q",
396
-				v.in, res, v.wantRes)
397
-		}
398
-	}
399
-}
400
-
401
-func TestFormatPAXRecord(t *testing.T) {
402
-	medName := strings.Repeat("CD", 50)
403
-	longName := strings.Repeat("AB", 100)
404
-
405
-	vectors := []struct {
406
-		inKey string
407
-		inVal string
408
-		want  string
409
-		ok    bool
410
-	}{
411
-		{"k", "v", "6 k=v\n", true},
412
-		{"path", "/etc/hosts", "19 path=/etc/hosts\n", true},
413
-		{"path", longName, "210 path=" + longName + "\n", true},
414
-		{"path", medName, "110 path=" + medName + "\n", true},
415
-		{"foo", "ba", "9 foo=ba\n", true},
416
-		{"foo", "bar", "11 foo=bar\n", true},
417
-		{"foo", "b=\nar=\n==\x00", "18 foo=b=\nar=\n==\x00\n", true},
418
-		{"foo", "hello9 foo=ba\nworld", "27 foo=hello9 foo=ba\nworld\n", true},
419
-		{"☺☻☹", "日a本b語ç", "27 ☺☻☹=日a本b語ç\n", true},
420
-		{"xhello", "\x00world", "17 xhello=\x00world\n", true},
421
-		{"path", "null\x00", "", false},
422
-		{"null\x00", "value", "", false},
423
-		{paxSchilyXattr + "key", "null\x00", "26 SCHILY.xattr.key=null\x00\n", true},
424
-	}
425
-
426
-	for _, v := range vectors {
427
-		got, err := formatPAXRecord(v.inKey, v.inVal)
428
-		ok := (err == nil)
429
-		if ok != v.ok {
430
-			if v.ok {
431
-				t.Errorf("formatPAXRecord(%q, %q): got format failure, want success", v.inKey, v.inVal)
432
-			} else {
433
-				t.Errorf("formatPAXRecord(%q, %q): got format success, want failure", v.inKey, v.inVal)
434
-			}
435
-		}
436
-		if got != v.want {
437
-			t.Errorf("formatPAXRecord(%q, %q): got %q, want %q",
438
-				v.inKey, v.inVal, got, v.want)
439
-		}
440
-	}
441
-}
442 1
deleted file mode 100644
... ...
@@ -1,850 +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
-	"errors"
10
-	"fmt"
11
-	"internal/testenv"
12
-	"io"
13
-	"io/fs"
14
-	"math"
15
-	"os"
16
-	"path"
17
-	"path/filepath"
18
-	"reflect"
19
-	"strings"
20
-	"testing"
21
-	"time"
22
-)
23
-
24
-type testError struct{ error }
25
-
26
-type fileOps []interface{} // []T where T is (string | int64)
27
-
28
-// testFile is an io.ReadWriteSeeker where the IO operations performed
29
-// on it must match the list of operations in ops.
30
-type testFile struct {
31
-	ops fileOps
32
-	pos int64
33
-}
34
-
35
-func (f *testFile) Read(b []byte) (int, error) {
36
-	if len(b) == 0 {
37
-		return 0, nil
38
-	}
39
-	if len(f.ops) == 0 {
40
-		return 0, io.EOF
41
-	}
42
-	s, ok := f.ops[0].(string)
43
-	if !ok {
44
-		return 0, errors.New("unexpected Read operation")
45
-	}
46
-
47
-	n := copy(b, s)
48
-	if len(s) > n {
49
-		f.ops[0] = s[n:]
50
-	} else {
51
-		f.ops = f.ops[1:]
52
-	}
53
-	f.pos += int64(len(b))
54
-	return n, nil
55
-}
56
-
57
-func (f *testFile) Write(b []byte) (int, error) {
58
-	if len(b) == 0 {
59
-		return 0, nil
60
-	}
61
-	if len(f.ops) == 0 {
62
-		return 0, errors.New("unexpected Write operation")
63
-	}
64
-	s, ok := f.ops[0].(string)
65
-	if !ok {
66
-		return 0, errors.New("unexpected Write operation")
67
-	}
68
-
69
-	if !strings.HasPrefix(s, string(b)) {
70
-		return 0, testError{fmt.Errorf("got Write(%q), want Write(%q)", b, s)}
71
-	}
72
-	if len(s) > len(b) {
73
-		f.ops[0] = s[len(b):]
74
-	} else {
75
-		f.ops = f.ops[1:]
76
-	}
77
-	f.pos += int64(len(b))
78
-	return len(b), nil
79
-}
80
-
81
-func (f *testFile) Seek(pos int64, whence int) (int64, error) {
82
-	if pos == 0 && whence == io.SeekCurrent {
83
-		return f.pos, nil
84
-	}
85
-	if len(f.ops) == 0 {
86
-		return 0, errors.New("unexpected Seek operation")
87
-	}
88
-	s, ok := f.ops[0].(int64)
89
-	if !ok {
90
-		return 0, errors.New("unexpected Seek operation")
91
-	}
92
-
93
-	if s != pos || whence != io.SeekCurrent {
94
-		return 0, testError{fmt.Errorf("got Seek(%d, %d), want Seek(%d, %d)", pos, whence, s, io.SeekCurrent)}
95
-	}
96
-	f.pos += s
97
-	f.ops = f.ops[1:]
98
-	return f.pos, nil
99
-}
100
-
101
-func equalSparseEntries(x, y []sparseEntry) bool {
102
-	return (len(x) == 0 && len(y) == 0) || reflect.DeepEqual(x, y)
103
-}
104
-
105
-func TestSparseEntries(t *testing.T) {
106
-	vectors := []struct {
107
-		in   []sparseEntry
108
-		size int64
109
-
110
-		wantValid    bool          // Result of validateSparseEntries
111
-		wantAligned  []sparseEntry // Result of alignSparseEntries
112
-		wantInverted []sparseEntry // Result of invertSparseEntries
113
-	}{{
114
-		in: []sparseEntry{}, size: 0,
115
-		wantValid:    true,
116
-		wantInverted: []sparseEntry{{0, 0}},
117
-	}, {
118
-		in: []sparseEntry{}, size: 5000,
119
-		wantValid:    true,
120
-		wantInverted: []sparseEntry{{0, 5000}},
121
-	}, {
122
-		in: []sparseEntry{{0, 5000}}, size: 5000,
123
-		wantValid:    true,
124
-		wantAligned:  []sparseEntry{{0, 5000}},
125
-		wantInverted: []sparseEntry{{5000, 0}},
126
-	}, {
127
-		in: []sparseEntry{{1000, 4000}}, size: 5000,
128
-		wantValid:    true,
129
-		wantAligned:  []sparseEntry{{1024, 3976}},
130
-		wantInverted: []sparseEntry{{0, 1000}, {5000, 0}},
131
-	}, {
132
-		in: []sparseEntry{{0, 3000}}, size: 5000,
133
-		wantValid:    true,
134
-		wantAligned:  []sparseEntry{{0, 2560}},
135
-		wantInverted: []sparseEntry{{3000, 2000}},
136
-	}, {
137
-		in: []sparseEntry{{3000, 2000}}, size: 5000,
138
-		wantValid:    true,
139
-		wantAligned:  []sparseEntry{{3072, 1928}},
140
-		wantInverted: []sparseEntry{{0, 3000}, {5000, 0}},
141
-	}, {
142
-		in: []sparseEntry{{2000, 2000}}, size: 5000,
143
-		wantValid:    true,
144
-		wantAligned:  []sparseEntry{{2048, 1536}},
145
-		wantInverted: []sparseEntry{{0, 2000}, {4000, 1000}},
146
-	}, {
147
-		in: []sparseEntry{{0, 2000}, {8000, 2000}}, size: 10000,
148
-		wantValid:    true,
149
-		wantAligned:  []sparseEntry{{0, 1536}, {8192, 1808}},
150
-		wantInverted: []sparseEntry{{2000, 6000}, {10000, 0}},
151
-	}, {
152
-		in: []sparseEntry{{0, 2000}, {2000, 2000}, {4000, 0}, {4000, 3000}, {7000, 1000}, {8000, 0}, {8000, 2000}}, size: 10000,
153
-		wantValid:    true,
154
-		wantAligned:  []sparseEntry{{0, 1536}, {2048, 1536}, {4096, 2560}, {7168, 512}, {8192, 1808}},
155
-		wantInverted: []sparseEntry{{10000, 0}},
156
-	}, {
157
-		in: []sparseEntry{{0, 0}, {1000, 0}, {2000, 0}, {3000, 0}, {4000, 0}, {5000, 0}}, size: 5000,
158
-		wantValid:    true,
159
-		wantInverted: []sparseEntry{{0, 5000}},
160
-	}, {
161
-		in: []sparseEntry{{1, 0}}, size: 0,
162
-		wantValid: false,
163
-	}, {
164
-		in: []sparseEntry{{-1, 0}}, size: 100,
165
-		wantValid: false,
166
-	}, {
167
-		in: []sparseEntry{{0, -1}}, size: 100,
168
-		wantValid: false,
169
-	}, {
170
-		in: []sparseEntry{{0, 0}}, size: -100,
171
-		wantValid: false,
172
-	}, {
173
-		in: []sparseEntry{{math.MaxInt64, 3}, {6, -5}}, size: 35,
174
-		wantValid: false,
175
-	}, {
176
-		in: []sparseEntry{{1, 3}, {6, -5}}, size: 35,
177
-		wantValid: false,
178
-	}, {
179
-		in: []sparseEntry{{math.MaxInt64, math.MaxInt64}}, size: math.MaxInt64,
180
-		wantValid: false,
181
-	}, {
182
-		in: []sparseEntry{{3, 3}}, size: 5,
183
-		wantValid: false,
184
-	}, {
185
-		in: []sparseEntry{{2, 0}, {1, 0}, {0, 0}}, size: 3,
186
-		wantValid: false,
187
-	}, {
188
-		in: []sparseEntry{{1, 3}, {2, 2}}, size: 10,
189
-		wantValid: false,
190
-	}}
191
-
192
-	for i, v := range vectors {
193
-		gotValid := validateSparseEntries(v.in, v.size)
194
-		if gotValid != v.wantValid {
195
-			t.Errorf("test %d, validateSparseEntries() = %v, want %v", i, gotValid, v.wantValid)
196
-		}
197
-		if !v.wantValid {
198
-			continue
199
-		}
200
-		gotAligned := alignSparseEntries(append([]sparseEntry{}, v.in...), v.size)
201
-		if !equalSparseEntries(gotAligned, v.wantAligned) {
202
-			t.Errorf("test %d, alignSparseEntries():\ngot  %v\nwant %v", i, gotAligned, v.wantAligned)
203
-		}
204
-		gotInverted := invertSparseEntries(append([]sparseEntry{}, v.in...), v.size)
205
-		if !equalSparseEntries(gotInverted, v.wantInverted) {
206
-			t.Errorf("test %d, inverseSparseEntries():\ngot  %v\nwant %v", i, gotInverted, v.wantInverted)
207
-		}
208
-	}
209
-}
210
-
211
-func TestFileInfoHeader(t *testing.T) {
212
-	fi, err := os.Stat("testdata/small.txt")
213
-	if err != nil {
214
-		t.Fatal(err)
215
-	}
216
-	h, err := FileInfoHeader(fi, "")
217
-	if err != nil {
218
-		t.Fatalf("FileInfoHeader: %v", err)
219
-	}
220
-	if g, e := h.Name, "small.txt"; g != e {
221
-		t.Errorf("Name = %q; want %q", g, e)
222
-	}
223
-	if g, e := h.Mode, int64(fi.Mode().Perm()); g != e {
224
-		t.Errorf("Mode = %#o; want %#o", g, e)
225
-	}
226
-	if g, e := h.Size, int64(5); g != e {
227
-		t.Errorf("Size = %v; want %v", g, e)
228
-	}
229
-	if g, e := h.ModTime, fi.ModTime(); !g.Equal(e) {
230
-		t.Errorf("ModTime = %v; want %v", g, e)
231
-	}
232
-	// FileInfoHeader should error when passing nil FileInfo
233
-	if _, err := FileInfoHeader(nil, ""); err == nil {
234
-		t.Fatalf("Expected error when passing nil to FileInfoHeader")
235
-	}
236
-}
237
-
238
-func TestFileInfoHeaderDir(t *testing.T) {
239
-	fi, err := os.Stat("testdata")
240
-	if err != nil {
241
-		t.Fatal(err)
242
-	}
243
-	h, err := FileInfoHeader(fi, "")
244
-	if err != nil {
245
-		t.Fatalf("FileInfoHeader: %v", err)
246
-	}
247
-	if g, e := h.Name, "testdata/"; g != e {
248
-		t.Errorf("Name = %q; want %q", g, e)
249
-	}
250
-	// Ignoring c_ISGID for golang.org/issue/4867
251
-	if g, e := h.Mode&^c_ISGID, int64(fi.Mode().Perm()); g != e {
252
-		t.Errorf("Mode = %#o; want %#o", g, e)
253
-	}
254
-	if g, e := h.Size, int64(0); g != e {
255
-		t.Errorf("Size = %v; want %v", g, e)
256
-	}
257
-	if g, e := h.ModTime, fi.ModTime(); !g.Equal(e) {
258
-		t.Errorf("ModTime = %v; want %v", g, e)
259
-	}
260
-}
261
-
262
-func TestFileInfoHeaderSymlink(t *testing.T) {
263
-	testenv.MustHaveSymlink(t)
264
-
265
-	tmpdir := t.TempDir()
266
-
267
-	link := filepath.Join(tmpdir, "link")
268
-	target := tmpdir
269
-	if err := os.Symlink(target, link); err != nil {
270
-		t.Fatal(err)
271
-	}
272
-	fi, err := os.Lstat(link)
273
-	if err != nil {
274
-		t.Fatal(err)
275
-	}
276
-
277
-	h, err := FileInfoHeader(fi, target)
278
-	if err != nil {
279
-		t.Fatal(err)
280
-	}
281
-	if g, e := h.Name, fi.Name(); g != e {
282
-		t.Errorf("Name = %q; want %q", g, e)
283
-	}
284
-	if g, e := h.Linkname, target; g != e {
285
-		t.Errorf("Linkname = %q; want %q", g, e)
286
-	}
287
-	if g, e := h.Typeflag, byte(TypeSymlink); g != e {
288
-		t.Errorf("Typeflag = %v; want %v", g, e)
289
-	}
290
-}
291
-
292
-func TestRoundTrip(t *testing.T) {
293
-	data := []byte("some file contents")
294
-
295
-	var b bytes.Buffer
296
-	tw := NewWriter(&b)
297
-	hdr := &Header{
298
-		Name:       "file.txt",
299
-		Uid:        1 << 21, // Too big for 8 octal digits
300
-		Size:       int64(len(data)),
301
-		ModTime:    time.Now().Round(time.Second),
302
-		PAXRecords: map[string]string{"uid": "2097152"},
303
-		Format:     FormatPAX,
304
-		Typeflag:   TypeReg,
305
-	}
306
-	if err := tw.WriteHeader(hdr); err != nil {
307
-		t.Fatalf("tw.WriteHeader: %v", err)
308
-	}
309
-	if _, err := tw.Write(data); err != nil {
310
-		t.Fatalf("tw.Write: %v", err)
311
-	}
312
-	if err := tw.Close(); err != nil {
313
-		t.Fatalf("tw.Close: %v", err)
314
-	}
315
-
316
-	// Read it back.
317
-	tr := NewReader(&b)
318
-	rHdr, err := tr.Next()
319
-	if err != nil {
320
-		t.Fatalf("tr.Next: %v", err)
321
-	}
322
-	if !reflect.DeepEqual(rHdr, hdr) {
323
-		t.Errorf("Header mismatch.\n got %+v\nwant %+v", rHdr, hdr)
324
-	}
325
-	rData, err := io.ReadAll(tr)
326
-	if err != nil {
327
-		t.Fatalf("Read: %v", err)
328
-	}
329
-	if !bytes.Equal(rData, data) {
330
-		t.Errorf("Data mismatch.\n got %q\nwant %q", rData, data)
331
-	}
332
-}
333
-
334
-type headerRoundTripTest struct {
335
-	h  *Header
336
-	fm fs.FileMode
337
-}
338
-
339
-func TestHeaderRoundTrip(t *testing.T) {
340
-	vectors := []headerRoundTripTest{{
341
-		// regular file.
342
-		h: &Header{
343
-			Name:     "test.txt",
344
-			Mode:     0644,
345
-			Size:     12,
346
-			ModTime:  time.Unix(1360600916, 0),
347
-			Typeflag: TypeReg,
348
-		},
349
-		fm: 0644,
350
-	}, {
351
-		// symbolic link.
352
-		h: &Header{
353
-			Name:     "link.txt",
354
-			Mode:     0777,
355
-			Size:     0,
356
-			ModTime:  time.Unix(1360600852, 0),
357
-			Typeflag: TypeSymlink,
358
-		},
359
-		fm: 0777 | fs.ModeSymlink,
360
-	}, {
361
-		// character device node.
362
-		h: &Header{
363
-			Name:     "dev/null",
364
-			Mode:     0666,
365
-			Size:     0,
366
-			ModTime:  time.Unix(1360578951, 0),
367
-			Typeflag: TypeChar,
368
-		},
369
-		fm: 0666 | fs.ModeDevice | fs.ModeCharDevice,
370
-	}, {
371
-		// block device node.
372
-		h: &Header{
373
-			Name:     "dev/sda",
374
-			Mode:     0660,
375
-			Size:     0,
376
-			ModTime:  time.Unix(1360578954, 0),
377
-			Typeflag: TypeBlock,
378
-		},
379
-		fm: 0660 | fs.ModeDevice,
380
-	}, {
381
-		// directory.
382
-		h: &Header{
383
-			Name:     "dir/",
384
-			Mode:     0755,
385
-			Size:     0,
386
-			ModTime:  time.Unix(1360601116, 0),
387
-			Typeflag: TypeDir,
388
-		},
389
-		fm: 0755 | fs.ModeDir,
390
-	}, {
391
-		// fifo node.
392
-		h: &Header{
393
-			Name:     "dev/initctl",
394
-			Mode:     0600,
395
-			Size:     0,
396
-			ModTime:  time.Unix(1360578949, 0),
397
-			Typeflag: TypeFifo,
398
-		},
399
-		fm: 0600 | fs.ModeNamedPipe,
400
-	}, {
401
-		// setuid.
402
-		h: &Header{
403
-			Name:     "bin/su",
404
-			Mode:     0755 | c_ISUID,
405
-			Size:     23232,
406
-			ModTime:  time.Unix(1355405093, 0),
407
-			Typeflag: TypeReg,
408
-		},
409
-		fm: 0755 | fs.ModeSetuid,
410
-	}, {
411
-		// setguid.
412
-		h: &Header{
413
-			Name:     "group.txt",
414
-			Mode:     0750 | c_ISGID,
415
-			Size:     0,
416
-			ModTime:  time.Unix(1360602346, 0),
417
-			Typeflag: TypeReg,
418
-		},
419
-		fm: 0750 | fs.ModeSetgid,
420
-	}, {
421
-		// sticky.
422
-		h: &Header{
423
-			Name:     "sticky.txt",
424
-			Mode:     0600 | c_ISVTX,
425
-			Size:     7,
426
-			ModTime:  time.Unix(1360602540, 0),
427
-			Typeflag: TypeReg,
428
-		},
429
-		fm: 0600 | fs.ModeSticky,
430
-	}, {
431
-		// hard link.
432
-		h: &Header{
433
-			Name:     "hard.txt",
434
-			Mode:     0644,
435
-			Size:     0,
436
-			Linkname: "file.txt",
437
-			ModTime:  time.Unix(1360600916, 0),
438
-			Typeflag: TypeLink,
439
-		},
440
-		fm: 0644,
441
-	}, {
442
-		// More information.
443
-		h: &Header{
444
-			Name:     "info.txt",
445
-			Mode:     0600,
446
-			Size:     0,
447
-			Uid:      1000,
448
-			Gid:      1000,
449
-			ModTime:  time.Unix(1360602540, 0),
450
-			Uname:    "slartibartfast",
451
-			Gname:    "users",
452
-			Typeflag: TypeReg,
453
-		},
454
-		fm: 0600,
455
-	}}
456
-
457
-	for i, v := range vectors {
458
-		fi := v.h.FileInfo()
459
-		h2, err := FileInfoHeader(fi, "")
460
-		if err != nil {
461
-			t.Error(err)
462
-			continue
463
-		}
464
-		if strings.Contains(fi.Name(), "/") {
465
-			t.Errorf("FileInfo of %q contains slash: %q", v.h.Name, fi.Name())
466
-		}
467
-		name := path.Base(v.h.Name)
468
-		if fi.IsDir() {
469
-			name += "/"
470
-		}
471
-		if got, want := h2.Name, name; got != want {
472
-			t.Errorf("i=%d: Name: got %v, want %v", i, got, want)
473
-		}
474
-		if got, want := h2.Size, v.h.Size; got != want {
475
-			t.Errorf("i=%d: Size: got %v, want %v", i, got, want)
476
-		}
477
-		if got, want := h2.Uid, v.h.Uid; got != want {
478
-			t.Errorf("i=%d: Uid: got %d, want %d", i, got, want)
479
-		}
480
-		if got, want := h2.Gid, v.h.Gid; got != want {
481
-			t.Errorf("i=%d: Gid: got %d, want %d", i, got, want)
482
-		}
483
-		if got, want := h2.Uname, v.h.Uname; got != want {
484
-			t.Errorf("i=%d: Uname: got %q, want %q", i, got, want)
485
-		}
486
-		if got, want := h2.Gname, v.h.Gname; got != want {
487
-			t.Errorf("i=%d: Gname: got %q, want %q", i, got, want)
488
-		}
489
-		if got, want := h2.Linkname, v.h.Linkname; got != want {
490
-			t.Errorf("i=%d: Linkname: got %v, want %v", i, got, want)
491
-		}
492
-		if got, want := h2.Typeflag, v.h.Typeflag; got != want {
493
-			t.Logf("%#v %#v", v.h, fi.Sys())
494
-			t.Errorf("i=%d: Typeflag: got %q, want %q", i, got, want)
495
-		}
496
-		if got, want := h2.Mode, v.h.Mode; got != want {
497
-			t.Errorf("i=%d: Mode: got %o, want %o", i, got, want)
498
-		}
499
-		if got, want := fi.Mode(), v.fm; got != want {
500
-			t.Errorf("i=%d: fi.Mode: got %o, want %o", i, got, want)
501
-		}
502
-		if got, want := h2.AccessTime, v.h.AccessTime; got != want {
503
-			t.Errorf("i=%d: AccessTime: got %v, want %v", i, got, want)
504
-		}
505
-		if got, want := h2.ChangeTime, v.h.ChangeTime; got != want {
506
-			t.Errorf("i=%d: ChangeTime: got %v, want %v", i, got, want)
507
-		}
508
-		if got, want := h2.ModTime, v.h.ModTime; got != want {
509
-			t.Errorf("i=%d: ModTime: got %v, want %v", i, got, want)
510
-		}
511
-		if sysh, ok := fi.Sys().(*Header); !ok || sysh != v.h {
512
-			t.Errorf("i=%d: Sys didn't return original *Header", i)
513
-		}
514
-	}
515
-}
516
-
517
-func TestHeaderAllowedFormats(t *testing.T) {
518
-	vectors := []struct {
519
-		header  *Header           // Input header
520
-		paxHdrs map[string]string // Expected PAX headers that may be needed
521
-		formats Format            // Expected formats that can encode the header
522
-	}{{
523
-		header:  &Header{},
524
-		formats: FormatUSTAR | FormatPAX | FormatGNU,
525
-	}, {
526
-		header:  &Header{Size: 077777777777},
527
-		formats: FormatUSTAR | FormatPAX | FormatGNU,
528
-	}, {
529
-		header:  &Header{Size: 077777777777, Format: FormatUSTAR},
530
-		formats: FormatUSTAR,
531
-	}, {
532
-		header:  &Header{Size: 077777777777, Format: FormatPAX},
533
-		formats: FormatUSTAR | FormatPAX,
534
-	}, {
535
-		header:  &Header{Size: 077777777777, Format: FormatGNU},
536
-		formats: FormatGNU,
537
-	}, {
538
-		header:  &Header{Size: 077777777777 + 1},
539
-		paxHdrs: map[string]string{paxSize: "8589934592"},
540
-		formats: FormatPAX | FormatGNU,
541
-	}, {
542
-		header:  &Header{Size: 077777777777 + 1, Format: FormatPAX},
543
-		paxHdrs: map[string]string{paxSize: "8589934592"},
544
-		formats: FormatPAX,
545
-	}, {
546
-		header:  &Header{Size: 077777777777 + 1, Format: FormatGNU},
547
-		paxHdrs: map[string]string{paxSize: "8589934592"},
548
-		formats: FormatGNU,
549
-	}, {
550
-		header:  &Header{Mode: 07777777},
551
-		formats: FormatUSTAR | FormatPAX | FormatGNU,
552
-	}, {
553
-		header:  &Header{Mode: 07777777 + 1},
554
-		formats: FormatGNU,
555
-	}, {
556
-		header:  &Header{Devmajor: -123},
557
-		formats: FormatGNU,
558
-	}, {
559
-		header:  &Header{Devmajor: 1<<56 - 1},
560
-		formats: FormatGNU,
561
-	}, {
562
-		header:  &Header{Devmajor: 1 << 56},
563
-		formats: FormatUnknown,
564
-	}, {
565
-		header:  &Header{Devmajor: -1 << 56},
566
-		formats: FormatGNU,
567
-	}, {
568
-		header:  &Header{Devmajor: -1<<56 - 1},
569
-		formats: FormatUnknown,
570
-	}, {
571
-		header:  &Header{Name: "用戶名", Devmajor: -1 << 56},
572
-		formats: FormatGNU,
573
-	}, {
574
-		header:  &Header{Size: math.MaxInt64},
575
-		paxHdrs: map[string]string{paxSize: "9223372036854775807"},
576
-		formats: FormatPAX | FormatGNU,
577
-	}, {
578
-		header:  &Header{Size: math.MinInt64},
579
-		paxHdrs: map[string]string{paxSize: "-9223372036854775808"},
580
-		formats: FormatUnknown,
581
-	}, {
582
-		header:  &Header{Uname: "0123456789abcdef0123456789abcdef"},
583
-		formats: FormatUSTAR | FormatPAX | FormatGNU,
584
-	}, {
585
-		header:  &Header{Uname: "0123456789abcdef0123456789abcdefx"},
586
-		paxHdrs: map[string]string{paxUname: "0123456789abcdef0123456789abcdefx"},
587
-		formats: FormatPAX,
588
-	}, {
589
-		header:  &Header{Name: "foobar"},
590
-		formats: FormatUSTAR | FormatPAX | FormatGNU,
591
-	}, {
592
-		header:  &Header{Name: strings.Repeat("a", nameSize)},
593
-		formats: FormatUSTAR | FormatPAX | FormatGNU,
594
-	}, {
595
-		header:  &Header{Name: strings.Repeat("a", nameSize+1)},
596
-		paxHdrs: map[string]string{paxPath: strings.Repeat("a", nameSize+1)},
597
-		formats: FormatPAX | FormatGNU,
598
-	}, {
599
-		header:  &Header{Linkname: "用戶名"},
600
-		paxHdrs: map[string]string{paxLinkpath: "用戶名"},
601
-		formats: FormatPAX | FormatGNU,
602
-	}, {
603
-		header:  &Header{Linkname: strings.Repeat("用戶名\x00", nameSize)},
604
-		paxHdrs: map[string]string{paxLinkpath: strings.Repeat("用戶名\x00", nameSize)},
605
-		formats: FormatUnknown,
606
-	}, {
607
-		header:  &Header{Linkname: "\x00hello"},
608
-		paxHdrs: map[string]string{paxLinkpath: "\x00hello"},
609
-		formats: FormatUnknown,
610
-	}, {
611
-		header:  &Header{Uid: 07777777},
612
-		formats: FormatUSTAR | FormatPAX | FormatGNU,
613
-	}, {
614
-		header:  &Header{Uid: 07777777 + 1},
615
-		paxHdrs: map[string]string{paxUid: "2097152"},
616
-		formats: FormatPAX | FormatGNU,
617
-	}, {
618
-		header:  &Header{Xattrs: nil},
619
-		formats: FormatUSTAR | FormatPAX | FormatGNU,
620
-	}, {
621
-		header:  &Header{Xattrs: map[string]string{"foo": "bar"}},
622
-		paxHdrs: map[string]string{paxSchilyXattr + "foo": "bar"},
623
-		formats: FormatPAX,
624
-	}, {
625
-		header:  &Header{Xattrs: map[string]string{"foo": "bar"}, Format: FormatGNU},
626
-		paxHdrs: map[string]string{paxSchilyXattr + "foo": "bar"},
627
-		formats: FormatUnknown,
628
-	}, {
629
-		header:  &Header{Xattrs: map[string]string{"用戶名": "\x00hello"}},
630
-		paxHdrs: map[string]string{paxSchilyXattr + "用戶名": "\x00hello"},
631
-		formats: FormatPAX,
632
-	}, {
633
-		header:  &Header{Xattrs: map[string]string{"foo=bar": "baz"}},
634
-		formats: FormatUnknown,
635
-	}, {
636
-		header:  &Header{Xattrs: map[string]string{"foo": ""}},
637
-		paxHdrs: map[string]string{paxSchilyXattr + "foo": ""},
638
-		formats: FormatPAX,
639
-	}, {
640
-		header:  &Header{ModTime: time.Unix(0, 0)},
641
-		formats: FormatUSTAR | FormatPAX | FormatGNU,
642
-	}, {
643
-		header:  &Header{ModTime: time.Unix(077777777777, 0)},
644
-		formats: FormatUSTAR | FormatPAX | FormatGNU,
645
-	}, {
646
-		header:  &Header{ModTime: time.Unix(077777777777+1, 0)},
647
-		paxHdrs: map[string]string{paxMtime: "8589934592"},
648
-		formats: FormatPAX | FormatGNU,
649
-	}, {
650
-		header:  &Header{ModTime: time.Unix(math.MaxInt64, 0)},
651
-		paxHdrs: map[string]string{paxMtime: "9223372036854775807"},
652
-		formats: FormatPAX | FormatGNU,
653
-	}, {
654
-		header:  &Header{ModTime: time.Unix(math.MaxInt64, 0), Format: FormatUSTAR},
655
-		paxHdrs: map[string]string{paxMtime: "9223372036854775807"},
656
-		formats: FormatUnknown,
657
-	}, {
658
-		header:  &Header{ModTime: time.Unix(-1, 0)},
659
-		paxHdrs: map[string]string{paxMtime: "-1"},
660
-		formats: FormatPAX | FormatGNU,
661
-	}, {
662
-		header:  &Header{ModTime: time.Unix(1, 500)},
663
-		paxHdrs: map[string]string{paxMtime: "1.0000005"},
664
-		formats: FormatUSTAR | FormatPAX | FormatGNU,
665
-	}, {
666
-		header:  &Header{ModTime: time.Unix(1, 0)},
667
-		formats: FormatUSTAR | FormatPAX | FormatGNU,
668
-	}, {
669
-		header:  &Header{ModTime: time.Unix(1, 0), Format: FormatPAX},
670
-		formats: FormatUSTAR | FormatPAX,
671
-	}, {
672
-		header:  &Header{ModTime: time.Unix(1, 500), Format: FormatUSTAR},
673
-		paxHdrs: map[string]string{paxMtime: "1.0000005"},
674
-		formats: FormatUSTAR,
675
-	}, {
676
-		header:  &Header{ModTime: time.Unix(1, 500), Format: FormatPAX},
677
-		paxHdrs: map[string]string{paxMtime: "1.0000005"},
678
-		formats: FormatPAX,
679
-	}, {
680
-		header:  &Header{ModTime: time.Unix(1, 500), Format: FormatGNU},
681
-		paxHdrs: map[string]string{paxMtime: "1.0000005"},
682
-		formats: FormatGNU,
683
-	}, {
684
-		header:  &Header{ModTime: time.Unix(-1, 500)},
685
-		paxHdrs: map[string]string{paxMtime: "-0.9999995"},
686
-		formats: FormatPAX | FormatGNU,
687
-	}, {
688
-		header:  &Header{ModTime: time.Unix(-1, 500), Format: FormatGNU},
689
-		paxHdrs: map[string]string{paxMtime: "-0.9999995"},
690
-		formats: FormatGNU,
691
-	}, {
692
-		header:  &Header{AccessTime: time.Unix(0, 0)},
693
-		paxHdrs: map[string]string{paxAtime: "0"},
694
-		formats: FormatPAX | FormatGNU,
695
-	}, {
696
-		header:  &Header{AccessTime: time.Unix(0, 0), Format: FormatUSTAR},
697
-		paxHdrs: map[string]string{paxAtime: "0"},
698
-		formats: FormatUnknown,
699
-	}, {
700
-		header:  &Header{AccessTime: time.Unix(0, 0), Format: FormatPAX},
701
-		paxHdrs: map[string]string{paxAtime: "0"},
702
-		formats: FormatPAX,
703
-	}, {
704
-		header:  &Header{AccessTime: time.Unix(0, 0), Format: FormatGNU},
705
-		paxHdrs: map[string]string{paxAtime: "0"},
706
-		formats: FormatGNU,
707
-	}, {
708
-		header:  &Header{AccessTime: time.Unix(-123, 0)},
709
-		paxHdrs: map[string]string{paxAtime: "-123"},
710
-		formats: FormatPAX | FormatGNU,
711
-	}, {
712
-		header:  &Header{AccessTime: time.Unix(-123, 0), Format: FormatPAX},
713
-		paxHdrs: map[string]string{paxAtime: "-123"},
714
-		formats: FormatPAX,
715
-	}, {
716
-		header:  &Header{ChangeTime: time.Unix(123, 456)},
717
-		paxHdrs: map[string]string{paxCtime: "123.000000456"},
718
-		formats: FormatPAX | FormatGNU,
719
-	}, {
720
-		header:  &Header{ChangeTime: time.Unix(123, 456), Format: FormatUSTAR},
721
-		paxHdrs: map[string]string{paxCtime: "123.000000456"},
722
-		formats: FormatUnknown,
723
-	}, {
724
-		header:  &Header{ChangeTime: time.Unix(123, 456), Format: FormatGNU},
725
-		paxHdrs: map[string]string{paxCtime: "123.000000456"},
726
-		formats: FormatGNU,
727
-	}, {
728
-		header:  &Header{ChangeTime: time.Unix(123, 456), Format: FormatPAX},
729
-		paxHdrs: map[string]string{paxCtime: "123.000000456"},
730
-		formats: FormatPAX,
731
-	}, {
732
-		header:  &Header{Name: "foo/", Typeflag: TypeDir},
733
-		formats: FormatUSTAR | FormatPAX | FormatGNU,
734
-	}, {
735
-		header:  &Header{Name: "foo/", Typeflag: TypeReg},
736
-		formats: FormatUnknown,
737
-	}, {
738
-		header:  &Header{Name: "foo/", Typeflag: TypeSymlink},
739
-		formats: FormatUSTAR | FormatPAX | FormatGNU,
740
-	}}
741
-
742
-	for i, v := range vectors {
743
-		formats, paxHdrs, err := v.header.allowedFormats()
744
-		if formats != v.formats {
745
-			t.Errorf("test %d, allowedFormats(): got %v, want %v", i, formats, v.formats)
746
-		}
747
-		if formats&FormatPAX > 0 && !reflect.DeepEqual(paxHdrs, v.paxHdrs) && !(len(paxHdrs) == 0 && len(v.paxHdrs) == 0) {
748
-			t.Errorf("test %d, allowedFormats():\ngot  %v\nwant %s", i, paxHdrs, v.paxHdrs)
749
-		}
750
-		if (formats != FormatUnknown) && (err != nil) {
751
-			t.Errorf("test %d, unexpected error: %v", i, err)
752
-		}
753
-		if (formats == FormatUnknown) && (err == nil) {
754
-			t.Errorf("test %d, got nil-error, want non-nil error", i)
755
-		}
756
-	}
757
-}
758
-
759
-func Benchmark(b *testing.B) {
760
-	type file struct {
761
-		hdr  *Header
762
-		body []byte
763
-	}
764
-
765
-	vectors := []struct {
766
-		label string
767
-		files []file
768
-	}{{
769
-		"USTAR",
770
-		[]file{{
771
-			&Header{Name: "bar", Mode: 0640, Size: int64(3)},
772
-			[]byte("foo"),
773
-		}, {
774
-			&Header{Name: "world", Mode: 0640, Size: int64(5)},
775
-			[]byte("hello"),
776
-		}},
777
-	}, {
778
-		"GNU",
779
-		[]file{{
780
-			&Header{Name: "bar", Mode: 0640, Size: int64(3), Devmajor: -1},
781
-			[]byte("foo"),
782
-		}, {
783
-			&Header{Name: "world", Mode: 0640, Size: int64(5), Devmajor: -1},
784
-			[]byte("hello"),
785
-		}},
786
-	}, {
787
-		"PAX",
788
-		[]file{{
789
-			&Header{Name: "bar", Mode: 0640, Size: int64(3), Xattrs: map[string]string{"foo": "bar"}},
790
-			[]byte("foo"),
791
-		}, {
792
-			&Header{Name: "world", Mode: 0640, Size: int64(5), Xattrs: map[string]string{"foo": "bar"}},
793
-			[]byte("hello"),
794
-		}},
795
-	}}
796
-
797
-	b.Run("Writer", func(b *testing.B) {
798
-		for _, v := range vectors {
799
-			b.Run(v.label, func(b *testing.B) {
800
-				b.ReportAllocs()
801
-				for i := 0; i < b.N; i++ {
802
-					// Writing to io.Discard because we want to
803
-					// test purely the writer code and not bring in disk performance into this.
804
-					tw := NewWriter(io.Discard)
805
-					for _, file := range v.files {
806
-						if err := tw.WriteHeader(file.hdr); err != nil {
807
-							b.Errorf("unexpected WriteHeader error: %v", err)
808
-						}
809
-						if _, err := tw.Write(file.body); err != nil {
810
-							b.Errorf("unexpected Write error: %v", err)
811
-						}
812
-					}
813
-					if err := tw.Close(); err != nil {
814
-						b.Errorf("unexpected Close error: %v", err)
815
-					}
816
-				}
817
-			})
818
-		}
819
-	})
820
-
821
-	b.Run("Reader", func(b *testing.B) {
822
-		for _, v := range vectors {
823
-			var buf bytes.Buffer
824
-			var r bytes.Reader
825
-
826
-			// Write the archive to a byte buffer.
827
-			tw := NewWriter(&buf)
828
-			for _, file := range v.files {
829
-				tw.WriteHeader(file.hdr)
830
-				tw.Write(file.body)
831
-			}
832
-			tw.Close()
833
-			b.Run(v.label, func(b *testing.B) {
834
-				b.ReportAllocs()
835
-				// Read from the byte buffer.
836
-				for i := 0; i < b.N; i++ {
837
-					r.Reset(buf.Bytes())
838
-					tr := NewReader(&r)
839
-					if _, err := tr.Next(); err != nil {
840
-						b.Errorf("unexpected Next error: %v", err)
841
-					}
842
-					if _, err := io.Copy(io.Discard, tr); err != nil {
843
-						b.Errorf("unexpected Copy error : %v", err)
844
-					}
845
-				}
846
-			})
847
-		}
848
-	})
849
-
850
-}
851 1
deleted file mode 100644
852 2
Binary files a/vendor/archive/tar/testdata/file-and-dir.tar and /dev/null differ
853 3
deleted file mode 100644
854 4
Binary files a/vendor/archive/tar/testdata/gnu-incremental.tar and /dev/null differ
855 5
deleted file mode 100644
856 6
Binary files a/vendor/archive/tar/testdata/gnu-long-nul.tar and /dev/null differ
857 7
deleted file mode 100644
858 8
Binary files a/vendor/archive/tar/testdata/gnu-multi-hdrs.tar and /dev/null differ
859 9
deleted file mode 100644
860 10
Binary files a/vendor/archive/tar/testdata/gnu-nil-sparse-data.tar and /dev/null differ
861 11
deleted file mode 100644
862 12
Binary files a/vendor/archive/tar/testdata/gnu-nil-sparse-hole.tar and /dev/null differ
863 13
deleted file mode 100644
864 14
Binary files a/vendor/archive/tar/testdata/gnu-not-utf8.tar and /dev/null differ
865 15
deleted file mode 100644
866 16
Binary files a/vendor/archive/tar/testdata/gnu-sparse-big.tar and /dev/null differ
867 17
deleted file mode 100644
868 18
Binary files a/vendor/archive/tar/testdata/gnu-utf8.tar and /dev/null differ
869 19
deleted file mode 100644
870 20
Binary files a/vendor/archive/tar/testdata/gnu.tar and /dev/null differ
871 21
deleted file mode 100644
872 22
Binary files a/vendor/archive/tar/testdata/hardlink.tar and /dev/null differ
873 23
deleted file mode 100644
874 24
Binary files a/vendor/archive/tar/testdata/hdr-only.tar and /dev/null differ
875 25
deleted file mode 100644
876 26
Binary files a/vendor/archive/tar/testdata/invalid-go17.tar and /dev/null differ
877 27
deleted file mode 100644
878 28
Binary files a/vendor/archive/tar/testdata/issue10968.tar and /dev/null differ
879 29
deleted file mode 100644
880 30
Binary files a/vendor/archive/tar/testdata/issue11169.tar and /dev/null differ
881 31
deleted file mode 100644
882 32
Binary files a/vendor/archive/tar/testdata/issue12435.tar and /dev/null differ
883 33
deleted file mode 100644
884 34
Binary files a/vendor/archive/tar/testdata/neg-size.tar and /dev/null differ
885 35
deleted file mode 100644
886 36
Binary files a/vendor/archive/tar/testdata/nil-uid.tar and /dev/null differ
887 37
deleted file mode 100644
888 38
Binary files a/vendor/archive/tar/testdata/pax-bad-hdr-file.tar and /dev/null differ
889 39
deleted file mode 100644
890 40
Binary files a/vendor/archive/tar/testdata/pax-bad-mtime-file.tar and /dev/null differ
891 41
deleted file mode 100644
892 42
Binary files a/vendor/archive/tar/testdata/pax-global-records.tar and /dev/null differ
893 43
deleted file mode 100644
894 44
Binary files a/vendor/archive/tar/testdata/pax-multi-hdrs.tar and /dev/null differ
895 45
deleted file mode 100644
896 46
Binary files a/vendor/archive/tar/testdata/pax-nil-sparse-data.tar and /dev/null differ
897 47
deleted file mode 100644
898 48
Binary files a/vendor/archive/tar/testdata/pax-nil-sparse-hole.tar and /dev/null differ
899 49
deleted file mode 100644
900 50
Binary files a/vendor/archive/tar/testdata/pax-nul-path.tar and /dev/null differ
901 51
deleted file mode 100644
902 52
Binary files a/vendor/archive/tar/testdata/pax-nul-xattrs.tar and /dev/null differ
903 53
deleted file mode 100644
904 54
Binary files a/vendor/archive/tar/testdata/pax-path-hdr.tar and /dev/null differ
905 55
deleted file mode 100644
906 56
Binary files a/vendor/archive/tar/testdata/pax-pos-size-file.tar and /dev/null differ
907 57
deleted file mode 100644
908 58
Binary files a/vendor/archive/tar/testdata/pax-records.tar and /dev/null differ
909 59
deleted file mode 100644
910 60
Binary files a/vendor/archive/tar/testdata/pax-sparse-big.tar and /dev/null differ
911 61
deleted file mode 100644
912 62
Binary files a/vendor/archive/tar/testdata/pax.tar and /dev/null differ
913 63
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/archive/tar/testdata/sparse-formats.tar and /dev/null differ
4 3
deleted file mode 100644
5 4
Binary files a/vendor/archive/tar/testdata/star.tar and /dev/null differ
6 5
deleted file mode 100644
7 6
Binary files a/vendor/archive/tar/testdata/trailing-slash.tar and /dev/null differ
8 7
deleted file mode 100644
9 8
Binary files a/vendor/archive/tar/testdata/ustar-file-devs.tar and /dev/null differ
10 9
deleted file mode 100644
11 10
Binary files a/vendor/archive/tar/testdata/ustar-file-reg.tar and /dev/null differ
12 11
deleted file mode 100644
13 12
Binary files a/vendor/archive/tar/testdata/ustar.tar and /dev/null differ
14 13
deleted file mode 100644
15 14
Binary files a/vendor/archive/tar/testdata/v7.tar and /dev/null differ
16 15
deleted file mode 100644
17 16
Binary files a/vendor/archive/tar/testdata/writer-big-long.tar and /dev/null differ
18 17
deleted file mode 100644
19 18
Binary files a/vendor/archive/tar/testdata/writer-big.tar and /dev/null differ
20 19
deleted file mode 100644
21 20
Binary files a/vendor/archive/tar/testdata/writer.tar and /dev/null differ
22 21
deleted file mode 100644
23 22
Binary files a/vendor/archive/tar/testdata/xattrs.tar and /dev/null differ
24 23
deleted file mode 100644
... ...
@@ -1,653 +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
-	"fmt"
9
-	"io"
10
-	"path"
11
-	"sort"
12
-	"strings"
13
-	"time"
14
-)
15
-
16
-// Writer provides sequential writing of a tar archive.
17
-// Write.WriteHeader begins a new file with the provided Header,
18
-// and then Writer can be treated as an io.Writer to supply that file's data.
19
-type Writer struct {
20
-	w    io.Writer
21
-	pad  int64      // Amount of padding to write after current file entry
22
-	curr fileWriter // Writer for current file entry
23
-	hdr  Header     // Shallow copy of Header that is safe for mutations
24
-	blk  block      // Buffer to use as temporary local storage
25
-
26
-	// err is a persistent error.
27
-	// It is only the responsibility of every exported method of Writer to
28
-	// ensure that this error is sticky.
29
-	err error
30
-}
31
-
32
-// NewWriter creates a new Writer writing to w.
33
-func NewWriter(w io.Writer) *Writer {
34
-	return &Writer{w: w, curr: &regFileWriter{w, 0}}
35
-}
36
-
37
-type fileWriter interface {
38
-	io.Writer
39
-	fileState
40
-
41
-	ReadFrom(io.Reader) (int64, error)
42
-}
43
-
44
-// Flush finishes writing the current file's block padding.
45
-// The current file must be fully written before Flush can be called.
46
-//
47
-// This is unnecessary as the next call to WriteHeader or Close
48
-// will implicitly flush out the file's padding.
49
-func (tw *Writer) Flush() error {
50
-	if tw.err != nil {
51
-		return tw.err
52
-	}
53
-	if nb := tw.curr.LogicalRemaining(); nb > 0 {
54
-		return fmt.Errorf("archive/tar: missed writing %d bytes", nb)
55
-	}
56
-	if _, tw.err = tw.w.Write(zeroBlock[:tw.pad]); tw.err != nil {
57
-		return tw.err
58
-	}
59
-	tw.pad = 0
60
-	return nil
61
-}
62
-
63
-// WriteHeader writes hdr and prepares to accept the file's contents.
64
-// The Header.Size determines how many bytes can be written for the next file.
65
-// If the current file is not fully written, then this returns an error.
66
-// This implicitly flushes any padding necessary before writing the header.
67
-func (tw *Writer) WriteHeader(hdr *Header) error {
68
-	if err := tw.Flush(); err != nil {
69
-		return err
70
-	}
71
-	tw.hdr = *hdr // Shallow copy of Header
72
-
73
-	// Avoid usage of the legacy TypeRegA flag, and automatically promote
74
-	// it to use TypeReg or TypeDir.
75
-	if tw.hdr.Typeflag == TypeRegA {
76
-		if strings.HasSuffix(tw.hdr.Name, "/") {
77
-			tw.hdr.Typeflag = TypeDir
78
-		} else {
79
-			tw.hdr.Typeflag = TypeReg
80
-		}
81
-	}
82
-
83
-	// Round ModTime and ignore AccessTime and ChangeTime unless
84
-	// the format is explicitly chosen.
85
-	// This ensures nominal usage of WriteHeader (without specifying the format)
86
-	// does not always result in the PAX format being chosen, which
87
-	// causes a 1KiB increase to every header.
88
-	if tw.hdr.Format == FormatUnknown {
89
-		tw.hdr.ModTime = tw.hdr.ModTime.Round(time.Second)
90
-		tw.hdr.AccessTime = time.Time{}
91
-		tw.hdr.ChangeTime = time.Time{}
92
-	}
93
-
94
-	allowedFormats, paxHdrs, err := tw.hdr.allowedFormats()
95
-	switch {
96
-	case allowedFormats.has(FormatUSTAR):
97
-		tw.err = tw.writeUSTARHeader(&tw.hdr)
98
-		return tw.err
99
-	case allowedFormats.has(FormatPAX):
100
-		tw.err = tw.writePAXHeader(&tw.hdr, paxHdrs)
101
-		return tw.err
102
-	case allowedFormats.has(FormatGNU):
103
-		tw.err = tw.writeGNUHeader(&tw.hdr)
104
-		return tw.err
105
-	default:
106
-		return err // Non-fatal error
107
-	}
108
-}
109
-
110
-func (tw *Writer) writeUSTARHeader(hdr *Header) error {
111
-	// Check if we can use USTAR prefix/suffix splitting.
112
-	var namePrefix string
113
-	if prefix, suffix, ok := splitUSTARPath(hdr.Name); ok {
114
-		namePrefix, hdr.Name = prefix, suffix
115
-	}
116
-
117
-	// Pack the main header.
118
-	var f formatter
119
-	blk := tw.templateV7Plus(hdr, f.formatString, f.formatOctal)
120
-	f.formatString(blk.USTAR().Prefix(), namePrefix)
121
-	blk.SetFormat(FormatUSTAR)
122
-	if f.err != nil {
123
-		return f.err // Should never happen since header is validated
124
-	}
125
-	return tw.writeRawHeader(blk, hdr.Size, hdr.Typeflag)
126
-}
127
-
128
-func (tw *Writer) writePAXHeader(hdr *Header, paxHdrs map[string]string) error {
129
-	realName, realSize := hdr.Name, hdr.Size
130
-
131
-	// TODO(dsnet): Re-enable this when adding sparse support.
132
-	// See https://golang.org/issue/22735
133
-	/*
134
-		// Handle sparse files.
135
-		var spd sparseDatas
136
-		var spb []byte
137
-		if len(hdr.SparseHoles) > 0 {
138
-			sph := append([]sparseEntry{}, hdr.SparseHoles...) // Copy sparse map
139
-			sph = alignSparseEntries(sph, hdr.Size)
140
-			spd = invertSparseEntries(sph, hdr.Size)
141
-
142
-			// Format the sparse map.
143
-			hdr.Size = 0 // Replace with encoded size
144
-			spb = append(strconv.AppendInt(spb, int64(len(spd)), 10), '\n')
145
-			for _, s := range spd {
146
-				hdr.Size += s.Length
147
-				spb = append(strconv.AppendInt(spb, s.Offset, 10), '\n')
148
-				spb = append(strconv.AppendInt(spb, s.Length, 10), '\n')
149
-			}
150
-			pad := blockPadding(int64(len(spb)))
151
-			spb = append(spb, zeroBlock[:pad]...)
152
-			hdr.Size += int64(len(spb)) // Accounts for encoded sparse map
153
-
154
-			// Add and modify appropriate PAX records.
155
-			dir, file := path.Split(realName)
156
-			hdr.Name = path.Join(dir, "GNUSparseFile.0", file)
157
-			paxHdrs[paxGNUSparseMajor] = "1"
158
-			paxHdrs[paxGNUSparseMinor] = "0"
159
-			paxHdrs[paxGNUSparseName] = realName
160
-			paxHdrs[paxGNUSparseRealSize] = strconv.FormatInt(realSize, 10)
161
-			paxHdrs[paxSize] = strconv.FormatInt(hdr.Size, 10)
162
-			delete(paxHdrs, paxPath) // Recorded by paxGNUSparseName
163
-		}
164
-	*/
165
-	_ = realSize
166
-
167
-	// Write PAX records to the output.
168
-	isGlobal := hdr.Typeflag == TypeXGlobalHeader
169
-	if len(paxHdrs) > 0 || isGlobal {
170
-		// Sort keys for deterministic ordering.
171
-		var keys []string
172
-		for k := range paxHdrs {
173
-			keys = append(keys, k)
174
-		}
175
-		sort.Strings(keys)
176
-
177
-		// Write each record to a buffer.
178
-		var buf strings.Builder
179
-		for _, k := range keys {
180
-			rec, err := formatPAXRecord(k, paxHdrs[k])
181
-			if err != nil {
182
-				return err
183
-			}
184
-			buf.WriteString(rec)
185
-		}
186
-
187
-		// Write the extended header file.
188
-		var name string
189
-		var flag byte
190
-		if isGlobal {
191
-			name = realName
192
-			if name == "" {
193
-				name = "GlobalHead.0.0"
194
-			}
195
-			flag = TypeXGlobalHeader
196
-		} else {
197
-			dir, file := path.Split(realName)
198
-			name = path.Join(dir, "PaxHeaders.0", file)
199
-			flag = TypeXHeader
200
-		}
201
-		data := buf.String()
202
-		if err := tw.writeRawFile(name, data, flag, FormatPAX); err != nil || isGlobal {
203
-			return err // Global headers return here
204
-		}
205
-	}
206
-
207
-	// Pack the main header.
208
-	var f formatter // Ignore errors since they are expected
209
-	fmtStr := func(b []byte, s string) { f.formatString(b, toASCII(s)) }
210
-	blk := tw.templateV7Plus(hdr, fmtStr, f.formatOctal)
211
-	blk.SetFormat(FormatPAX)
212
-	if err := tw.writeRawHeader(blk, hdr.Size, hdr.Typeflag); err != nil {
213
-		return err
214
-	}
215
-
216
-	// TODO(dsnet): Re-enable this when adding sparse support.
217
-	// See https://golang.org/issue/22735
218
-	/*
219
-		// Write the sparse map and setup the sparse writer if necessary.
220
-		if len(spd) > 0 {
221
-			// Use tw.curr since the sparse map is accounted for in hdr.Size.
222
-			if _, err := tw.curr.Write(spb); err != nil {
223
-				return err
224
-			}
225
-			tw.curr = &sparseFileWriter{tw.curr, spd, 0}
226
-		}
227
-	*/
228
-	return nil
229
-}
230
-
231
-func (tw *Writer) writeGNUHeader(hdr *Header) error {
232
-	// Use long-link files if Name or Linkname exceeds the field size.
233
-	const longName = "././@LongLink"
234
-	if len(hdr.Name) > nameSize {
235
-		data := hdr.Name + "\x00"
236
-		if err := tw.writeRawFile(longName, data, TypeGNULongName, FormatGNU); err != nil {
237
-			return err
238
-		}
239
-	}
240
-	if len(hdr.Linkname) > nameSize {
241
-		data := hdr.Linkname + "\x00"
242
-		if err := tw.writeRawFile(longName, data, TypeGNULongLink, FormatGNU); err != nil {
243
-			return err
244
-		}
245
-	}
246
-
247
-	// Pack the main header.
248
-	var f formatter // Ignore errors since they are expected
249
-	var spd sparseDatas
250
-	var spb []byte
251
-	blk := tw.templateV7Plus(hdr, f.formatString, f.formatNumeric)
252
-	if !hdr.AccessTime.IsZero() {
253
-		f.formatNumeric(blk.GNU().AccessTime(), hdr.AccessTime.Unix())
254
-	}
255
-	if !hdr.ChangeTime.IsZero() {
256
-		f.formatNumeric(blk.GNU().ChangeTime(), hdr.ChangeTime.Unix())
257
-	}
258
-	// TODO(dsnet): Re-enable this when adding sparse support.
259
-	// See https://golang.org/issue/22735
260
-	/*
261
-		if hdr.Typeflag == TypeGNUSparse {
262
-			sph := append([]sparseEntry{}, hdr.SparseHoles...) // Copy sparse map
263
-			sph = alignSparseEntries(sph, hdr.Size)
264
-			spd = invertSparseEntries(sph, hdr.Size)
265
-
266
-			// Format the sparse map.
267
-			formatSPD := func(sp sparseDatas, sa sparseArray) sparseDatas {
268
-				for i := 0; len(sp) > 0 && i < sa.MaxEntries(); i++ {
269
-					f.formatNumeric(sa.Entry(i).Offset(), sp[0].Offset)
270
-					f.formatNumeric(sa.Entry(i).Length(), sp[0].Length)
271
-					sp = sp[1:]
272
-				}
273
-				if len(sp) > 0 {
274
-					sa.IsExtended()[0] = 1
275
-				}
276
-				return sp
277
-			}
278
-			sp2 := formatSPD(spd, blk.GNU().Sparse())
279
-			for len(sp2) > 0 {
280
-				var spHdr block
281
-				sp2 = formatSPD(sp2, spHdr.Sparse())
282
-				spb = append(spb, spHdr[:]...)
283
-			}
284
-
285
-			// Update size fields in the header block.
286
-			realSize := hdr.Size
287
-			hdr.Size = 0 // Encoded size; does not account for encoded sparse map
288
-			for _, s := range spd {
289
-				hdr.Size += s.Length
290
-			}
291
-			copy(blk.V7().Size(), zeroBlock[:]) // Reset field
292
-			f.formatNumeric(blk.V7().Size(), hdr.Size)
293
-			f.formatNumeric(blk.GNU().RealSize(), realSize)
294
-		}
295
-	*/
296
-	blk.SetFormat(FormatGNU)
297
-	if err := tw.writeRawHeader(blk, hdr.Size, hdr.Typeflag); err != nil {
298
-		return err
299
-	}
300
-
301
-	// Write the extended sparse map and setup the sparse writer if necessary.
302
-	if len(spd) > 0 {
303
-		// Use tw.w since the sparse map is not accounted for in hdr.Size.
304
-		if _, err := tw.w.Write(spb); err != nil {
305
-			return err
306
-		}
307
-		tw.curr = &sparseFileWriter{tw.curr, spd, 0}
308
-	}
309
-	return nil
310
-}
311
-
312
-type (
313
-	stringFormatter func([]byte, string)
314
-	numberFormatter func([]byte, int64)
315
-)
316
-
317
-// templateV7Plus fills out the V7 fields of a block using values from hdr.
318
-// It also fills out fields (uname, gname, devmajor, devminor) that are
319
-// shared in the USTAR, PAX, and GNU formats using the provided formatters.
320
-//
321
-// The block returned is only valid until the next call to
322
-// templateV7Plus or writeRawFile.
323
-func (tw *Writer) templateV7Plus(hdr *Header, fmtStr stringFormatter, fmtNum numberFormatter) *block {
324
-	tw.blk.Reset()
325
-
326
-	modTime := hdr.ModTime
327
-	if modTime.IsZero() {
328
-		modTime = time.Unix(0, 0)
329
-	}
330
-
331
-	v7 := tw.blk.V7()
332
-	v7.TypeFlag()[0] = hdr.Typeflag
333
-	fmtStr(v7.Name(), hdr.Name)
334
-	fmtStr(v7.LinkName(), hdr.Linkname)
335
-	fmtNum(v7.Mode(), hdr.Mode)
336
-	fmtNum(v7.UID(), int64(hdr.Uid))
337
-	fmtNum(v7.GID(), int64(hdr.Gid))
338
-	fmtNum(v7.Size(), hdr.Size)
339
-	fmtNum(v7.ModTime(), modTime.Unix())
340
-
341
-	ustar := tw.blk.USTAR()
342
-	fmtStr(ustar.UserName(), hdr.Uname)
343
-	fmtStr(ustar.GroupName(), hdr.Gname)
344
-	fmtNum(ustar.DevMajor(), hdr.Devmajor)
345
-	fmtNum(ustar.DevMinor(), hdr.Devminor)
346
-
347
-	return &tw.blk
348
-}
349
-
350
-// writeRawFile writes a minimal file with the given name and flag type.
351
-// It uses format to encode the header format and will write data as the body.
352
-// It uses default values for all of the other fields (as BSD and GNU tar does).
353
-func (tw *Writer) writeRawFile(name, data string, flag byte, format Format) error {
354
-	tw.blk.Reset()
355
-
356
-	// Best effort for the filename.
357
-	name = toASCII(name)
358
-	if len(name) > nameSize {
359
-		name = name[:nameSize]
360
-	}
361
-	name = strings.TrimRight(name, "/")
362
-
363
-	var f formatter
364
-	v7 := tw.blk.V7()
365
-	v7.TypeFlag()[0] = flag
366
-	f.formatString(v7.Name(), name)
367
-	f.formatOctal(v7.Mode(), 0)
368
-	f.formatOctal(v7.UID(), 0)
369
-	f.formatOctal(v7.GID(), 0)
370
-	f.formatOctal(v7.Size(), int64(len(data))) // Must be < 8GiB
371
-	f.formatOctal(v7.ModTime(), 0)
372
-	tw.blk.SetFormat(format)
373
-	if f.err != nil {
374
-		return f.err // Only occurs if size condition is violated
375
-	}
376
-
377
-	// Write the header and data.
378
-	if err := tw.writeRawHeader(&tw.blk, int64(len(data)), flag); err != nil {
379
-		return err
380
-	}
381
-	_, err := io.WriteString(tw, data)
382
-	return err
383
-}
384
-
385
-// writeRawHeader writes the value of blk, regardless of its value.
386
-// It sets up the Writer such that it can accept a file of the given size.
387
-// If the flag is a special header-only flag, then the size is treated as zero.
388
-func (tw *Writer) writeRawHeader(blk *block, size int64, flag byte) error {
389
-	if err := tw.Flush(); err != nil {
390
-		return err
391
-	}
392
-	if _, err := tw.w.Write(blk[:]); err != nil {
393
-		return err
394
-	}
395
-	if isHeaderOnlyType(flag) {
396
-		size = 0
397
-	}
398
-	tw.curr = &regFileWriter{tw.w, size}
399
-	tw.pad = blockPadding(size)
400
-	return nil
401
-}
402
-
403
-// splitUSTARPath splits a path according to USTAR prefix and suffix rules.
404
-// If the path is not splittable, then it will return ("", "", false).
405
-func splitUSTARPath(name string) (prefix, suffix string, ok bool) {
406
-	length := len(name)
407
-	if length <= nameSize || !isASCII(name) {
408
-		return "", "", false
409
-	} else if length > prefixSize+1 {
410
-		length = prefixSize + 1
411
-	} else if name[length-1] == '/' {
412
-		length--
413
-	}
414
-
415
-	i := strings.LastIndex(name[:length], "/")
416
-	nlen := len(name) - i - 1 // nlen is length of suffix
417
-	plen := i                 // plen is length of prefix
418
-	if i <= 0 || nlen > nameSize || nlen == 0 || plen > prefixSize {
419
-		return "", "", false
420
-	}
421
-	return name[:i], name[i+1:], true
422
-}
423
-
424
-// Write writes to the current file in the tar archive.
425
-// Write returns the error ErrWriteTooLong if more than
426
-// Header.Size bytes are written after WriteHeader.
427
-//
428
-// Calling Write on special types like TypeLink, TypeSymlink, TypeChar,
429
-// TypeBlock, TypeDir, and TypeFifo returns (0, ErrWriteTooLong) regardless
430
-// of what the Header.Size claims.
431
-func (tw *Writer) Write(b []byte) (int, error) {
432
-	if tw.err != nil {
433
-		return 0, tw.err
434
-	}
435
-	n, err := tw.curr.Write(b)
436
-	if err != nil && err != ErrWriteTooLong {
437
-		tw.err = err
438
-	}
439
-	return n, err
440
-}
441
-
442
-// readFrom populates the content of the current file by reading from r.
443
-// The bytes read must match the number of remaining bytes in the current file.
444
-//
445
-// If the current file is sparse and r is an io.ReadSeeker,
446
-// then readFrom uses Seek to skip past holes defined in Header.SparseHoles,
447
-// assuming that skipped regions are all NULs.
448
-// This always reads the last byte to ensure r is the right size.
449
-//
450
-// TODO(dsnet): Re-export this when adding sparse file support.
451
-// See https://golang.org/issue/22735
452
-func (tw *Writer) readFrom(r io.Reader) (int64, error) {
453
-	if tw.err != nil {
454
-		return 0, tw.err
455
-	}
456
-	n, err := tw.curr.ReadFrom(r)
457
-	if err != nil && err != ErrWriteTooLong {
458
-		tw.err = err
459
-	}
460
-	return n, err
461
-}
462
-
463
-// Close closes the tar archive by flushing the padding, and writing the footer.
464
-// If the current file (from a prior call to WriteHeader) is not fully written,
465
-// then this returns an error.
466
-func (tw *Writer) Close() error {
467
-	if tw.err == ErrWriteAfterClose {
468
-		return nil
469
-	}
470
-	if tw.err != nil {
471
-		return tw.err
472
-	}
473
-
474
-	// Trailer: two zero blocks.
475
-	err := tw.Flush()
476
-	for i := 0; i < 2 && err == nil; i++ {
477
-		_, err = tw.w.Write(zeroBlock[:])
478
-	}
479
-
480
-	// Ensure all future actions are invalid.
481
-	tw.err = ErrWriteAfterClose
482
-	return err // Report IO errors
483
-}
484
-
485
-// regFileWriter is a fileWriter for writing data to a regular file entry.
486
-type regFileWriter struct {
487
-	w  io.Writer // Underlying Writer
488
-	nb int64     // Number of remaining bytes to write
489
-}
490
-
491
-func (fw *regFileWriter) Write(b []byte) (n int, err error) {
492
-	overwrite := int64(len(b)) > fw.nb
493
-	if overwrite {
494
-		b = b[:fw.nb]
495
-	}
496
-	if len(b) > 0 {
497
-		n, err = fw.w.Write(b)
498
-		fw.nb -= int64(n)
499
-	}
500
-	switch {
501
-	case err != nil:
502
-		return n, err
503
-	case overwrite:
504
-		return n, ErrWriteTooLong
505
-	default:
506
-		return n, nil
507
-	}
508
-}
509
-
510
-func (fw *regFileWriter) ReadFrom(r io.Reader) (int64, error) {
511
-	return io.Copy(struct{ io.Writer }{fw}, r)
512
-}
513
-
514
-func (fw regFileWriter) LogicalRemaining() int64 {
515
-	return fw.nb
516
-}
517
-func (fw regFileWriter) PhysicalRemaining() int64 {
518
-	return fw.nb
519
-}
520
-
521
-// sparseFileWriter is a fileWriter for writing data to a sparse file entry.
522
-type sparseFileWriter struct {
523
-	fw  fileWriter  // Underlying fileWriter
524
-	sp  sparseDatas // Normalized list of data fragments
525
-	pos int64       // Current position in sparse file
526
-}
527
-
528
-func (sw *sparseFileWriter) Write(b []byte) (n int, err error) {
529
-	overwrite := int64(len(b)) > sw.LogicalRemaining()
530
-	if overwrite {
531
-		b = b[:sw.LogicalRemaining()]
532
-	}
533
-
534
-	b0 := b
535
-	endPos := sw.pos + int64(len(b))
536
-	for endPos > sw.pos && err == nil {
537
-		var nf int // Bytes written in fragment
538
-		dataStart, dataEnd := sw.sp[0].Offset, sw.sp[0].endOffset()
539
-		if sw.pos < dataStart { // In a hole fragment
540
-			bf := b[:min(int64(len(b)), dataStart-sw.pos)]
541
-			nf, err = zeroWriter{}.Write(bf)
542
-		} else { // In a data fragment
543
-			bf := b[:min(int64(len(b)), dataEnd-sw.pos)]
544
-			nf, err = sw.fw.Write(bf)
545
-		}
546
-		b = b[nf:]
547
-		sw.pos += int64(nf)
548
-		if sw.pos >= dataEnd && len(sw.sp) > 1 {
549
-			sw.sp = sw.sp[1:] // Ensure last fragment always remains
550
-		}
551
-	}
552
-
553
-	n = len(b0) - len(b)
554
-	switch {
555
-	case err == ErrWriteTooLong:
556
-		return n, errMissData // Not possible; implies bug in validation logic
557
-	case err != nil:
558
-		return n, err
559
-	case sw.LogicalRemaining() == 0 && sw.PhysicalRemaining() > 0:
560
-		return n, errUnrefData // Not possible; implies bug in validation logic
561
-	case overwrite:
562
-		return n, ErrWriteTooLong
563
-	default:
564
-		return n, nil
565
-	}
566
-}
567
-
568
-func (sw *sparseFileWriter) ReadFrom(r io.Reader) (n int64, err error) {
569
-	rs, ok := r.(io.ReadSeeker)
570
-	if ok {
571
-		if _, err := rs.Seek(0, io.SeekCurrent); err != nil {
572
-			ok = false // Not all io.Seeker can really seek
573
-		}
574
-	}
575
-	if !ok {
576
-		return io.Copy(struct{ io.Writer }{sw}, r)
577
-	}
578
-
579
-	var readLastByte bool
580
-	pos0 := sw.pos
581
-	for sw.LogicalRemaining() > 0 && !readLastByte && err == nil {
582
-		var nf int64 // Size of fragment
583
-		dataStart, dataEnd := sw.sp[0].Offset, sw.sp[0].endOffset()
584
-		if sw.pos < dataStart { // In a hole fragment
585
-			nf = dataStart - sw.pos
586
-			if sw.PhysicalRemaining() == 0 {
587
-				readLastByte = true
588
-				nf--
589
-			}
590
-			_, err = rs.Seek(nf, io.SeekCurrent)
591
-		} else { // In a data fragment
592
-			nf = dataEnd - sw.pos
593
-			nf, err = io.CopyN(sw.fw, rs, nf)
594
-		}
595
-		sw.pos += nf
596
-		if sw.pos >= dataEnd && len(sw.sp) > 1 {
597
-			sw.sp = sw.sp[1:] // Ensure last fragment always remains
598
-		}
599
-	}
600
-
601
-	// If the last fragment is a hole, then seek to 1-byte before EOF, and
602
-	// read a single byte to ensure the file is the right size.
603
-	if readLastByte && err == nil {
604
-		_, err = mustReadFull(rs, []byte{0})
605
-		sw.pos++
606
-	}
607
-
608
-	n = sw.pos - pos0
609
-	switch {
610
-	case err == io.EOF:
611
-		return n, io.ErrUnexpectedEOF
612
-	case err == ErrWriteTooLong:
613
-		return n, errMissData // Not possible; implies bug in validation logic
614
-	case err != nil:
615
-		return n, err
616
-	case sw.LogicalRemaining() == 0 && sw.PhysicalRemaining() > 0:
617
-		return n, errUnrefData // Not possible; implies bug in validation logic
618
-	default:
619
-		return n, ensureEOF(rs)
620
-	}
621
-}
622
-
623
-func (sw sparseFileWriter) LogicalRemaining() int64 {
624
-	return sw.sp[len(sw.sp)-1].endOffset() - sw.pos
625
-}
626
-func (sw sparseFileWriter) PhysicalRemaining() int64 {
627
-	return sw.fw.PhysicalRemaining()
628
-}
629
-
630
-// zeroWriter may only be written with NULs, otherwise it returns errWriteHole.
631
-type zeroWriter struct{}
632
-
633
-func (zeroWriter) Write(b []byte) (int, error) {
634
-	for i, c := range b {
635
-		if c != 0 {
636
-			return i, errWriteHole
637
-		}
638
-	}
639
-	return len(b), nil
640
-}
641
-
642
-// ensureEOF checks whether r is at EOF, reporting ErrWriteTooLong if not so.
643
-func ensureEOF(r io.Reader) error {
644
-	n, err := tryReadFull(r, []byte{0})
645
-	switch {
646
-	case n > 0:
647
-		return ErrWriteTooLong
648
-	case err == io.EOF:
649
-		return nil
650
-	default:
651
-		return err
652
-	}
653
-}
654 1
deleted file mode 100644
... ...
@@ -1,1310 +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
-	"encoding/hex"
10
-	"errors"
11
-	"io"
12
-	"os"
13
-	"path"
14
-	"reflect"
15
-	"sort"
16
-	"strings"
17
-	"testing"
18
-	"testing/iotest"
19
-	"time"
20
-)
21
-
22
-func bytediff(a, b []byte) string {
23
-	const (
24
-		uniqueA  = "-  "
25
-		uniqueB  = "+  "
26
-		identity = "   "
27
-	)
28
-	var ss []string
29
-	sa := strings.Split(strings.TrimSpace(hex.Dump(a)), "\n")
30
-	sb := strings.Split(strings.TrimSpace(hex.Dump(b)), "\n")
31
-	for len(sa) > 0 && len(sb) > 0 {
32
-		if sa[0] == sb[0] {
33
-			ss = append(ss, identity+sa[0])
34
-		} else {
35
-			ss = append(ss, uniqueA+sa[0])
36
-			ss = append(ss, uniqueB+sb[0])
37
-		}
38
-		sa, sb = sa[1:], sb[1:]
39
-	}
40
-	for len(sa) > 0 {
41
-		ss = append(ss, uniqueA+sa[0])
42
-		sa = sa[1:]
43
-	}
44
-	for len(sb) > 0 {
45
-		ss = append(ss, uniqueB+sb[0])
46
-		sb = sb[1:]
47
-	}
48
-	return strings.Join(ss, "\n")
49
-}
50
-
51
-func TestWriter(t *testing.T) {
52
-	type (
53
-		testHeader struct { // WriteHeader(hdr) == wantErr
54
-			hdr     Header
55
-			wantErr error
56
-		}
57
-		testWrite struct { // Write(str) == (wantCnt, wantErr)
58
-			str     string
59
-			wantCnt int
60
-			wantErr error
61
-		}
62
-		testReadFrom struct { // ReadFrom(testFile{ops}) == (wantCnt, wantErr)
63
-			ops     fileOps
64
-			wantCnt int64
65
-			wantErr error
66
-		}
67
-		testClose struct { // Close() == wantErr
68
-			wantErr error
69
-		}
70
-		testFnc interface{} // testHeader | testWrite | testReadFrom | testClose
71
-	)
72
-
73
-	vectors := []struct {
74
-		file  string // Optional filename of expected output
75
-		tests []testFnc
76
-	}{{
77
-		// The writer test file was produced with this command:
78
-		// tar (GNU tar) 1.26
79
-		//   ln -s small.txt link.txt
80
-		//   tar -b 1 --format=ustar -c -f writer.tar small.txt small2.txt link.txt
81
-		file: "testdata/writer.tar",
82
-		tests: []testFnc{
83
-			testHeader{Header{
84
-				Typeflag: TypeReg,
85
-				Name:     "small.txt",
86
-				Size:     5,
87
-				Mode:     0640,
88
-				Uid:      73025,
89
-				Gid:      5000,
90
-				Uname:    "dsymonds",
91
-				Gname:    "eng",
92
-				ModTime:  time.Unix(1246508266, 0),
93
-			}, nil},
94
-			testWrite{"Kilts", 5, nil},
95
-
96
-			testHeader{Header{
97
-				Typeflag: TypeReg,
98
-				Name:     "small2.txt",
99
-				Size:     11,
100
-				Mode:     0640,
101
-				Uid:      73025,
102
-				Uname:    "dsymonds",
103
-				Gname:    "eng",
104
-				Gid:      5000,
105
-				ModTime:  time.Unix(1245217492, 0),
106
-			}, nil},
107
-			testWrite{"Google.com\n", 11, nil},
108
-
109
-			testHeader{Header{
110
-				Typeflag: TypeSymlink,
111
-				Name:     "link.txt",
112
-				Linkname: "small.txt",
113
-				Mode:     0777,
114
-				Uid:      1000,
115
-				Gid:      1000,
116
-				Uname:    "strings",
117
-				Gname:    "strings",
118
-				ModTime:  time.Unix(1314603082, 0),
119
-			}, nil},
120
-			testWrite{"", 0, nil},
121
-
122
-			testClose{nil},
123
-		},
124
-	}, {
125
-		// The truncated test file was produced using these commands:
126
-		//   dd if=/dev/zero bs=1048576 count=16384 > /tmp/16gig.txt
127
-		//   tar -b 1 -c -f- /tmp/16gig.txt | dd bs=512 count=8 > writer-big.tar
128
-		file: "testdata/writer-big.tar",
129
-		tests: []testFnc{
130
-			testHeader{Header{
131
-				Typeflag: TypeReg,
132
-				Name:     "tmp/16gig.txt",
133
-				Size:     16 << 30,
134
-				Mode:     0640,
135
-				Uid:      73025,
136
-				Gid:      5000,
137
-				Uname:    "dsymonds",
138
-				Gname:    "eng",
139
-				ModTime:  time.Unix(1254699560, 0),
140
-				Format:   FormatGNU,
141
-			}, nil},
142
-		},
143
-	}, {
144
-		// This truncated file was produced using this library.
145
-		// It was verified to work with GNU tar 1.27.1 and BSD tar 3.1.2.
146
-		//  dd if=/dev/zero bs=1G count=16 >> writer-big-long.tar
147
-		//  gnutar -xvf writer-big-long.tar
148
-		//  bsdtar -xvf writer-big-long.tar
149
-		//
150
-		// This file is in PAX format.
151
-		file: "testdata/writer-big-long.tar",
152
-		tests: []testFnc{
153
-			testHeader{Header{
154
-				Typeflag: TypeReg,
155
-				Name:     strings.Repeat("longname/", 15) + "16gig.txt",
156
-				Size:     16 << 30,
157
-				Mode:     0644,
158
-				Uid:      1000,
159
-				Gid:      1000,
160
-				Uname:    "guillaume",
161
-				Gname:    "guillaume",
162
-				ModTime:  time.Unix(1399583047, 0),
163
-			}, nil},
164
-		},
165
-	}, {
166
-		// This file was produced using GNU tar v1.17.
167
-		//	gnutar -b 4 --format=ustar (longname/)*15 + file.txt
168
-		file: "testdata/ustar.tar",
169
-		tests: []testFnc{
170
-			testHeader{Header{
171
-				Typeflag: TypeReg,
172
-				Name:     strings.Repeat("longname/", 15) + "file.txt",
173
-				Size:     6,
174
-				Mode:     0644,
175
-				Uid:      501,
176
-				Gid:      20,
177
-				Uname:    "shane",
178
-				Gname:    "staff",
179
-				ModTime:  time.Unix(1360135598, 0),
180
-			}, nil},
181
-			testWrite{"hello\n", 6, nil},
182
-			testClose{nil},
183
-		},
184
-	}, {
185
-		// This file was produced using GNU tar v1.26:
186
-		//	echo "Slartibartfast" > file.txt
187
-		//	ln file.txt hard.txt
188
-		//	tar -b 1 --format=ustar -c -f hardlink.tar file.txt hard.txt
189
-		file: "testdata/hardlink.tar",
190
-		tests: []testFnc{
191
-			testHeader{Header{
192
-				Typeflag: TypeReg,
193
-				Name:     "file.txt",
194
-				Size:     15,
195
-				Mode:     0644,
196
-				Uid:      1000,
197
-				Gid:      100,
198
-				Uname:    "vbatts",
199
-				Gname:    "users",
200
-				ModTime:  time.Unix(1425484303, 0),
201
-			}, nil},
202
-			testWrite{"Slartibartfast\n", 15, nil},
203
-
204
-			testHeader{Header{
205
-				Typeflag: TypeLink,
206
-				Name:     "hard.txt",
207
-				Linkname: "file.txt",
208
-				Mode:     0644,
209
-				Uid:      1000,
210
-				Gid:      100,
211
-				Uname:    "vbatts",
212
-				Gname:    "users",
213
-				ModTime:  time.Unix(1425484303, 0),
214
-			}, nil},
215
-			testWrite{"", 0, nil},
216
-
217
-			testClose{nil},
218
-		},
219
-	}, {
220
-		tests: []testFnc{
221
-			testHeader{Header{
222
-				Typeflag: TypeReg,
223
-				Name:     "bad-null.txt",
224
-				Xattrs:   map[string]string{"null\x00null\x00": "fizzbuzz"},
225
-			}, headerError{}},
226
-		},
227
-	}, {
228
-		tests: []testFnc{
229
-			testHeader{Header{
230
-				Typeflag: TypeReg,
231
-				Name:     "null\x00.txt",
232
-			}, headerError{}},
233
-		},
234
-	}, {
235
-		file: "testdata/pax-records.tar",
236
-		tests: []testFnc{
237
-			testHeader{Header{
238
-				Typeflag: TypeReg,
239
-				Name:     "file",
240
-				Uname:    strings.Repeat("long", 10),
241
-				PAXRecords: map[string]string{
242
-					"path":           "FILE", // Should be ignored
243
-					"GNU.sparse.map": "0,0",  // Should be ignored
244
-					"comment":        "Hello, 世界",
245
-					"GOLANG.pkg":     "tar",
246
-				},
247
-			}, nil},
248
-			testClose{nil},
249
-		},
250
-	}, {
251
-		// Craft a theoretically valid PAX archive with global headers.
252
-		// The GNU and BSD tar tools do not parse these the same way.
253
-		//
254
-		// BSD tar v3.1.2 parses and ignores all global headers;
255
-		// the behavior is verified by researching the source code.
256
-		//
257
-		//	$ bsdtar -tvf pax-global-records.tar
258
-		//	----------  0 0      0           0 Dec 31  1969 file1
259
-		//	----------  0 0      0           0 Dec 31  1969 file2
260
-		//	----------  0 0      0           0 Dec 31  1969 file3
261
-		//	----------  0 0      0           0 May 13  2014 file4
262
-		//
263
-		// GNU tar v1.27.1 applies global headers to subsequent records,
264
-		// but does not do the following properly:
265
-		//	* It does not treat an empty record as deletion.
266
-		//	* It does not use subsequent global headers to update previous ones.
267
-		//
268
-		//	$ gnutar -tvf pax-global-records.tar
269
-		//	---------- 0/0               0 2017-07-13 19:40 global1
270
-		//	---------- 0/0               0 2017-07-13 19:40 file2
271
-		//	gnutar: Substituting `.' for empty member name
272
-		//	---------- 0/0               0 1969-12-31 16:00
273
-		//	gnutar: Substituting `.' for empty member name
274
-		//	---------- 0/0               0 2014-05-13 09:53
275
-		//
276
-		// According to the PAX specification, this should have been the result:
277
-		//	---------- 0/0               0 2017-07-13 19:40 global1
278
-		//	---------- 0/0               0 2017-07-13 19:40 file2
279
-		//	---------- 0/0               0 2017-07-13 19:40 file3
280
-		//	---------- 0/0               0 2014-05-13 09:53 file4
281
-		file: "testdata/pax-global-records.tar",
282
-		tests: []testFnc{
283
-			testHeader{Header{
284
-				Typeflag:   TypeXGlobalHeader,
285
-				PAXRecords: map[string]string{"path": "global1", "mtime": "1500000000.0"},
286
-			}, nil},
287
-			testHeader{Header{
288
-				Typeflag: TypeReg, Name: "file1",
289
-			}, nil},
290
-			testHeader{Header{
291
-				Typeflag:   TypeReg,
292
-				Name:       "file2",
293
-				PAXRecords: map[string]string{"path": "file2"},
294
-			}, nil},
295
-			testHeader{Header{
296
-				Typeflag:   TypeXGlobalHeader,
297
-				PAXRecords: map[string]string{"path": ""}, // Should delete "path", but keep "mtime"
298
-			}, nil},
299
-			testHeader{Header{
300
-				Typeflag: TypeReg, Name: "file3",
301
-			}, nil},
302
-			testHeader{Header{
303
-				Typeflag:   TypeReg,
304
-				Name:       "file4",
305
-				ModTime:    time.Unix(1400000000, 0),
306
-				PAXRecords: map[string]string{"mtime": "1400000000"},
307
-			}, nil},
308
-			testClose{nil},
309
-		},
310
-	}, {
311
-		file: "testdata/gnu-utf8.tar",
312
-		tests: []testFnc{
313
-			testHeader{Header{
314
-				Typeflag: TypeReg,
315
-				Name:     "☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹",
316
-				Mode:     0644,
317
-				Uid:      1000, Gid: 1000,
318
-				Uname:   "☺",
319
-				Gname:   "⚹",
320
-				ModTime: time.Unix(0, 0),
321
-				Format:  FormatGNU,
322
-			}, nil},
323
-			testClose{nil},
324
-		},
325
-	}, {
326
-		file: "testdata/gnu-not-utf8.tar",
327
-		tests: []testFnc{
328
-			testHeader{Header{
329
-				Typeflag: TypeReg,
330
-				Name:     "hi\x80\x81\x82\x83bye",
331
-				Mode:     0644,
332
-				Uid:      1000,
333
-				Gid:      1000,
334
-				Uname:    "rawr",
335
-				Gname:    "dsnet",
336
-				ModTime:  time.Unix(0, 0),
337
-				Format:   FormatGNU,
338
-			}, nil},
339
-			testClose{nil},
340
-		},
341
-		// TODO(dsnet): Re-enable this test when adding sparse support.
342
-		// See https://golang.org/issue/22735
343
-		/*
344
-			}, {
345
-				file: "testdata/gnu-nil-sparse-data.tar",
346
-				tests: []testFnc{
347
-					testHeader{Header{
348
-						Typeflag:    TypeGNUSparse,
349
-						Name:        "sparse.db",
350
-						Size:        1000,
351
-						SparseHoles: []sparseEntry{{Offset: 1000, Length: 0}},
352
-					}, nil},
353
-					testWrite{strings.Repeat("0123456789", 100), 1000, nil},
354
-					testClose{},
355
-				},
356
-			}, {
357
-				file: "testdata/gnu-nil-sparse-hole.tar",
358
-				tests: []testFnc{
359
-					testHeader{Header{
360
-						Typeflag:    TypeGNUSparse,
361
-						Name:        "sparse.db",
362
-						Size:        1000,
363
-						SparseHoles: []sparseEntry{{Offset: 0, Length: 1000}},
364
-					}, nil},
365
-					testWrite{strings.Repeat("\x00", 1000), 1000, nil},
366
-					testClose{},
367
-				},
368
-			}, {
369
-				file: "testdata/pax-nil-sparse-data.tar",
370
-				tests: []testFnc{
371
-					testHeader{Header{
372
-						Typeflag:    TypeReg,
373
-						Name:        "sparse.db",
374
-						Size:        1000,
375
-						SparseHoles: []sparseEntry{{Offset: 1000, Length: 0}},
376
-					}, nil},
377
-					testWrite{strings.Repeat("0123456789", 100), 1000, nil},
378
-					testClose{},
379
-				},
380
-			}, {
381
-				file: "testdata/pax-nil-sparse-hole.tar",
382
-				tests: []testFnc{
383
-					testHeader{Header{
384
-						Typeflag:    TypeReg,
385
-						Name:        "sparse.db",
386
-						Size:        1000,
387
-						SparseHoles: []sparseEntry{{Offset: 0, Length: 1000}},
388
-					}, nil},
389
-					testWrite{strings.Repeat("\x00", 1000), 1000, nil},
390
-					testClose{},
391
-				},
392
-			}, {
393
-				file: "testdata/gnu-sparse-big.tar",
394
-				tests: []testFnc{
395
-					testHeader{Header{
396
-						Typeflag: TypeGNUSparse,
397
-						Name:     "gnu-sparse",
398
-						Size:     6e10,
399
-						SparseHoles: []sparseEntry{
400
-							{Offset: 0e10, Length: 1e10 - 100},
401
-							{Offset: 1e10, Length: 1e10 - 100},
402
-							{Offset: 2e10, Length: 1e10 - 100},
403
-							{Offset: 3e10, Length: 1e10 - 100},
404
-							{Offset: 4e10, Length: 1e10 - 100},
405
-							{Offset: 5e10, Length: 1e10 - 100},
406
-						},
407
-					}, nil},
408
-					testReadFrom{fileOps{
409
-						int64(1e10 - blockSize),
410
-						strings.Repeat("\x00", blockSize-100) + strings.Repeat("0123456789", 10),
411
-						int64(1e10 - blockSize),
412
-						strings.Repeat("\x00", blockSize-100) + strings.Repeat("0123456789", 10),
413
-						int64(1e10 - blockSize),
414
-						strings.Repeat("\x00", blockSize-100) + strings.Repeat("0123456789", 10),
415
-						int64(1e10 - blockSize),
416
-						strings.Repeat("\x00", blockSize-100) + strings.Repeat("0123456789", 10),
417
-						int64(1e10 - blockSize),
418
-						strings.Repeat("\x00", blockSize-100) + strings.Repeat("0123456789", 10),
419
-						int64(1e10 - blockSize),
420
-						strings.Repeat("\x00", blockSize-100) + strings.Repeat("0123456789", 10),
421
-					}, 6e10, nil},
422
-					testClose{nil},
423
-				},
424
-			}, {
425
-				file: "testdata/pax-sparse-big.tar",
426
-				tests: []testFnc{
427
-					testHeader{Header{
428
-						Typeflag: TypeReg,
429
-						Name:     "pax-sparse",
430
-						Size:     6e10,
431
-						SparseHoles: []sparseEntry{
432
-							{Offset: 0e10, Length: 1e10 - 100},
433
-							{Offset: 1e10, Length: 1e10 - 100},
434
-							{Offset: 2e10, Length: 1e10 - 100},
435
-							{Offset: 3e10, Length: 1e10 - 100},
436
-							{Offset: 4e10, Length: 1e10 - 100},
437
-							{Offset: 5e10, Length: 1e10 - 100},
438
-						},
439
-					}, nil},
440
-					testReadFrom{fileOps{
441
-						int64(1e10 - blockSize),
442
-						strings.Repeat("\x00", blockSize-100) + strings.Repeat("0123456789", 10),
443
-						int64(1e10 - blockSize),
444
-						strings.Repeat("\x00", blockSize-100) + strings.Repeat("0123456789", 10),
445
-						int64(1e10 - blockSize),
446
-						strings.Repeat("\x00", blockSize-100) + strings.Repeat("0123456789", 10),
447
-						int64(1e10 - blockSize),
448
-						strings.Repeat("\x00", blockSize-100) + strings.Repeat("0123456789", 10),
449
-						int64(1e10 - blockSize),
450
-						strings.Repeat("\x00", blockSize-100) + strings.Repeat("0123456789", 10),
451
-						int64(1e10 - blockSize),
452
-						strings.Repeat("\x00", blockSize-100) + strings.Repeat("0123456789", 10),
453
-					}, 6e10, nil},
454
-					testClose{nil},
455
-				},
456
-		*/
457
-	}, {
458
-		file: "testdata/trailing-slash.tar",
459
-		tests: []testFnc{
460
-			testHeader{Header{Name: strings.Repeat("123456789/", 30)}, nil},
461
-			testClose{nil},
462
-		},
463
-	}, {
464
-		// Automatically promote zero value of Typeflag depending on the name.
465
-		file: "testdata/file-and-dir.tar",
466
-		tests: []testFnc{
467
-			testHeader{Header{Name: "small.txt", Size: 5}, nil},
468
-			testWrite{"Kilts", 5, nil},
469
-			testHeader{Header{Name: "dir/"}, nil},
470
-			testClose{nil},
471
-		},
472
-	}}
473
-
474
-	equalError := func(x, y error) bool {
475
-		_, ok1 := x.(headerError)
476
-		_, ok2 := y.(headerError)
477
-		if ok1 || ok2 {
478
-			return ok1 && ok2
479
-		}
480
-		return x == y
481
-	}
482
-	for _, v := range vectors {
483
-		t.Run(path.Base(v.file), func(t *testing.T) {
484
-			const maxSize = 10 << 10 // 10KiB
485
-			buf := new(bytes.Buffer)
486
-			tw := NewWriter(iotest.TruncateWriter(buf, maxSize))
487
-
488
-			for i, tf := range v.tests {
489
-				switch tf := tf.(type) {
490
-				case testHeader:
491
-					err := tw.WriteHeader(&tf.hdr)
492
-					if !equalError(err, tf.wantErr) {
493
-						t.Fatalf("test %d, WriteHeader() = %v, want %v", i, err, tf.wantErr)
494
-					}
495
-				case testWrite:
496
-					got, err := tw.Write([]byte(tf.str))
497
-					if got != tf.wantCnt || !equalError(err, tf.wantErr) {
498
-						t.Fatalf("test %d, Write() = (%d, %v), want (%d, %v)", i, got, err, tf.wantCnt, tf.wantErr)
499
-					}
500
-				case testReadFrom:
501
-					f := &testFile{ops: tf.ops}
502
-					got, err := tw.readFrom(f)
503
-					if _, ok := err.(testError); ok {
504
-						t.Errorf("test %d, ReadFrom(): %v", i, err)
505
-					} else if got != tf.wantCnt || !equalError(err, tf.wantErr) {
506
-						t.Errorf("test %d, ReadFrom() = (%d, %v), want (%d, %v)", i, got, err, tf.wantCnt, tf.wantErr)
507
-					}
508
-					if len(f.ops) > 0 {
509
-						t.Errorf("test %d, expected %d more operations", i, len(f.ops))
510
-					}
511
-				case testClose:
512
-					err := tw.Close()
513
-					if !equalError(err, tf.wantErr) {
514
-						t.Fatalf("test %d, Close() = %v, want %v", i, err, tf.wantErr)
515
-					}
516
-				default:
517
-					t.Fatalf("test %d, unknown test operation: %T", i, tf)
518
-				}
519
-			}
520
-
521
-			if v.file != "" {
522
-				want, err := os.ReadFile(v.file)
523
-				if err != nil {
524
-					t.Fatalf("ReadFile() = %v, want nil", err)
525
-				}
526
-				got := buf.Bytes()
527
-				if !bytes.Equal(want, got) {
528
-					t.Fatalf("incorrect result: (-got +want)\n%v", bytediff(got, want))
529
-				}
530
-			}
531
-		})
532
-	}
533
-}
534
-
535
-func TestPax(t *testing.T) {
536
-	// Create an archive with a large name
537
-	fileinfo, err := os.Stat("testdata/small.txt")
538
-	if err != nil {
539
-		t.Fatal(err)
540
-	}
541
-	hdr, err := FileInfoHeader(fileinfo, "")
542
-	if err != nil {
543
-		t.Fatalf("os.Stat: %v", err)
544
-	}
545
-	// Force a PAX long name to be written
546
-	longName := strings.Repeat("ab", 100)
547
-	contents := strings.Repeat(" ", int(hdr.Size))
548
-	hdr.Name = longName
549
-	var buf bytes.Buffer
550
-	writer := NewWriter(&buf)
551
-	if err := writer.WriteHeader(hdr); err != nil {
552
-		t.Fatal(err)
553
-	}
554
-	if _, err = writer.Write([]byte(contents)); err != nil {
555
-		t.Fatal(err)
556
-	}
557
-	if err := writer.Close(); err != nil {
558
-		t.Fatal(err)
559
-	}
560
-	// Simple test to make sure PAX extensions are in effect
561
-	if !bytes.Contains(buf.Bytes(), []byte("PaxHeaders.0")) {
562
-		t.Fatal("Expected at least one PAX header to be written.")
563
-	}
564
-	// Test that we can get a long name back out of the archive.
565
-	reader := NewReader(&buf)
566
-	hdr, err = reader.Next()
567
-	if err != nil {
568
-		t.Fatal(err)
569
-	}
570
-	if hdr.Name != longName {
571
-		t.Fatal("Couldn't recover long file name")
572
-	}
573
-}
574
-
575
-func TestPaxSymlink(t *testing.T) {
576
-	// Create an archive with a large linkname
577
-	fileinfo, err := os.Stat("testdata/small.txt")
578
-	if err != nil {
579
-		t.Fatal(err)
580
-	}
581
-	hdr, err := FileInfoHeader(fileinfo, "")
582
-	hdr.Typeflag = TypeSymlink
583
-	if err != nil {
584
-		t.Fatalf("os.Stat:1 %v", err)
585
-	}
586
-	// Force a PAX long linkname to be written
587
-	longLinkname := strings.Repeat("1234567890/1234567890", 10)
588
-	hdr.Linkname = longLinkname
589
-
590
-	hdr.Size = 0
591
-	var buf bytes.Buffer
592
-	writer := NewWriter(&buf)
593
-	if err := writer.WriteHeader(hdr); err != nil {
594
-		t.Fatal(err)
595
-	}
596
-	if err := writer.Close(); err != nil {
597
-		t.Fatal(err)
598
-	}
599
-	// Simple test to make sure PAX extensions are in effect
600
-	if !bytes.Contains(buf.Bytes(), []byte("PaxHeaders.0")) {
601
-		t.Fatal("Expected at least one PAX header to be written.")
602
-	}
603
-	// Test that we can get a long name back out of the archive.
604
-	reader := NewReader(&buf)
605
-	hdr, err = reader.Next()
606
-	if err != nil {
607
-		t.Fatal(err)
608
-	}
609
-	if hdr.Linkname != longLinkname {
610
-		t.Fatal("Couldn't recover long link name")
611
-	}
612
-}
613
-
614
-func TestPaxNonAscii(t *testing.T) {
615
-	// Create an archive with non ascii. These should trigger a pax header
616
-	// because pax headers have a defined utf-8 encoding.
617
-	fileinfo, err := os.Stat("testdata/small.txt")
618
-	if err != nil {
619
-		t.Fatal(err)
620
-	}
621
-
622
-	hdr, err := FileInfoHeader(fileinfo, "")
623
-	if err != nil {
624
-		t.Fatalf("os.Stat:1 %v", err)
625
-	}
626
-
627
-	// some sample data
628
-	chineseFilename := "文件名"
629
-	chineseGroupname := "組"
630
-	chineseUsername := "用戶名"
631
-
632
-	hdr.Name = chineseFilename
633
-	hdr.Gname = chineseGroupname
634
-	hdr.Uname = chineseUsername
635
-
636
-	contents := strings.Repeat(" ", int(hdr.Size))
637
-
638
-	var buf bytes.Buffer
639
-	writer := NewWriter(&buf)
640
-	if err := writer.WriteHeader(hdr); err != nil {
641
-		t.Fatal(err)
642
-	}
643
-	if _, err = writer.Write([]byte(contents)); err != nil {
644
-		t.Fatal(err)
645
-	}
646
-	if err := writer.Close(); err != nil {
647
-		t.Fatal(err)
648
-	}
649
-	// Simple test to make sure PAX extensions are in effect
650
-	if !bytes.Contains(buf.Bytes(), []byte("PaxHeaders.0")) {
651
-		t.Fatal("Expected at least one PAX header to be written.")
652
-	}
653
-	// Test that we can get a long name back out of the archive.
654
-	reader := NewReader(&buf)
655
-	hdr, err = reader.Next()
656
-	if err != nil {
657
-		t.Fatal(err)
658
-	}
659
-	if hdr.Name != chineseFilename {
660
-		t.Fatal("Couldn't recover unicode name")
661
-	}
662
-	if hdr.Gname != chineseGroupname {
663
-		t.Fatal("Couldn't recover unicode group")
664
-	}
665
-	if hdr.Uname != chineseUsername {
666
-		t.Fatal("Couldn't recover unicode user")
667
-	}
668
-}
669
-
670
-func TestPaxXattrs(t *testing.T) {
671
-	xattrs := map[string]string{
672
-		"user.key": "value",
673
-	}
674
-
675
-	// Create an archive with an xattr
676
-	fileinfo, err := os.Stat("testdata/small.txt")
677
-	if err != nil {
678
-		t.Fatal(err)
679
-	}
680
-	hdr, err := FileInfoHeader(fileinfo, "")
681
-	if err != nil {
682
-		t.Fatalf("os.Stat: %v", err)
683
-	}
684
-	contents := "Kilts"
685
-	hdr.Xattrs = xattrs
686
-	var buf bytes.Buffer
687
-	writer := NewWriter(&buf)
688
-	if err := writer.WriteHeader(hdr); err != nil {
689
-		t.Fatal(err)
690
-	}
691
-	if _, err = writer.Write([]byte(contents)); err != nil {
692
-		t.Fatal(err)
693
-	}
694
-	if err := writer.Close(); err != nil {
695
-		t.Fatal(err)
696
-	}
697
-	// Test that we can get the xattrs back out of the archive.
698
-	reader := NewReader(&buf)
699
-	hdr, err = reader.Next()
700
-	if err != nil {
701
-		t.Fatal(err)
702
-	}
703
-	if !reflect.DeepEqual(hdr.Xattrs, xattrs) {
704
-		t.Fatalf("xattrs did not survive round trip: got %+v, want %+v",
705
-			hdr.Xattrs, xattrs)
706
-	}
707
-}
708
-
709
-func TestPaxHeadersSorted(t *testing.T) {
710
-	fileinfo, err := os.Stat("testdata/small.txt")
711
-	if err != nil {
712
-		t.Fatal(err)
713
-	}
714
-	hdr, err := FileInfoHeader(fileinfo, "")
715
-	if err != nil {
716
-		t.Fatalf("os.Stat: %v", err)
717
-	}
718
-	contents := strings.Repeat(" ", int(hdr.Size))
719
-
720
-	hdr.Xattrs = map[string]string{
721
-		"foo": "foo",
722
-		"bar": "bar",
723
-		"baz": "baz",
724
-		"qux": "qux",
725
-	}
726
-
727
-	var buf bytes.Buffer
728
-	writer := NewWriter(&buf)
729
-	if err := writer.WriteHeader(hdr); err != nil {
730
-		t.Fatal(err)
731
-	}
732
-	if _, err = writer.Write([]byte(contents)); err != nil {
733
-		t.Fatal(err)
734
-	}
735
-	if err := writer.Close(); err != nil {
736
-		t.Fatal(err)
737
-	}
738
-	// Simple test to make sure PAX extensions are in effect
739
-	if !bytes.Contains(buf.Bytes(), []byte("PaxHeaders.0")) {
740
-		t.Fatal("Expected at least one PAX header to be written.")
741
-	}
742
-
743
-	// xattr bar should always appear before others
744
-	indices := []int{
745
-		bytes.Index(buf.Bytes(), []byte("bar=bar")),
746
-		bytes.Index(buf.Bytes(), []byte("baz=baz")),
747
-		bytes.Index(buf.Bytes(), []byte("foo=foo")),
748
-		bytes.Index(buf.Bytes(), []byte("qux=qux")),
749
-	}
750
-	if !sort.IntsAreSorted(indices) {
751
-		t.Fatal("PAX headers are not sorted")
752
-	}
753
-}
754
-
755
-func TestUSTARLongName(t *testing.T) {
756
-	// Create an archive with a path that failed to split with USTAR extension in previous versions.
757
-	fileinfo, err := os.Stat("testdata/small.txt")
758
-	if err != nil {
759
-		t.Fatal(err)
760
-	}
761
-	hdr, err := FileInfoHeader(fileinfo, "")
762
-	hdr.Typeflag = TypeDir
763
-	if err != nil {
764
-		t.Fatalf("os.Stat:1 %v", err)
765
-	}
766
-	// Force a PAX long name to be written. The name was taken from a practical example
767
-	// that fails and replaced ever char through numbers to anonymize the sample.
768
-	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/"
769
-	hdr.Name = longName
770
-
771
-	hdr.Size = 0
772
-	var buf bytes.Buffer
773
-	writer := NewWriter(&buf)
774
-	if err := writer.WriteHeader(hdr); err != nil {
775
-		t.Fatal(err)
776
-	}
777
-	if err := writer.Close(); err != nil {
778
-		t.Fatal(err)
779
-	}
780
-	// Test that we can get a long name back out of the archive.
781
-	reader := NewReader(&buf)
782
-	hdr, err = reader.Next()
783
-	if err != nil {
784
-		t.Fatal(err)
785
-	}
786
-	if hdr.Name != longName {
787
-		t.Fatal("Couldn't recover long name")
788
-	}
789
-}
790
-
791
-func TestValidTypeflagWithPAXHeader(t *testing.T) {
792
-	var buffer bytes.Buffer
793
-	tw := NewWriter(&buffer)
794
-
795
-	fileName := strings.Repeat("ab", 100)
796
-
797
-	hdr := &Header{
798
-		Name:     fileName,
799
-		Size:     4,
800
-		Typeflag: 0,
801
-	}
802
-	if err := tw.WriteHeader(hdr); err != nil {
803
-		t.Fatalf("Failed to write header: %s", err)
804
-	}
805
-	if _, err := tw.Write([]byte("fooo")); err != nil {
806
-		t.Fatalf("Failed to write the file's data: %s", err)
807
-	}
808
-	tw.Close()
809
-
810
-	tr := NewReader(&buffer)
811
-
812
-	for {
813
-		header, err := tr.Next()
814
-		if err == io.EOF {
815
-			break
816
-		}
817
-		if err != nil {
818
-			t.Fatalf("Failed to read header: %s", err)
819
-		}
820
-		if header.Typeflag != TypeReg {
821
-			t.Fatalf("Typeflag should've been %d, found %d", TypeReg, header.Typeflag)
822
-		}
823
-	}
824
-}
825
-
826
-// failOnceWriter fails exactly once and then always reports success.
827
-type failOnceWriter bool
828
-
829
-func (w *failOnceWriter) Write(b []byte) (int, error) {
830
-	if !*w {
831
-		return 0, io.ErrShortWrite
832
-	}
833
-	*w = true
834
-	return len(b), nil
835
-}
836
-
837
-func TestWriterErrors(t *testing.T) {
838
-	t.Run("HeaderOnly", func(t *testing.T) {
839
-		tw := NewWriter(new(bytes.Buffer))
840
-		hdr := &Header{Name: "dir/", Typeflag: TypeDir}
841
-		if err := tw.WriteHeader(hdr); err != nil {
842
-			t.Fatalf("WriteHeader() = %v, want nil", err)
843
-		}
844
-		if _, err := tw.Write([]byte{0x00}); err != ErrWriteTooLong {
845
-			t.Fatalf("Write() = %v, want %v", err, ErrWriteTooLong)
846
-		}
847
-	})
848
-
849
-	t.Run("NegativeSize", func(t *testing.T) {
850
-		tw := NewWriter(new(bytes.Buffer))
851
-		hdr := &Header{Name: "small.txt", Size: -1}
852
-		if err := tw.WriteHeader(hdr); err == nil {
853
-			t.Fatalf("WriteHeader() = nil, want non-nil error")
854
-		}
855
-	})
856
-
857
-	t.Run("BeforeHeader", func(t *testing.T) {
858
-		tw := NewWriter(new(bytes.Buffer))
859
-		if _, err := tw.Write([]byte("Kilts")); err != ErrWriteTooLong {
860
-			t.Fatalf("Write() = %v, want %v", err, ErrWriteTooLong)
861
-		}
862
-	})
863
-
864
-	t.Run("AfterClose", func(t *testing.T) {
865
-		tw := NewWriter(new(bytes.Buffer))
866
-		hdr := &Header{Name: "small.txt"}
867
-		if err := tw.WriteHeader(hdr); err != nil {
868
-			t.Fatalf("WriteHeader() = %v, want nil", err)
869
-		}
870
-		if err := tw.Close(); err != nil {
871
-			t.Fatalf("Close() = %v, want nil", err)
872
-		}
873
-		if _, err := tw.Write([]byte("Kilts")); err != ErrWriteAfterClose {
874
-			t.Fatalf("Write() = %v, want %v", err, ErrWriteAfterClose)
875
-		}
876
-		if err := tw.Flush(); err != ErrWriteAfterClose {
877
-			t.Fatalf("Flush() = %v, want %v", err, ErrWriteAfterClose)
878
-		}
879
-		if err := tw.Close(); err != nil {
880
-			t.Fatalf("Close() = %v, want nil", err)
881
-		}
882
-	})
883
-
884
-	t.Run("PrematureFlush", func(t *testing.T) {
885
-		tw := NewWriter(new(bytes.Buffer))
886
-		hdr := &Header{Name: "small.txt", Size: 5}
887
-		if err := tw.WriteHeader(hdr); err != nil {
888
-			t.Fatalf("WriteHeader() = %v, want nil", err)
889
-		}
890
-		if err := tw.Flush(); err == nil {
891
-			t.Fatalf("Flush() = %v, want non-nil error", err)
892
-		}
893
-	})
894
-
895
-	t.Run("PrematureClose", func(t *testing.T) {
896
-		tw := NewWriter(new(bytes.Buffer))
897
-		hdr := &Header{Name: "small.txt", Size: 5}
898
-		if err := tw.WriteHeader(hdr); err != nil {
899
-			t.Fatalf("WriteHeader() = %v, want nil", err)
900
-		}
901
-		if err := tw.Close(); err == nil {
902
-			t.Fatalf("Close() = %v, want non-nil error", err)
903
-		}
904
-	})
905
-
906
-	t.Run("Persistence", func(t *testing.T) {
907
-		tw := NewWriter(new(failOnceWriter))
908
-		if err := tw.WriteHeader(&Header{}); err != io.ErrShortWrite {
909
-			t.Fatalf("WriteHeader() = %v, want %v", err, io.ErrShortWrite)
910
-		}
911
-		if err := tw.WriteHeader(&Header{Name: "small.txt"}); err == nil {
912
-			t.Errorf("WriteHeader() = got %v, want non-nil error", err)
913
-		}
914
-		if _, err := tw.Write(nil); err == nil {
915
-			t.Errorf("Write() = %v, want non-nil error", err)
916
-		}
917
-		if err := tw.Flush(); err == nil {
918
-			t.Errorf("Flush() = %v, want non-nil error", err)
919
-		}
920
-		if err := tw.Close(); err == nil {
921
-			t.Errorf("Close() = %v, want non-nil error", err)
922
-		}
923
-	})
924
-}
925
-
926
-func TestSplitUSTARPath(t *testing.T) {
927
-	sr := strings.Repeat
928
-
929
-	vectors := []struct {
930
-		input  string // Input path
931
-		prefix string // Expected output prefix
932
-		suffix string // Expected output suffix
933
-		ok     bool   // Split success?
934
-	}{
935
-		{"", "", "", false},
936
-		{"abc", "", "", false},
937
-		{"用戶名", "", "", false},
938
-		{sr("a", nameSize), "", "", false},
939
-		{sr("a", nameSize) + "/", "", "", false},
940
-		{sr("a", nameSize) + "/a", sr("a", nameSize), "a", true},
941
-		{sr("a", prefixSize) + "/", "", "", false},
942
-		{sr("a", prefixSize) + "/a", sr("a", prefixSize), "a", true},
943
-		{sr("a", nameSize+1), "", "", false},
944
-		{sr("/", nameSize+1), sr("/", nameSize-1), "/", true},
945
-		{sr("a", prefixSize) + "/" + sr("b", nameSize),
946
-			sr("a", prefixSize), sr("b", nameSize), true},
947
-		{sr("a", prefixSize) + "//" + sr("b", nameSize), "", "", false},
948
-		{sr("a/", nameSize), sr("a/", 77) + "a", sr("a/", 22), true},
949
-	}
950
-
951
-	for _, v := range vectors {
952
-		prefix, suffix, ok := splitUSTARPath(v.input)
953
-		if prefix != v.prefix || suffix != v.suffix || ok != v.ok {
954
-			t.Errorf("splitUSTARPath(%q):\ngot  (%q, %q, %v)\nwant (%q, %q, %v)",
955
-				v.input, prefix, suffix, ok, v.prefix, v.suffix, v.ok)
956
-		}
957
-	}
958
-}
959
-
960
-// TestIssue12594 tests that the Writer does not attempt to populate the prefix
961
-// field when encoding a header in the GNU format. The prefix field is valid
962
-// in USTAR and PAX, but not GNU.
963
-func TestIssue12594(t *testing.T) {
964
-	names := []string{
965
-		"0/1/2/3/4/5/6/7/8/9/10/11/12/13/14/15/16/17/18/19/20/21/22/23/24/25/26/27/28/29/30/file.txt",
966
-		"0/1/2/3/4/5/6/7/8/9/10/11/12/13/14/15/16/17/18/19/20/21/22/23/24/25/26/27/28/29/30/31/32/33/file.txt",
967
-		"0/1/2/3/4/5/6/7/8/9/10/11/12/13/14/15/16/17/18/19/20/21/22/23/24/25/26/27/28/29/30/31/32/333/file.txt",
968
-		"0/1/2/3/4/5/6/7/8/9/10/11/12/13/14/15/16/17/18/19/20/21/22/23/24/25/26/27/28/29/30/31/32/33/34/35/36/37/38/39/40/file.txt",
969
-		"0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000/file.txt",
970
-		"/home/support/.openoffice.org/3/user/uno_packages/cache/registry/com.sun.star.comp.deployment.executable.PackageRegistryBackend",
971
-	}
972
-
973
-	for i, name := range names {
974
-		var b bytes.Buffer
975
-
976
-		tw := NewWriter(&b)
977
-		if err := tw.WriteHeader(&Header{
978
-			Name: name,
979
-			Uid:  1 << 25, // Prevent USTAR format
980
-		}); err != nil {
981
-			t.Errorf("test %d, unexpected WriteHeader error: %v", i, err)
982
-		}
983
-		if err := tw.Close(); err != nil {
984
-			t.Errorf("test %d, unexpected Close error: %v", i, err)
985
-		}
986
-
987
-		// The prefix field should never appear in the GNU format.
988
-		var blk block
989
-		copy(blk[:], b.Bytes())
990
-		prefix := string(blk.USTAR().Prefix())
991
-		if i := strings.IndexByte(prefix, 0); i >= 0 {
992
-			prefix = prefix[:i] // Truncate at the NUL terminator
993
-		}
994
-		if blk.GetFormat() == FormatGNU && len(prefix) > 0 && strings.HasPrefix(name, prefix) {
995
-			t.Errorf("test %d, found prefix in GNU format: %s", i, prefix)
996
-		}
997
-
998
-		tr := NewReader(&b)
999
-		hdr, err := tr.Next()
1000
-		if err != nil {
1001
-			t.Errorf("test %d, unexpected Next error: %v", i, err)
1002
-		}
1003
-		if hdr.Name != name {
1004
-			t.Errorf("test %d, hdr.Name = %s, want %s", i, hdr.Name, name)
1005
-		}
1006
-	}
1007
-}
1008
-
1009
-// testNonEmptyWriter wraps an io.Writer and ensures that
1010
-// Write is never called with an empty buffer.
1011
-type testNonEmptyWriter struct{ io.Writer }
1012
-
1013
-func (w testNonEmptyWriter) Write(b []byte) (int, error) {
1014
-	if len(b) == 0 {
1015
-		return 0, errors.New("unexpected empty Write call")
1016
-	}
1017
-	return w.Writer.Write(b)
1018
-}
1019
-
1020
-func TestFileWriter(t *testing.T) {
1021
-	type (
1022
-		testWrite struct { // Write(str) == (wantCnt, wantErr)
1023
-			str     string
1024
-			wantCnt int
1025
-			wantErr error
1026
-		}
1027
-		testReadFrom struct { // ReadFrom(testFile{ops}) == (wantCnt, wantErr)
1028
-			ops     fileOps
1029
-			wantCnt int64
1030
-			wantErr error
1031
-		}
1032
-		testRemaining struct { // LogicalRemaining() == wantLCnt, PhysicalRemaining() == wantPCnt
1033
-			wantLCnt int64
1034
-			wantPCnt int64
1035
-		}
1036
-		testFnc interface{} // testWrite | testReadFrom | testRemaining
1037
-	)
1038
-
1039
-	type (
1040
-		makeReg struct {
1041
-			size    int64
1042
-			wantStr string
1043
-		}
1044
-		makeSparse struct {
1045
-			makeReg makeReg
1046
-			sph     sparseHoles
1047
-			size    int64
1048
-		}
1049
-		fileMaker interface{} // makeReg | makeSparse
1050
-	)
1051
-
1052
-	vectors := []struct {
1053
-		maker fileMaker
1054
-		tests []testFnc
1055
-	}{{
1056
-		maker: makeReg{0, ""},
1057
-		tests: []testFnc{
1058
-			testRemaining{0, 0},
1059
-			testWrite{"", 0, nil},
1060
-			testWrite{"a", 0, ErrWriteTooLong},
1061
-			testReadFrom{fileOps{""}, 0, nil},
1062
-			testReadFrom{fileOps{"a"}, 0, ErrWriteTooLong},
1063
-			testRemaining{0, 0},
1064
-		},
1065
-	}, {
1066
-		maker: makeReg{1, "a"},
1067
-		tests: []testFnc{
1068
-			testRemaining{1, 1},
1069
-			testWrite{"", 0, nil},
1070
-			testWrite{"a", 1, nil},
1071
-			testWrite{"bcde", 0, ErrWriteTooLong},
1072
-			testWrite{"", 0, nil},
1073
-			testReadFrom{fileOps{""}, 0, nil},
1074
-			testReadFrom{fileOps{"a"}, 0, ErrWriteTooLong},
1075
-			testRemaining{0, 0},
1076
-		},
1077
-	}, {
1078
-		maker: makeReg{5, "hello"},
1079
-		tests: []testFnc{
1080
-			testRemaining{5, 5},
1081
-			testWrite{"hello", 5, nil},
1082
-			testRemaining{0, 0},
1083
-		},
1084
-	}, {
1085
-		maker: makeReg{5, "\x00\x00\x00\x00\x00"},
1086
-		tests: []testFnc{
1087
-			testRemaining{5, 5},
1088
-			testReadFrom{fileOps{"\x00\x00\x00\x00\x00"}, 5, nil},
1089
-			testRemaining{0, 0},
1090
-		},
1091
-	}, {
1092
-		maker: makeReg{5, "\x00\x00\x00\x00\x00"},
1093
-		tests: []testFnc{
1094
-			testRemaining{5, 5},
1095
-			testReadFrom{fileOps{"\x00\x00\x00\x00\x00extra"}, 5, ErrWriteTooLong},
1096
-			testRemaining{0, 0},
1097
-		},
1098
-	}, {
1099
-		maker: makeReg{5, "abc\x00\x00"},
1100
-		tests: []testFnc{
1101
-			testRemaining{5, 5},
1102
-			testWrite{"abc", 3, nil},
1103
-			testRemaining{2, 2},
1104
-			testReadFrom{fileOps{"\x00\x00"}, 2, nil},
1105
-			testRemaining{0, 0},
1106
-		},
1107
-	}, {
1108
-		maker: makeReg{5, "\x00\x00abc"},
1109
-		tests: []testFnc{
1110
-			testRemaining{5, 5},
1111
-			testWrite{"\x00\x00", 2, nil},
1112
-			testRemaining{3, 3},
1113
-			testWrite{"abc", 3, nil},
1114
-			testReadFrom{fileOps{"z"}, 0, ErrWriteTooLong},
1115
-			testWrite{"z", 0, ErrWriteTooLong},
1116
-			testRemaining{0, 0},
1117
-		},
1118
-	}, {
1119
-		maker: makeSparse{makeReg{5, "abcde"}, sparseHoles{{2, 3}}, 8},
1120
-		tests: []testFnc{
1121
-			testRemaining{8, 5},
1122
-			testWrite{"ab\x00\x00\x00cde", 8, nil},
1123
-			testWrite{"a", 0, ErrWriteTooLong},
1124
-			testRemaining{0, 0},
1125
-		},
1126
-	}, {
1127
-		maker: makeSparse{makeReg{5, "abcde"}, sparseHoles{{2, 3}}, 8},
1128
-		tests: []testFnc{
1129
-			testWrite{"ab\x00\x00\x00cdez", 8, ErrWriteTooLong},
1130
-			testRemaining{0, 0},
1131
-		},
1132
-	}, {
1133
-		maker: makeSparse{makeReg{5, "abcde"}, sparseHoles{{2, 3}}, 8},
1134
-		tests: []testFnc{
1135
-			testWrite{"ab\x00", 3, nil},
1136
-			testRemaining{5, 3},
1137
-			testWrite{"\x00\x00cde", 5, nil},
1138
-			testWrite{"a", 0, ErrWriteTooLong},
1139
-			testRemaining{0, 0},
1140
-		},
1141
-	}, {
1142
-		maker: makeSparse{makeReg{5, "abcde"}, sparseHoles{{2, 3}}, 8},
1143
-		tests: []testFnc{
1144
-			testWrite{"ab", 2, nil},
1145
-			testRemaining{6, 3},
1146
-			testReadFrom{fileOps{int64(3), "cde"}, 6, nil},
1147
-			testRemaining{0, 0},
1148
-		},
1149
-	}, {
1150
-		maker: makeSparse{makeReg{5, "abcde"}, sparseHoles{{2, 3}}, 8},
1151
-		tests: []testFnc{
1152
-			testReadFrom{fileOps{"ab", int64(3), "cde"}, 8, nil},
1153
-			testRemaining{0, 0},
1154
-		},
1155
-	}, {
1156
-		maker: makeSparse{makeReg{5, "abcde"}, sparseHoles{{2, 3}}, 8},
1157
-		tests: []testFnc{
1158
-			testReadFrom{fileOps{"ab", int64(3), "cdeX"}, 8, ErrWriteTooLong},
1159
-			testRemaining{0, 0},
1160
-		},
1161
-	}, {
1162
-		maker: makeSparse{makeReg{4, "abcd"}, sparseHoles{{2, 3}}, 8},
1163
-		tests: []testFnc{
1164
-			testReadFrom{fileOps{"ab", int64(3), "cd"}, 7, io.ErrUnexpectedEOF},
1165
-			testRemaining{1, 0},
1166
-		},
1167
-	}, {
1168
-		maker: makeSparse{makeReg{4, "abcd"}, sparseHoles{{2, 3}}, 8},
1169
-		tests: []testFnc{
1170
-			testReadFrom{fileOps{"ab", int64(3), "cde"}, 7, errMissData},
1171
-			testRemaining{1, 0},
1172
-		},
1173
-	}, {
1174
-		maker: makeSparse{makeReg{6, "abcde"}, sparseHoles{{2, 3}}, 8},
1175
-		tests: []testFnc{
1176
-			testReadFrom{fileOps{"ab", int64(3), "cde"}, 8, errUnrefData},
1177
-			testRemaining{0, 1},
1178
-		},
1179
-	}, {
1180
-		maker: makeSparse{makeReg{4, "abcd"}, sparseHoles{{2, 3}}, 8},
1181
-		tests: []testFnc{
1182
-			testWrite{"ab", 2, nil},
1183
-			testRemaining{6, 2},
1184
-			testWrite{"\x00\x00\x00", 3, nil},
1185
-			testRemaining{3, 2},
1186
-			testWrite{"cde", 2, errMissData},
1187
-			testRemaining{1, 0},
1188
-		},
1189
-	}, {
1190
-		maker: makeSparse{makeReg{6, "abcde"}, sparseHoles{{2, 3}}, 8},
1191
-		tests: []testFnc{
1192
-			testWrite{"ab", 2, nil},
1193
-			testRemaining{6, 4},
1194
-			testWrite{"\x00\x00\x00", 3, nil},
1195
-			testRemaining{3, 4},
1196
-			testWrite{"cde", 3, errUnrefData},
1197
-			testRemaining{0, 1},
1198
-		},
1199
-	}, {
1200
-		maker: makeSparse{makeReg{3, "abc"}, sparseHoles{{0, 2}, {5, 2}}, 7},
1201
-		tests: []testFnc{
1202
-			testRemaining{7, 3},
1203
-			testWrite{"\x00\x00abc\x00\x00", 7, nil},
1204
-			testRemaining{0, 0},
1205
-		},
1206
-	}, {
1207
-		maker: makeSparse{makeReg{3, "abc"}, sparseHoles{{0, 2}, {5, 2}}, 7},
1208
-		tests: []testFnc{
1209
-			testRemaining{7, 3},
1210
-			testReadFrom{fileOps{int64(2), "abc", int64(1), "\x00"}, 7, nil},
1211
-			testRemaining{0, 0},
1212
-		},
1213
-	}, {
1214
-		maker: makeSparse{makeReg{3, ""}, sparseHoles{{0, 2}, {5, 2}}, 7},
1215
-		tests: []testFnc{
1216
-			testWrite{"abcdefg", 0, errWriteHole},
1217
-		},
1218
-	}, {
1219
-		maker: makeSparse{makeReg{3, "abc"}, sparseHoles{{0, 2}, {5, 2}}, 7},
1220
-		tests: []testFnc{
1221
-			testWrite{"\x00\x00abcde", 5, errWriteHole},
1222
-		},
1223
-	}, {
1224
-		maker: makeSparse{makeReg{3, "abc"}, sparseHoles{{0, 2}, {5, 2}}, 7},
1225
-		tests: []testFnc{
1226
-			testWrite{"\x00\x00abc\x00\x00z", 7, ErrWriteTooLong},
1227
-			testRemaining{0, 0},
1228
-		},
1229
-	}, {
1230
-		maker: makeSparse{makeReg{3, "abc"}, sparseHoles{{0, 2}, {5, 2}}, 7},
1231
-		tests: []testFnc{
1232
-			testWrite{"\x00\x00", 2, nil},
1233
-			testRemaining{5, 3},
1234
-			testWrite{"abc", 3, nil},
1235
-			testRemaining{2, 0},
1236
-			testWrite{"\x00\x00", 2, nil},
1237
-			testRemaining{0, 0},
1238
-		},
1239
-	}, {
1240
-		maker: makeSparse{makeReg{2, "ab"}, sparseHoles{{0, 2}, {5, 2}}, 7},
1241
-		tests: []testFnc{
1242
-			testWrite{"\x00\x00", 2, nil},
1243
-			testWrite{"abc", 2, errMissData},
1244
-			testWrite{"\x00\x00", 0, errMissData},
1245
-		},
1246
-	}, {
1247
-		maker: makeSparse{makeReg{4, "abc"}, sparseHoles{{0, 2}, {5, 2}}, 7},
1248
-		tests: []testFnc{
1249
-			testWrite{"\x00\x00", 2, nil},
1250
-			testWrite{"abc", 3, nil},
1251
-			testWrite{"\x00\x00", 2, errUnrefData},
1252
-		},
1253
-	}}
1254
-
1255
-	for i, v := range vectors {
1256
-		var wantStr string
1257
-		bb := new(bytes.Buffer)
1258
-		w := testNonEmptyWriter{bb}
1259
-		var fw fileWriter
1260
-		switch maker := v.maker.(type) {
1261
-		case makeReg:
1262
-			fw = &regFileWriter{w, maker.size}
1263
-			wantStr = maker.wantStr
1264
-		case makeSparse:
1265
-			if !validateSparseEntries(maker.sph, maker.size) {
1266
-				t.Fatalf("invalid sparse map: %v", maker.sph)
1267
-			}
1268
-			spd := invertSparseEntries(maker.sph, maker.size)
1269
-			fw = &regFileWriter{w, maker.makeReg.size}
1270
-			fw = &sparseFileWriter{fw, spd, 0}
1271
-			wantStr = maker.makeReg.wantStr
1272
-		default:
1273
-			t.Fatalf("test %d, unknown make operation: %T", i, maker)
1274
-		}
1275
-
1276
-		for j, tf := range v.tests {
1277
-			switch tf := tf.(type) {
1278
-			case testWrite:
1279
-				got, err := fw.Write([]byte(tf.str))
1280
-				if got != tf.wantCnt || err != tf.wantErr {
1281
-					t.Errorf("test %d.%d, Write(%s):\ngot  (%d, %v)\nwant (%d, %v)", i, j, tf.str, got, err, tf.wantCnt, tf.wantErr)
1282
-				}
1283
-			case testReadFrom:
1284
-				f := &testFile{ops: tf.ops}
1285
-				got, err := fw.ReadFrom(f)
1286
-				if _, ok := err.(testError); ok {
1287
-					t.Errorf("test %d.%d, ReadFrom(): %v", i, j, err)
1288
-				} else if got != tf.wantCnt || err != tf.wantErr {
1289
-					t.Errorf("test %d.%d, ReadFrom() = (%d, %v), want (%d, %v)", i, j, got, err, tf.wantCnt, tf.wantErr)
1290
-				}
1291
-				if len(f.ops) > 0 {
1292
-					t.Errorf("test %d.%d, expected %d more operations", i, j, len(f.ops))
1293
-				}
1294
-			case testRemaining:
1295
-				if got := fw.LogicalRemaining(); got != tf.wantLCnt {
1296
-					t.Errorf("test %d.%d, LogicalRemaining() = %d, want %d", i, j, got, tf.wantLCnt)
1297
-				}
1298
-				if got := fw.PhysicalRemaining(); got != tf.wantPCnt {
1299
-					t.Errorf("test %d.%d, PhysicalRemaining() = %d, want %d", i, j, got, tf.wantPCnt)
1300
-				}
1301
-			default:
1302
-				t.Fatalf("test %d.%d, unknown test operation: %T", i, j, tf)
1303
-			}
1304
-		}
1305
-
1306
-		if got := bb.String(); got != wantStr {
1307
-			t.Fatalf("test %d, String() = %q, want %q", i, got, wantStr)
1308
-		}
1309
-	}
1310
-}