Home | History | Annotate | Download | only in tar
      1 // Copyright 2009 The Go Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style
      3 // license that can be found in the LICENSE file.
      4 
      5 package tar
      6 
      7 import (
      8 	"bytes"
      9 	"crypto/md5"
     10 	"fmt"
     11 	"io"
     12 	"io/ioutil"
     13 	"math"
     14 	"os"
     15 	"reflect"
     16 	"strings"
     17 	"testing"
     18 	"time"
     19 )
     20 
     21 func TestReader(t *testing.T) {
     22 	vectors := []struct {
     23 		file    string    // Test input file
     24 		headers []*Header // Expected output headers
     25 		chksums []string  // MD5 checksum of files, leave as nil if not checked
     26 		err     error     // Expected error to occur
     27 	}{{
     28 		file: "testdata/gnu.tar",
     29 		headers: []*Header{{
     30 			Name:     "small.txt",
     31 			Mode:     0640,
     32 			Uid:      73025,
     33 			Gid:      5000,
     34 			Size:     5,
     35 			ModTime:  time.Unix(1244428340, 0),
     36 			Typeflag: '0',
     37 			Uname:    "dsymonds",
     38 			Gname:    "eng",
     39 		}, {
     40 			Name:     "small2.txt",
     41 			Mode:     0640,
     42 			Uid:      73025,
     43 			Gid:      5000,
     44 			Size:     11,
     45 			ModTime:  time.Unix(1244436044, 0),
     46 			Typeflag: '0',
     47 			Uname:    "dsymonds",
     48 			Gname:    "eng",
     49 		}},
     50 		chksums: []string{
     51 			"e38b27eaccb4391bdec553a7f3ae6b2f",
     52 			"c65bd2e50a56a2138bf1716f2fd56fe9",
     53 		},
     54 	}, {
     55 		file: "testdata/sparse-formats.tar",
     56 		headers: []*Header{{
     57 			Name:     "sparse-gnu",
     58 			Mode:     420,
     59 			Uid:      1000,
     60 			Gid:      1000,
     61 			Size:     200,
     62 			ModTime:  time.Unix(1392395740, 0),
     63 			Typeflag: 0x53,
     64 			Linkname: "",
     65 			Uname:    "david",
     66 			Gname:    "david",
     67 			Devmajor: 0,
     68 			Devminor: 0,
     69 		}, {
     70 			Name:     "sparse-posix-0.0",
     71 			Mode:     420,
     72 			Uid:      1000,
     73 			Gid:      1000,
     74 			Size:     200,
     75 			ModTime:  time.Unix(1392342187, 0),
     76 			Typeflag: 0x30,
     77 			Linkname: "",
     78 			Uname:    "david",
     79 			Gname:    "david",
     80 			Devmajor: 0,
     81 			Devminor: 0,
     82 		}, {
     83 			Name:     "sparse-posix-0.1",
     84 			Mode:     420,
     85 			Uid:      1000,
     86 			Gid:      1000,
     87 			Size:     200,
     88 			ModTime:  time.Unix(1392340456, 0),
     89 			Typeflag: 0x30,
     90 			Linkname: "",
     91 			Uname:    "david",
     92 			Gname:    "david",
     93 			Devmajor: 0,
     94 			Devminor: 0,
     95 		}, {
     96 			Name:     "sparse-posix-1.0",
     97 			Mode:     420,
     98 			Uid:      1000,
     99 			Gid:      1000,
    100 			Size:     200,
    101 			ModTime:  time.Unix(1392337404, 0),
    102 			Typeflag: 0x30,
    103 			Linkname: "",
    104 			Uname:    "david",
    105 			Gname:    "david",
    106 			Devmajor: 0,
    107 			Devminor: 0,
    108 		}, {
    109 			Name:     "end",
    110 			Mode:     420,
    111 			Uid:      1000,
    112 			Gid:      1000,
    113 			Size:     4,
    114 			ModTime:  time.Unix(1392398319, 0),
    115 			Typeflag: 0x30,
    116 			Linkname: "",
    117 			Uname:    "david",
    118 			Gname:    "david",
    119 			Devmajor: 0,
    120 			Devminor: 0,
    121 		}},
    122 		chksums: []string{
    123 			"6f53234398c2449fe67c1812d993012f",
    124 			"6f53234398c2449fe67c1812d993012f",
    125 			"6f53234398c2449fe67c1812d993012f",
    126 			"6f53234398c2449fe67c1812d993012f",
    127 			"b0061974914468de549a2af8ced10316",
    128 		},
    129 	}, {
    130 		file: "testdata/star.tar",
    131 		headers: []*Header{{
    132 			Name:       "small.txt",
    133 			Mode:       0640,
    134 			Uid:        73025,
    135 			Gid:        5000,
    136 			Size:       5,
    137 			ModTime:    time.Unix(1244592783, 0),
    138 			Typeflag:   '0',
    139 			Uname:      "dsymonds",
    140 			Gname:      "eng",
    141 			AccessTime: time.Unix(1244592783, 0),
    142 			ChangeTime: time.Unix(1244592783, 0),
    143 		}, {
    144 			Name:       "small2.txt",
    145 			Mode:       0640,
    146 			Uid:        73025,
    147 			Gid:        5000,
    148 			Size:       11,
    149 			ModTime:    time.Unix(1244592783, 0),
    150 			Typeflag:   '0',
    151 			Uname:      "dsymonds",
    152 			Gname:      "eng",
    153 			AccessTime: time.Unix(1244592783, 0),
    154 			ChangeTime: time.Unix(1244592783, 0),
    155 		}},
    156 	}, {
    157 		file: "testdata/v7.tar",
    158 		headers: []*Header{{
    159 			Name:     "small.txt",
    160 			Mode:     0444,
    161 			Uid:      73025,
    162 			Gid:      5000,
    163 			Size:     5,
    164 			ModTime:  time.Unix(1244593104, 0),
    165 			Typeflag: '\x00',
    166 		}, {
    167 			Name:     "small2.txt",
    168 			Mode:     0444,
    169 			Uid:      73025,
    170 			Gid:      5000,
    171 			Size:     11,
    172 			ModTime:  time.Unix(1244593104, 0),
    173 			Typeflag: '\x00',
    174 		}},
    175 	}, {
    176 		file: "testdata/pax.tar",
    177 		headers: []*Header{{
    178 			Name:       "a/123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100",
    179 			Mode:       0664,
    180 			Uid:        1000,
    181 			Gid:        1000,
    182 			Uname:      "shane",
    183 			Gname:      "shane",
    184 			Size:       7,
    185 			ModTime:    time.Unix(1350244992, 23960108),
    186 			ChangeTime: time.Unix(1350244992, 23960108),
    187 			AccessTime: time.Unix(1350244992, 23960108),
    188 			Typeflag:   TypeReg,
    189 		}, {
    190 			Name:       "a/b",
    191 			Mode:       0777,
    192 			Uid:        1000,
    193 			Gid:        1000,
    194 			Uname:      "shane",
    195 			Gname:      "shane",
    196 			Size:       0,
    197 			ModTime:    time.Unix(1350266320, 910238425),
    198 			ChangeTime: time.Unix(1350266320, 910238425),
    199 			AccessTime: time.Unix(1350266320, 910238425),
    200 			Typeflag:   TypeSymlink,
    201 			Linkname:   "123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100",
    202 		}},
    203 	}, {
    204 		file: "testdata/pax-bad-hdr-file.tar",
    205 		err:  ErrHeader,
    206 	}, {
    207 		file: "testdata/pax-bad-mtime-file.tar",
    208 		err:  ErrHeader,
    209 	}, {
    210 		file: "testdata/pax-pos-size-file.tar",
    211 		headers: []*Header{{
    212 			Name:     "foo",
    213 			Mode:     0640,
    214 			Uid:      319973,
    215 			Gid:      5000,
    216 			Size:     999,
    217 			ModTime:  time.Unix(1442282516, 0),
    218 			Typeflag: '0',
    219 			Uname:    "joetsai",
    220 			Gname:    "eng",
    221 		}},
    222 		chksums: []string{
    223 			"0afb597b283fe61b5d4879669a350556",
    224 		},
    225 	}, {
    226 		file: "testdata/nil-uid.tar", // golang.org/issue/5290
    227 		headers: []*Header{{
    228 			Name:     "P1050238.JPG.log",
    229 			Mode:     0664,
    230 			Uid:      0,
    231 			Gid:      0,
    232 			Size:     14,
    233 			ModTime:  time.Unix(1365454838, 0),
    234 			Typeflag: TypeReg,
    235 			Linkname: "",
    236 			Uname:    "eyefi",
    237 			Gname:    "eyefi",
    238 			Devmajor: 0,
    239 			Devminor: 0,
    240 		}},
    241 	}, {
    242 		file: "testdata/xattrs.tar",
    243 		headers: []*Header{{
    244 			Name:       "small.txt",
    245 			Mode:       0644,
    246 			Uid:        1000,
    247 			Gid:        10,
    248 			Size:       5,
    249 			ModTime:    time.Unix(1386065770, 448252320),
    250 			Typeflag:   '0',
    251 			Uname:      "alex",
    252 			Gname:      "wheel",
    253 			AccessTime: time.Unix(1389782991, 419875220),
    254 			ChangeTime: time.Unix(1389782956, 794414986),
    255 			Xattrs: map[string]string{
    256 				"user.key":  "value",
    257 				"user.key2": "value2",
    258 				// Interestingly, selinux encodes the terminating null inside the xattr
    259 				"security.selinux": "unconfined_u:object_r:default_t:s0\x00",
    260 			},
    261 		}, {
    262 			Name:       "small2.txt",
    263 			Mode:       0644,
    264 			Uid:        1000,
    265 			Gid:        10,
    266 			Size:       11,
    267 			ModTime:    time.Unix(1386065770, 449252304),
    268 			Typeflag:   '0',
    269 			Uname:      "alex",
    270 			Gname:      "wheel",
    271 			AccessTime: time.Unix(1389782991, 419875220),
    272 			ChangeTime: time.Unix(1386065770, 449252304),
    273 			Xattrs: map[string]string{
    274 				"security.selinux": "unconfined_u:object_r:default_t:s0\x00",
    275 			},
    276 		}},
    277 	}, {
    278 		// Matches the behavior of GNU, BSD, and STAR tar utilities.
    279 		file: "testdata/gnu-multi-hdrs.tar",
    280 		headers: []*Header{{
    281 			Name:     "GNU2/GNU2/long-path-name",
    282 			Linkname: "GNU4/GNU4/long-linkpath-name",
    283 			ModTime:  time.Unix(0, 0),
    284 			Typeflag: '2',
    285 		}},
    286 	}, {
    287 		// GNU tar file with atime and ctime fields set.
    288 		// Created with the GNU tar v1.27.1.
    289 		//	tar --incremental -S -cvf gnu-incremental.tar test2
    290 		file: "testdata/gnu-incremental.tar",
    291 		headers: []*Header{{
    292 			Name:       "test2/",
    293 			Mode:       16877,
    294 			Uid:        1000,
    295 			Gid:        1000,
    296 			Size:       14,
    297 			ModTime:    time.Unix(1441973427, 0),
    298 			Typeflag:   'D',
    299 			Uname:      "rawr",
    300 			Gname:      "dsnet",
    301 			AccessTime: time.Unix(1441974501, 0),
    302 			ChangeTime: time.Unix(1441973436, 0),
    303 		}, {
    304 			Name:       "test2/foo",
    305 			Mode:       33188,
    306 			Uid:        1000,
    307 			Gid:        1000,
    308 			Size:       64,
    309 			ModTime:    time.Unix(1441973363, 0),
    310 			Typeflag:   '0',
    311 			Uname:      "rawr",
    312 			Gname:      "dsnet",
    313 			AccessTime: time.Unix(1441974501, 0),
    314 			ChangeTime: time.Unix(1441973436, 0),
    315 		}, {
    316 			Name:       "test2/sparse",
    317 			Mode:       33188,
    318 			Uid:        1000,
    319 			Gid:        1000,
    320 			Size:       536870912,
    321 			ModTime:    time.Unix(1441973427, 0),
    322 			Typeflag:   'S',
    323 			Uname:      "rawr",
    324 			Gname:      "dsnet",
    325 			AccessTime: time.Unix(1441991948, 0),
    326 			ChangeTime: time.Unix(1441973436, 0),
    327 		}},
    328 	}, {
    329 		// Matches the behavior of GNU and BSD tar utilities.
    330 		file: "testdata/pax-multi-hdrs.tar",
    331 		headers: []*Header{{
    332 			Name:     "bar",
    333 			Linkname: "PAX4/PAX4/long-linkpath-name",
    334 			ModTime:  time.Unix(0, 0),
    335 			Typeflag: '2',
    336 		}},
    337 	}, {
    338 		file: "testdata/neg-size.tar",
    339 		err:  ErrHeader,
    340 	}, {
    341 		file: "testdata/issue10968.tar",
    342 		err:  ErrHeader,
    343 	}, {
    344 		file: "testdata/issue11169.tar",
    345 		err:  ErrHeader,
    346 	}, {
    347 		file: "testdata/issue12435.tar",
    348 		err:  ErrHeader,
    349 	}}
    350 
    351 	for i, v := range vectors {
    352 		f, err := os.Open(v.file)
    353 		if err != nil {
    354 			t.Errorf("file %s, test %d: unexpected error: %v", v.file, i, err)
    355 			continue
    356 		}
    357 		defer f.Close()
    358 
    359 		// Capture all headers and checksums.
    360 		var (
    361 			tr      = NewReader(f)
    362 			hdrs    []*Header
    363 			chksums []string
    364 			rdbuf   = make([]byte, 8)
    365 		)
    366 		for {
    367 			var hdr *Header
    368 			hdr, err = tr.Next()
    369 			if err != nil {
    370 				if err == io.EOF {
    371 					err = nil // Expected error
    372 				}
    373 				break
    374 			}
    375 			hdrs = append(hdrs, hdr)
    376 
    377 			if v.chksums == nil {
    378 				continue
    379 			}
    380 			h := md5.New()
    381 			_, err = io.CopyBuffer(h, tr, rdbuf) // Effectively an incremental read
    382 			if err != nil {
    383 				break
    384 			}
    385 			chksums = append(chksums, fmt.Sprintf("%x", h.Sum(nil)))
    386 		}
    387 
    388 		for j, hdr := range hdrs {
    389 			if j >= len(v.headers) {
    390 				t.Errorf("file %s, test %d, entry %d: unexpected header:\ngot %+v",
    391 					v.file, i, j, *hdr)
    392 				continue
    393 			}
    394 			if !reflect.DeepEqual(*hdr, *v.headers[j]) {
    395 				t.Errorf("file %s, test %d, entry %d: incorrect header:\ngot  %+v\nwant %+v",
    396 					v.file, i, j, *hdr, *v.headers[j])
    397 			}
    398 		}
    399 		if len(hdrs) != len(v.headers) {
    400 			t.Errorf("file %s, test %d: got %d headers, want %d headers",
    401 				v.file, i, len(hdrs), len(v.headers))
    402 		}
    403 
    404 		for j, sum := range chksums {
    405 			if j >= len(v.chksums) {
    406 				t.Errorf("file %s, test %d, entry %d: unexpected sum: got %s",
    407 					v.file, i, j, sum)
    408 				continue
    409 			}
    410 			if sum != v.chksums[j] {
    411 				t.Errorf("file %s, test %d, entry %d: incorrect checksum: got %s, want %s",
    412 					v.file, i, j, sum, v.chksums[j])
    413 			}
    414 		}
    415 
    416 		if err != v.err {
    417 			t.Errorf("file %s, test %d: unexpected error: got %v, want %v",
    418 				v.file, i, err, v.err)
    419 		}
    420 		f.Close()
    421 	}
    422 }
    423 
    424 func TestPartialRead(t *testing.T) {
    425 	f, err := os.Open("testdata/gnu.tar")
    426 	if err != nil {
    427 		t.Fatalf("Unexpected error: %v", err)
    428 	}
    429 	defer f.Close()
    430 
    431 	tr := NewReader(f)
    432 
    433 	// Read the first four bytes; Next() should skip the last byte.
    434 	hdr, err := tr.Next()
    435 	if err != nil || hdr == nil {
    436 		t.Fatalf("Didn't get first file: %v", err)
    437 	}
    438 	buf := make([]byte, 4)
    439 	if _, err := io.ReadFull(tr, buf); err != nil {
    440 		t.Fatalf("Unexpected error: %v", err)
    441 	}
    442 	if expected := []byte("Kilt"); !bytes.Equal(buf, expected) {
    443 		t.Errorf("Contents = %v, want %v", buf, expected)
    444 	}
    445 
    446 	// Second file
    447 	hdr, err = tr.Next()
    448 	if err != nil || hdr == nil {
    449 		t.Fatalf("Didn't get second file: %v", err)
    450 	}
    451 	buf = make([]byte, 6)
    452 	if _, err := io.ReadFull(tr, buf); err != nil {
    453 		t.Fatalf("Unexpected error: %v", err)
    454 	}
    455 	if expected := []byte("Google"); !bytes.Equal(buf, expected) {
    456 		t.Errorf("Contents = %v, want %v", buf, expected)
    457 	}
    458 }
    459 
    460 func TestSparseFileReader(t *testing.T) {
    461 	vectors := []struct {
    462 		realSize   int64         // Real size of the output file
    463 		sparseMap  []sparseEntry // Input sparse map
    464 		sparseData string        // Input compact data
    465 		expected   string        // Expected output data
    466 		err        error         // Expected error outcome
    467 	}{{
    468 		realSize: 8,
    469 		sparseMap: []sparseEntry{
    470 			{offset: 0, numBytes: 2},
    471 			{offset: 5, numBytes: 3},
    472 		},
    473 		sparseData: "abcde",
    474 		expected:   "ab\x00\x00\x00cde",
    475 	}, {
    476 		realSize: 10,
    477 		sparseMap: []sparseEntry{
    478 			{offset: 0, numBytes: 2},
    479 			{offset: 5, numBytes: 3},
    480 		},
    481 		sparseData: "abcde",
    482 		expected:   "ab\x00\x00\x00cde\x00\x00",
    483 	}, {
    484 		realSize: 8,
    485 		sparseMap: []sparseEntry{
    486 			{offset: 1, numBytes: 3},
    487 			{offset: 6, numBytes: 2},
    488 		},
    489 		sparseData: "abcde",
    490 		expected:   "\x00abc\x00\x00de",
    491 	}, {
    492 		realSize: 8,
    493 		sparseMap: []sparseEntry{
    494 			{offset: 1, numBytes: 3},
    495 			{offset: 6, numBytes: 0},
    496 			{offset: 6, numBytes: 0},
    497 			{offset: 6, numBytes: 2},
    498 		},
    499 		sparseData: "abcde",
    500 		expected:   "\x00abc\x00\x00de",
    501 	}, {
    502 		realSize: 10,
    503 		sparseMap: []sparseEntry{
    504 			{offset: 1, numBytes: 3},
    505 			{offset: 6, numBytes: 2},
    506 		},
    507 		sparseData: "abcde",
    508 		expected:   "\x00abc\x00\x00de\x00\x00",
    509 	}, {
    510 		realSize: 10,
    511 		sparseMap: []sparseEntry{
    512 			{offset: 1, numBytes: 3},
    513 			{offset: 6, numBytes: 2},
    514 			{offset: 8, numBytes: 0},
    515 			{offset: 8, numBytes: 0},
    516 			{offset: 8, numBytes: 0},
    517 			{offset: 8, numBytes: 0},
    518 		},
    519 		sparseData: "abcde",
    520 		expected:   "\x00abc\x00\x00de\x00\x00",
    521 	}, {
    522 		realSize:   2,
    523 		sparseMap:  []sparseEntry{},
    524 		sparseData: "",
    525 		expected:   "\x00\x00",
    526 	}, {
    527 		realSize:  -2,
    528 		sparseMap: []sparseEntry{},
    529 		err:       ErrHeader,
    530 	}, {
    531 		realSize: -10,
    532 		sparseMap: []sparseEntry{
    533 			{offset: 1, numBytes: 3},
    534 			{offset: 6, numBytes: 2},
    535 		},
    536 		sparseData: "abcde",
    537 		err:        ErrHeader,
    538 	}, {
    539 		realSize: 10,
    540 		sparseMap: []sparseEntry{
    541 			{offset: 1, numBytes: 3},
    542 			{offset: 6, numBytes: 5},
    543 		},
    544 		sparseData: "abcde",
    545 		err:        ErrHeader,
    546 	}, {
    547 		realSize: 35,
    548 		sparseMap: []sparseEntry{
    549 			{offset: 1, numBytes: 3},
    550 			{offset: 6, numBytes: 5},
    551 		},
    552 		sparseData: "abcde",
    553 		err:        io.ErrUnexpectedEOF,
    554 	}, {
    555 		realSize: 35,
    556 		sparseMap: []sparseEntry{
    557 			{offset: 1, numBytes: 3},
    558 			{offset: 6, numBytes: -5},
    559 		},
    560 		sparseData: "abcde",
    561 		err:        ErrHeader,
    562 	}, {
    563 		realSize: 35,
    564 		sparseMap: []sparseEntry{
    565 			{offset: math.MaxInt64, numBytes: 3},
    566 			{offset: 6, numBytes: -5},
    567 		},
    568 		sparseData: "abcde",
    569 		err:        ErrHeader,
    570 	}, {
    571 		realSize: 10,
    572 		sparseMap: []sparseEntry{
    573 			{offset: 1, numBytes: 3},
    574 			{offset: 2, numBytes: 2},
    575 		},
    576 		sparseData: "abcde",
    577 		err:        ErrHeader,
    578 	}}
    579 
    580 	for i, v := range vectors {
    581 		r := bytes.NewReader([]byte(v.sparseData))
    582 		rfr := &regFileReader{r: r, nb: int64(len(v.sparseData))}
    583 
    584 		var (
    585 			sfr *sparseFileReader
    586 			err error
    587 			buf []byte
    588 		)
    589 
    590 		sfr, err = newSparseFileReader(rfr, v.sparseMap, v.realSize)
    591 		if err != nil {
    592 			goto fail
    593 		}
    594 		if sfr.numBytes() != int64(len(v.sparseData)) {
    595 			t.Errorf("test %d, numBytes() before reading: got %d, want %d", i, sfr.numBytes(), len(v.sparseData))
    596 		}
    597 		buf, err = ioutil.ReadAll(sfr)
    598 		if err != nil {
    599 			goto fail
    600 		}
    601 		if string(buf) != v.expected {
    602 			t.Errorf("test %d, ReadAll(): got %q, want %q", i, string(buf), v.expected)
    603 		}
    604 		if sfr.numBytes() != 0 {
    605 			t.Errorf("test %d, numBytes() after reading: got %d, want %d", i, sfr.numBytes(), 0)
    606 		}
    607 
    608 	fail:
    609 		if err != v.err {
    610 			t.Errorf("test %d, unexpected error: got %v, want %v", i, err, v.err)
    611 		}
    612 	}
    613 }
    614 
    615 func TestReadOldGNUSparseMap(t *testing.T) {
    616 	const (
    617 		t00 = "00000000000\x0000000000000\x00"
    618 		t11 = "00000000001\x0000000000001\x00"
    619 		t12 = "00000000001\x0000000000002\x00"
    620 		t21 = "00000000002\x0000000000001\x00"
    621 	)
    622 
    623 	mkBlk := func(size, sp0, sp1, sp2, sp3, ext string, format int) *block {
    624 		var blk block
    625 		copy(blk.GNU().RealSize(), size)
    626 		copy(blk.GNU().Sparse().Entry(0), sp0)
    627 		copy(blk.GNU().Sparse().Entry(1), sp1)
    628 		copy(blk.GNU().Sparse().Entry(2), sp2)
    629 		copy(blk.GNU().Sparse().Entry(3), sp3)
    630 		copy(blk.GNU().Sparse().IsExtended(), ext)
    631 		if format != formatUnknown {
    632 			blk.SetFormat(format)
    633 		}
    634 		return &blk
    635 	}
    636 
    637 	vectors := []struct {
    638 		data   string        // Input data
    639 		rawHdr *block        // Input raw header
    640 		want   []sparseEntry // Expected sparse entries to be outputted
    641 		err    error         // Expected error to be returned
    642 	}{
    643 		{"", mkBlk("", "", "", "", "", "", formatUnknown), nil, ErrHeader},
    644 		{"", mkBlk("1234", "fewa", "", "", "", "", formatGNU), nil, ErrHeader},
    645 		{"", mkBlk("0031", "", "", "", "", "", formatGNU), nil, nil},
    646 		{"", mkBlk("1234", t00, t11, "", "", "", formatGNU),
    647 			[]sparseEntry{{0, 0}, {1, 1}}, nil},
    648 		{"", mkBlk("1234", t11, t12, t21, t11, "", formatGNU),
    649 			[]sparseEntry{{1, 1}, {1, 2}, {2, 1}, {1, 1}}, nil},
    650 		{"", mkBlk("1234", t11, t12, t21, t11, "\x80", formatGNU),
    651 			[]sparseEntry{}, io.ErrUnexpectedEOF},
    652 		{t11 + t11,
    653 			mkBlk("1234", t11, t12, t21, t11, "\x80", formatGNU),
    654 			[]sparseEntry{}, io.ErrUnexpectedEOF},
    655 		{t11 + t21 + strings.Repeat("\x00", 512),
    656 			mkBlk("1234", t11, t12, t21, t11, "\x80", formatGNU),
    657 			[]sparseEntry{{1, 1}, {1, 2}, {2, 1}, {1, 1}, {1, 1}, {2, 1}}, nil},
    658 	}
    659 
    660 	for i, v := range vectors {
    661 		tr := Reader{r: strings.NewReader(v.data)}
    662 		hdr := new(Header)
    663 		got, err := tr.readOldGNUSparseMap(hdr, v.rawHdr)
    664 		if !reflect.DeepEqual(got, v.want) && !(len(got) == 0 && len(v.want) == 0) {
    665 			t.Errorf("test %d, readOldGNUSparseMap(...): got %v, want %v", i, got, v.want)
    666 		}
    667 		if err != v.err {
    668 			t.Errorf("test %d, unexpected error: got %v, want %v", i, err, v.err)
    669 		}
    670 	}
    671 }
    672 
    673 func TestReadGNUSparseMap0x1(t *testing.T) {
    674 	const (
    675 		maxUint = ^uint(0)
    676 		maxInt  = int(maxUint >> 1)
    677 	)
    678 	var (
    679 		big1 = fmt.Sprintf("%d", int64(maxInt))
    680 		big2 = fmt.Sprintf("%d", (int64(maxInt)/2)+1)
    681 		big3 = fmt.Sprintf("%d", (int64(maxInt) / 3))
    682 	)
    683 
    684 	vectors := []struct {
    685 		extHdrs   map[string]string // Input data
    686 		sparseMap []sparseEntry     // Expected sparse entries to be outputted
    687 		err       error             // Expected errors that may be raised
    688 	}{{
    689 		extHdrs: map[string]string{paxGNUSparseNumBlocks: "-4"},
    690 		err:     ErrHeader,
    691 	}, {
    692 		extHdrs: map[string]string{paxGNUSparseNumBlocks: "fee "},
    693 		err:     ErrHeader,
    694 	}, {
    695 		extHdrs: map[string]string{
    696 			paxGNUSparseNumBlocks: big1,
    697 			paxGNUSparseMap:       "0,5,10,5,20,5,30,5",
    698 		},
    699 		err: ErrHeader,
    700 	}, {
    701 		extHdrs: map[string]string{
    702 			paxGNUSparseNumBlocks: big2,
    703 			paxGNUSparseMap:       "0,5,10,5,20,5,30,5",
    704 		},
    705 		err: ErrHeader,
    706 	}, {
    707 		extHdrs: map[string]string{
    708 			paxGNUSparseNumBlocks: big3,
    709 			paxGNUSparseMap:       "0,5,10,5,20,5,30,5",
    710 		},
    711 		err: ErrHeader,
    712 	}, {
    713 		extHdrs: map[string]string{
    714 			paxGNUSparseNumBlocks: "4",
    715 			paxGNUSparseMap:       "0.5,5,10,5,20,5,30,5",
    716 		},
    717 		err: ErrHeader,
    718 	}, {
    719 		extHdrs: map[string]string{
    720 			paxGNUSparseNumBlocks: "4",
    721 			paxGNUSparseMap:       "0,5.5,10,5,20,5,30,5",
    722 		},
    723 		err: ErrHeader,
    724 	}, {
    725 		extHdrs: map[string]string{
    726 			paxGNUSparseNumBlocks: "4",
    727 			paxGNUSparseMap:       "0,fewafewa.5,fewafw,5,20,5,30,5",
    728 		},
    729 		err: ErrHeader,
    730 	}, {
    731 		extHdrs: map[string]string{
    732 			paxGNUSparseNumBlocks: "4",
    733 			paxGNUSparseMap:       "0,5,10,5,20,5,30,5",
    734 		},
    735 		sparseMap: []sparseEntry{{0, 5}, {10, 5}, {20, 5}, {30, 5}},
    736 	}}
    737 
    738 	for i, v := range vectors {
    739 		sp, err := readGNUSparseMap0x1(v.extHdrs)
    740 		if !reflect.DeepEqual(sp, v.sparseMap) && !(len(sp) == 0 && len(v.sparseMap) == 0) {
    741 			t.Errorf("test %d, readGNUSparseMap0x1(...): got %v, want %v", i, sp, v.sparseMap)
    742 		}
    743 		if err != v.err {
    744 			t.Errorf("test %d, unexpected error: got %v, want %v", i, err, v.err)
    745 		}
    746 	}
    747 }
    748 
    749 func TestReadGNUSparseMap1x0(t *testing.T) {
    750 	sp := []sparseEntry{{1, 2}, {3, 4}}
    751 	for i := 0; i < 98; i++ {
    752 		sp = append(sp, sparseEntry{54321, 12345})
    753 	}
    754 
    755 	vectors := []struct {
    756 		input     string        // Input data
    757 		sparseMap []sparseEntry // Expected sparse entries to be outputted
    758 		cnt       int           // Expected number of bytes read
    759 		err       error         // Expected errors that may be raised
    760 	}{{
    761 		input: "",
    762 		cnt:   0,
    763 		err:   io.ErrUnexpectedEOF,
    764 	}, {
    765 		input: "ab",
    766 		cnt:   2,
    767 		err:   io.ErrUnexpectedEOF,
    768 	}, {
    769 		input: strings.Repeat("\x00", 512),
    770 		cnt:   512,
    771 		err:   io.ErrUnexpectedEOF,
    772 	}, {
    773 		input: strings.Repeat("\x00", 511) + "\n",
    774 		cnt:   512,
    775 		err:   ErrHeader,
    776 	}, {
    777 		input: strings.Repeat("\n", 512),
    778 		cnt:   512,
    779 		err:   ErrHeader,
    780 	}, {
    781 		input:     "0\n" + strings.Repeat("\x00", 510) + strings.Repeat("a", 512),
    782 		sparseMap: []sparseEntry{},
    783 		cnt:       512,
    784 	}, {
    785 		input:     strings.Repeat("0", 512) + "0\n" + strings.Repeat("\x00", 510),
    786 		sparseMap: []sparseEntry{},
    787 		cnt:       1024,
    788 	}, {
    789 		input:     strings.Repeat("0", 1024) + "1\n2\n3\n" + strings.Repeat("\x00", 506),
    790 		sparseMap: []sparseEntry{{2, 3}},
    791 		cnt:       1536,
    792 	}, {
    793 		input: strings.Repeat("0", 1024) + "1\n2\n\n" + strings.Repeat("\x00", 509),
    794 		cnt:   1536,
    795 		err:   ErrHeader,
    796 	}, {
    797 		input: strings.Repeat("0", 1024) + "1\n2\n" + strings.Repeat("\x00", 508),
    798 		cnt:   1536,
    799 		err:   io.ErrUnexpectedEOF,
    800 	}, {
    801 		input: "-1\n2\n\n" + strings.Repeat("\x00", 506),
    802 		cnt:   512,
    803 		err:   ErrHeader,
    804 	}, {
    805 		input: "1\nk\n2\n" + strings.Repeat("\x00", 506),
    806 		cnt:   512,
    807 		err:   ErrHeader,
    808 	}, {
    809 		input:     "100\n1\n2\n3\n4\n" + strings.Repeat("54321\n0000000000000012345\n", 98) + strings.Repeat("\x00", 512),
    810 		cnt:       2560,
    811 		sparseMap: sp,
    812 	}}
    813 
    814 	for i, v := range vectors {
    815 		r := strings.NewReader(v.input)
    816 		sp, err := readGNUSparseMap1x0(r)
    817 		if !reflect.DeepEqual(sp, v.sparseMap) && !(len(sp) == 0 && len(v.sparseMap) == 0) {
    818 			t.Errorf("test %d, readGNUSparseMap1x0(...): got %v, want %v", i, sp, v.sparseMap)
    819 		}
    820 		if numBytes := len(v.input) - r.Len(); numBytes != v.cnt {
    821 			t.Errorf("test %d, bytes read: got %v, want %v", i, numBytes, v.cnt)
    822 		}
    823 		if err != v.err {
    824 			t.Errorf("test %d, unexpected error: got %v, want %v", i, err, v.err)
    825 		}
    826 	}
    827 }
    828 
    829 func TestUninitializedRead(t *testing.T) {
    830 	f, err := os.Open("testdata/gnu.tar")
    831 	if err != nil {
    832 		t.Fatalf("Unexpected error: %v", err)
    833 	}
    834 	defer f.Close()
    835 
    836 	tr := NewReader(f)
    837 	_, err = tr.Read([]byte{})
    838 	if err == nil || err != io.EOF {
    839 		t.Errorf("Unexpected error: %v, wanted %v", err, io.EOF)
    840 	}
    841 
    842 }
    843 
    844 type reader struct{ io.Reader }
    845 type readSeeker struct{ io.ReadSeeker }
    846 type readBadSeeker struct{ io.ReadSeeker }
    847 
    848 func (rbs *readBadSeeker) Seek(int64, int) (int64, error) { return 0, fmt.Errorf("illegal seek") }
    849 
    850 // TestReadTruncation test the ending condition on various truncated files and
    851 // that truncated files are still detected even if the underlying io.Reader
    852 // satisfies io.Seeker.
    853 func TestReadTruncation(t *testing.T) {
    854 	var ss []string
    855 	for _, p := range []string{
    856 		"testdata/gnu.tar",
    857 		"testdata/ustar-file-reg.tar",
    858 		"testdata/pax-path-hdr.tar",
    859 		"testdata/sparse-formats.tar",
    860 	} {
    861 		buf, err := ioutil.ReadFile(p)
    862 		if err != nil {
    863 			t.Fatalf("unexpected error: %v", err)
    864 		}
    865 		ss = append(ss, string(buf))
    866 	}
    867 
    868 	data1, data2, pax, sparse := ss[0], ss[1], ss[2], ss[3]
    869 	data2 += strings.Repeat("\x00", 10*512)
    870 	trash := strings.Repeat("garbage ", 64) // Exactly 512 bytes
    871 
    872 	vectors := []struct {
    873 		input string // Input stream
    874 		cnt   int    // Expected number of headers read
    875 		err   error  // Expected error outcome
    876 	}{
    877 		{"", 0, io.EOF}, // Empty file is a "valid" tar file
    878 		{data1[:511], 0, io.ErrUnexpectedEOF},
    879 		{data1[:512], 1, io.ErrUnexpectedEOF},
    880 		{data1[:1024], 1, io.EOF},
    881 		{data1[:1536], 2, io.ErrUnexpectedEOF},
    882 		{data1[:2048], 2, io.EOF},
    883 		{data1, 2, io.EOF},
    884 		{data1[:2048] + data2[:1536], 3, io.EOF},
    885 		{data2[:511], 0, io.ErrUnexpectedEOF},
    886 		{data2[:512], 1, io.ErrUnexpectedEOF},
    887 		{data2[:1195], 1, io.ErrUnexpectedEOF},
    888 		{data2[:1196], 1, io.EOF}, // Exact end of data and start of padding
    889 		{data2[:1200], 1, io.EOF},
    890 		{data2[:1535], 1, io.EOF},
    891 		{data2[:1536], 1, io.EOF}, // Exact end of padding
    892 		{data2[:1536] + trash[:1], 1, io.ErrUnexpectedEOF},
    893 		{data2[:1536] + trash[:511], 1, io.ErrUnexpectedEOF},
    894 		{data2[:1536] + trash, 1, ErrHeader},
    895 		{data2[:2048], 1, io.EOF}, // Exactly 1 empty block
    896 		{data2[:2048] + trash[:1], 1, io.ErrUnexpectedEOF},
    897 		{data2[:2048] + trash[:511], 1, io.ErrUnexpectedEOF},
    898 		{data2[:2048] + trash, 1, ErrHeader},
    899 		{data2[:2560], 1, io.EOF}, // Exactly 2 empty blocks (normal end-of-stream)
    900 		{data2[:2560] + trash[:1], 1, io.EOF},
    901 		{data2[:2560] + trash[:511], 1, io.EOF},
    902 		{data2[:2560] + trash, 1, io.EOF},
    903 		{data2[:3072], 1, io.EOF},
    904 		{pax, 0, io.EOF}, // PAX header without data is a "valid" tar file
    905 		{pax + trash[:1], 0, io.ErrUnexpectedEOF},
    906 		{pax + trash[:511], 0, io.ErrUnexpectedEOF},
    907 		{sparse[:511], 0, io.ErrUnexpectedEOF},
    908 		{sparse[:512], 0, io.ErrUnexpectedEOF},
    909 		{sparse[:3584], 1, io.EOF},
    910 		{sparse[:9200], 1, io.EOF}, // Terminate in padding of sparse header
    911 		{sparse[:9216], 1, io.EOF},
    912 		{sparse[:9728], 2, io.ErrUnexpectedEOF},
    913 		{sparse[:10240], 2, io.EOF},
    914 		{sparse[:11264], 2, io.ErrUnexpectedEOF},
    915 		{sparse, 5, io.EOF},
    916 		{sparse + trash, 5, io.EOF},
    917 	}
    918 
    919 	for i, v := range vectors {
    920 		for j := 0; j < 6; j++ {
    921 			var tr *Reader
    922 			var s1, s2 string
    923 
    924 			switch j {
    925 			case 0:
    926 				tr = NewReader(&reader{strings.NewReader(v.input)})
    927 				s1, s2 = "io.Reader", "auto"
    928 			case 1:
    929 				tr = NewReader(&reader{strings.NewReader(v.input)})
    930 				s1, s2 = "io.Reader", "manual"
    931 			case 2:
    932 				tr = NewReader(&readSeeker{strings.NewReader(v.input)})
    933 				s1, s2 = "io.ReadSeeker", "auto"
    934 			case 3:
    935 				tr = NewReader(&readSeeker{strings.NewReader(v.input)})
    936 				s1, s2 = "io.ReadSeeker", "manual"
    937 			case 4:
    938 				tr = NewReader(&readBadSeeker{strings.NewReader(v.input)})
    939 				s1, s2 = "ReadBadSeeker", "auto"
    940 			case 5:
    941 				tr = NewReader(&readBadSeeker{strings.NewReader(v.input)})
    942 				s1, s2 = "ReadBadSeeker", "manual"
    943 			}
    944 
    945 			var cnt int
    946 			var err error
    947 			for {
    948 				if _, err = tr.Next(); err != nil {
    949 					break
    950 				}
    951 				cnt++
    952 				if s2 == "manual" {
    953 					if _, err = io.Copy(ioutil.Discard, tr); err != nil {
    954 						break
    955 					}
    956 				}
    957 			}
    958 			if err != v.err {
    959 				t.Errorf("test %d, NewReader(%s(...)) with %s discard: got %v, want %v",
    960 					i, s1, s2, err, v.err)
    961 			}
    962 			if cnt != v.cnt {
    963 				t.Errorf("test %d, NewReader(%s(...)) with %s discard: got %d headers, want %d headers",
    964 					i, s1, s2, cnt, v.cnt)
    965 			}
    966 		}
    967 	}
    968 }
    969 
    970 // TestReadHeaderOnly tests that Reader does not attempt to read special
    971 // header-only files.
    972 func TestReadHeaderOnly(t *testing.T) {
    973 	f, err := os.Open("testdata/hdr-only.tar")
    974 	if err != nil {
    975 		t.Fatalf("unexpected error: %v", err)
    976 	}
    977 	defer f.Close()
    978 
    979 	var hdrs []*Header
    980 	tr := NewReader(f)
    981 	for {
    982 		hdr, err := tr.Next()
    983 		if err == io.EOF {
    984 			break
    985 		}
    986 		if err != nil {
    987 			t.Errorf("Next(): got %v, want %v", err, nil)
    988 			continue
    989 		}
    990 		hdrs = append(hdrs, hdr)
    991 
    992 		// If a special flag, we should read nothing.
    993 		cnt, _ := io.ReadFull(tr, []byte{0})
    994 		if cnt > 0 && hdr.Typeflag != TypeReg {
    995 			t.Errorf("ReadFull(...): got %d bytes, want 0 bytes", cnt)
    996 		}
    997 	}
    998 
    999 	// File is crafted with 16 entries. The later 8 are identical to the first
   1000 	// 8 except that the size is set.
   1001 	if len(hdrs) != 16 {
   1002 		t.Fatalf("len(hdrs): got %d, want %d", len(hdrs), 16)
   1003 	}
   1004 	for i := 0; i < 8; i++ {
   1005 		hdr1, hdr2 := hdrs[i+0], hdrs[i+8]
   1006 		hdr1.Size, hdr2.Size = 0, 0
   1007 		if !reflect.DeepEqual(*hdr1, *hdr2) {
   1008 			t.Errorf("incorrect header:\ngot  %+v\nwant %+v", *hdr1, *hdr2)
   1009 		}
   1010 	}
   1011 }
   1012 
   1013 func TestMergePAX(t *testing.T) {
   1014 	vectors := []struct {
   1015 		in   map[string]string
   1016 		want *Header
   1017 		ok   bool
   1018 	}{{
   1019 		in: map[string]string{
   1020 			"path":  "a/b/c",
   1021 			"uid":   "1000",
   1022 			"mtime": "1350244992.023960108",
   1023 		},
   1024 		want: &Header{
   1025 			Name:    "a/b/c",
   1026 			Uid:     1000,
   1027 			ModTime: time.Unix(1350244992, 23960108),
   1028 		},
   1029 		ok: true,
   1030 	}, {
   1031 		in: map[string]string{
   1032 			"gid": "gtgergergersagersgers",
   1033 		},
   1034 	}, {
   1035 		in: map[string]string{
   1036 			"missing":          "missing",
   1037 			"SCHILY.xattr.key": "value",
   1038 		},
   1039 		want: &Header{
   1040 			Xattrs: map[string]string{"key": "value"},
   1041 		},
   1042 		ok: true,
   1043 	}}
   1044 
   1045 	for i, v := range vectors {
   1046 		got := new(Header)
   1047 		err := mergePAX(got, v.in)
   1048 		if v.ok && !reflect.DeepEqual(*got, *v.want) {
   1049 			t.Errorf("test %d, mergePAX(...):\ngot  %+v\nwant %+v", i, *got, *v.want)
   1050 		}
   1051 		if ok := err == nil; ok != v.ok {
   1052 			t.Errorf("test %d, mergePAX(...): got %v, want %v", i, ok, v.ok)
   1053 		}
   1054 	}
   1055 }
   1056 
   1057 func TestParsePAX(t *testing.T) {
   1058 	vectors := []struct {
   1059 		in   string
   1060 		want map[string]string
   1061 		ok   bool
   1062 	}{
   1063 		{"", nil, true},
   1064 		{"6 k=1\n", map[string]string{"k": "1"}, true},
   1065 		{"10 a=name\n", map[string]string{"a": "name"}, true},
   1066 		{"9 a=name\n", map[string]string{"a": "name"}, true},
   1067 		{"30 mtime=1350244992.023960108\n", map[string]string{"mtime": "1350244992.023960108"}, true},
   1068 		{"3 somelongkey=\n", nil, false},
   1069 		{"50 tooshort=\n", nil, false},
   1070 		{"13 key1=haha\n13 key2=nana\n13 key3=kaka\n",
   1071 			map[string]string{"key1": "haha", "key2": "nana", "key3": "kaka"}, true},
   1072 		{"13 key1=val1\n13 key2=val2\n8 key1=\n",
   1073 			map[string]string{"key2": "val2"}, true},
   1074 		{"22 GNU.sparse.size=10\n26 GNU.sparse.numblocks=2\n" +
   1075 			"23 GNU.sparse.offset=1\n25 GNU.sparse.numbytes=2\n" +
   1076 			"23 GNU.sparse.offset=3\n25 GNU.sparse.numbytes=4\n",
   1077 			map[string]string{paxGNUSparseSize: "10", paxGNUSparseNumBlocks: "2", paxGNUSparseMap: "1,2,3,4"}, true},
   1078 		{"22 GNU.sparse.size=10\n26 GNU.sparse.numblocks=1\n" +
   1079 			"25 GNU.sparse.numbytes=2\n23 GNU.sparse.offset=1\n",
   1080 			nil, false},
   1081 		{"22 GNU.sparse.size=10\n26 GNU.sparse.numblocks=1\n" +
   1082 			"25 GNU.sparse.offset=1,2\n25 GNU.sparse.numbytes=2\n",
   1083 			nil, false},
   1084 	}
   1085 
   1086 	for i, v := range vectors {
   1087 		r := strings.NewReader(v.in)
   1088 		got, err := parsePAX(r)
   1089 		if !reflect.DeepEqual(got, v.want) && !(len(got) == 0 && len(v.want) == 0) {
   1090 			t.Errorf("test %d, parsePAX(...):\ngot  %v\nwant %v", i, got, v.want)
   1091 		}
   1092 		if ok := err == nil; ok != v.ok {
   1093 			t.Errorf("test %d, parsePAX(...): got %v, want %v", i, ok, v.ok)
   1094 		}
   1095 	}
   1096 }
   1097