github.com/bgentry/go@v0.0.0-20150121062915-6cf5a733d54d/src/archive/tar/reader_test.go (about)

     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  	badHeader := bytes.NewReader([]byte("3 somelongkey="))
   466  	if _, err := parsePAX(badHeader); err != ErrHeader {
   467  		t.Fatal("Unexpected success when parsing bad header")
   468  	}
   469  }
   470  
   471  func TestParsePAXTime(t *testing.T) {
   472  	// Some valid PAX time values
   473  	timestamps := map[string]time.Time{
   474  		"1350244992.023960108":  time.Unix(1350244992, 23960108), // The common case
   475  		"1350244992.02396010":   time.Unix(1350244992, 23960100), // Lower precision value
   476  		"1350244992.0239601089": time.Unix(1350244992, 23960108), // Higher precision value
   477  		"1350244992":            time.Unix(1350244992, 0),        // Low precision value
   478  	}
   479  	for input, expected := range timestamps {
   480  		ts, err := parsePAXTime(input)
   481  		if err != nil {
   482  			t.Fatal(err)
   483  		}
   484  		if !ts.Equal(expected) {
   485  			t.Fatalf("Time parsing failure %s %s", ts, expected)
   486  		}
   487  	}
   488  }
   489  
   490  func TestMergePAX(t *testing.T) {
   491  	hdr := new(Header)
   492  	// Test a string, integer, and time based value.
   493  	headers := map[string]string{
   494  		"path":  "a/b/c",
   495  		"uid":   "1000",
   496  		"mtime": "1350244992.023960108",
   497  	}
   498  	err := mergePAX(hdr, headers)
   499  	if err != nil {
   500  		t.Fatal(err)
   501  	}
   502  	want := &Header{
   503  		Name:    "a/b/c",
   504  		Uid:     1000,
   505  		ModTime: time.Unix(1350244992, 23960108),
   506  	}
   507  	if !reflect.DeepEqual(hdr, want) {
   508  		t.Errorf("incorrect merge: got %+v, want %+v", hdr, want)
   509  	}
   510  }
   511  
   512  func TestSparseEndToEnd(t *testing.T) {
   513  	test := sparseTarTest
   514  	f, err := os.Open(test.file)
   515  	if err != nil {
   516  		t.Fatalf("Unexpected error: %v", err)
   517  	}
   518  	defer f.Close()
   519  
   520  	tr := NewReader(f)
   521  
   522  	headers := test.headers
   523  	cksums := test.cksums
   524  	nread := 0
   525  
   526  	// loop over all files
   527  	for ; ; nread++ {
   528  		hdr, err := tr.Next()
   529  		if hdr == nil || err == io.EOF {
   530  			break
   531  		}
   532  
   533  		// check the header
   534  		if !reflect.DeepEqual(*hdr, *headers[nread]) {
   535  			t.Errorf("Incorrect header:\nhave %+v\nwant %+v",
   536  				*hdr, headers[nread])
   537  		}
   538  
   539  		// read and checksum the file data
   540  		h := md5.New()
   541  		_, err = io.Copy(h, tr)
   542  		if err != nil {
   543  			t.Fatalf("Unexpected error: %v", err)
   544  		}
   545  
   546  		// verify checksum
   547  		have := fmt.Sprintf("%x", h.Sum(nil))
   548  		want := cksums[nread]
   549  		if want != have {
   550  			t.Errorf("Bad checksum on file %s:\nhave %+v\nwant %+v", hdr.Name, have, want)
   551  		}
   552  	}
   553  	if nread != len(headers) {
   554  		t.Errorf("Didn't process all files\nexpected: %d\nprocessed %d\n", len(headers), nread)
   555  	}
   556  }
   557  
   558  type sparseFileReadTest struct {
   559  	sparseData []byte
   560  	sparseMap  []sparseEntry
   561  	realSize   int64
   562  	expected   []byte
   563  }
   564  
   565  var sparseFileReadTests = []sparseFileReadTest{
   566  	{
   567  		sparseData: []byte("abcde"),
   568  		sparseMap: []sparseEntry{
   569  			{offset: 0, numBytes: 2},
   570  			{offset: 5, numBytes: 3},
   571  		},
   572  		realSize: 8,
   573  		expected: []byte("ab\x00\x00\x00cde"),
   574  	},
   575  	{
   576  		sparseData: []byte("abcde"),
   577  		sparseMap: []sparseEntry{
   578  			{offset: 0, numBytes: 2},
   579  			{offset: 5, numBytes: 3},
   580  		},
   581  		realSize: 10,
   582  		expected: []byte("ab\x00\x00\x00cde\x00\x00"),
   583  	},
   584  	{
   585  		sparseData: []byte("abcde"),
   586  		sparseMap: []sparseEntry{
   587  			{offset: 1, numBytes: 3},
   588  			{offset: 6, numBytes: 2},
   589  		},
   590  		realSize: 8,
   591  		expected: []byte("\x00abc\x00\x00de"),
   592  	},
   593  	{
   594  		sparseData: []byte("abcde"),
   595  		sparseMap: []sparseEntry{
   596  			{offset: 1, numBytes: 3},
   597  			{offset: 6, numBytes: 2},
   598  		},
   599  		realSize: 10,
   600  		expected: []byte("\x00abc\x00\x00de\x00\x00"),
   601  	},
   602  	{
   603  		sparseData: []byte(""),
   604  		sparseMap:  nil,
   605  		realSize:   2,
   606  		expected:   []byte("\x00\x00"),
   607  	},
   608  }
   609  
   610  func TestSparseFileReader(t *testing.T) {
   611  	for i, test := range sparseFileReadTests {
   612  		r := bytes.NewReader(test.sparseData)
   613  		nb := int64(r.Len())
   614  		sfr := &sparseFileReader{
   615  			rfr: &regFileReader{r: r, nb: nb},
   616  			sp:  test.sparseMap,
   617  			pos: 0,
   618  			tot: test.realSize,
   619  		}
   620  		if sfr.numBytes() != nb {
   621  			t.Errorf("test %d: Before reading, sfr.numBytes() = %d, want %d", i, sfr.numBytes(), nb)
   622  		}
   623  		buf, err := ioutil.ReadAll(sfr)
   624  		if err != nil {
   625  			t.Errorf("test %d: Unexpected error: %v", i, err)
   626  		}
   627  		if e := test.expected; !bytes.Equal(buf, e) {
   628  			t.Errorf("test %d: Contents = %v, want %v", i, buf, e)
   629  		}
   630  		if sfr.numBytes() != 0 {
   631  			t.Errorf("test %d: After draining the reader, numBytes() was nonzero", i)
   632  		}
   633  	}
   634  }
   635  
   636  func TestSparseIncrementalRead(t *testing.T) {
   637  	sparseMap := []sparseEntry{{10, 2}}
   638  	sparseData := []byte("Go")
   639  	expected := "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00Go\x00\x00\x00\x00\x00\x00\x00\x00"
   640  
   641  	r := bytes.NewReader(sparseData)
   642  	nb := int64(r.Len())
   643  	sfr := &sparseFileReader{
   644  		rfr: &regFileReader{r: r, nb: nb},
   645  		sp:  sparseMap,
   646  		pos: 0,
   647  		tot: int64(len(expected)),
   648  	}
   649  
   650  	// We'll read the data 6 bytes at a time, with a hole of size 10 at
   651  	// the beginning and one of size 8 at the end.
   652  	var outputBuf bytes.Buffer
   653  	buf := make([]byte, 6)
   654  	for {
   655  		n, err := sfr.Read(buf)
   656  		if err == io.EOF {
   657  			break
   658  		}
   659  		if err != nil {
   660  			t.Errorf("Read: unexpected error %v\n", err)
   661  		}
   662  		if n > 0 {
   663  			_, err := outputBuf.Write(buf[:n])
   664  			if err != nil {
   665  				t.Errorf("Write: unexpected error %v\n", err)
   666  			}
   667  		}
   668  	}
   669  	got := outputBuf.String()
   670  	if got != expected {
   671  		t.Errorf("Contents = %v, want %v", got, expected)
   672  	}
   673  }
   674  
   675  func TestReadGNUSparseMap0x1(t *testing.T) {
   676  	headers := map[string]string{
   677  		paxGNUSparseNumBlocks: "4",
   678  		paxGNUSparseMap:       "0,5,10,5,20,5,30,5",
   679  	}
   680  	expected := []sparseEntry{
   681  		{offset: 0, numBytes: 5},
   682  		{offset: 10, numBytes: 5},
   683  		{offset: 20, numBytes: 5},
   684  		{offset: 30, numBytes: 5},
   685  	}
   686  
   687  	sp, err := readGNUSparseMap0x1(headers)
   688  	if err != nil {
   689  		t.Errorf("Unexpected error: %v", err)
   690  	}
   691  	if !reflect.DeepEqual(sp, expected) {
   692  		t.Errorf("Incorrect sparse map: got %v, wanted %v", sp, expected)
   693  	}
   694  }
   695  
   696  func TestReadGNUSparseMap1x0(t *testing.T) {
   697  	// This test uses lots of holes so the sparse header takes up more than two blocks
   698  	numEntries := 100
   699  	expected := make([]sparseEntry, 0, numEntries)
   700  	sparseMap := new(bytes.Buffer)
   701  
   702  	fmt.Fprintf(sparseMap, "%d\n", numEntries)
   703  	for i := 0; i < numEntries; i++ {
   704  		offset := int64(2048 * i)
   705  		numBytes := int64(1024)
   706  		expected = append(expected, sparseEntry{offset: offset, numBytes: numBytes})
   707  		fmt.Fprintf(sparseMap, "%d\n%d\n", offset, numBytes)
   708  	}
   709  
   710  	// Make the header the smallest multiple of blockSize that fits the sparseMap
   711  	headerBlocks := (sparseMap.Len() + blockSize - 1) / blockSize
   712  	bufLen := blockSize * headerBlocks
   713  	buf := make([]byte, bufLen)
   714  	copy(buf, sparseMap.Bytes())
   715  
   716  	// Get an reader to read the sparse map
   717  	r := bytes.NewReader(buf)
   718  
   719  	// Read the sparse map
   720  	sp, err := readGNUSparseMap1x0(r)
   721  	if err != nil {
   722  		t.Errorf("Unexpected error: %v", err)
   723  	}
   724  	if !reflect.DeepEqual(sp, expected) {
   725  		t.Errorf("Incorrect sparse map: got %v, wanted %v", sp, expected)
   726  	}
   727  }
   728  
   729  func TestUninitializedRead(t *testing.T) {
   730  	test := gnuTarTest
   731  	f, err := os.Open(test.file)
   732  	if err != nil {
   733  		t.Fatalf("Unexpected error: %v", err)
   734  	}
   735  	defer f.Close()
   736  
   737  	tr := NewReader(f)
   738  	_, err = tr.Read([]byte{})
   739  	if err == nil || err != io.EOF {
   740  		t.Errorf("Unexpected error: %v, wanted %v", err, io.EOF)
   741  	}
   742  
   743  }