Browse code

Merge pull request #4131 from tianon/vendor-golang-xattrs

Add vendored archive/tar that includes xattrs patch

unclejack authored on 2014/02/18 04:12:28
Showing 31 changed files
... ...
@@ -1,8 +1,8 @@
1 1
 package archive
2 2
 
3 3
 import (
4
-	"archive/tar"
5 4
 	"bytes"
5
+	"code.google.com/p/go/src/pkg/archive/tar"
6 6
 	"compress/bzip2"
7 7
 	"compress/gzip"
8 8
 	"errors"
... ...
@@ -1,8 +1,8 @@
1 1
 package archive
2 2
 
3 3
 import (
4
-	"archive/tar"
5 4
 	"bytes"
5
+	"code.google.com/p/go/src/pkg/archive/tar"
6 6
 	"fmt"
7 7
 	"io"
8 8
 	"io/ioutil"
... ...
@@ -1,7 +1,7 @@
1 1
 package archive
2 2
 
3 3
 import (
4
-	"archive/tar"
4
+	"code.google.com/p/go/src/pkg/archive/tar"
5 5
 	"fmt"
6 6
 	"github.com/dotcloud/docker/utils"
7 7
 	"io"
... ...
@@ -1,7 +1,7 @@
1 1
 package archive
2 2
 
3 3
 import (
4
-	"archive/tar"
4
+	"code.google.com/p/go/src/pkg/archive/tar"
5 5
 	"io"
6 6
 	"os"
7 7
 	"path/filepath"
... ...
@@ -1,8 +1,8 @@
1 1
 package archive
2 2
 
3 3
 import (
4
-	"archive/tar"
5 4
 	"bytes"
5
+	"code.google.com/p/go/src/pkg/archive/tar"
6 6
 	"io/ioutil"
7 7
 )
8 8
 
... ...
@@ -50,3 +50,11 @@ clone git github.com/syndtr/gocapability 3454319be2
50 50
 clone hg code.google.com/p/go.net 84a4013f96e0
51 51
 
52 52
 clone hg code.google.com/p/gosqlite 74691fb6f837
53
+
54
+# get Go tip's archive/tar, for xattr support
55
+# TODO after Go 1.3 drops, bump our minimum supported version and drop this vendored dep
56
+clone hg code.google.com/p/go a15f344a9efa
57
+mv src/code.google.com/p/go/src/pkg/archive/tar tmp-tar
58
+rm -rf src/code.google.com/p/go
59
+mkdir -p src/code.google.com/p/go/src/pkg/archive
60
+mv tmp-tar src/code.google.com/p/go/src/pkg/archive/tar
... ...
@@ -1,9 +1,9 @@
1 1
 package docker
2 2
 
3 3
 import (
4
-	"archive/tar"
5 4
 	"bufio"
6 5
 	"bytes"
6
+	"code.google.com/p/go/src/pkg/archive/tar"
7 7
 	"encoding/json"
8 8
 	"fmt"
9 9
 	"github.com/dotcloud/docker"
... ...
@@ -1,8 +1,8 @@
1 1
 package docker
2 2
 
3 3
 import (
4
-	"archive/tar"
5 4
 	"bytes"
5
+	"code.google.com/p/go/src/pkg/archive/tar"
6 6
 	"fmt"
7 7
 	"io"
8 8
 	"io/ioutil"
... ...
@@ -1,8 +1,8 @@
1 1
 package utils
2 2
 
3 3
 import (
4
-	"archive/tar"
5 4
 	"bytes"
5
+	"code.google.com/p/go/src/pkg/archive/tar"
6 6
 	"compress/gzip"
7 7
 	"crypto/sha256"
8 8
 	"encoding/hex"
... ...
@@ -1,8 +1,8 @@
1 1
 package docker
2 2
 
3 3
 import (
4
-	"archive/tar"
5 4
 	"bytes"
5
+	"code.google.com/p/go/src/pkg/archive/tar"
6 6
 	"io"
7 7
 )
8 8
 
9 9
new file mode 100644
... ...
@@ -0,0 +1,304 @@
0
+// Copyright 2009 The Go Authors. All rights reserved.
1
+// Use of this source code is governed by a BSD-style
2
+// license that can be found in the LICENSE file.
3
+
4
+// Package tar implements access to tar archives.
5
+// It aims to cover most of the variations, including those produced
6
+// by GNU and BSD tars.
7
+//
8
+// References:
9
+//   http://www.freebsd.org/cgi/man.cgi?query=tar&sektion=5
10
+//   http://www.gnu.org/software/tar/manual/html_node/Standard.html
11
+//   http://pubs.opengroup.org/onlinepubs/9699919799/utilities/pax.html
12
+package tar
13
+
14
+import (
15
+	"bytes"
16
+	"errors"
17
+	"fmt"
18
+	"os"
19
+	"path"
20
+	"time"
21
+)
22
+
23
+const (
24
+	blockSize = 512
25
+
26
+	// Types
27
+	TypeReg           = '0'    // regular file
28
+	TypeRegA          = '\x00' // regular file
29
+	TypeLink          = '1'    // hard link
30
+	TypeSymlink       = '2'    // symbolic link
31
+	TypeChar          = '3'    // character device node
32
+	TypeBlock         = '4'    // block device node
33
+	TypeDir           = '5'    // directory
34
+	TypeFifo          = '6'    // fifo node
35
+	TypeCont          = '7'    // reserved
36
+	TypeXHeader       = 'x'    // extended header
37
+	TypeXGlobalHeader = 'g'    // global extended header
38
+	TypeGNULongName   = 'L'    // Next file has a long name
39
+	TypeGNULongLink   = 'K'    // Next file symlinks to a file w/ a long name
40
+)
41
+
42
+// A Header represents a single header in a tar archive.
43
+// Some fields may not be populated.
44
+type Header struct {
45
+	Name       string    // name of header file entry
46
+	Mode       int64     // permission and mode bits
47
+	Uid        int       // user id of owner
48
+	Gid        int       // group id of owner
49
+	Size       int64     // length in bytes
50
+	ModTime    time.Time // modified time
51
+	Typeflag   byte      // type of header entry
52
+	Linkname   string    // target name of link
53
+	Uname      string    // user name of owner
54
+	Gname      string    // group name of owner
55
+	Devmajor   int64     // major number of character or block device
56
+	Devminor   int64     // minor number of character or block device
57
+	AccessTime time.Time // access time
58
+	ChangeTime time.Time // status change time
59
+	Xattrs     map[string]string
60
+}
61
+
62
+// File name constants from the tar spec.
63
+const (
64
+	fileNameSize       = 100 // Maximum number of bytes in a standard tar name.
65
+	fileNamePrefixSize = 155 // Maximum number of ustar extension bytes.
66
+)
67
+
68
+// FileInfo returns an os.FileInfo for the Header.
69
+func (h *Header) FileInfo() os.FileInfo {
70
+	return headerFileInfo{h}
71
+}
72
+
73
+// headerFileInfo implements os.FileInfo.
74
+type headerFileInfo struct {
75
+	h *Header
76
+}
77
+
78
+func (fi headerFileInfo) Size() int64        { return fi.h.Size }
79
+func (fi headerFileInfo) IsDir() bool        { return fi.Mode().IsDir() }
80
+func (fi headerFileInfo) ModTime() time.Time { return fi.h.ModTime }
81
+func (fi headerFileInfo) Sys() interface{}   { return fi.h }
82
+
83
+// Name returns the base name of the file.
84
+func (fi headerFileInfo) Name() string {
85
+	if fi.IsDir() {
86
+		return path.Base(path.Clean(fi.h.Name))
87
+	}
88
+	return path.Base(fi.h.Name)
89
+}
90
+
91
+// Mode returns the permission and mode bits for the headerFileInfo.
92
+func (fi headerFileInfo) Mode() (mode os.FileMode) {
93
+	// Set file permission bits.
94
+	mode = os.FileMode(fi.h.Mode).Perm()
95
+
96
+	// Set setuid, setgid and sticky bits.
97
+	if fi.h.Mode&c_ISUID != 0 {
98
+		// setuid
99
+		mode |= os.ModeSetuid
100
+	}
101
+	if fi.h.Mode&c_ISGID != 0 {
102
+		// setgid
103
+		mode |= os.ModeSetgid
104
+	}
105
+	if fi.h.Mode&c_ISVTX != 0 {
106
+		// sticky
107
+		mode |= os.ModeSticky
108
+	}
109
+
110
+	// Set file mode bits.
111
+	// clear perm, setuid, setgid and sticky bits.
112
+	m := os.FileMode(fi.h.Mode) &^ 07777
113
+	if m == c_ISDIR {
114
+		// directory
115
+		mode |= os.ModeDir
116
+	}
117
+	if m == c_ISFIFO {
118
+		// named pipe (FIFO)
119
+		mode |= os.ModeNamedPipe
120
+	}
121
+	if m == c_ISLNK {
122
+		// symbolic link
123
+		mode |= os.ModeSymlink
124
+	}
125
+	if m == c_ISBLK {
126
+		// device file
127
+		mode |= os.ModeDevice
128
+	}
129
+	if m == c_ISCHR {
130
+		// Unix character device
131
+		mode |= os.ModeDevice
132
+		mode |= os.ModeCharDevice
133
+	}
134
+	if m == c_ISSOCK {
135
+		// Unix domain socket
136
+		mode |= os.ModeSocket
137
+	}
138
+
139
+	switch fi.h.Typeflag {
140
+	case TypeLink, TypeSymlink:
141
+		// hard link, symbolic link
142
+		mode |= os.ModeSymlink
143
+	case TypeChar:
144
+		// character device node
145
+		mode |= os.ModeDevice
146
+		mode |= os.ModeCharDevice
147
+	case TypeBlock:
148
+		// block device node
149
+		mode |= os.ModeDevice
150
+	case TypeDir:
151
+		// directory
152
+		mode |= os.ModeDir
153
+	case TypeFifo:
154
+		// fifo node
155
+		mode |= os.ModeNamedPipe
156
+	}
157
+
158
+	return mode
159
+}
160
+
161
+// sysStat, if non-nil, populates h from system-dependent fields of fi.
162
+var sysStat func(fi os.FileInfo, h *Header) error
163
+
164
+// Mode constants from the tar spec.
165
+const (
166
+	c_ISUID  = 04000   // Set uid
167
+	c_ISGID  = 02000   // Set gid
168
+	c_ISVTX  = 01000   // Save text (sticky bit)
169
+	c_ISDIR  = 040000  // Directory
170
+	c_ISFIFO = 010000  // FIFO
171
+	c_ISREG  = 0100000 // Regular file
172
+	c_ISLNK  = 0120000 // Symbolic link
173
+	c_ISBLK  = 060000  // Block special file
174
+	c_ISCHR  = 020000  // Character special file
175
+	c_ISSOCK = 0140000 // Socket
176
+)
177
+
178
+// Keywords for the PAX Extended Header
179
+const (
180
+	paxAtime    = "atime"
181
+	paxCharset  = "charset"
182
+	paxComment  = "comment"
183
+	paxCtime    = "ctime" // please note that ctime is not a valid pax header.
184
+	paxGid      = "gid"
185
+	paxGname    = "gname"
186
+	paxLinkpath = "linkpath"
187
+	paxMtime    = "mtime"
188
+	paxPath     = "path"
189
+	paxSize     = "size"
190
+	paxUid      = "uid"
191
+	paxUname    = "uname"
192
+	paxXattr    = "SCHILY.xattr."
193
+	paxNone     = ""
194
+)
195
+
196
+// FileInfoHeader creates a partially-populated Header from fi.
197
+// If fi describes a symlink, FileInfoHeader records link as the link target.
198
+// If fi describes a directory, a slash is appended to the name.
199
+// Because os.FileInfo's Name method returns only the base name of
200
+// the file it describes, it may be necessary to modify the Name field
201
+// of the returned header to provide the full path name of the file.
202
+func FileInfoHeader(fi os.FileInfo, link string) (*Header, error) {
203
+	if fi == nil {
204
+		return nil, errors.New("tar: FileInfo is nil")
205
+	}
206
+	fm := fi.Mode()
207
+	h := &Header{
208
+		Name:    fi.Name(),
209
+		ModTime: fi.ModTime(),
210
+		Mode:    int64(fm.Perm()), // or'd with c_IS* constants later
211
+	}
212
+	switch {
213
+	case fm.IsRegular():
214
+		h.Mode |= c_ISREG
215
+		h.Typeflag = TypeReg
216
+		h.Size = fi.Size()
217
+	case fi.IsDir():
218
+		h.Typeflag = TypeDir
219
+		h.Mode |= c_ISDIR
220
+		h.Name += "/"
221
+	case fm&os.ModeSymlink != 0:
222
+		h.Typeflag = TypeSymlink
223
+		h.Mode |= c_ISLNK
224
+		h.Linkname = link
225
+	case fm&os.ModeDevice != 0:
226
+		if fm&os.ModeCharDevice != 0 {
227
+			h.Mode |= c_ISCHR
228
+			h.Typeflag = TypeChar
229
+		} else {
230
+			h.Mode |= c_ISBLK
231
+			h.Typeflag = TypeBlock
232
+		}
233
+	case fm&os.ModeNamedPipe != 0:
234
+		h.Typeflag = TypeFifo
235
+		h.Mode |= c_ISFIFO
236
+	case fm&os.ModeSocket != 0:
237
+		h.Mode |= c_ISSOCK
238
+	default:
239
+		return nil, fmt.Errorf("archive/tar: unknown file mode %v", fm)
240
+	}
241
+	if fm&os.ModeSetuid != 0 {
242
+		h.Mode |= c_ISUID
243
+	}
244
+	if fm&os.ModeSetgid != 0 {
245
+		h.Mode |= c_ISGID
246
+	}
247
+	if fm&os.ModeSticky != 0 {
248
+		h.Mode |= c_ISVTX
249
+	}
250
+	if sysStat != nil {
251
+		return h, sysStat(fi, h)
252
+	}
253
+	return h, nil
254
+}
255
+
256
+var zeroBlock = make([]byte, blockSize)
257
+
258
+// POSIX specifies a sum of the unsigned byte values, but the Sun tar uses signed byte values.
259
+// We compute and return both.
260
+func checksum(header []byte) (unsigned int64, signed int64) {
261
+	for i := 0; i < len(header); i++ {
262
+		if i == 148 {
263
+			// The chksum field (header[148:156]) is special: it should be treated as space bytes.
264
+			unsigned += ' ' * 8
265
+			signed += ' ' * 8
266
+			i += 7
267
+			continue
268
+		}
269
+		unsigned += int64(header[i])
270
+		signed += int64(int8(header[i]))
271
+	}
272
+	return
273
+}
274
+
275
+type slicer []byte
276
+
277
+func (sp *slicer) next(n int) (b []byte) {
278
+	s := *sp
279
+	b, *sp = s[0:n], s[n:]
280
+	return
281
+}
282
+
283
+func isASCII(s string) bool {
284
+	for _, c := range s {
285
+		if c >= 0x80 {
286
+			return false
287
+		}
288
+	}
289
+	return true
290
+}
291
+
292
+func toASCII(s string) string {
293
+	if isASCII(s) {
294
+		return s
295
+	}
296
+	var buf bytes.Buffer
297
+	for _, c := range s {
298
+		if c < 0x80 {
299
+			buf.WriteByte(byte(c))
300
+		}
301
+	}
302
+	return buf.String()
303
+}
0 304
new file mode 100644
... ...
@@ -0,0 +1,79 @@
0
+// Copyright 2013 The Go Authors. All rights reserved.
1
+// Use of this source code is governed by a BSD-style
2
+// license that can be found in the LICENSE file.
3
+
4
+package tar_test
5
+
6
+import (
7
+	"archive/tar"
8
+	"bytes"
9
+	"fmt"
10
+	"io"
11
+	"log"
12
+	"os"
13
+)
14
+
15
+func Example() {
16
+	// Create a buffer to write our archive to.
17
+	buf := new(bytes.Buffer)
18
+
19
+	// Create a new tar archive.
20
+	tw := tar.NewWriter(buf)
21
+
22
+	// Add some files to the archive.
23
+	var files = []struct {
24
+		Name, Body string
25
+	}{
26
+		{"readme.txt", "This archive contains some text files."},
27
+		{"gopher.txt", "Gopher names:\nGeorge\nGeoffrey\nGonzo"},
28
+		{"todo.txt", "Get animal handling licence."},
29
+	}
30
+	for _, file := range files {
31
+		hdr := &tar.Header{
32
+			Name: file.Name,
33
+			Size: int64(len(file.Body)),
34
+		}
35
+		if err := tw.WriteHeader(hdr); err != nil {
36
+			log.Fatalln(err)
37
+		}
38
+		if _, err := tw.Write([]byte(file.Body)); err != nil {
39
+			log.Fatalln(err)
40
+		}
41
+	}
42
+	// Make sure to check the error on Close.
43
+	if err := tw.Close(); err != nil {
44
+		log.Fatalln(err)
45
+	}
46
+
47
+	// Open the tar archive for reading.
48
+	r := bytes.NewReader(buf.Bytes())
49
+	tr := tar.NewReader(r)
50
+
51
+	// Iterate through the files in the archive.
52
+	for {
53
+		hdr, err := tr.Next()
54
+		if err == io.EOF {
55
+			// end of tar archive
56
+			break
57
+		}
58
+		if err != nil {
59
+			log.Fatalln(err)
60
+		}
61
+		fmt.Printf("Contents of %s:\n", hdr.Name)
62
+		if _, err := io.Copy(os.Stdout, tr); err != nil {
63
+			log.Fatalln(err)
64
+		}
65
+		fmt.Println()
66
+	}
67
+
68
+	// Output:
69
+	// Contents of readme.txt:
70
+	// This archive contains some text files.
71
+	// Contents of gopher.txt:
72
+	// Gopher names:
73
+	// George
74
+	// Geoffrey
75
+	// Gonzo
76
+	// Contents of todo.txt:
77
+	// Get animal handling licence.
78
+}
0 79
new file mode 100644
... ...
@@ -0,0 +1,402 @@
0
+// Copyright 2009 The Go Authors. All rights reserved.
1
+// Use of this source code is governed by a BSD-style
2
+// license that can be found in the LICENSE file.
3
+
4
+package tar
5
+
6
+// TODO(dsymonds):
7
+//   - pax extensions
8
+
9
+import (
10
+	"bytes"
11
+	"errors"
12
+	"io"
13
+	"io/ioutil"
14
+	"os"
15
+	"strconv"
16
+	"strings"
17
+	"time"
18
+)
19
+
20
+var (
21
+	ErrHeader = errors.New("archive/tar: invalid tar header")
22
+)
23
+
24
+const maxNanoSecondIntSize = 9
25
+
26
+// A Reader provides sequential access to the contents of a tar archive.
27
+// A tar archive consists of a sequence of files.
28
+// The Next method advances to the next file in the archive (including the first),
29
+// and then it can be treated as an io.Reader to access the file's data.
30
+type Reader struct {
31
+	r   io.Reader
32
+	err error
33
+	nb  int64 // number of unread bytes for current file entry
34
+	pad int64 // amount of padding (ignored) after current file entry
35
+}
36
+
37
+// NewReader creates a new Reader reading from r.
38
+func NewReader(r io.Reader) *Reader { return &Reader{r: r} }
39
+
40
+// Next advances to the next entry in the tar archive.
41
+func (tr *Reader) Next() (*Header, error) {
42
+	var hdr *Header
43
+	if tr.err == nil {
44
+		tr.skipUnread()
45
+	}
46
+	if tr.err != nil {
47
+		return hdr, tr.err
48
+	}
49
+	hdr = tr.readHeader()
50
+	if hdr == nil {
51
+		return hdr, tr.err
52
+	}
53
+	// Check for PAX/GNU header.
54
+	switch hdr.Typeflag {
55
+	case TypeXHeader:
56
+		//  PAX extended header
57
+		headers, err := parsePAX(tr)
58
+		if err != nil {
59
+			return nil, err
60
+		}
61
+		// We actually read the whole file,
62
+		// but this skips alignment padding
63
+		tr.skipUnread()
64
+		hdr = tr.readHeader()
65
+		mergePAX(hdr, headers)
66
+		return hdr, nil
67
+	case TypeGNULongName:
68
+		// We have a GNU long name header. Its contents are the real file name.
69
+		realname, err := ioutil.ReadAll(tr)
70
+		if err != nil {
71
+			return nil, err
72
+		}
73
+		hdr, err := tr.Next()
74
+		hdr.Name = cString(realname)
75
+		return hdr, err
76
+	case TypeGNULongLink:
77
+		// We have a GNU long link header.
78
+		realname, err := ioutil.ReadAll(tr)
79
+		if err != nil {
80
+			return nil, err
81
+		}
82
+		hdr, err := tr.Next()
83
+		hdr.Linkname = cString(realname)
84
+		return hdr, err
85
+	}
86
+	return hdr, tr.err
87
+}
88
+
89
+// mergePAX merges well known headers according to PAX standard.
90
+// In general headers with the same name as those found
91
+// in the header struct overwrite those found in the header
92
+// struct with higher precision or longer values. Esp. useful
93
+// for name and linkname fields.
94
+func mergePAX(hdr *Header, headers map[string]string) error {
95
+	for k, v := range headers {
96
+		switch k {
97
+		case paxPath:
98
+			hdr.Name = v
99
+		case paxLinkpath:
100
+			hdr.Linkname = v
101
+		case paxGname:
102
+			hdr.Gname = v
103
+		case paxUname:
104
+			hdr.Uname = v
105
+		case paxUid:
106
+			uid, err := strconv.ParseInt(v, 10, 0)
107
+			if err != nil {
108
+				return err
109
+			}
110
+			hdr.Uid = int(uid)
111
+		case paxGid:
112
+			gid, err := strconv.ParseInt(v, 10, 0)
113
+			if err != nil {
114
+				return err
115
+			}
116
+			hdr.Gid = int(gid)
117
+		case paxAtime:
118
+			t, err := parsePAXTime(v)
119
+			if err != nil {
120
+				return err
121
+			}
122
+			hdr.AccessTime = t
123
+		case paxMtime:
124
+			t, err := parsePAXTime(v)
125
+			if err != nil {
126
+				return err
127
+			}
128
+			hdr.ModTime = t
129
+		case paxCtime:
130
+			t, err := parsePAXTime(v)
131
+			if err != nil {
132
+				return err
133
+			}
134
+			hdr.ChangeTime = t
135
+		case paxSize:
136
+			size, err := strconv.ParseInt(v, 10, 0)
137
+			if err != nil {
138
+				return err
139
+			}
140
+			hdr.Size = int64(size)
141
+		default:
142
+			if strings.HasPrefix(k, paxXattr) {
143
+				if hdr.Xattrs == nil {
144
+					hdr.Xattrs = make(map[string]string)
145
+				}
146
+				hdr.Xattrs[k[len(paxXattr):]] = v
147
+			}
148
+		}
149
+	}
150
+	return nil
151
+}
152
+
153
+// parsePAXTime takes a string of the form %d.%d as described in
154
+// the PAX specification.
155
+func parsePAXTime(t string) (time.Time, error) {
156
+	buf := []byte(t)
157
+	pos := bytes.IndexByte(buf, '.')
158
+	var seconds, nanoseconds int64
159
+	var err error
160
+	if pos == -1 {
161
+		seconds, err = strconv.ParseInt(t, 10, 0)
162
+		if err != nil {
163
+			return time.Time{}, err
164
+		}
165
+	} else {
166
+		seconds, err = strconv.ParseInt(string(buf[:pos]), 10, 0)
167
+		if err != nil {
168
+			return time.Time{}, err
169
+		}
170
+		nano_buf := string(buf[pos+1:])
171
+		// Pad as needed before converting to a decimal.
172
+		// For example .030 -> .030000000 -> 30000000 nanoseconds
173
+		if len(nano_buf) < maxNanoSecondIntSize {
174
+			// Right pad
175
+			nano_buf += strings.Repeat("0", maxNanoSecondIntSize-len(nano_buf))
176
+		} else if len(nano_buf) > maxNanoSecondIntSize {
177
+			// Right truncate
178
+			nano_buf = nano_buf[:maxNanoSecondIntSize]
179
+		}
180
+		nanoseconds, err = strconv.ParseInt(string(nano_buf), 10, 0)
181
+		if err != nil {
182
+			return time.Time{}, err
183
+		}
184
+	}
185
+	ts := time.Unix(seconds, nanoseconds)
186
+	return ts, nil
187
+}
188
+
189
+// parsePAX parses PAX headers.
190
+// If an extended header (type 'x') is invalid, ErrHeader is returned
191
+func parsePAX(r io.Reader) (map[string]string, error) {
192
+	buf, err := ioutil.ReadAll(r)
193
+	if err != nil {
194
+		return nil, err
195
+	}
196
+	headers := make(map[string]string)
197
+	// Each record is constructed as
198
+	//     "%d %s=%s\n", length, keyword, value
199
+	for len(buf) > 0 {
200
+		// or the header was empty to start with.
201
+		var sp int
202
+		// The size field ends at the first space.
203
+		sp = bytes.IndexByte(buf, ' ')
204
+		if sp == -1 {
205
+			return nil, ErrHeader
206
+		}
207
+		// Parse the first token as a decimal integer.
208
+		n, err := strconv.ParseInt(string(buf[:sp]), 10, 0)
209
+		if err != nil {
210
+			return nil, ErrHeader
211
+		}
212
+		// Extract everything between the decimal and the n -1 on the
213
+		// beginning to to eat the ' ', -1 on the end to skip the newline.
214
+		var record []byte
215
+		record, buf = buf[sp+1:n-1], buf[n:]
216
+		// The first equals is guaranteed to mark the end of the key.
217
+		// Everything else is value.
218
+		eq := bytes.IndexByte(record, '=')
219
+		if eq == -1 {
220
+			return nil, ErrHeader
221
+		}
222
+		key, value := record[:eq], record[eq+1:]
223
+		headers[string(key)] = string(value)
224
+	}
225
+	return headers, nil
226
+}
227
+
228
+// cString parses bytes as a NUL-terminated C-style string.
229
+// If a NUL byte is not found then the whole slice is returned as a string.
230
+func cString(b []byte) string {
231
+	n := 0
232
+	for n < len(b) && b[n] != 0 {
233
+		n++
234
+	}
235
+	return string(b[0:n])
236
+}
237
+
238
+func (tr *Reader) octal(b []byte) int64 {
239
+	// Check for binary format first.
240
+	if len(b) > 0 && b[0]&0x80 != 0 {
241
+		var x int64
242
+		for i, c := range b {
243
+			if i == 0 {
244
+				c &= 0x7f // ignore signal bit in first byte
245
+			}
246
+			x = x<<8 | int64(c)
247
+		}
248
+		return x
249
+	}
250
+
251
+	// Because unused fields are filled with NULs, we need
252
+	// to skip leading NULs. Fields may also be padded with
253
+	// spaces or NULs.
254
+	// So we remove leading and trailing NULs and spaces to
255
+	// be sure.
256
+	b = bytes.Trim(b, " \x00")
257
+
258
+	if len(b) == 0 {
259
+		return 0
260
+	}
261
+	x, err := strconv.ParseUint(cString(b), 8, 64)
262
+	if err != nil {
263
+		tr.err = err
264
+	}
265
+	return int64(x)
266
+}
267
+
268
+// skipUnread skips any unread bytes in the existing file entry, as well as any alignment padding.
269
+func (tr *Reader) skipUnread() {
270
+	nr := tr.nb + tr.pad // number of bytes to skip
271
+	tr.nb, tr.pad = 0, 0
272
+	if sr, ok := tr.r.(io.Seeker); ok {
273
+		if _, err := sr.Seek(nr, os.SEEK_CUR); err == nil {
274
+			return
275
+		}
276
+	}
277
+	_, tr.err = io.CopyN(ioutil.Discard, tr.r, nr)
278
+}
279
+
280
+func (tr *Reader) verifyChecksum(header []byte) bool {
281
+	if tr.err != nil {
282
+		return false
283
+	}
284
+
285
+	given := tr.octal(header[148:156])
286
+	unsigned, signed := checksum(header)
287
+	return given == unsigned || given == signed
288
+}
289
+
290
+func (tr *Reader) readHeader() *Header {
291
+	header := make([]byte, blockSize)
292
+	if _, tr.err = io.ReadFull(tr.r, header); tr.err != nil {
293
+		return nil
294
+	}
295
+
296
+	// Two blocks of zero bytes marks the end of the archive.
297
+	if bytes.Equal(header, zeroBlock[0:blockSize]) {
298
+		if _, tr.err = io.ReadFull(tr.r, header); tr.err != nil {
299
+			return nil
300
+		}
301
+		if bytes.Equal(header, zeroBlock[0:blockSize]) {
302
+			tr.err = io.EOF
303
+		} else {
304
+			tr.err = ErrHeader // zero block and then non-zero block
305
+		}
306
+		return nil
307
+	}
308
+
309
+	if !tr.verifyChecksum(header) {
310
+		tr.err = ErrHeader
311
+		return nil
312
+	}
313
+
314
+	// Unpack
315
+	hdr := new(Header)
316
+	s := slicer(header)
317
+
318
+	hdr.Name = cString(s.next(100))
319
+	hdr.Mode = tr.octal(s.next(8))
320
+	hdr.Uid = int(tr.octal(s.next(8)))
321
+	hdr.Gid = int(tr.octal(s.next(8)))
322
+	hdr.Size = tr.octal(s.next(12))
323
+	hdr.ModTime = time.Unix(tr.octal(s.next(12)), 0)
324
+	s.next(8) // chksum
325
+	hdr.Typeflag = s.next(1)[0]
326
+	hdr.Linkname = cString(s.next(100))
327
+
328
+	// The remainder of the header depends on the value of magic.
329
+	// The original (v7) version of tar had no explicit magic field,
330
+	// so its magic bytes, like the rest of the block, are NULs.
331
+	magic := string(s.next(8)) // contains version field as well.
332
+	var format string
333
+	switch magic {
334
+	case "ustar\x0000": // POSIX tar (1003.1-1988)
335
+		if string(header[508:512]) == "tar\x00" {
336
+			format = "star"
337
+		} else {
338
+			format = "posix"
339
+		}
340
+	case "ustar  \x00": // old GNU tar
341
+		format = "gnu"
342
+	}
343
+
344
+	switch format {
345
+	case "posix", "gnu", "star":
346
+		hdr.Uname = cString(s.next(32))
347
+		hdr.Gname = cString(s.next(32))
348
+		devmajor := s.next(8)
349
+		devminor := s.next(8)
350
+		if hdr.Typeflag == TypeChar || hdr.Typeflag == TypeBlock {
351
+			hdr.Devmajor = tr.octal(devmajor)
352
+			hdr.Devminor = tr.octal(devminor)
353
+		}
354
+		var prefix string
355
+		switch format {
356
+		case "posix", "gnu":
357
+			prefix = cString(s.next(155))
358
+		case "star":
359
+			prefix = cString(s.next(131))
360
+			hdr.AccessTime = time.Unix(tr.octal(s.next(12)), 0)
361
+			hdr.ChangeTime = time.Unix(tr.octal(s.next(12)), 0)
362
+		}
363
+		if len(prefix) > 0 {
364
+			hdr.Name = prefix + "/" + hdr.Name
365
+		}
366
+	}
367
+
368
+	if tr.err != nil {
369
+		tr.err = ErrHeader
370
+		return nil
371
+	}
372
+
373
+	// Maximum value of hdr.Size is 64 GB (12 octal digits),
374
+	// so there's no risk of int64 overflowing.
375
+	tr.nb = int64(hdr.Size)
376
+	tr.pad = -tr.nb & (blockSize - 1) // blockSize is a power of two
377
+
378
+	return hdr
379
+}
380
+
381
+// Read reads from the current entry in the tar archive.
382
+// It returns 0, io.EOF when it reaches the end of that entry,
383
+// until Next is called to advance to the next entry.
384
+func (tr *Reader) Read(b []byte) (n int, err error) {
385
+	if tr.nb == 0 {
386
+		// file consumed
387
+		return 0, io.EOF
388
+	}
389
+
390
+	if int64(len(b)) > tr.nb {
391
+		b = b[0:tr.nb]
392
+	}
393
+	n, err = tr.r.Read(b)
394
+	tr.nb -= int64(n)
395
+
396
+	if err == io.EOF && tr.nb > 0 {
397
+		err = io.ErrUnexpectedEOF
398
+	}
399
+	tr.err = err
400
+	return
401
+}
0 402
new file mode 100644
... ...
@@ -0,0 +1,425 @@
0
+// Copyright 2009 The Go Authors. All rights reserved.
1
+// Use of this source code is governed by a BSD-style
2
+// license that can be found in the LICENSE file.
3
+
4
+package tar
5
+
6
+import (
7
+	"bytes"
8
+	"crypto/md5"
9
+	"fmt"
10
+	"io"
11
+	"os"
12
+	"reflect"
13
+	"strings"
14
+	"testing"
15
+	"time"
16
+)
17
+
18
+type untarTest struct {
19
+	file    string
20
+	headers []*Header
21
+	cksums  []string
22
+}
23
+
24
+var gnuTarTest = &untarTest{
25
+	file: "testdata/gnu.tar",
26
+	headers: []*Header{
27
+		{
28
+			Name:     "small.txt",
29
+			Mode:     0640,
30
+			Uid:      73025,
31
+			Gid:      5000,
32
+			Size:     5,
33
+			ModTime:  time.Unix(1244428340, 0),
34
+			Typeflag: '0',
35
+			Uname:    "dsymonds",
36
+			Gname:    "eng",
37
+		},
38
+		{
39
+			Name:     "small2.txt",
40
+			Mode:     0640,
41
+			Uid:      73025,
42
+			Gid:      5000,
43
+			Size:     11,
44
+			ModTime:  time.Unix(1244436044, 0),
45
+			Typeflag: '0',
46
+			Uname:    "dsymonds",
47
+			Gname:    "eng",
48
+		},
49
+	},
50
+	cksums: []string{
51
+		"e38b27eaccb4391bdec553a7f3ae6b2f",
52
+		"c65bd2e50a56a2138bf1716f2fd56fe9",
53
+	},
54
+}
55
+
56
+var untarTests = []*untarTest{
57
+	gnuTarTest,
58
+	{
59
+		file: "testdata/star.tar",
60
+		headers: []*Header{
61
+			{
62
+				Name:       "small.txt",
63
+				Mode:       0640,
64
+				Uid:        73025,
65
+				Gid:        5000,
66
+				Size:       5,
67
+				ModTime:    time.Unix(1244592783, 0),
68
+				Typeflag:   '0',
69
+				Uname:      "dsymonds",
70
+				Gname:      "eng",
71
+				AccessTime: time.Unix(1244592783, 0),
72
+				ChangeTime: time.Unix(1244592783, 0),
73
+			},
74
+			{
75
+				Name:       "small2.txt",
76
+				Mode:       0640,
77
+				Uid:        73025,
78
+				Gid:        5000,
79
+				Size:       11,
80
+				ModTime:    time.Unix(1244592783, 0),
81
+				Typeflag:   '0',
82
+				Uname:      "dsymonds",
83
+				Gname:      "eng",
84
+				AccessTime: time.Unix(1244592783, 0),
85
+				ChangeTime: time.Unix(1244592783, 0),
86
+			},
87
+		},
88
+	},
89
+	{
90
+		file: "testdata/v7.tar",
91
+		headers: []*Header{
92
+			{
93
+				Name:     "small.txt",
94
+				Mode:     0444,
95
+				Uid:      73025,
96
+				Gid:      5000,
97
+				Size:     5,
98
+				ModTime:  time.Unix(1244593104, 0),
99
+				Typeflag: '\x00',
100
+			},
101
+			{
102
+				Name:     "small2.txt",
103
+				Mode:     0444,
104
+				Uid:      73025,
105
+				Gid:      5000,
106
+				Size:     11,
107
+				ModTime:  time.Unix(1244593104, 0),
108
+				Typeflag: '\x00',
109
+			},
110
+		},
111
+	},
112
+	{
113
+		file: "testdata/pax.tar",
114
+		headers: []*Header{
115
+			{
116
+				Name:       "a/123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100",
117
+				Mode:       0664,
118
+				Uid:        1000,
119
+				Gid:        1000,
120
+				Uname:      "shane",
121
+				Gname:      "shane",
122
+				Size:       7,
123
+				ModTime:    time.Unix(1350244992, 23960108),
124
+				ChangeTime: time.Unix(1350244992, 23960108),
125
+				AccessTime: time.Unix(1350244992, 23960108),
126
+				Typeflag:   TypeReg,
127
+			},
128
+			{
129
+				Name:       "a/b",
130
+				Mode:       0777,
131
+				Uid:        1000,
132
+				Gid:        1000,
133
+				Uname:      "shane",
134
+				Gname:      "shane",
135
+				Size:       0,
136
+				ModTime:    time.Unix(1350266320, 910238425),
137
+				ChangeTime: time.Unix(1350266320, 910238425),
138
+				AccessTime: time.Unix(1350266320, 910238425),
139
+				Typeflag:   TypeSymlink,
140
+				Linkname:   "123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100",
141
+			},
142
+		},
143
+	},
144
+	{
145
+		file: "testdata/nil-uid.tar", // golang.org/issue/5290
146
+		headers: []*Header{
147
+			{
148
+				Name:     "P1050238.JPG.log",
149
+				Mode:     0664,
150
+				Uid:      0,
151
+				Gid:      0,
152
+				Size:     14,
153
+				ModTime:  time.Unix(1365454838, 0),
154
+				Typeflag: TypeReg,
155
+				Linkname: "",
156
+				Uname:    "eyefi",
157
+				Gname:    "eyefi",
158
+				Devmajor: 0,
159
+				Devminor: 0,
160
+			},
161
+		},
162
+	},
163
+	{
164
+		file: "testdata/xattrs.tar",
165
+		headers: []*Header{
166
+			{
167
+				Name:       "small.txt",
168
+				Mode:       0644,
169
+				Uid:        1000,
170
+				Gid:        10,
171
+				Size:       5,
172
+				ModTime:    time.Unix(1386065770, 448252320),
173
+				Typeflag:   '0',
174
+				Uname:      "alex",
175
+				Gname:      "wheel",
176
+				AccessTime: time.Unix(1389782991, 419875220),
177
+				ChangeTime: time.Unix(1389782956, 794414986),
178
+				Xattrs: map[string]string{
179
+					"user.key":  "value",
180
+					"user.key2": "value2",
181
+					// Interestingly, selinux encodes the terminating null inside the xattr
182
+					"security.selinux": "unconfined_u:object_r:default_t:s0\x00",
183
+				},
184
+			},
185
+			{
186
+				Name:       "small2.txt",
187
+				Mode:       0644,
188
+				Uid:        1000,
189
+				Gid:        10,
190
+				Size:       11,
191
+				ModTime:    time.Unix(1386065770, 449252304),
192
+				Typeflag:   '0',
193
+				Uname:      "alex",
194
+				Gname:      "wheel",
195
+				AccessTime: time.Unix(1389782991, 419875220),
196
+				ChangeTime: time.Unix(1386065770, 449252304),
197
+				Xattrs: map[string]string{
198
+					"security.selinux": "unconfined_u:object_r:default_t:s0\x00",
199
+				},
200
+			},
201
+		},
202
+	},
203
+}
204
+
205
+func TestReader(t *testing.T) {
206
+testLoop:
207
+	for i, test := range untarTests {
208
+		f, err := os.Open(test.file)
209
+		if err != nil {
210
+			t.Errorf("test %d: Unexpected error: %v", i, err)
211
+			continue
212
+		}
213
+		defer f.Close()
214
+		tr := NewReader(f)
215
+		for j, header := range test.headers {
216
+			hdr, err := tr.Next()
217
+			if err != nil || hdr == nil {
218
+				t.Errorf("test %d, entry %d: Didn't get entry: %v", i, j, err)
219
+				f.Close()
220
+				continue testLoop
221
+			}
222
+			if !reflect.DeepEqual(*hdr, *header) {
223
+				t.Errorf("test %d, entry %d: Incorrect header:\nhave %+v\nwant %+v",
224
+					i, j, *hdr, *header)
225
+			}
226
+		}
227
+		hdr, err := tr.Next()
228
+		if err == io.EOF {
229
+			continue testLoop
230
+		}
231
+		if hdr != nil || err != nil {
232
+			t.Errorf("test %d: Unexpected entry or error: hdr=%v err=%v", i, hdr, err)
233
+		}
234
+	}
235
+}
236
+
237
+func TestPartialRead(t *testing.T) {
238
+	f, err := os.Open("testdata/gnu.tar")
239
+	if err != nil {
240
+		t.Fatalf("Unexpected error: %v", err)
241
+	}
242
+	defer f.Close()
243
+
244
+	tr := NewReader(f)
245
+
246
+	// Read the first four bytes; Next() should skip the last byte.
247
+	hdr, err := tr.Next()
248
+	if err != nil || hdr == nil {
249
+		t.Fatalf("Didn't get first file: %v", err)
250
+	}
251
+	buf := make([]byte, 4)
252
+	if _, err := io.ReadFull(tr, buf); err != nil {
253
+		t.Fatalf("Unexpected error: %v", err)
254
+	}
255
+	if expected := []byte("Kilt"); !bytes.Equal(buf, expected) {
256
+		t.Errorf("Contents = %v, want %v", buf, expected)
257
+	}
258
+
259
+	// Second file
260
+	hdr, err = tr.Next()
261
+	if err != nil || hdr == nil {
262
+		t.Fatalf("Didn't get second file: %v", err)
263
+	}
264
+	buf = make([]byte, 6)
265
+	if _, err := io.ReadFull(tr, buf); err != nil {
266
+		t.Fatalf("Unexpected error: %v", err)
267
+	}
268
+	if expected := []byte("Google"); !bytes.Equal(buf, expected) {
269
+		t.Errorf("Contents = %v, want %v", buf, expected)
270
+	}
271
+}
272
+
273
+func TestIncrementalRead(t *testing.T) {
274
+	test := gnuTarTest
275
+	f, err := os.Open(test.file)
276
+	if err != nil {
277
+		t.Fatalf("Unexpected error: %v", err)
278
+	}
279
+	defer f.Close()
280
+
281
+	tr := NewReader(f)
282
+
283
+	headers := test.headers
284
+	cksums := test.cksums
285
+	nread := 0
286
+
287
+	// loop over all files
288
+	for ; ; nread++ {
289
+		hdr, err := tr.Next()
290
+		if hdr == nil || err == io.EOF {
291
+			break
292
+		}
293
+
294
+		// check the header
295
+		if !reflect.DeepEqual(*hdr, *headers[nread]) {
296
+			t.Errorf("Incorrect header:\nhave %+v\nwant %+v",
297
+				*hdr, headers[nread])
298
+		}
299
+
300
+		// read file contents in little chunks EOF,
301
+		// checksumming all the way
302
+		h := md5.New()
303
+		rdbuf := make([]uint8, 8)
304
+		for {
305
+			nr, err := tr.Read(rdbuf)
306
+			if err == io.EOF {
307
+				break
308
+			}
309
+			if err != nil {
310
+				t.Errorf("Read: unexpected error %v\n", err)
311
+				break
312
+			}
313
+			h.Write(rdbuf[0:nr])
314
+		}
315
+		// verify checksum
316
+		have := fmt.Sprintf("%x", h.Sum(nil))
317
+		want := cksums[nread]
318
+		if want != have {
319
+			t.Errorf("Bad checksum on file %s:\nhave %+v\nwant %+v", hdr.Name, have, want)
320
+		}
321
+	}
322
+	if nread != len(headers) {
323
+		t.Errorf("Didn't process all files\nexpected: %d\nprocessed %d\n", len(headers), nread)
324
+	}
325
+}
326
+
327
+func TestNonSeekable(t *testing.T) {
328
+	test := gnuTarTest
329
+	f, err := os.Open(test.file)
330
+	if err != nil {
331
+		t.Fatalf("Unexpected error: %v", err)
332
+	}
333
+	defer f.Close()
334
+
335
+	type readerOnly struct {
336
+		io.Reader
337
+	}
338
+	tr := NewReader(readerOnly{f})
339
+	nread := 0
340
+
341
+	for ; ; nread++ {
342
+		_, err := tr.Next()
343
+		if err == io.EOF {
344
+			break
345
+		}
346
+		if err != nil {
347
+			t.Fatalf("Unexpected error: %v", err)
348
+		}
349
+	}
350
+
351
+	if nread != len(test.headers) {
352
+		t.Errorf("Didn't process all files\nexpected: %d\nprocessed %d\n", len(test.headers), nread)
353
+	}
354
+}
355
+
356
+func TestParsePAXHeader(t *testing.T) {
357
+	paxTests := [][3]string{
358
+		{"a", "a=name", "10 a=name\n"}, // Test case involving multiple acceptable lengths
359
+		{"a", "a=name", "9 a=name\n"},  // Test case involving multiple acceptable length
360
+		{"mtime", "mtime=1350244992.023960108", "30 mtime=1350244992.023960108\n"}}
361
+	for _, test := range paxTests {
362
+		key, expected, raw := test[0], test[1], test[2]
363
+		reader := bytes.NewReader([]byte(raw))
364
+		headers, err := parsePAX(reader)
365
+		if err != nil {
366
+			t.Errorf("Couldn't parse correctly formatted headers: %v", err)
367
+			continue
368
+		}
369
+		if strings.EqualFold(headers[key], expected) {
370
+			t.Errorf("mtime header incorrectly parsed: got %s, wanted %s", headers[key], expected)
371
+			continue
372
+		}
373
+		trailer := make([]byte, 100)
374
+		n, err := reader.Read(trailer)
375
+		if err != io.EOF || n != 0 {
376
+			t.Error("Buffer wasn't consumed")
377
+		}
378
+	}
379
+	badHeader := bytes.NewReader([]byte("3 somelongkey="))
380
+	if _, err := parsePAX(badHeader); err != ErrHeader {
381
+		t.Fatal("Unexpected success when parsing bad header")
382
+	}
383
+}
384
+
385
+func TestParsePAXTime(t *testing.T) {
386
+	// Some valid PAX time values
387
+	timestamps := map[string]time.Time{
388
+		"1350244992.023960108":  time.Unix(1350244992, 23960108), // The commoon case
389
+		"1350244992.02396010":   time.Unix(1350244992, 23960100), // Lower precision value
390
+		"1350244992.0239601089": time.Unix(1350244992, 23960108), // Higher precision value
391
+		"1350244992":            time.Unix(1350244992, 0),        // Low precision value
392
+	}
393
+	for input, expected := range timestamps {
394
+		ts, err := parsePAXTime(input)
395
+		if err != nil {
396
+			t.Fatal(err)
397
+		}
398
+		if !ts.Equal(expected) {
399
+			t.Fatalf("Time parsing failure %s %s", ts, expected)
400
+		}
401
+	}
402
+}
403
+
404
+func TestMergePAX(t *testing.T) {
405
+	hdr := new(Header)
406
+	// Test a string, integer, and time based value.
407
+	headers := map[string]string{
408
+		"path":  "a/b/c",
409
+		"uid":   "1000",
410
+		"mtime": "1350244992.023960108",
411
+	}
412
+	err := mergePAX(hdr, headers)
413
+	if err != nil {
414
+		t.Fatal(err)
415
+	}
416
+	want := &Header{
417
+		Name:    "a/b/c",
418
+		Uid:     1000,
419
+		ModTime: time.Unix(1350244992, 23960108),
420
+	}
421
+	if !reflect.DeepEqual(hdr, want) {
422
+		t.Errorf("incorrect merge: got %+v, want %+v", hdr, want)
423
+	}
424
+}
0 425
new file mode 100644
... ...
@@ -0,0 +1,20 @@
0
+// Copyright 2012 The Go Authors. All rights reserved.
1
+// Use of this source code is governed by a BSD-style
2
+// license that can be found in the LICENSE file.
3
+
4
+// +build linux dragonfly openbsd solaris
5
+
6
+package tar
7
+
8
+import (
9
+	"syscall"
10
+	"time"
11
+)
12
+
13
+func statAtime(st *syscall.Stat_t) time.Time {
14
+	return time.Unix(st.Atim.Unix())
15
+}
16
+
17
+func statCtime(st *syscall.Stat_t) time.Time {
18
+	return time.Unix(st.Ctim.Unix())
19
+}
0 20
new file mode 100644
... ...
@@ -0,0 +1,20 @@
0
+// Copyright 2012 The Go Authors. All rights reserved.
1
+// Use of this source code is governed by a BSD-style
2
+// license that can be found in the LICENSE file.
3
+
4
+// +build darwin freebsd netbsd
5
+
6
+package tar
7
+
8
+import (
9
+	"syscall"
10
+	"time"
11
+)
12
+
13
+func statAtime(st *syscall.Stat_t) time.Time {
14
+	return time.Unix(st.Atimespec.Unix())
15
+}
16
+
17
+func statCtime(st *syscall.Stat_t) time.Time {
18
+	return time.Unix(st.Ctimespec.Unix())
19
+}
0 20
new file mode 100644
... ...
@@ -0,0 +1,32 @@
0
+// Copyright 2012 The Go Authors. All rights reserved.
1
+// Use of this source code is governed by a BSD-style
2
+// license that can be found in the LICENSE file.
3
+
4
+// +build linux darwin dragonfly freebsd openbsd netbsd solaris
5
+
6
+package tar
7
+
8
+import (
9
+	"os"
10
+	"syscall"
11
+)
12
+
13
+func init() {
14
+	sysStat = statUnix
15
+}
16
+
17
+func statUnix(fi os.FileInfo, h *Header) error {
18
+	sys, ok := fi.Sys().(*syscall.Stat_t)
19
+	if !ok {
20
+		return nil
21
+	}
22
+	h.Uid = int(sys.Uid)
23
+	h.Gid = int(sys.Gid)
24
+	// TODO(bradfitz): populate username & group.  os/user
25
+	// doesn't cache LookupId lookups, and lacks group
26
+	// lookup functions.
27
+	h.AccessTime = statAtime(sys)
28
+	h.ChangeTime = statCtime(sys)
29
+	// TODO(bradfitz): major/minor device numbers?
30
+	return nil
31
+}
0 32
new file mode 100644
... ...
@@ -0,0 +1,284 @@
0
+// Copyright 2012 The Go Authors. All rights reserved.
1
+// Use of this source code is governed by a BSD-style
2
+// license that can be found in the LICENSE file.
3
+
4
+package tar
5
+
6
+import (
7
+	"bytes"
8
+	"io/ioutil"
9
+	"os"
10
+	"path"
11
+	"reflect"
12
+	"strings"
13
+	"testing"
14
+	"time"
15
+)
16
+
17
+func TestFileInfoHeader(t *testing.T) {
18
+	fi, err := os.Stat("testdata/small.txt")
19
+	if err != nil {
20
+		t.Fatal(err)
21
+	}
22
+	h, err := FileInfoHeader(fi, "")
23
+	if err != nil {
24
+		t.Fatalf("FileInfoHeader: %v", err)
25
+	}
26
+	if g, e := h.Name, "small.txt"; g != e {
27
+		t.Errorf("Name = %q; want %q", g, e)
28
+	}
29
+	if g, e := h.Mode, int64(fi.Mode().Perm())|c_ISREG; g != e {
30
+		t.Errorf("Mode = %#o; want %#o", g, e)
31
+	}
32
+	if g, e := h.Size, int64(5); g != e {
33
+		t.Errorf("Size = %v; want %v", g, e)
34
+	}
35
+	if g, e := h.ModTime, fi.ModTime(); !g.Equal(e) {
36
+		t.Errorf("ModTime = %v; want %v", g, e)
37
+	}
38
+	// FileInfoHeader should error when passing nil FileInfo
39
+	if _, err := FileInfoHeader(nil, ""); err == nil {
40
+		t.Fatalf("Expected error when passing nil to FileInfoHeader")
41
+	}
42
+}
43
+
44
+func TestFileInfoHeaderDir(t *testing.T) {
45
+	fi, err := os.Stat("testdata")
46
+	if err != nil {
47
+		t.Fatal(err)
48
+	}
49
+	h, err := FileInfoHeader(fi, "")
50
+	if err != nil {
51
+		t.Fatalf("FileInfoHeader: %v", err)
52
+	}
53
+	if g, e := h.Name, "testdata/"; g != e {
54
+		t.Errorf("Name = %q; want %q", g, e)
55
+	}
56
+	// Ignoring c_ISGID for golang.org/issue/4867
57
+	if g, e := h.Mode&^c_ISGID, int64(fi.Mode().Perm())|c_ISDIR; g != e {
58
+		t.Errorf("Mode = %#o; want %#o", g, e)
59
+	}
60
+	if g, e := h.Size, int64(0); g != e {
61
+		t.Errorf("Size = %v; want %v", g, e)
62
+	}
63
+	if g, e := h.ModTime, fi.ModTime(); !g.Equal(e) {
64
+		t.Errorf("ModTime = %v; want %v", g, e)
65
+	}
66
+}
67
+
68
+func TestFileInfoHeaderSymlink(t *testing.T) {
69
+	h, err := FileInfoHeader(symlink{}, "some-target")
70
+	if err != nil {
71
+		t.Fatal(err)
72
+	}
73
+	if g, e := h.Name, "some-symlink"; g != e {
74
+		t.Errorf("Name = %q; want %q", g, e)
75
+	}
76
+	if g, e := h.Linkname, "some-target"; g != e {
77
+		t.Errorf("Linkname = %q; want %q", g, e)
78
+	}
79
+}
80
+
81
+type symlink struct{}
82
+
83
+func (symlink) Name() string       { return "some-symlink" }
84
+func (symlink) Size() int64        { return 0 }
85
+func (symlink) Mode() os.FileMode  { return os.ModeSymlink }
86
+func (symlink) ModTime() time.Time { return time.Time{} }
87
+func (symlink) IsDir() bool        { return false }
88
+func (symlink) Sys() interface{}   { return nil }
89
+
90
+func TestRoundTrip(t *testing.T) {
91
+	data := []byte("some file contents")
92
+
93
+	var b bytes.Buffer
94
+	tw := NewWriter(&b)
95
+	hdr := &Header{
96
+		Name:    "file.txt",
97
+		Uid:     1 << 21, // too big for 8 octal digits
98
+		Size:    int64(len(data)),
99
+		ModTime: time.Now(),
100
+	}
101
+	// tar only supports second precision.
102
+	hdr.ModTime = hdr.ModTime.Add(-time.Duration(hdr.ModTime.Nanosecond()) * time.Nanosecond)
103
+	if err := tw.WriteHeader(hdr); err != nil {
104
+		t.Fatalf("tw.WriteHeader: %v", err)
105
+	}
106
+	if _, err := tw.Write(data); err != nil {
107
+		t.Fatalf("tw.Write: %v", err)
108
+	}
109
+	if err := tw.Close(); err != nil {
110
+		t.Fatalf("tw.Close: %v", err)
111
+	}
112
+
113
+	// Read it back.
114
+	tr := NewReader(&b)
115
+	rHdr, err := tr.Next()
116
+	if err != nil {
117
+		t.Fatalf("tr.Next: %v", err)
118
+	}
119
+	if !reflect.DeepEqual(rHdr, hdr) {
120
+		t.Errorf("Header mismatch.\n got %+v\nwant %+v", rHdr, hdr)
121
+	}
122
+	rData, err := ioutil.ReadAll(tr)
123
+	if err != nil {
124
+		t.Fatalf("Read: %v", err)
125
+	}
126
+	if !bytes.Equal(rData, data) {
127
+		t.Errorf("Data mismatch.\n got %q\nwant %q", rData, data)
128
+	}
129
+}
130
+
131
+type headerRoundTripTest struct {
132
+	h  *Header
133
+	fm os.FileMode
134
+}
135
+
136
+func TestHeaderRoundTrip(t *testing.T) {
137
+	golden := []headerRoundTripTest{
138
+		// regular file.
139
+		{
140
+			h: &Header{
141
+				Name:     "test.txt",
142
+				Mode:     0644 | c_ISREG,
143
+				Size:     12,
144
+				ModTime:  time.Unix(1360600916, 0),
145
+				Typeflag: TypeReg,
146
+			},
147
+			fm: 0644,
148
+		},
149
+		// hard link.
150
+		{
151
+			h: &Header{
152
+				Name:     "hard.txt",
153
+				Mode:     0644 | c_ISLNK,
154
+				Size:     0,
155
+				ModTime:  time.Unix(1360600916, 0),
156
+				Typeflag: TypeLink,
157
+			},
158
+			fm: 0644 | os.ModeSymlink,
159
+		},
160
+		// symbolic link.
161
+		{
162
+			h: &Header{
163
+				Name:     "link.txt",
164
+				Mode:     0777 | c_ISLNK,
165
+				Size:     0,
166
+				ModTime:  time.Unix(1360600852, 0),
167
+				Typeflag: TypeSymlink,
168
+			},
169
+			fm: 0777 | os.ModeSymlink,
170
+		},
171
+		// character device node.
172
+		{
173
+			h: &Header{
174
+				Name:     "dev/null",
175
+				Mode:     0666 | c_ISCHR,
176
+				Size:     0,
177
+				ModTime:  time.Unix(1360578951, 0),
178
+				Typeflag: TypeChar,
179
+			},
180
+			fm: 0666 | os.ModeDevice | os.ModeCharDevice,
181
+		},
182
+		// block device node.
183
+		{
184
+			h: &Header{
185
+				Name:     "dev/sda",
186
+				Mode:     0660 | c_ISBLK,
187
+				Size:     0,
188
+				ModTime:  time.Unix(1360578954, 0),
189
+				Typeflag: TypeBlock,
190
+			},
191
+			fm: 0660 | os.ModeDevice,
192
+		},
193
+		// directory.
194
+		{
195
+			h: &Header{
196
+				Name:     "dir/",
197
+				Mode:     0755 | c_ISDIR,
198
+				Size:     0,
199
+				ModTime:  time.Unix(1360601116, 0),
200
+				Typeflag: TypeDir,
201
+			},
202
+			fm: 0755 | os.ModeDir,
203
+		},
204
+		// fifo node.
205
+		{
206
+			h: &Header{
207
+				Name:     "dev/initctl",
208
+				Mode:     0600 | c_ISFIFO,
209
+				Size:     0,
210
+				ModTime:  time.Unix(1360578949, 0),
211
+				Typeflag: TypeFifo,
212
+			},
213
+			fm: 0600 | os.ModeNamedPipe,
214
+		},
215
+		// setuid.
216
+		{
217
+			h: &Header{
218
+				Name:     "bin/su",
219
+				Mode:     0755 | c_ISREG | c_ISUID,
220
+				Size:     23232,
221
+				ModTime:  time.Unix(1355405093, 0),
222
+				Typeflag: TypeReg,
223
+			},
224
+			fm: 0755 | os.ModeSetuid,
225
+		},
226
+		// setguid.
227
+		{
228
+			h: &Header{
229
+				Name:     "group.txt",
230
+				Mode:     0750 | c_ISREG | c_ISGID,
231
+				Size:     0,
232
+				ModTime:  time.Unix(1360602346, 0),
233
+				Typeflag: TypeReg,
234
+			},
235
+			fm: 0750 | os.ModeSetgid,
236
+		},
237
+		// sticky.
238
+		{
239
+			h: &Header{
240
+				Name:     "sticky.txt",
241
+				Mode:     0600 | c_ISREG | c_ISVTX,
242
+				Size:     7,
243
+				ModTime:  time.Unix(1360602540, 0),
244
+				Typeflag: TypeReg,
245
+			},
246
+			fm: 0600 | os.ModeSticky,
247
+		},
248
+	}
249
+
250
+	for i, g := range golden {
251
+		fi := g.h.FileInfo()
252
+		h2, err := FileInfoHeader(fi, "")
253
+		if err != nil {
254
+			t.Error(err)
255
+			continue
256
+		}
257
+		if strings.Contains(fi.Name(), "/") {
258
+			t.Errorf("FileInfo of %q contains slash: %q", g.h.Name, fi.Name())
259
+		}
260
+		name := path.Base(g.h.Name)
261
+		if fi.IsDir() {
262
+			name += "/"
263
+		}
264
+		if got, want := h2.Name, name; got != want {
265
+			t.Errorf("i=%d: Name: got %v, want %v", i, got, want)
266
+		}
267
+		if got, want := h2.Size, g.h.Size; got != want {
268
+			t.Errorf("i=%d: Size: got %v, want %v", i, got, want)
269
+		}
270
+		if got, want := h2.Mode, g.h.Mode; got != want {
271
+			t.Errorf("i=%d: Mode: got %o, want %o", i, got, want)
272
+		}
273
+		if got, want := fi.Mode(), g.fm; got != want {
274
+			t.Errorf("i=%d: fi.Mode: got %o, want %o", i, got, want)
275
+		}
276
+		if got, want := h2.ModTime, g.h.ModTime; got != want {
277
+			t.Errorf("i=%d: ModTime: got %v, want %v", i, got, want)
278
+		}
279
+		if sysh, ok := fi.Sys().(*Header); !ok || sysh != g.h {
280
+			t.Errorf("i=%d: Sys didn't return original *Header", i)
281
+		}
282
+	}
283
+}
0 284
new file mode 100644
1 285
Binary files /dev/null and b/vendor/src/code.google.com/p/go/src/pkg/archive/tar/testdata/gnu.tar differ
2 286
new file mode 100644
3 287
Binary files /dev/null and b/vendor/src/code.google.com/p/go/src/pkg/archive/tar/testdata/nil-uid.tar differ
4 288
new file mode 100644
5 289
Binary files /dev/null and b/vendor/src/code.google.com/p/go/src/pkg/archive/tar/testdata/pax.tar differ
6 290
new file mode 100644
... ...
@@ -0,0 +1 @@
0
+Kilts
0 1
\ No newline at end of file
1 2
new file mode 100644
... ...
@@ -0,0 +1 @@
0
+Google.com
0 1
new file mode 100644
1 2
Binary files /dev/null and b/vendor/src/code.google.com/p/go/src/pkg/archive/tar/testdata/star.tar differ
2 3
new file mode 100644
3 4
Binary files /dev/null and b/vendor/src/code.google.com/p/go/src/pkg/archive/tar/testdata/ustar.tar differ
4 5
new file mode 100644
5 6
Binary files /dev/null and b/vendor/src/code.google.com/p/go/src/pkg/archive/tar/testdata/v7.tar differ
6 7
new file mode 100644
7 8
Binary files /dev/null and b/vendor/src/code.google.com/p/go/src/pkg/archive/tar/testdata/writer-big.tar differ
8 9
new file mode 100644
9 10
Binary files /dev/null and b/vendor/src/code.google.com/p/go/src/pkg/archive/tar/testdata/writer.tar differ
10 11
new file mode 100644
11 12
Binary files /dev/null and b/vendor/src/code.google.com/p/go/src/pkg/archive/tar/testdata/xattrs.tar differ
12 13
new file mode 100644
... ...
@@ -0,0 +1,383 @@
0
+// Copyright 2009 The Go Authors. All rights reserved.
1
+// Use of this source code is governed by a BSD-style
2
+// license that can be found in the LICENSE file.
3
+
4
+package tar
5
+
6
+// TODO(dsymonds):
7
+// - catch more errors (no first header, etc.)
8
+
9
+import (
10
+	"bytes"
11
+	"errors"
12
+	"fmt"
13
+	"io"
14
+	"os"
15
+	"path"
16
+	"strconv"
17
+	"strings"
18
+	"time"
19
+)
20
+
21
+var (
22
+	ErrWriteTooLong    = errors.New("archive/tar: write too long")
23
+	ErrFieldTooLong    = errors.New("archive/tar: header field too long")
24
+	ErrWriteAfterClose = errors.New("archive/tar: write after close")
25
+	errNameTooLong     = errors.New("archive/tar: name too long")
26
+	errInvalidHeader   = errors.New("archive/tar: header field too long or contains invalid values")
27
+)
28
+
29
+// A Writer provides sequential writing of a tar archive in POSIX.1 format.
30
+// A tar archive consists of a sequence of files.
31
+// Call WriteHeader to begin a new file, and then call Write to supply that file's data,
32
+// writing at most hdr.Size bytes in total.
33
+type Writer struct {
34
+	w          io.Writer
35
+	err        error
36
+	nb         int64 // number of unwritten bytes for current file entry
37
+	pad        int64 // amount of padding to write after current file entry
38
+	closed     bool
39
+	usedBinary bool // whether the binary numeric field extension was used
40
+	preferPax  bool // use pax header instead of binary numeric header
41
+}
42
+
43
+// NewWriter creates a new Writer writing to w.
44
+func NewWriter(w io.Writer) *Writer { return &Writer{w: w} }
45
+
46
+// Flush finishes writing the current file (optional).
47
+func (tw *Writer) Flush() error {
48
+	if tw.nb > 0 {
49
+		tw.err = fmt.Errorf("archive/tar: missed writing %d bytes", tw.nb)
50
+		return tw.err
51
+	}
52
+
53
+	n := tw.nb + tw.pad
54
+	for n > 0 && tw.err == nil {
55
+		nr := n
56
+		if nr > blockSize {
57
+			nr = blockSize
58
+		}
59
+		var nw int
60
+		nw, tw.err = tw.w.Write(zeroBlock[0:nr])
61
+		n -= int64(nw)
62
+	}
63
+	tw.nb = 0
64
+	tw.pad = 0
65
+	return tw.err
66
+}
67
+
68
+// Write s into b, terminating it with a NUL if there is room.
69
+// If the value is too long for the field and allowPax is true add a paxheader record instead
70
+func (tw *Writer) cString(b []byte, s string, allowPax bool, paxKeyword string, paxHeaders map[string]string) {
71
+	needsPaxHeader := allowPax && len(s) > len(b) || !isASCII(s)
72
+	if needsPaxHeader {
73
+		paxHeaders[paxKeyword] = s
74
+		return
75
+	}
76
+	if len(s) > len(b) {
77
+		if tw.err == nil {
78
+			tw.err = ErrFieldTooLong
79
+		}
80
+		return
81
+	}
82
+	ascii := toASCII(s)
83
+	copy(b, ascii)
84
+	if len(ascii) < len(b) {
85
+		b[len(ascii)] = 0
86
+	}
87
+}
88
+
89
+// Encode x as an octal ASCII string and write it into b with leading zeros.
90
+func (tw *Writer) octal(b []byte, x int64) {
91
+	s := strconv.FormatInt(x, 8)
92
+	// leading zeros, but leave room for a NUL.
93
+	for len(s)+1 < len(b) {
94
+		s = "0" + s
95
+	}
96
+	tw.cString(b, s, false, paxNone, nil)
97
+}
98
+
99
+// Write x into b, either as octal or as binary (GNUtar/star extension).
100
+// If the value is too long for the field and writingPax is enabled both for the field and the add a paxheader record instead
101
+func (tw *Writer) numeric(b []byte, x int64, allowPax bool, paxKeyword string, paxHeaders map[string]string) {
102
+	// Try octal first.
103
+	s := strconv.FormatInt(x, 8)
104
+	if len(s) < len(b) {
105
+		tw.octal(b, x)
106
+		return
107
+	}
108
+
109
+	// If it is too long for octal, and pax is preferred, use a pax header
110
+	if allowPax && tw.preferPax {
111
+		tw.octal(b, 0)
112
+		s := strconv.FormatInt(x, 10)
113
+		paxHeaders[paxKeyword] = s
114
+		return
115
+	}
116
+
117
+	// Too big: use binary (big-endian).
118
+	tw.usedBinary = true
119
+	for i := len(b) - 1; x > 0 && i >= 0; i-- {
120
+		b[i] = byte(x)
121
+		x >>= 8
122
+	}
123
+	b[0] |= 0x80 // highest bit indicates binary format
124
+}
125
+
126
+var (
127
+	minTime = time.Unix(0, 0)
128
+	// There is room for 11 octal digits (33 bits) of mtime.
129
+	maxTime = minTime.Add((1<<33 - 1) * time.Second)
130
+)
131
+
132
+// WriteHeader writes hdr and prepares to accept the file's contents.
133
+// WriteHeader calls Flush if it is not the first header.
134
+// Calling after a Close will return ErrWriteAfterClose.
135
+func (tw *Writer) WriteHeader(hdr *Header) error {
136
+	return tw.writeHeader(hdr, true)
137
+}
138
+
139
+// WriteHeader writes hdr and prepares to accept the file's contents.
140
+// WriteHeader calls Flush if it is not the first header.
141
+// Calling after a Close will return ErrWriteAfterClose.
142
+// As this method is called internally by writePax header to allow it to
143
+// suppress writing the pax header.
144
+func (tw *Writer) writeHeader(hdr *Header, allowPax bool) error {
145
+	if tw.closed {
146
+		return ErrWriteAfterClose
147
+	}
148
+	if tw.err == nil {
149
+		tw.Flush()
150
+	}
151
+	if tw.err != nil {
152
+		return tw.err
153
+	}
154
+
155
+	// a map to hold pax header records, if any are needed
156
+	paxHeaders := make(map[string]string)
157
+
158
+	// TODO(shanemhansen): we might want to use PAX headers for
159
+	// subsecond time resolution, but for now let's just capture
160
+	// too long fields or non ascii characters
161
+
162
+	header := make([]byte, blockSize)
163
+	s := slicer(header)
164
+
165
+	// keep a reference to the filename to allow to overwrite it later if we detect that we can use ustar longnames instead of pax
166
+	pathHeaderBytes := s.next(fileNameSize)
167
+
168
+	tw.cString(pathHeaderBytes, hdr.Name, true, paxPath, paxHeaders)
169
+
170
+	// Handle out of range ModTime carefully.
171
+	var modTime int64
172
+	if !hdr.ModTime.Before(minTime) && !hdr.ModTime.After(maxTime) {
173
+		modTime = hdr.ModTime.Unix()
174
+	}
175
+
176
+	tw.octal(s.next(8), hdr.Mode)                                   // 100:108
177
+	tw.numeric(s.next(8), int64(hdr.Uid), true, paxUid, paxHeaders) // 108:116
178
+	tw.numeric(s.next(8), int64(hdr.Gid), true, paxGid, paxHeaders) // 116:124
179
+	tw.numeric(s.next(12), hdr.Size, true, paxSize, paxHeaders)     // 124:136
180
+	tw.numeric(s.next(12), modTime, false, paxNone, nil)            // 136:148 --- consider using pax for finer granularity
181
+	s.next(8)                                                       // chksum (148:156)
182
+	s.next(1)[0] = hdr.Typeflag                                     // 156:157
183
+
184
+	tw.cString(s.next(100), hdr.Linkname, true, paxLinkpath, paxHeaders)
185
+
186
+	copy(s.next(8), []byte("ustar\x0000"))                        // 257:265
187
+	tw.cString(s.next(32), hdr.Uname, true, paxUname, paxHeaders) // 265:297
188
+	tw.cString(s.next(32), hdr.Gname, true, paxGname, paxHeaders) // 297:329
189
+	tw.numeric(s.next(8), hdr.Devmajor, false, paxNone, nil)      // 329:337
190
+	tw.numeric(s.next(8), hdr.Devminor, false, paxNone, nil)      // 337:345
191
+
192
+	// keep a reference to the prefix to allow to overwrite it later if we detect that we can use ustar longnames instead of pax
193
+	prefixHeaderBytes := s.next(155)
194
+	tw.cString(prefixHeaderBytes, "", false, paxNone, nil) // 345:500  prefix
195
+
196
+	// Use the GNU magic instead of POSIX magic if we used any GNU extensions.
197
+	if tw.usedBinary {
198
+		copy(header[257:265], []byte("ustar  \x00"))
199
+	}
200
+
201
+	_, paxPathUsed := paxHeaders[paxPath]
202
+	// try to use a ustar header when only the name is too long
203
+	if !tw.preferPax && len(paxHeaders) == 1 && paxPathUsed {
204
+		suffix := hdr.Name
205
+		prefix := ""
206
+		if len(hdr.Name) > fileNameSize && isASCII(hdr.Name) {
207
+			var err error
208
+			prefix, suffix, err = tw.splitUSTARLongName(hdr.Name)
209
+			if err == nil {
210
+				// ok we can use a ustar long name instead of pax, now correct the fields
211
+
212
+				// remove the path field from the pax header. this will suppress the pax header
213
+				delete(paxHeaders, paxPath)
214
+
215
+				// update the path fields
216
+				tw.cString(pathHeaderBytes, suffix, false, paxNone, nil)
217
+				tw.cString(prefixHeaderBytes, prefix, false, paxNone, nil)
218
+
219
+				// Use the ustar magic if we used ustar long names.
220
+				if len(prefix) > 0 {
221
+					copy(header[257:265], []byte("ustar\000"))
222
+				}
223
+			}
224
+		}
225
+	}
226
+
227
+	// The chksum field is terminated by a NUL and a space.
228
+	// This is different from the other octal fields.
229
+	chksum, _ := checksum(header)
230
+	tw.octal(header[148:155], chksum)
231
+	header[155] = ' '
232
+
233
+	if tw.err != nil {
234
+		// problem with header; probably integer too big for a field.
235
+		return tw.err
236
+	}
237
+
238
+	if allowPax {
239
+		for k, v := range hdr.Xattrs {
240
+			paxHeaders[paxXattr+k] = v
241
+		}
242
+	}
243
+
244
+	if len(paxHeaders) > 0 {
245
+		if !allowPax {
246
+			return errInvalidHeader
247
+		}
248
+		if err := tw.writePAXHeader(hdr, paxHeaders); err != nil {
249
+			return err
250
+		}
251
+	}
252
+	tw.nb = int64(hdr.Size)
253
+	tw.pad = (blockSize - (tw.nb % blockSize)) % blockSize
254
+
255
+	_, tw.err = tw.w.Write(header)
256
+	return tw.err
257
+}
258
+
259
+// writeUSTARLongName splits a USTAR long name hdr.Name.
260
+// name must be < 256 characters. errNameTooLong is returned
261
+// if hdr.Name can't be split. The splitting heuristic
262
+// is compatible with gnu tar.
263
+func (tw *Writer) splitUSTARLongName(name string) (prefix, suffix string, err error) {
264
+	length := len(name)
265
+	if length > fileNamePrefixSize+1 {
266
+		length = fileNamePrefixSize + 1
267
+	} else if name[length-1] == '/' {
268
+		length--
269
+	}
270
+	i := strings.LastIndex(name[:length], "/")
271
+	// nlen contains the resulting length in the name field.
272
+	// plen contains the resulting length in the prefix field.
273
+	nlen := len(name) - i - 1
274
+	plen := i
275
+	if i <= 0 || nlen > fileNameSize || nlen == 0 || plen > fileNamePrefixSize {
276
+		err = errNameTooLong
277
+		return
278
+	}
279
+	prefix, suffix = name[:i], name[i+1:]
280
+	return
281
+}
282
+
283
+// writePaxHeader writes an extended pax header to the
284
+// archive.
285
+func (tw *Writer) writePAXHeader(hdr *Header, paxHeaders map[string]string) error {
286
+	// Prepare extended header
287
+	ext := new(Header)
288
+	ext.Typeflag = TypeXHeader
289
+	// Setting ModTime is required for reader parsing to
290
+	// succeed, and seems harmless enough.
291
+	ext.ModTime = hdr.ModTime
292
+	// The spec asks that we namespace our pseudo files
293
+	// with the current pid.
294
+	pid := os.Getpid()
295
+	dir, file := path.Split(hdr.Name)
296
+	fullName := path.Join(dir,
297
+		fmt.Sprintf("PaxHeaders.%d", pid), file)
298
+
299
+	ascii := toASCII(fullName)
300
+	if len(ascii) > 100 {
301
+		ascii = ascii[:100]
302
+	}
303
+	ext.Name = ascii
304
+	// Construct the body
305
+	var buf bytes.Buffer
306
+
307
+	for k, v := range paxHeaders {
308
+		fmt.Fprint(&buf, paxHeader(k+"="+v))
309
+	}
310
+
311
+	ext.Size = int64(len(buf.Bytes()))
312
+	if err := tw.writeHeader(ext, false); err != nil {
313
+		return err
314
+	}
315
+	if _, err := tw.Write(buf.Bytes()); err != nil {
316
+		return err
317
+	}
318
+	if err := tw.Flush(); err != nil {
319
+		return err
320
+	}
321
+	return nil
322
+}
323
+
324
+// paxHeader formats a single pax record, prefixing it with the appropriate length
325
+func paxHeader(msg string) string {
326
+	const padding = 2 // Extra padding for space and newline
327
+	size := len(msg) + padding
328
+	size += len(strconv.Itoa(size))
329
+	record := fmt.Sprintf("%d %s\n", size, msg)
330
+	if len(record) != size {
331
+		// Final adjustment if adding size increased
332
+		// the number of digits in size
333
+		size = len(record)
334
+		record = fmt.Sprintf("%d %s\n", size, msg)
335
+	}
336
+	return record
337
+}
338
+
339
+// Write writes to the current entry in the tar archive.
340
+// Write returns the error ErrWriteTooLong if more than
341
+// hdr.Size bytes are written after WriteHeader.
342
+func (tw *Writer) Write(b []byte) (n int, err error) {
343
+	if tw.closed {
344
+		err = ErrWriteTooLong
345
+		return
346
+	}
347
+	overwrite := false
348
+	if int64(len(b)) > tw.nb {
349
+		b = b[0:tw.nb]
350
+		overwrite = true
351
+	}
352
+	n, err = tw.w.Write(b)
353
+	tw.nb -= int64(n)
354
+	if err == nil && overwrite {
355
+		err = ErrWriteTooLong
356
+		return
357
+	}
358
+	tw.err = err
359
+	return
360
+}
361
+
362
+// Close closes the tar archive, flushing any unwritten
363
+// data to the underlying writer.
364
+func (tw *Writer) Close() error {
365
+	if tw.err != nil || tw.closed {
366
+		return tw.err
367
+	}
368
+	tw.Flush()
369
+	tw.closed = true
370
+	if tw.err != nil {
371
+		return tw.err
372
+	}
373
+
374
+	// trailer: two zero blocks
375
+	for i := 0; i < 2; i++ {
376
+		_, tw.err = tw.w.Write(zeroBlock)
377
+		if tw.err != nil {
378
+			break
379
+		}
380
+	}
381
+	return tw.err
382
+}
0 383
new file mode 100644
... ...
@@ -0,0 +1,433 @@
0
+// Copyright 2009 The Go Authors. All rights reserved.
1
+// Use of this source code is governed by a BSD-style
2
+// license that can be found in the LICENSE file.
3
+
4
+package tar
5
+
6
+import (
7
+	"bytes"
8
+	"fmt"
9
+	"io"
10
+	"io/ioutil"
11
+	"os"
12
+	"reflect"
13
+	"strings"
14
+	"testing"
15
+	"testing/iotest"
16
+	"time"
17
+)
18
+
19
+type writerTestEntry struct {
20
+	header   *Header
21
+	contents string
22
+}
23
+
24
+type writerTest struct {
25
+	file    string // filename of expected output
26
+	entries []*writerTestEntry
27
+}
28
+
29
+var writerTests = []*writerTest{
30
+	// The writer test file was produced with this command:
31
+	// tar (GNU tar) 1.26
32
+	//   ln -s small.txt link.txt
33
+	//   tar -b 1 --format=ustar -c -f writer.tar small.txt small2.txt link.txt
34
+	{
35
+		file: "testdata/writer.tar",
36
+		entries: []*writerTestEntry{
37
+			{
38
+				header: &Header{
39
+					Name:     "small.txt",
40
+					Mode:     0640,
41
+					Uid:      73025,
42
+					Gid:      5000,
43
+					Size:     5,
44
+					ModTime:  time.Unix(1246508266, 0),
45
+					Typeflag: '0',
46
+					Uname:    "dsymonds",
47
+					Gname:    "eng",
48
+				},
49
+				contents: "Kilts",
50
+			},
51
+			{
52
+				header: &Header{
53
+					Name:     "small2.txt",
54
+					Mode:     0640,
55
+					Uid:      73025,
56
+					Gid:      5000,
57
+					Size:     11,
58
+					ModTime:  time.Unix(1245217492, 0),
59
+					Typeflag: '0',
60
+					Uname:    "dsymonds",
61
+					Gname:    "eng",
62
+				},
63
+				contents: "Google.com\n",
64
+			},
65
+			{
66
+				header: &Header{
67
+					Name:     "link.txt",
68
+					Mode:     0777,
69
+					Uid:      1000,
70
+					Gid:      1000,
71
+					Size:     0,
72
+					ModTime:  time.Unix(1314603082, 0),
73
+					Typeflag: '2',
74
+					Linkname: "small.txt",
75
+					Uname:    "strings",
76
+					Gname:    "strings",
77
+				},
78
+				// no contents
79
+			},
80
+		},
81
+	},
82
+	// The truncated test file was produced using these commands:
83
+	//   dd if=/dev/zero bs=1048576 count=16384 > /tmp/16gig.txt
84
+	//   tar -b 1 -c -f- /tmp/16gig.txt | dd bs=512 count=8 > writer-big.tar
85
+	{
86
+		file: "testdata/writer-big.tar",
87
+		entries: []*writerTestEntry{
88
+			{
89
+				header: &Header{
90
+					Name:     "tmp/16gig.txt",
91
+					Mode:     0640,
92
+					Uid:      73025,
93
+					Gid:      5000,
94
+					Size:     16 << 30,
95
+					ModTime:  time.Unix(1254699560, 0),
96
+					Typeflag: '0',
97
+					Uname:    "dsymonds",
98
+					Gname:    "eng",
99
+				},
100
+				// fake contents
101
+				contents: strings.Repeat("\x00", 4<<10),
102
+			},
103
+		},
104
+	},
105
+	// This file was produced using gnu tar 1.17
106
+	// gnutar  -b 4 --format=ustar (longname/)*15 + file.txt
107
+	{
108
+		file: "testdata/ustar.tar",
109
+		entries: []*writerTestEntry{
110
+			{
111
+				header: &Header{
112
+					Name:     strings.Repeat("longname/", 15) + "file.txt",
113
+					Mode:     0644,
114
+					Uid:      0765,
115
+					Gid:      024,
116
+					Size:     06,
117
+					ModTime:  time.Unix(1360135598, 0),
118
+					Typeflag: '0',
119
+					Uname:    "shane",
120
+					Gname:    "staff",
121
+				},
122
+				contents: "hello\n",
123
+			},
124
+		},
125
+	},
126
+}
127
+
128
+// Render byte array in a two-character hexadecimal string, spaced for easy visual inspection.
129
+func bytestr(offset int, b []byte) string {
130
+	const rowLen = 32
131
+	s := fmt.Sprintf("%04x ", offset)
132
+	for _, ch := range b {
133
+		switch {
134
+		case '0' <= ch && ch <= '9', 'A' <= ch && ch <= 'Z', 'a' <= ch && ch <= 'z':
135
+			s += fmt.Sprintf("  %c", ch)
136
+		default:
137
+			s += fmt.Sprintf(" %02x", ch)
138
+		}
139
+	}
140
+	return s
141
+}
142
+
143
+// Render a pseudo-diff between two blocks of bytes.
144
+func bytediff(a []byte, b []byte) string {
145
+	const rowLen = 32
146
+	s := fmt.Sprintf("(%d bytes vs. %d bytes)\n", len(a), len(b))
147
+	for offset := 0; len(a)+len(b) > 0; offset += rowLen {
148
+		na, nb := rowLen, rowLen
149
+		if na > len(a) {
150
+			na = len(a)
151
+		}
152
+		if nb > len(b) {
153
+			nb = len(b)
154
+		}
155
+		sa := bytestr(offset, a[0:na])
156
+		sb := bytestr(offset, b[0:nb])
157
+		if sa != sb {
158
+			s += fmt.Sprintf("-%v\n+%v\n", sa, sb)
159
+		}
160
+		a = a[na:]
161
+		b = b[nb:]
162
+	}
163
+	return s
164
+}
165
+
166
+func TestWriter(t *testing.T) {
167
+testLoop:
168
+	for i, test := range writerTests {
169
+		expected, err := ioutil.ReadFile(test.file)
170
+		if err != nil {
171
+			t.Errorf("test %d: Unexpected error: %v", i, err)
172
+			continue
173
+		}
174
+
175
+		buf := new(bytes.Buffer)
176
+		tw := NewWriter(iotest.TruncateWriter(buf, 4<<10)) // only catch the first 4 KB
177
+		big := false
178
+		for j, entry := range test.entries {
179
+			big = big || entry.header.Size > 1<<10
180
+			if err := tw.WriteHeader(entry.header); err != nil {
181
+				t.Errorf("test %d, entry %d: Failed writing header: %v", i, j, err)
182
+				continue testLoop
183
+			}
184
+			if _, err := io.WriteString(tw, entry.contents); err != nil {
185
+				t.Errorf("test %d, entry %d: Failed writing contents: %v", i, j, err)
186
+				continue testLoop
187
+			}
188
+		}
189
+		// Only interested in Close failures for the small tests.
190
+		if err := tw.Close(); err != nil && !big {
191
+			t.Errorf("test %d: Failed closing archive: %v", i, err)
192
+			continue testLoop
193
+		}
194
+
195
+		actual := buf.Bytes()
196
+		if !bytes.Equal(expected, actual) {
197
+			t.Errorf("test %d: Incorrect result: (-=expected, +=actual)\n%v",
198
+				i, bytediff(expected, actual))
199
+		}
200
+		if testing.Short() { // The second test is expensive.
201
+			break
202
+		}
203
+	}
204
+}
205
+
206
+func TestPax(t *testing.T) {
207
+	// Create an archive with a large name
208
+	fileinfo, err := os.Stat("testdata/small.txt")
209
+	if err != nil {
210
+		t.Fatal(err)
211
+	}
212
+	hdr, err := FileInfoHeader(fileinfo, "")
213
+	if err != nil {
214
+		t.Fatalf("os.Stat: %v", err)
215
+	}
216
+	// Force a PAX long name to be written
217
+	longName := strings.Repeat("ab", 100)
218
+	contents := strings.Repeat(" ", int(hdr.Size))
219
+	hdr.Name = longName
220
+	var buf bytes.Buffer
221
+	writer := NewWriter(&buf)
222
+	if err := writer.WriteHeader(hdr); err != nil {
223
+		t.Fatal(err)
224
+	}
225
+	if _, err = writer.Write([]byte(contents)); err != nil {
226
+		t.Fatal(err)
227
+	}
228
+	if err := writer.Close(); err != nil {
229
+		t.Fatal(err)
230
+	}
231
+	// Simple test to make sure PAX extensions are in effect
232
+	if !bytes.Contains(buf.Bytes(), []byte("PaxHeaders.")) {
233
+		t.Fatal("Expected at least one PAX header to be written.")
234
+	}
235
+	// Test that we can get a long name back out of the archive.
236
+	reader := NewReader(&buf)
237
+	hdr, err = reader.Next()
238
+	if err != nil {
239
+		t.Fatal(err)
240
+	}
241
+	if hdr.Name != longName {
242
+		t.Fatal("Couldn't recover long file name")
243
+	}
244
+}
245
+
246
+func TestPaxSymlink(t *testing.T) {
247
+	// Create an archive with a large linkname
248
+	fileinfo, err := os.Stat("testdata/small.txt")
249
+	if err != nil {
250
+		t.Fatal(err)
251
+	}
252
+	hdr, err := FileInfoHeader(fileinfo, "")
253
+	hdr.Typeflag = TypeSymlink
254
+	if err != nil {
255
+		t.Fatalf("os.Stat:1 %v", err)
256
+	}
257
+	// Force a PAX long linkname to be written
258
+	longLinkname := strings.Repeat("1234567890/1234567890", 10)
259
+	hdr.Linkname = longLinkname
260
+
261
+	hdr.Size = 0
262
+	var buf bytes.Buffer
263
+	writer := NewWriter(&buf)
264
+	if err := writer.WriteHeader(hdr); err != nil {
265
+		t.Fatal(err)
266
+	}
267
+	if err := writer.Close(); err != nil {
268
+		t.Fatal(err)
269
+	}
270
+	// Simple test to make sure PAX extensions are in effect
271
+	if !bytes.Contains(buf.Bytes(), []byte("PaxHeaders.")) {
272
+		t.Fatal("Expected at least one PAX header to be written.")
273
+	}
274
+	// Test that we can get a long name back out of the archive.
275
+	reader := NewReader(&buf)
276
+	hdr, err = reader.Next()
277
+	if err != nil {
278
+		t.Fatal(err)
279
+	}
280
+	if hdr.Linkname != longLinkname {
281
+		t.Fatal("Couldn't recover long link name")
282
+	}
283
+}
284
+
285
+func TestPaxNonAscii(t *testing.T) {
286
+	// Create an archive with non ascii. These should trigger a pax header
287
+	// because pax headers have a defined utf-8 encoding.
288
+	fileinfo, err := os.Stat("testdata/small.txt")
289
+	if err != nil {
290
+		t.Fatal(err)
291
+	}
292
+
293
+	hdr, err := FileInfoHeader(fileinfo, "")
294
+	if err != nil {
295
+		t.Fatalf("os.Stat:1 %v", err)
296
+	}
297
+
298
+	// some sample data
299
+	chineseFilename := "文件名"
300
+	chineseGroupname := "組"
301
+	chineseUsername := "用戶名"
302
+
303
+	hdr.Name = chineseFilename
304
+	hdr.Gname = chineseGroupname
305
+	hdr.Uname = chineseUsername
306
+
307
+	contents := strings.Repeat(" ", int(hdr.Size))
308
+
309
+	var buf bytes.Buffer
310
+	writer := NewWriter(&buf)
311
+	if err := writer.WriteHeader(hdr); err != nil {
312
+		t.Fatal(err)
313
+	}
314
+	if _, err = writer.Write([]byte(contents)); err != nil {
315
+		t.Fatal(err)
316
+	}
317
+	if err := writer.Close(); err != nil {
318
+		t.Fatal(err)
319
+	}
320
+	// Simple test to make sure PAX extensions are in effect
321
+	if !bytes.Contains(buf.Bytes(), []byte("PaxHeaders.")) {
322
+		t.Fatal("Expected at least one PAX header to be written.")
323
+	}
324
+	// Test that we can get a long name back out of the archive.
325
+	reader := NewReader(&buf)
326
+	hdr, err = reader.Next()
327
+	if err != nil {
328
+		t.Fatal(err)
329
+	}
330
+	if hdr.Name != chineseFilename {
331
+		t.Fatal("Couldn't recover unicode name")
332
+	}
333
+	if hdr.Gname != chineseGroupname {
334
+		t.Fatal("Couldn't recover unicode group")
335
+	}
336
+	if hdr.Uname != chineseUsername {
337
+		t.Fatal("Couldn't recover unicode user")
338
+	}
339
+}
340
+
341
+func TestPaxXattrs(t *testing.T) {
342
+	xattrs := map[string]string{
343
+		"user.key": "value",
344
+	}
345
+
346
+	// Create an archive with an xattr
347
+	fileinfo, err := os.Stat("testdata/small.txt")
348
+	if err != nil {
349
+		t.Fatal(err)
350
+	}
351
+	hdr, err := FileInfoHeader(fileinfo, "")
352
+	if err != nil {
353
+		t.Fatalf("os.Stat: %v", err)
354
+	}
355
+	contents := "Kilts"
356
+	hdr.Xattrs = xattrs
357
+	var buf bytes.Buffer
358
+	writer := NewWriter(&buf)
359
+	if err := writer.WriteHeader(hdr); err != nil {
360
+		t.Fatal(err)
361
+	}
362
+	if _, err = writer.Write([]byte(contents)); err != nil {
363
+		t.Fatal(err)
364
+	}
365
+	if err := writer.Close(); err != nil {
366
+		t.Fatal(err)
367
+	}
368
+	// Test that we can get the xattrs back out of the archive.
369
+	reader := NewReader(&buf)
370
+	hdr, err = reader.Next()
371
+	if err != nil {
372
+		t.Fatal(err)
373
+	}
374
+	if !reflect.DeepEqual(hdr.Xattrs, xattrs) {
375
+		t.Fatalf("xattrs did not survive round trip: got %+v, want %+v",
376
+			hdr.Xattrs, xattrs)
377
+	}
378
+}
379
+
380
+func TestPAXHeader(t *testing.T) {
381
+	medName := strings.Repeat("CD", 50)
382
+	longName := strings.Repeat("AB", 100)
383
+	paxTests := [][2]string{
384
+		{paxPath + "=/etc/hosts", "19 path=/etc/hosts\n"},
385
+		{"a=b", "6 a=b\n"},          // Single digit length
386
+		{"a=names", "11 a=names\n"}, // Test case involving carries
387
+		{paxPath + "=" + longName, fmt.Sprintf("210 path=%s\n", longName)},
388
+		{paxPath + "=" + medName, fmt.Sprintf("110 path=%s\n", medName)}}
389
+
390
+	for _, test := range paxTests {
391
+		key, expected := test[0], test[1]
392
+		if result := paxHeader(key); result != expected {
393
+			t.Fatalf("paxHeader: got %s, expected %s", result, expected)
394
+		}
395
+	}
396
+}
397
+
398
+func TestUSTARLongName(t *testing.T) {
399
+	// Create an archive with a path that failed to split with USTAR extension in previous versions.
400
+	fileinfo, err := os.Stat("testdata/small.txt")
401
+	if err != nil {
402
+		t.Fatal(err)
403
+	}
404
+	hdr, err := FileInfoHeader(fileinfo, "")
405
+	hdr.Typeflag = TypeDir
406
+	if err != nil {
407
+		t.Fatalf("os.Stat:1 %v", err)
408
+	}
409
+	// Force a PAX long name to be written. The name was taken from a practical example
410
+	// that fails and replaced ever char through numbers to anonymize the sample.
411
+	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/"
412
+	hdr.Name = longName
413
+
414
+	hdr.Size = 0
415
+	var buf bytes.Buffer
416
+	writer := NewWriter(&buf)
417
+	if err := writer.WriteHeader(hdr); err != nil {
418
+		t.Fatal(err)
419
+	}
420
+	if err := writer.Close(); err != nil {
421
+		t.Fatal(err)
422
+	}
423
+	// Test that we can get a long name back out of the archive.
424
+	reader := NewReader(&buf)
425
+	hdr, err = reader.Next()
426
+	if err != nil {
427
+		t.Fatal(err)
428
+	}
429
+	if hdr.Name != longName {
430
+		t.Fatal("Couldn't recover long name")
431
+	}
432
+}