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 	"os"
     14 	"reflect"
     15 	"strings"
     16 	"testing"
     17 	"time"
     18 )
     19 
     20 type untarTest struct {
     21 	file    string
     22 	headers []*Header
     23 	cksums  []string
     24 }
     25 
     26 var gnuTarTest = &untarTest{
     27 	file: "testdata/gnu.tar",
     28 	headers: []*Header{
     29 		{
     30 			Name:     "small.txt",
     31 			Mode:     0640,
     32 			Uid:      73025,
     33 			Gid:      5000,
     34 			Size:     5,
     35 			ModTime:  time.Unix(1244428340, 0),
     36 			Typeflag: '0',
     37 			Uname:    "dsymonds",
     38 			Gname:    "eng",
     39 		},
     40 		{
     41 			Name:     "small2.txt",
     42 			Mode:     0640,
     43 			Uid:      73025,
     44 			Gid:      5000,
     45 			Size:     11,
     46 			ModTime:  time.Unix(1244436044, 0),
     47 			Typeflag: '0',
     48 			Uname:    "dsymonds",
     49 			Gname:    "eng",
     50 		},
     51 	},
     52 	cksums: []string{
     53 		"e38b27eaccb4391bdec553a7f3ae6b2f",
     54 		"c65bd2e50a56a2138bf1716f2fd56fe9",
     55 	},
     56 }
     57 
     58 var sparseTarTest = &untarTest{
     59 	file: "testdata/sparse-formats.tar",
     60 	headers: []*Header{
     61 		{
     62 			Name:     "sparse-gnu",
     63 			Mode:     420,
     64 			Uid:      1000,
     65 			Gid:      1000,
     66 			Size:     200,
     67 			ModTime:  time.Unix(1392395740, 0),
     68 			Typeflag: 0x53,
     69 			Linkname: "",
     70 			Uname:    "david",
     71 			Gname:    "david",
     72 			Devmajor: 0,
     73 			Devminor: 0,
     74 		},
     75 		{
     76 			Name:     "sparse-posix-0.0",
     77 			Mode:     420,
     78 			Uid:      1000,
     79 			Gid:      1000,
     80 			Size:     200,
     81 			ModTime:  time.Unix(1392342187, 0),
     82 			Typeflag: 0x30,
     83 			Linkname: "",
     84 			Uname:    "david",
     85 			Gname:    "david",
     86 			Devmajor: 0,
     87 			Devminor: 0,
     88 		},
     89 		{
     90 			Name:     "sparse-posix-0.1",
     91 			Mode:     420,
     92 			Uid:      1000,
     93 			Gid:      1000,
     94 			Size:     200,
     95 			ModTime:  time.Unix(1392340456, 0),
     96 			Typeflag: 0x30,
     97 			Linkname: "",
     98 			Uname:    "david",
     99 			Gname:    "david",
    100 			Devmajor: 0,
    101 			Devminor: 0,
    102 		},
    103 		{
    104 			Name:     "sparse-posix-1.0",
    105 			Mode:     420,
    106 			Uid:      1000,
    107 			Gid:      1000,
    108 			Size:     200,
    109 			ModTime:  time.Unix(1392337404, 0),
    110 			Typeflag: 0x30,
    111 			Linkname: "",
    112 			Uname:    "david",
    113 			Gname:    "david",
    114 			Devmajor: 0,
    115 			Devminor: 0,
    116 		},
    117 		{
    118 			Name:     "end",
    119 			Mode:     420,
    120 			Uid:      1000,
    121 			Gid:      1000,
    122 			Size:     4,
    123 			ModTime:  time.Unix(1392398319, 0),
    124 			Typeflag: 0x30,
    125 			Linkname: "",
    126 			Uname:    "david",
    127 			Gname:    "david",
    128 			Devmajor: 0,
    129 			Devminor: 0,
    130 		},
    131 	},
    132 	cksums: []string{
    133 		"6f53234398c2449fe67c1812d993012f",
    134 		"6f53234398c2449fe67c1812d993012f",
    135 		"6f53234398c2449fe67c1812d993012f",
    136 		"6f53234398c2449fe67c1812d993012f",
    137 		"b0061974914468de549a2af8ced10316",
    138 	},
    139 }
    140 
    141 var untarTests = []*untarTest{
    142 	gnuTarTest,
    143 	sparseTarTest,
    144 	{
    145 		file: "testdata/star.tar",
    146 		headers: []*Header{
    147 			{
    148 				Name:       "small.txt",
    149 				Mode:       0640,
    150 				Uid:        73025,
    151 				Gid:        5000,
    152 				Size:       5,
    153 				ModTime:    time.Unix(1244592783, 0),
    154 				Typeflag:   '0',
    155 				Uname:      "dsymonds",
    156 				Gname:      "eng",
    157 				AccessTime: time.Unix(1244592783, 0),
    158 				ChangeTime: time.Unix(1244592783, 0),
    159 			},
    160 			{
    161 				Name:       "small2.txt",
    162 				Mode:       0640,
    163 				Uid:        73025,
    164 				Gid:        5000,
    165 				Size:       11,
    166 				ModTime:    time.Unix(1244592783, 0),
    167 				Typeflag:   '0',
    168 				Uname:      "dsymonds",
    169 				Gname:      "eng",
    170 				AccessTime: time.Unix(1244592783, 0),
    171 				ChangeTime: time.Unix(1244592783, 0),
    172 			},
    173 		},
    174 	},
    175 	{
    176 		file: "testdata/v7.tar",
    177 		headers: []*Header{
    178 			{
    179 				Name:     "small.txt",
    180 				Mode:     0444,
    181 				Uid:      73025,
    182 				Gid:      5000,
    183 				Size:     5,
    184 				ModTime:  time.Unix(1244593104, 0),
    185 				Typeflag: '\x00',
    186 			},
    187 			{
    188 				Name:     "small2.txt",
    189 				Mode:     0444,
    190 				Uid:      73025,
    191 				Gid:      5000,
    192 				Size:     11,
    193 				ModTime:  time.Unix(1244593104, 0),
    194 				Typeflag: '\x00',
    195 			},
    196 		},
    197 	},
    198 	{
    199 		file: "testdata/pax.tar",
    200 		headers: []*Header{
    201 			{
    202 				Name:       "a/123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100",
    203 				Mode:       0664,
    204 				Uid:        1000,
    205 				Gid:        1000,
    206 				Uname:      "shane",
    207 				Gname:      "shane",
    208 				Size:       7,
    209 				ModTime:    time.Unix(1350244992, 23960108),
    210 				ChangeTime: time.Unix(1350244992, 23960108),
    211 				AccessTime: time.Unix(1350244992, 23960108),
    212 				Typeflag:   TypeReg,
    213 			},
    214 			{
    215 				Name:       "a/b",
    216 				Mode:       0777,
    217 				Uid:        1000,
    218 				Gid:        1000,
    219 				Uname:      "shane",
    220 				Gname:      "shane",
    221 				Size:       0,
    222 				ModTime:    time.Unix(1350266320, 910238425),
    223 				ChangeTime: time.Unix(1350266320, 910238425),
    224 				AccessTime: time.Unix(1350266320, 910238425),
    225 				Typeflag:   TypeSymlink,
    226 				Linkname:   "123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100",
    227 			},
    228 		},
    229 	},
    230 	{
    231 		file: "testdata/nil-uid.tar", // golang.org/issue/5290
    232 		headers: []*Header{
    233 			{
    234 				Name:     "P1050238.JPG.log",
    235 				Mode:     0664,
    236 				Uid:      0,
    237 				Gid:      0,
    238 				Size:     14,
    239 				ModTime:  time.Unix(1365454838, 0),
    240 				Typeflag: TypeReg,
    241 				Linkname: "",
    242 				Uname:    "eyefi",
    243 				Gname:    "eyefi",
    244 				Devmajor: 0,
    245 				Devminor: 0,
    246 			},
    247 		},
    248 	},
    249 	{
    250 		file: "testdata/xattrs.tar",
    251 		headers: []*Header{
    252 			{
    253 				Name:       "small.txt",
    254 				Mode:       0644,
    255 				Uid:        1000,
    256 				Gid:        10,
    257 				Size:       5,
    258 				ModTime:    time.Unix(1386065770, 448252320),
    259 				Typeflag:   '0',
    260 				Uname:      "alex",
    261 				Gname:      "wheel",
    262 				AccessTime: time.Unix(1389782991, 419875220),
    263 				ChangeTime: time.Unix(1389782956, 794414986),
    264 				Xattrs: map[string]string{
    265 					"user.key":  "value",
    266 					"user.key2": "value2",
    267 					// Interestingly, selinux encodes the terminating null inside the xattr
    268 					"security.selinux": "unconfined_u:object_r:default_t:s0\x00",
    269 				},
    270 			},
    271 			{
    272 				Name:       "small2.txt",
    273 				Mode:       0644,
    274 				Uid:        1000,
    275 				Gid:        10,
    276 				Size:       11,
    277 				ModTime:    time.Unix(1386065770, 449252304),
    278 				Typeflag:   '0',
    279 				Uname:      "alex",
    280 				Gname:      "wheel",
    281 				AccessTime: time.Unix(1389782991, 419875220),
    282 				ChangeTime: time.Unix(1386065770, 449252304),
    283 				Xattrs: map[string]string{
    284 					"security.selinux": "unconfined_u:object_r:default_t:s0\x00",
    285 				},
    286 			},
    287 		},
    288 	},
    289 }
    290 
    291 func TestReader(t *testing.T) {
    292 testLoop:
    293 	for i, test := range untarTests {
    294 		f, err := os.Open(test.file)
    295 		if err != nil {
    296 			t.Errorf("test %d: Unexpected error: %v", i, err)
    297 			continue
    298 		}
    299 		defer f.Close()
    300 		tr := NewReader(f)
    301 		for j, header := range test.headers {
    302 			hdr, err := tr.Next()
    303 			if err != nil || hdr == nil {
    304 				t.Errorf("test %d, entry %d: Didn't get entry: %v", i, j, err)
    305 				f.Close()
    306 				continue testLoop
    307 			}
    308 			if !reflect.DeepEqual(*hdr, *header) {
    309 				t.Errorf("test %d, entry %d: Incorrect header:\nhave %+v\nwant %+v",
    310 					i, j, *hdr, *header)
    311 			}
    312 		}
    313 		hdr, err := tr.Next()
    314 		if err == io.EOF {
    315 			continue testLoop
    316 		}
    317 		if hdr != nil || err != nil {
    318 			t.Errorf("test %d: Unexpected entry or error: hdr=%v err=%v", i, hdr, err)
    319 		}
    320 	}
    321 }
    322 
    323 func TestPartialRead(t *testing.T) {
    324 	f, err := os.Open("testdata/gnu.tar")
    325 	if err != nil {
    326 		t.Fatalf("Unexpected error: %v", err)
    327 	}
    328 	defer f.Close()
    329 
    330 	tr := NewReader(f)
    331 
    332 	// Read the first four bytes; Next() should skip the last byte.
    333 	hdr, err := tr.Next()
    334 	if err != nil || hdr == nil {
    335 		t.Fatalf("Didn't get first file: %v", err)
    336 	}
    337 	buf := make([]byte, 4)
    338 	if _, err := io.ReadFull(tr, buf); err != nil {
    339 		t.Fatalf("Unexpected error: %v", err)
    340 	}
    341 	if expected := []byte("Kilt"); !bytes.Equal(buf, expected) {
    342 		t.Errorf("Contents = %v, want %v", buf, expected)
    343 	}
    344 
    345 	// Second file
    346 	hdr, err = tr.Next()
    347 	if err != nil || hdr == nil {
    348 		t.Fatalf("Didn't get second file: %v", err)
    349 	}
    350 	buf = make([]byte, 6)
    351 	if _, err := io.ReadFull(tr, buf); err != nil {
    352 		t.Fatalf("Unexpected error: %v", err)
    353 	}
    354 	if expected := []byte("Google"); !bytes.Equal(buf, expected) {
    355 		t.Errorf("Contents = %v, want %v", buf, expected)
    356 	}
    357 }
    358 
    359 func TestIncrementalRead(t *testing.T) {
    360 	test := gnuTarTest
    361 	f, err := os.Open(test.file)
    362 	if err != nil {
    363 		t.Fatalf("Unexpected error: %v", err)
    364 	}
    365 	defer f.Close()
    366 
    367 	tr := NewReader(f)
    368 
    369 	headers := test.headers
    370 	cksums := test.cksums
    371 	nread := 0
    372 
    373 	// loop over all files
    374 	for ; ; nread++ {
    375 		hdr, err := tr.Next()
    376 		if hdr == nil || err == io.EOF {
    377 			break
    378 		}
    379 
    380 		// check the header
    381 		if !reflect.DeepEqual(*hdr, *headers[nread]) {
    382 			t.Errorf("Incorrect header:\nhave %+v\nwant %+v",
    383 				*hdr, headers[nread])
    384 		}
    385 
    386 		// read file contents in little chunks EOF,
    387 		// checksumming all the way
    388 		h := md5.New()
    389 		rdbuf := make([]uint8, 8)
    390 		for {
    391 			nr, err := tr.Read(rdbuf)
    392 			if err == io.EOF {
    393 				break
    394 			}
    395 			if err != nil {
    396 				t.Errorf("Read: unexpected error %v\n", err)
    397 				break
    398 			}
    399 			h.Write(rdbuf[0:nr])
    400 		}
    401 		// verify checksum
    402 		have := fmt.Sprintf("%x", h.Sum(nil))
    403 		want := cksums[nread]
    404 		if want != have {
    405 			t.Errorf("Bad checksum on file %s:\nhave %+v\nwant %+v", hdr.Name, have, want)
    406 		}
    407 	}
    408 	if nread != len(headers) {
    409 		t.Errorf("Didn't process all files\nexpected: %d\nprocessed %d\n", len(headers), nread)
    410 	}
    411 }
    412 
    413 func TestNonSeekable(t *testing.T) {
    414 	test := gnuTarTest
    415 	f, err := os.Open(test.file)
    416 	if err != nil {
    417 		t.Fatalf("Unexpected error: %v", err)
    418 	}
    419 	defer f.Close()
    420 
    421 	type readerOnly struct {
    422 		io.Reader
    423 	}
    424 	tr := NewReader(readerOnly{f})
    425 	nread := 0
    426 
    427 	for ; ; nread++ {
    428 		_, err := tr.Next()
    429 		if err == io.EOF {
    430 			break
    431 		}
    432 		if err != nil {
    433 			t.Fatalf("Unexpected error: %v", err)
    434 		}
    435 	}
    436 
    437 	if nread != len(test.headers) {
    438 		t.Errorf("Didn't process all files\nexpected: %d\nprocessed %d\n", len(test.headers), nread)
    439 	}
    440 }
    441 
    442 func TestParsePAXHeader(t *testing.T) {
    443 	paxTests := [][3]string{
    444 		{"a", "a=name", "10 a=name\n"}, // Test case involving multiple acceptable lengths
    445 		{"a", "a=name", "9 a=name\n"},  // Test case involving multiple acceptable length
    446 		{"mtime", "mtime=1350244992.023960108", "30 mtime=1350244992.023960108\n"}}
    447 	for _, test := range paxTests {
    448 		key, expected, raw := test[0], test[1], test[2]
    449 		reader := bytes.NewReader([]byte(raw))
    450 		headers, err := parsePAX(reader)
    451 		if err != nil {
    452 			t.Errorf("Couldn't parse correctly formatted headers: %v", err)
    453 			continue
    454 		}
    455 		if strings.EqualFold(headers[key], expected) {
    456 			t.Errorf("mtime header incorrectly parsed: got %s, wanted %s", headers[key], expected)
    457 			continue
    458 		}
    459 		trailer := make([]byte, 100)
    460 		n, err := reader.Read(trailer)
    461 		if err != io.EOF || n != 0 {
    462 			t.Error("Buffer wasn't consumed")
    463 		}
    464 	}
    465 	badHeaderTests := [][]byte{
    466 		[]byte("3 somelongkey=\n"),
    467 		[]byte("50 tooshort=\n"),
    468 	}
    469 	for _, test := range badHeaderTests {
    470 		if _, err := parsePAX(bytes.NewReader(test)); err != ErrHeader {
    471 			t.Fatal("Unexpected success when parsing bad header")
    472 		}
    473 	}
    474 }
    475 
    476 func TestParsePAXTime(t *testing.T) {
    477 	// Some valid PAX time values
    478 	timestamps := map[string]time.Time{
    479 		"1350244992.023960108":  time.Unix(1350244992, 23960108), // The common case
    480 		"1350244992.02396010":   time.Unix(1350244992, 23960100), // Lower precision value
    481 		"1350244992.0239601089": time.Unix(1350244992, 23960108), // Higher precision value
    482 		"1350244992":            time.Unix(1350244992, 0),        // Low precision value
    483 	}
    484 	for input, expected := range timestamps {
    485 		ts, err := parsePAXTime(input)
    486 		if err != nil {
    487 			t.Fatal(err)
    488 		}
    489 		if !ts.Equal(expected) {
    490 			t.Fatalf("Time parsing failure %s %s", ts, expected)
    491 		}
    492 	}
    493 }
    494 
    495 func TestMergePAX(t *testing.T) {
    496 	hdr := new(Header)
    497 	// Test a string, integer, and time based value.
    498 	headers := map[string]string{
    499 		"path":  "a/b/c",
    500 		"uid":   "1000",
    501 		"mtime": "1350244992.023960108",
    502 	}
    503 	err := mergePAX(hdr, headers)
    504 	if err != nil {
    505 		t.Fatal(err)
    506 	}
    507 	want := &Header{
    508 		Name:    "a/b/c",
    509 		Uid:     1000,
    510 		ModTime: time.Unix(1350244992, 23960108),
    511 	}
    512 	if !reflect.DeepEqual(hdr, want) {
    513 		t.Errorf("incorrect merge: got %+v, want %+v", hdr, want)
    514 	}
    515 }
    516 
    517 func TestSparseEndToEnd(t *testing.T) {
    518 	test := sparseTarTest
    519 	f, err := os.Open(test.file)
    520 	if err != nil {
    521 		t.Fatalf("Unexpected error: %v", err)
    522 	}
    523 	defer f.Close()
    524 
    525 	tr := NewReader(f)
    526 
    527 	headers := test.headers
    528 	cksums := test.cksums
    529 	nread := 0
    530 
    531 	// loop over all files
    532 	for ; ; nread++ {
    533 		hdr, err := tr.Next()
    534 		if hdr == nil || err == io.EOF {
    535 			break
    536 		}
    537 
    538 		// check the header
    539 		if !reflect.DeepEqual(*hdr, *headers[nread]) {
    540 			t.Errorf("Incorrect header:\nhave %+v\nwant %+v",
    541 				*hdr, headers[nread])
    542 		}
    543 
    544 		// read and checksum the file data
    545 		h := md5.New()
    546 		_, err = io.Copy(h, tr)
    547 		if err != nil {
    548 			t.Fatalf("Unexpected error: %v", err)
    549 		}
    550 
    551 		// verify checksum
    552 		have := fmt.Sprintf("%x", h.Sum(nil))
    553 		want := cksums[nread]
    554 		if want != have {
    555 			t.Errorf("Bad checksum on file %s:\nhave %+v\nwant %+v", hdr.Name, have, want)
    556 		}
    557 	}
    558 	if nread != len(headers) {
    559 		t.Errorf("Didn't process all files\nexpected: %d\nprocessed %d\n", len(headers), nread)
    560 	}
    561 }
    562 
    563 type sparseFileReadTest struct {
    564 	sparseData []byte
    565 	sparseMap  []sparseEntry
    566 	realSize   int64
    567 	expected   []byte
    568 }
    569 
    570 var sparseFileReadTests = []sparseFileReadTest{
    571 	{
    572 		sparseData: []byte("abcde"),
    573 		sparseMap: []sparseEntry{
    574 			{offset: 0, numBytes: 2},
    575 			{offset: 5, numBytes: 3},
    576 		},
    577 		realSize: 8,
    578 		expected: []byte("ab\x00\x00\x00cde"),
    579 	},
    580 	{
    581 		sparseData: []byte("abcde"),
    582 		sparseMap: []sparseEntry{
    583 			{offset: 0, numBytes: 2},
    584 			{offset: 5, numBytes: 3},
    585 		},
    586 		realSize: 10,
    587 		expected: []byte("ab\x00\x00\x00cde\x00\x00"),
    588 	},
    589 	{
    590 		sparseData: []byte("abcde"),
    591 		sparseMap: []sparseEntry{
    592 			{offset: 1, numBytes: 3},
    593 			{offset: 6, numBytes: 2},
    594 		},
    595 		realSize: 8,
    596 		expected: []byte("\x00abc\x00\x00de"),
    597 	},
    598 	{
    599 		sparseData: []byte("abcde"),
    600 		sparseMap: []sparseEntry{
    601 			{offset: 1, numBytes: 3},
    602 			{offset: 6, numBytes: 2},
    603 		},
    604 		realSize: 10,
    605 		expected: []byte("\x00abc\x00\x00de\x00\x00"),
    606 	},
    607 	{
    608 		sparseData: []byte(""),
    609 		sparseMap:  nil,
    610 		realSize:   2,
    611 		expected:   []byte("\x00\x00"),
    612 	},
    613 }
    614 
    615 func TestSparseFileReader(t *testing.T) {
    616 	for i, test := range sparseFileReadTests {
    617 		r := bytes.NewReader(test.sparseData)
    618 		nb := int64(r.Len())
    619 		sfr := &sparseFileReader{
    620 			rfr: &regFileReader{r: r, nb: nb},
    621 			sp:  test.sparseMap,
    622 			pos: 0,
    623 			tot: test.realSize,
    624 		}
    625 		if sfr.numBytes() != nb {
    626 			t.Errorf("test %d: Before reading, sfr.numBytes() = %d, want %d", i, sfr.numBytes(), nb)
    627 		}
    628 		buf, err := ioutil.ReadAll(sfr)
    629 		if err != nil {
    630 			t.Errorf("test %d: Unexpected error: %v", i, err)
    631 		}
    632 		if e := test.expected; !bytes.Equal(buf, e) {
    633 			t.Errorf("test %d: Contents = %v, want %v", i, buf, e)
    634 		}
    635 		if sfr.numBytes() != 0 {
    636 			t.Errorf("test %d: After draining the reader, numBytes() was nonzero", i)
    637 		}
    638 	}
    639 }
    640 
    641 func TestSparseIncrementalRead(t *testing.T) {
    642 	sparseMap := []sparseEntry{{10, 2}}
    643 	sparseData := []byte("Go")
    644 	expected := "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00Go\x00\x00\x00\x00\x00\x00\x00\x00"
    645 
    646 	r := bytes.NewReader(sparseData)
    647 	nb := int64(r.Len())
    648 	sfr := &sparseFileReader{
    649 		rfr: &regFileReader{r: r, nb: nb},
    650 		sp:  sparseMap,
    651 		pos: 0,
    652 		tot: int64(len(expected)),
    653 	}
    654 
    655 	// We'll read the data 6 bytes at a time, with a hole of size 10 at
    656 	// the beginning and one of size 8 at the end.
    657 	var outputBuf bytes.Buffer
    658 	buf := make([]byte, 6)
    659 	for {
    660 		n, err := sfr.Read(buf)
    661 		if err == io.EOF {
    662 			break
    663 		}
    664 		if err != nil {
    665 			t.Errorf("Read: unexpected error %v\n", err)
    666 		}
    667 		if n > 0 {
    668 			_, err := outputBuf.Write(buf[:n])
    669 			if err != nil {
    670 				t.Errorf("Write: unexpected error %v\n", err)
    671 			}
    672 		}
    673 	}
    674 	got := outputBuf.String()
    675 	if got != expected {
    676 		t.Errorf("Contents = %v, want %v", got, expected)
    677 	}
    678 }
    679 
    680 func TestReadGNUSparseMap0x1(t *testing.T) {
    681 	headers := map[string]string{
    682 		paxGNUSparseNumBlocks: "4",
    683 		paxGNUSparseMap:       "0,5,10,5,20,5,30,5",
    684 	}
    685 	expected := []sparseEntry{
    686 		{offset: 0, numBytes: 5},
    687 		{offset: 10, numBytes: 5},
    688 		{offset: 20, numBytes: 5},
    689 		{offset: 30, numBytes: 5},
    690 	}
    691 
    692 	sp, err := readGNUSparseMap0x1(headers)
    693 	if err != nil {
    694 		t.Errorf("Unexpected error: %v", err)
    695 	}
    696 	if !reflect.DeepEqual(sp, expected) {
    697 		t.Errorf("Incorrect sparse map: got %v, wanted %v", sp, expected)
    698 	}
    699 }
    700 
    701 func TestReadGNUSparseMap1x0(t *testing.T) {
    702 	// This test uses lots of holes so the sparse header takes up more than two blocks
    703 	numEntries := 100
    704 	expected := make([]sparseEntry, 0, numEntries)
    705 	sparseMap := new(bytes.Buffer)
    706 
    707 	fmt.Fprintf(sparseMap, "%d\n", numEntries)
    708 	for i := 0; i < numEntries; i++ {
    709 		offset := int64(2048 * i)
    710 		numBytes := int64(1024)
    711 		expected = append(expected, sparseEntry{offset: offset, numBytes: numBytes})
    712 		fmt.Fprintf(sparseMap, "%d\n%d\n", offset, numBytes)
    713 	}
    714 
    715 	// Make the header the smallest multiple of blockSize that fits the sparseMap
    716 	headerBlocks := (sparseMap.Len() + blockSize - 1) / blockSize
    717 	bufLen := blockSize * headerBlocks
    718 	buf := make([]byte, bufLen)
    719 	copy(buf, sparseMap.Bytes())
    720 
    721 	// Get an reader to read the sparse map
    722 	r := bytes.NewReader(buf)
    723 
    724 	// Read the sparse map
    725 	sp, err := readGNUSparseMap1x0(r)
    726 	if err != nil {
    727 		t.Errorf("Unexpected error: %v", err)
    728 	}
    729 	if !reflect.DeepEqual(sp, expected) {
    730 		t.Errorf("Incorrect sparse map: got %v, wanted %v", sp, expected)
    731 	}
    732 }
    733 
    734 func TestUninitializedRead(t *testing.T) {
    735 	test := gnuTarTest
    736 	f, err := os.Open(test.file)
    737 	if err != nil {
    738 		t.Fatalf("Unexpected error: %v", err)
    739 	}
    740 	defer f.Close()
    741 
    742 	tr := NewReader(f)
    743 	_, err = tr.Read([]byte{})
    744 	if err == nil || err != io.EOF {
    745 		t.Errorf("Unexpected error: %v, wanted %v", err, io.EOF)
    746 	}
    747 
    748 }
    749 
    750 // Negative header size should not cause panic.
    751 // Issues 10959 and 10960.
    752 func TestNegativeHdrSize(t *testing.T) {
    753 	f, err := os.Open("testdata/neg-size.tar")
    754 	if err != nil {
    755 		t.Fatal(err)
    756 	}
    757 	defer f.Close()
    758 	r := NewReader(f)
    759 	_, err = r.Next()
    760 	if err != ErrHeader {
    761 		t.Error("want ErrHeader, got", err)
    762 	}
    763 	io.Copy(ioutil.Discard, r)
    764 }
    765 
    766 // This used to hang in (*sparseFileReader).readHole due to missing
    767 // verification of sparse offsets against file size.
    768 func TestIssue10968(t *testing.T) {
    769 	f, err := os.Open("testdata/issue10968.tar")
    770 	if err != nil {
    771 		t.Fatal(err)
    772 	}
    773 	defer f.Close()
    774 	r := NewReader(f)
    775 	_, err = r.Next()
    776 	if err != nil {
    777 		t.Fatal(err)
    778 	}
    779 	_, err = io.Copy(ioutil.Discard, r)
    780 	if err != io.ErrUnexpectedEOF {
    781 		t.Fatalf("expected %q, got %q", io.ErrUnexpectedEOF, err)
    782 	}
    783 }
    784 
    785 // Do not panic if there are errors in header blocks after the pax header.
    786 // Issue 11169
    787 func TestIssue11169(t *testing.T) {
    788 	f, err := os.Open("testdata/issue11169.tar")
    789 	if err != nil {
    790 		t.Fatal(err)
    791 	}
    792 	defer f.Close()
    793 	r := NewReader(f)
    794 	_, err = r.Next()
    795 	if err == nil {
    796 		t.Fatal("Unexpected success")
    797 	}
    798 }
    799