func TestReadTruncation(t *testing.T) {
	var ss []string
	for _, p := range []string{
		"testdata/gnu.tar",
		"testdata/ustar-file-reg.tar",
		"testdata/pax-path-hdr.tar",
		"testdata/sparse-formats.tar",
	} {
		buf, err := ioutil.ReadFile(p)
		if err != nil {
			t.Fatalf("unexpected error: %v", err)
		}
		ss = append(ss, string(buf))
	}
	data1, data2, pax, sparse := ss[0], ss[1], ss[2], ss[3]
	data2 += strings.Repeat("\x00", 10*512)
	trash := strings.Repeat("garbage ", 64)  
	vectors := []struct {
		input string  
		cnt   int     
		err   error   
	}{
		{"", 0, io.EOF},  
		{data1[:511], 0, io.ErrUnexpectedEOF},
		{data1[:512], 1, io.ErrUnexpectedEOF},
		{data1[:1024], 1, io.EOF},
		{data1[:1536], 2, io.ErrUnexpectedEOF},
		{data1[:2048], 2, io.EOF},
		{data1, 2, io.EOF},
		{data1[:2048] + data2[:1536], 3, io.EOF},
		{data2[:511], 0, io.ErrUnexpectedEOF},
		{data2[:512], 1, io.ErrUnexpectedEOF},
		{data2[:1195], 1, io.ErrUnexpectedEOF},
		{data2[:1196], 1, io.EOF},  
		{data2[:1200], 1, io.EOF},
		{data2[:1535], 1, io.EOF},
		{data2[:1536], 1, io.EOF},  
		{data2[:1536] + trash[:1], 1, io.ErrUnexpectedEOF},
		{data2[:1536] + trash[:511], 1, io.ErrUnexpectedEOF},
		{data2[:1536] + trash, 1, ErrHeader},
		{data2[:2048], 1, io.EOF},  
		{data2[:2048] + trash[:1], 1, io.ErrUnexpectedEOF},
		{data2[:2048] + trash[:511], 1, io.ErrUnexpectedEOF},
		{data2[:2048] + trash, 1, ErrHeader},
		{data2[:2560], 1, io.EOF},  
		{data2[:2560] + trash[:1], 1, io.EOF},
		{data2[:2560] + trash[:511], 1, io.EOF},
		{data2[:2560] + trash, 1, io.EOF},
		{data2[:3072], 1, io.EOF},
		{pax, 0, io.EOF},  
		{pax + trash[:1], 0, io.ErrUnexpectedEOF},
		{pax + trash[:511], 0, io.ErrUnexpectedEOF},
		{sparse[:511], 0, io.ErrUnexpectedEOF},
		{sparse[:512], 0, io.ErrUnexpectedEOF},
		{sparse[:3584], 1, io.EOF},
		{sparse[:9200], 1, io.EOF},  
		{sparse[:9216], 1, io.EOF},
		{sparse[:9728], 2, io.ErrUnexpectedEOF},
		{sparse[:10240], 2, io.EOF},
		{sparse[:11264], 2, io.ErrUnexpectedEOF},
		{sparse, 5, io.EOF},
		{sparse + trash, 5, io.EOF},
	}
	for i, v := range vectors {
		for j := 0; j < 6; j++ {
			var tr *Reader
			var s1, s2 string
			switch j {
			case 0:
				tr = NewReader(&reader{strings.NewReader(v.input)})
				s1, s2 = "io.Reader", "auto"
			case 1:
				tr = NewReader(&reader{strings.NewReader(v.input)})
				s1, s2 = "io.Reader", "manual"
			case 2:
				tr = NewReader(&readSeeker{strings.NewReader(v.input)})
				s1, s2 = "io.ReadSeeker", "auto"
			case 3:
				tr = NewReader(&readSeeker{strings.NewReader(v.input)})
				s1, s2 = "io.ReadSeeker", "manual"
			case 4:
				tr = NewReader(&readBadSeeker{strings.NewReader(v.input)})
				s1, s2 = "ReadBadSeeker", "auto"
			case 5:
				tr = NewReader(&readBadSeeker{strings.NewReader(v.input)})
				s1, s2 = "ReadBadSeeker", "manual"
			}
			var cnt int
			var err error
			for {
				if _, err = tr.Next(); err != nil {
					break
				}
				cnt++
				if s2 == "manual" {
					if _, err = tr.writeTo(ioutil.Discard); err != nil {
						break
					}
				}
			}
			if err != v.err {
				t.Errorf("test %d, NewReader(%s) with %s discard: got %v, want %v",
					i, s1, s2, err, v.err)
			}
			if cnt != v.cnt {
				t.Errorf("test %d, NewReader(%s) with %s discard: got %d headers, want %d headers",
					i, s1, s2, cnt, v.cnt)
			}
		}
	}
}
TestReadTruncation test the ending condition on various truncated files and
that truncated files are still detected even if the underlying io.Reader
satisfies io.Seeker.
package tar
import (
	"bytes"
	"crypto/md5"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"math"
	"os"
	"path"
	"reflect"
	"strconv"
	"strings"
	"testing"
	"time"
)
func TestReader(t *testing.T) {
	vectors := []struct {
		file    string     
		headers []*Header  
		chksums []string   
		err     error      
	}{{
		file: "testdata/gnu.tar",
		headers: []*Header{{
			Name:     "small.txt",
			Mode:     0640,
			Uid:      73025,
			Gid:      5000,
			Size:     5,
			ModTime:  time.Unix(1244428340, 0),
			Typeflag: '0',
			Uname:    "dsymonds",
			Gname:    "eng",
			Format:   FormatGNU,
		}, {
			Name:     "small2.txt",
			Mode:     0640,
			Uid:      73025,
			Gid:      5000,
			Size:     11,
			ModTime:  time.Unix(1244436044, 0),
			Typeflag: '0',
			Uname:    "dsymonds",
			Gname:    "eng",
			Format:   FormatGNU,
		}},
		chksums: []string{
			"e38b27eaccb4391bdec553a7f3ae6b2f",
			"c65bd2e50a56a2138bf1716f2fd56fe9",
		},
	}, {
		file: "testdata/sparse-formats.tar",
		headers: []*Header{{
			Name:     "sparse-gnu",
			Mode:     420,
			Uid:      1000,
			Gid:      1000,
			Size:     200,
			ModTime:  time.Unix(1392395740, 0),
			Typeflag: 0x53,
			Linkname: "",
			Uname:    "david",
			Gname:    "david",
			Devmajor: 0,
			Devminor: 0,
			Format:   FormatGNU,
		}, {
			Name:     "sparse-posix-0.0",
			Mode:     420,
			Uid:      1000,
			Gid:      1000,
			Size:     200,
			ModTime:  time.Unix(1392342187, 0),
			Typeflag: 0x30,
			Linkname: "",
			Uname:    "david",
			Gname:    "david",
			Devmajor: 0,
			Devminor: 0,
			PAXRecords: map[string]string{
				"GNU.sparse.size":      "200",
				"GNU.sparse.numblocks": "95",
				"GNU.sparse.map":       "1,1,3,1,5,1,7,1,9,1,11,1,13,1,15,1,17,1,19,1,21,1,23,1,25,1,27,1,29,1,31,1,33,1,35,1,37,1,39,1,41,1,43,1,45,1,47,1,49,1,51,1,53,1,55,1,57,1,59,1,61,1,63,1,65,1,67,1,69,1,71,1,73,1,75,1,77,1,79,1,81,1,83,1,85,1,87,1,89,1,91,1,93,1,95,1,97,1,99,1,101,1,103,1,105,1,107,1,109,1,111,1,113,1,115,1,117,1,119,1,121,1,123,1,125,1,127,1,129,1,131,1,133,1,135,1,137,1,139,1,141,1,143,1,145,1,147,1,149,1,151,1,153,1,155,1,157,1,159,1,161,1,163,1,165,1,167,1,169,1,171,1,173,1,175,1,177,1,179,1,181,1,183,1,185,1,187,1,189,1",
			},
			Format: FormatPAX,
		}, {
			Name:     "sparse-posix-0.1",
			Mode:     420,
			Uid:      1000,
			Gid:      1000,
			Size:     200,
			ModTime:  time.Unix(1392340456, 0),
			Typeflag: 0x30,
			Linkname: "",
			Uname:    "david",
			Gname:    "david",
			Devmajor: 0,
			Devminor: 0,
			PAXRecords: map[string]string{
				"GNU.sparse.size":      "200",
				"GNU.sparse.numblocks": "95",
				"GNU.sparse.map":       "1,1,3,1,5,1,7,1,9,1,11,1,13,1,15,1,17,1,19,1,21,1,23,1,25,1,27,1,29,1,31,1,33,1,35,1,37,1,39,1,41,1,43,1,45,1,47,1,49,1,51,1,53,1,55,1,57,1,59,1,61,1,63,1,65,1,67,1,69,1,71,1,73,1,75,1,77,1,79,1,81,1,83,1,85,1,87,1,89,1,91,1,93,1,95,1,97,1,99,1,101,1,103,1,105,1,107,1,109,1,111,1,113,1,115,1,117,1,119,1,121,1,123,1,125,1,127,1,129,1,131,1,133,1,135,1,137,1,139,1,141,1,143,1,145,1,147,1,149,1,151,1,153,1,155,1,157,1,159,1,161,1,163,1,165,1,167,1,169,1,171,1,173,1,175,1,177,1,179,1,181,1,183,1,185,1,187,1,189,1",
				"GNU.sparse.name":      "sparse-posix-0.1",
			},
			Format: FormatPAX,
		}, {
			Name:     "sparse-posix-1.0",
			Mode:     420,
			Uid:      1000,
			Gid:      1000,
			Size:     200,
			ModTime:  time.Unix(1392337404, 0),
			Typeflag: 0x30,
			Linkname: "",
			Uname:    "david",
			Gname:    "david",
			Devmajor: 0,
			Devminor: 0,
			PAXRecords: map[string]string{
				"GNU.sparse.major":    "1",
				"GNU.sparse.minor":    "0",
				"GNU.sparse.realsize": "200",
				"GNU.sparse.name":     "sparse-posix-1.0",
			},
			Format: FormatPAX,
		}, {
			Name:     "end",
			Mode:     420,
			Uid:      1000,
			Gid:      1000,
			Size:     4,
			ModTime:  time.Unix(1392398319, 0),
			Typeflag: 0x30,
			Linkname: "",
			Uname:    "david",
			Gname:    "david",
			Devmajor: 0,
			Devminor: 0,
			Format:   FormatGNU,
		}},
		chksums: []string{
			"6f53234398c2449fe67c1812d993012f",
			"6f53234398c2449fe67c1812d993012f",
			"6f53234398c2449fe67c1812d993012f",
			"6f53234398c2449fe67c1812d993012f",
			"b0061974914468de549a2af8ced10316",
		},
	}, {
		file: "testdata/star.tar",
		headers: []*Header{{
			Name:       "small.txt",
			Mode:       0640,
			Uid:        73025,
			Gid:        5000,
			Size:       5,
			ModTime:    time.Unix(1244592783, 0),
			Typeflag:   '0',
			Uname:      "dsymonds",
			Gname:      "eng",
			AccessTime: time.Unix(1244592783, 0),
			ChangeTime: time.Unix(1244592783, 0),
		}, {
			Name:       "small2.txt",
			Mode:       0640,
			Uid:        73025,
			Gid:        5000,
			Size:       11,
			ModTime:    time.Unix(1244592783, 0),
			Typeflag:   '0',
			Uname:      "dsymonds",
			Gname:      "eng",
			AccessTime: time.Unix(1244592783, 0),
			ChangeTime: time.Unix(1244592783, 0),
		}},
	}, {
		file: "testdata/v7.tar",
		headers: []*Header{{
			Name:     "small.txt",
			Mode:     0444,
			Uid:      73025,
			Gid:      5000,
			Size:     5,
			ModTime:  time.Unix(1244593104, 0),
			Typeflag: '0',
		}, {
			Name:     "small2.txt",
			Mode:     0444,
			Uid:      73025,
			Gid:      5000,
			Size:     11,
			ModTime:  time.Unix(1244593104, 0),
			Typeflag: '0',
		}},
	}, {
		file: "testdata/pax.tar",
		headers: []*Header{{
			Name:       "a/123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100",
			Mode:       0664,
			Uid:        1000,
			Gid:        1000,
			Uname:      "shane",
			Gname:      "shane",
			Size:       7,
			ModTime:    time.Unix(1350244992, 23960108),
			ChangeTime: time.Unix(1350244992, 23960108),
			AccessTime: time.Unix(1350244992, 23960108),
			Typeflag:   TypeReg,
			PAXRecords: map[string]string{
				"path":  "a/123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100",
				"mtime": "1350244992.023960108",
				"atime": "1350244992.023960108",
				"ctime": "1350244992.023960108",
			},
			Format: FormatPAX,
		}, {
			Name:       "a/b",
			Mode:       0777,
			Uid:        1000,
			Gid:        1000,
			Uname:      "shane",
			Gname:      "shane",
			Size:       0,
			ModTime:    time.Unix(1350266320, 910238425),
			ChangeTime: time.Unix(1350266320, 910238425),
			AccessTime: time.Unix(1350266320, 910238425),
			Typeflag:   TypeSymlink,
			Linkname:   "123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100",
			PAXRecords: map[string]string{
				"linkpath": "123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100",
				"mtime":    "1350266320.910238425",
				"atime":    "1350266320.910238425",
				"ctime":    "1350266320.910238425",
			},
			Format: FormatPAX,
		}},
	}, {
		file: "testdata/pax-bad-hdr-file.tar",
		err:  ErrHeader,
	}, {
		file: "testdata/pax-bad-mtime-file.tar",
		err:  ErrHeader,
	}, {
		file: "testdata/pax-pos-size-file.tar",
		headers: []*Header{{
			Name:     "foo",
			Mode:     0640,
			Uid:      319973,
			Gid:      5000,
			Size:     999,
			ModTime:  time.Unix(1442282516, 0),
			Typeflag: '0',
			Uname:    "joetsai",
			Gname:    "eng",
			PAXRecords: map[string]string{
				"size": "000000000000000000000999",
			},
			Format: FormatPAX,
		}},
		chksums: []string{
			"0afb597b283fe61b5d4879669a350556",
		},
	}, {
		file: "testdata/pax-records.tar",
		headers: []*Header{{
			Typeflag: TypeReg,
			Name:     "file",
			Uname:    strings.Repeat("long", 10),
			ModTime:  time.Unix(0, 0),
			PAXRecords: map[string]string{
				"GOLANG.pkg": "tar",
				"comment":    "Hello, 世界",
				"uname":      strings.Repeat("long", 10),
			},
			Format: FormatPAX,
		}},
	}, {
		file: "testdata/pax-global-records.tar",
		headers: []*Header{{
			Typeflag:   TypeXGlobalHeader,
			Name:       "global1",
			PAXRecords: map[string]string{"path": "global1", "mtime": "1500000000.0"},
			Format:     FormatPAX,
		}, {
			Typeflag: TypeReg,
			Name:     "file1",
			ModTime:  time.Unix(0, 0),
			Format:   FormatUSTAR,
		}, {
			Typeflag:   TypeReg,
			Name:       "file2",
			PAXRecords: map[string]string{"path": "file2"},
			ModTime:    time.Unix(0, 0),
			Format:     FormatPAX,
		}, {
			Typeflag:   TypeXGlobalHeader,
			Name:       "GlobalHead.0.0",
			PAXRecords: map[string]string{"path": ""},
			Format:     FormatPAX,
		}, {
			Typeflag: TypeReg,
			Name:     "file3",
			ModTime:  time.Unix(0, 0),
			Format:   FormatUSTAR,
		}, {
			Typeflag:   TypeReg,
			Name:       "file4",
			ModTime:    time.Unix(1400000000, 0),
			PAXRecords: map[string]string{"mtime": "1400000000"},
			Format:     FormatPAX,
		}},
	}, {
		file: "testdata/nil-uid.tar",  
		headers: []*Header{{
			Name:     "P1050238.JPG.log",
			Mode:     0664,
			Uid:      0,
			Gid:      0,
			Size:     14,
			ModTime:  time.Unix(1365454838, 0),
			Typeflag: TypeReg,
			Linkname: "",
			Uname:    "eyefi",
			Gname:    "eyefi",
			Devmajor: 0,
			Devminor: 0,
			Format:   FormatGNU,
		}},
	}, {
		file: "testdata/xattrs.tar",
		headers: []*Header{{
			Name:       "small.txt",
			Mode:       0644,
			Uid:        1000,
			Gid:        10,
			Size:       5,
			ModTime:    time.Unix(1386065770, 448252320),
			Typeflag:   '0',
			Uname:      "alex",
			Gname:      "wheel",
			AccessTime: time.Unix(1389782991, 419875220),
			ChangeTime: time.Unix(1389782956, 794414986),
			Xattrs: map[string]string{
				"user.key":  "value",
				"user.key2": "value2",
				"security.selinux": "unconfined_u:object_r:default_t:s0\x00",
			},
			PAXRecords: map[string]string{
				"mtime":                         "1386065770.44825232",
				"atime":                         "1389782991.41987522",
				"ctime":                         "1389782956.794414986",
				"SCHILY.xattr.user.key":         "value",
				"SCHILY.xattr.user.key2":        "value2",
				"SCHILY.xattr.security.selinux": "unconfined_u:object_r:default_t:s0\x00",
			},
			Format: FormatPAX,
		}, {
			Name:       "small2.txt",
			Mode:       0644,
			Uid:        1000,
			Gid:        10,
			Size:       11,
			ModTime:    time.Unix(1386065770, 449252304),
			Typeflag:   '0',
			Uname:      "alex",
			Gname:      "wheel",
			AccessTime: time.Unix(1389782991, 419875220),
			ChangeTime: time.Unix(1386065770, 449252304),
			Xattrs: map[string]string{
				"security.selinux": "unconfined_u:object_r:default_t:s0\x00",
			},
			PAXRecords: map[string]string{
				"mtime":                         "1386065770.449252304",
				"atime":                         "1389782991.41987522",
				"ctime":                         "1386065770.449252304",
				"SCHILY.xattr.security.selinux": "unconfined_u:object_r:default_t:s0\x00",
			},
			Format: FormatPAX,
		}},
	}, {
		file: "testdata/gnu-multi-hdrs.tar",
		headers: []*Header{{
			Name:     "GNU2/GNU2/long-path-name",
			Linkname: "GNU4/GNU4/long-linkpath-name",
			ModTime:  time.Unix(0, 0),
			Typeflag: '2',
			Format:   FormatGNU,
		}},
	}, {
		file: "testdata/gnu-incremental.tar",
		headers: []*Header{{
			Name:       "test2/",
			Mode:       16877,
			Uid:        1000,
			Gid:        1000,
			Size:       14,
			ModTime:    time.Unix(1441973427, 0),
			Typeflag:   'D',
			Uname:      "rawr",
			Gname:      "dsnet",
			AccessTime: time.Unix(1441974501, 0),
			ChangeTime: time.Unix(1441973436, 0),
			Format:     FormatGNU,
		}, {
			Name:       "test2/foo",
			Mode:       33188,
			Uid:        1000,
			Gid:        1000,
			Size:       64,
			ModTime:    time.Unix(1441973363, 0),
			Typeflag:   '0',
			Uname:      "rawr",
			Gname:      "dsnet",
			AccessTime: time.Unix(1441974501, 0),
			ChangeTime: time.Unix(1441973436, 0),
			Format:     FormatGNU,
		}, {
			Name:       "test2/sparse",
			Mode:       33188,
			Uid:        1000,
			Gid:        1000,
			Size:       536870912,
			ModTime:    time.Unix(1441973427, 0),
			Typeflag:   'S',
			Uname:      "rawr",
			Gname:      "dsnet",
			AccessTime: time.Unix(1441991948, 0),
			ChangeTime: time.Unix(1441973436, 0),
			Format:     FormatGNU,
		}},
	}, {
		file: "testdata/pax-multi-hdrs.tar",
		headers: []*Header{{
			Name:     "bar",
			Linkname: "PAX4/PAX4/long-linkpath-name",
			ModTime:  time.Unix(0, 0),
			Typeflag: '2',
			PAXRecords: map[string]string{
				"linkpath": "PAX4/PAX4/long-linkpath-name",
			},
			Format: FormatPAX,
		}},
	}, {
		file: "testdata/gnu-long-nul.tar",
		headers: []*Header{{
			Name:     "0123456789",
			Mode:     0644,
			Uid:      1000,
			Gid:      1000,
			ModTime:  time.Unix(1486082191, 0),
			Typeflag: '0',
			Uname:    "rawr",
			Gname:    "dsnet",
			Format:   FormatGNU,
		}},
	}, {
		file: "testdata/gnu-utf8.tar",
		headers: []*Header{{
			Name: "☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹",
			Mode: 0644,
			Uid:  1000, Gid: 1000,
			ModTime:  time.Unix(0, 0),
			Typeflag: '0',
			Uname:    "☺",
			Gname:    "⚹",
			Format:   FormatGNU,
		}},
	}, {
		file: "testdata/gnu-not-utf8.tar",
		headers: []*Header{{
			Name:     "hi\x80\x81\x82\x83bye",
			Mode:     0644,
			Uid:      1000,
			Gid:      1000,
			ModTime:  time.Unix(0, 0),
			Typeflag: '0',
			Uname:    "rawr",
			Gname:    "dsnet",
			Format:   FormatGNU,
		}},
	}, {
		file: "testdata/pax-nul-xattrs.tar",
		err:  ErrHeader,
	}, {
		file: "testdata/pax-nul-path.tar",
		err:  ErrHeader,
	}, {
		file: "testdata/neg-size.tar",
		err:  ErrHeader,
	}, {
		file: "testdata/issue10968.tar",
		err:  ErrHeader,
	}, {
		file: "testdata/issue11169.tar",
		err:  ErrHeader,
	}, {
		file: "testdata/issue12435.tar",
		err:  ErrHeader,
	}, {
		file: "testdata/invalid-go17.tar",
		headers: []*Header{{
			Name:     "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/foo",
			Uid:      010000000,
			ModTime:  time.Unix(0, 0),
			Typeflag: '0',
		}},
	}, {
		file: "testdata/ustar-file-devs.tar",
		headers: []*Header{{
			Name:     "file",
			Mode:     0644,
			Typeflag: '0',
			ModTime:  time.Unix(0, 0),
			Devmajor: 1,
			Devminor: 1,
			Format:   FormatUSTAR,
		}},
	}, {
		file: "testdata/gnu-nil-sparse-data.tar",
		headers: []*Header{{
			Name:     "sparse.db",
			Typeflag: TypeGNUSparse,
			Size:     1000,
			ModTime:  time.Unix(0, 0),
			Format:   FormatGNU,
		}},
	}, {
		file: "testdata/gnu-nil-sparse-hole.tar",
		headers: []*Header{{
			Name:     "sparse.db",
			Typeflag: TypeGNUSparse,
			Size:     1000,
			ModTime:  time.Unix(0, 0),
			Format:   FormatGNU,
		}},
	}, {
		file: "testdata/pax-nil-sparse-data.tar",
		headers: []*Header{{
			Name:     "sparse.db",
			Typeflag: TypeReg,
			Size:     1000,
			ModTime:  time.Unix(0, 0),
			PAXRecords: map[string]string{
				"size":                "1512",
				"GNU.sparse.major":    "1",
				"GNU.sparse.minor":    "0",
				"GNU.sparse.realsize": "1000",
				"GNU.sparse.name":     "sparse.db",
			},
			Format: FormatPAX,
		}},
	}, {
		file: "testdata/pax-nil-sparse-hole.tar",
		headers: []*Header{{
			Name:     "sparse.db",
			Typeflag: TypeReg,
			Size:     1000,
			ModTime:  time.Unix(0, 0),
			PAXRecords: map[string]string{
				"size":                "512",
				"GNU.sparse.major":    "1",
				"GNU.sparse.minor":    "0",
				"GNU.sparse.realsize": "1000",
				"GNU.sparse.name":     "sparse.db",
			},
			Format: FormatPAX,
		}},
	}, {
		file: "testdata/trailing-slash.tar",
		headers: []*Header{{
			Typeflag: TypeDir,
			Name:     strings.Repeat("123456789/", 30),
			ModTime:  time.Unix(0, 0),
			PAXRecords: map[string]string{
				"path": strings.Repeat("123456789/", 30),
			},
			Format: FormatPAX,
		}},
	}}
	for _, v := range vectors {
		t.Run(path.Base(v.file), func(t *testing.T) {
			f, err := os.Open(v.file)
			if err != nil {
				t.Fatalf("unexpected error: %v", err)
			}
			defer f.Close()
			var (
				tr      = NewReader(f)
				hdrs    []*Header
				chksums []string
				rdbuf   = make([]byte, 8)
			)
			for {
				var hdr *Header
				hdr, err = tr.Next()
				if err != nil {
					if err == io.EOF {
						err = nil  
					}
					break
				}
				hdrs = append(hdrs, hdr)
				if v.chksums == nil {
					continue
				}
				h := md5.New()
				_, err = io.CopyBuffer(h, tr, rdbuf)  
				if err != nil {
					break
				}
				chksums = append(chksums, fmt.Sprintf("%x", h.Sum(nil)))
			}
			for i, hdr := range hdrs {
				if i >= len(v.headers) {
					t.Fatalf("entry %d: unexpected header:\ngot %+v", i, *hdr)
					continue
				}
				if !reflect.DeepEqual(*hdr, *v.headers[i]) {
					t.Fatalf("entry %d: incorrect header:\ngot  %+v\nwant %+v", i, *hdr, *v.headers[i])
				}
			}
			if len(hdrs) != len(v.headers) {
				t.Fatalf("got %d headers, want %d headers", len(hdrs), len(v.headers))
			}
			for i, sum := range chksums {
				if i >= len(v.chksums) {
					t.Fatalf("entry %d: unexpected sum: got %s", i, sum)
					continue
				}
				if sum != v.chksums[i] {
					t.Fatalf("entry %d: incorrect checksum: got %s, want %s", i, sum, v.chksums[i])
				}
			}
			if err != v.err {
				t.Fatalf("unexpected error: got %v, want %v", err, v.err)
			}
			f.Close()
		})
	}
}
func TestPartialRead(t *testing.T) {
	type testCase struct {
		cnt    int     
		output string  
	}
	vectors := []struct {
		file  string
		cases []testCase
	}{{
		file: "testdata/gnu.tar",
		cases: []testCase{
			{4, "Kilt"},
			{6, "Google"},
		},
	}, {
		file: "testdata/sparse-formats.tar",
		cases: []testCase{
			{2, "\x00G"},
			{4, "\x00G\x00o"},
			{6, "\x00G\x00o\x00G"},
			{8, "\x00G\x00o\x00G\x00o"},
			{4, "end\n"},
		},
	}}
	for _, v := range vectors {
		t.Run(path.Base(v.file), func(t *testing.T) {
			f, err := os.Open(v.file)
			if err != nil {
				t.Fatalf("Open() error: %v", err)
			}
			defer f.Close()
			tr := NewReader(f)
			for i, tc := range v.cases {
				hdr, err := tr.Next()
				if err != nil || hdr == nil {
					t.Fatalf("entry %d, Next(): got %v, want %v", i, err, nil)
				}
				buf := make([]byte, tc.cnt)
				if _, err := io.ReadFull(tr, buf); err != nil {
					t.Fatalf("entry %d, ReadFull(): got %v, want %v", i, err, nil)
				}
				if string(buf) != tc.output {
					t.Fatalf("entry %d, ReadFull(): got %q, want %q", i, string(buf), tc.output)
				}
			}
			if _, err := tr.Next(); err != io.EOF {
				t.Fatalf("Next(): got %v, want EOF", err)
			}
		})
	}
}
func TestUninitializedRead(t *testing.T) {
	f, err := os.Open("testdata/gnu.tar")
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	defer f.Close()
	tr := NewReader(f)
	_, err = tr.Read([]byte{})
	if err == nil || err != io.EOF {
		t.Errorf("Unexpected error: %v, wanted %v", err, io.EOF)
	}
}
type reader struct{ io.Reader }
type readSeeker struct{ io.ReadSeeker }
type readBadSeeker struct{ io.ReadSeeker }
func (rbs *readBadSeeker) Seek(int64, int) (int64, error) { return 0, fmt.Errorf("illegal seek") }