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