github.com/mdempsky/go@v0.0.0-20151201204031-5dd372bd1e70/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 TestParsePAXHeader(t *testing.T) {
   426  	paxTests := [][3]string{
   427  		{"a", "a=name", "10 a=name\n"}, // Test case involving multiple acceptable lengths
   428  		{"a", "a=name", "9 a=name\n"},  // Test case involving multiple acceptable length
   429  		{"mtime", "mtime=1350244992.023960108", "30 mtime=1350244992.023960108\n"}}
   430  	for _, test := range paxTests {
   431  		key, expected, raw := test[0], test[1], test[2]
   432  		reader := bytes.NewReader([]byte(raw))
   433  		headers, err := parsePAX(reader)
   434  		if err != nil {
   435  			t.Errorf("Couldn't parse correctly formatted headers: %v", err)
   436  			continue
   437  		}
   438  		if strings.EqualFold(headers[key], expected) {
   439  			t.Errorf("mtime header incorrectly parsed: got %s, wanted %s", headers[key], expected)
   440  			continue
   441  		}
   442  		trailer := make([]byte, 100)
   443  		n, err := reader.Read(trailer)
   444  		if err != io.EOF || n != 0 {
   445  			t.Error("Buffer wasn't consumed")
   446  		}
   447  	}
   448  	badHeaderTests := [][]byte{
   449  		[]byte("3 somelongkey=\n"),
   450  		[]byte("50 tooshort=\n"),
   451  	}
   452  	for _, test := range badHeaderTests {
   453  		if _, err := parsePAX(bytes.NewReader(test)); err != ErrHeader {
   454  			t.Fatal("Unexpected success when parsing bad header")
   455  		}
   456  	}
   457  }
   458  
   459  func TestParsePAXTime(t *testing.T) {
   460  	// Some valid PAX time values
   461  	timestamps := map[string]time.Time{
   462  		"1350244992.023960108":  time.Unix(1350244992, 23960108), // The common case
   463  		"1350244992.02396010":   time.Unix(1350244992, 23960100), // Lower precision value
   464  		"1350244992.0239601089": time.Unix(1350244992, 23960108), // Higher precision value
   465  		"1350244992":            time.Unix(1350244992, 0),        // Low precision value
   466  	}
   467  	for input, expected := range timestamps {
   468  		ts, err := parsePAXTime(input)
   469  		if err != nil {
   470  			t.Fatal(err)
   471  		}
   472  		if !ts.Equal(expected) {
   473  			t.Fatalf("Time parsing failure %s %s", ts, expected)
   474  		}
   475  	}
   476  }
   477  
   478  func TestMergePAX(t *testing.T) {
   479  	hdr := new(Header)
   480  	// Test a string, integer, and time based value.
   481  	headers := map[string]string{
   482  		"path":  "a/b/c",
   483  		"uid":   "1000",
   484  		"mtime": "1350244992.023960108",
   485  	}
   486  	err := mergePAX(hdr, headers)
   487  	if err != nil {
   488  		t.Fatal(err)
   489  	}
   490  	want := &Header{
   491  		Name:    "a/b/c",
   492  		Uid:     1000,
   493  		ModTime: time.Unix(1350244992, 23960108),
   494  	}
   495  	if !reflect.DeepEqual(hdr, want) {
   496  		t.Errorf("incorrect merge: got %+v, want %+v", hdr, want)
   497  	}
   498  }
   499  
   500  func TestSparseFileReader(t *testing.T) {
   501  	var vectors = []struct {
   502  		realSize   int64         // Real size of the output file
   503  		sparseMap  []sparseEntry // Input sparse map
   504  		sparseData string        // Input compact data
   505  		expected   string        // Expected output data
   506  		err        error         // Expected error outcome
   507  	}{{
   508  		realSize: 8,
   509  		sparseMap: []sparseEntry{
   510  			{offset: 0, numBytes: 2},
   511  			{offset: 5, numBytes: 3},
   512  		},
   513  		sparseData: "abcde",
   514  		expected:   "ab\x00\x00\x00cde",
   515  	}, {
   516  		realSize: 10,
   517  		sparseMap: []sparseEntry{
   518  			{offset: 0, numBytes: 2},
   519  			{offset: 5, numBytes: 3},
   520  		},
   521  		sparseData: "abcde",
   522  		expected:   "ab\x00\x00\x00cde\x00\x00",
   523  	}, {
   524  		realSize: 8,
   525  		sparseMap: []sparseEntry{
   526  			{offset: 1, numBytes: 3},
   527  			{offset: 6, numBytes: 2},
   528  		},
   529  		sparseData: "abcde",
   530  		expected:   "\x00abc\x00\x00de",
   531  	}, {
   532  		realSize: 8,
   533  		sparseMap: []sparseEntry{
   534  			{offset: 1, numBytes: 3},
   535  			{offset: 6, numBytes: 0},
   536  			{offset: 6, numBytes: 0},
   537  			{offset: 6, numBytes: 2},
   538  		},
   539  		sparseData: "abcde",
   540  		expected:   "\x00abc\x00\x00de",
   541  	}, {
   542  		realSize: 10,
   543  		sparseMap: []sparseEntry{
   544  			{offset: 1, numBytes: 3},
   545  			{offset: 6, numBytes: 2},
   546  		},
   547  		sparseData: "abcde",
   548  		expected:   "\x00abc\x00\x00de\x00\x00",
   549  	}, {
   550  		realSize: 10,
   551  		sparseMap: []sparseEntry{
   552  			{offset: 1, numBytes: 3},
   553  			{offset: 6, numBytes: 2},
   554  			{offset: 8, numBytes: 0},
   555  			{offset: 8, numBytes: 0},
   556  			{offset: 8, numBytes: 0},
   557  			{offset: 8, numBytes: 0},
   558  		},
   559  		sparseData: "abcde",
   560  		expected:   "\x00abc\x00\x00de\x00\x00",
   561  	}, {
   562  		realSize:   2,
   563  		sparseMap:  []sparseEntry{},
   564  		sparseData: "",
   565  		expected:   "\x00\x00",
   566  	}, {
   567  		realSize:  -2,
   568  		sparseMap: []sparseEntry{},
   569  		err:       ErrHeader,
   570  	}, {
   571  		realSize: -10,
   572  		sparseMap: []sparseEntry{
   573  			{offset: 1, numBytes: 3},
   574  			{offset: 6, numBytes: 2},
   575  		},
   576  		sparseData: "abcde",
   577  		err:        ErrHeader,
   578  	}, {
   579  		realSize: 10,
   580  		sparseMap: []sparseEntry{
   581  			{offset: 1, numBytes: 3},
   582  			{offset: 6, numBytes: 5},
   583  		},
   584  		sparseData: "abcde",
   585  		err:        ErrHeader,
   586  	}, {
   587  		realSize: 35,
   588  		sparseMap: []sparseEntry{
   589  			{offset: 1, numBytes: 3},
   590  			{offset: 6, numBytes: 5},
   591  		},
   592  		sparseData: "abcde",
   593  		err:        io.ErrUnexpectedEOF,
   594  	}, {
   595  		realSize: 35,
   596  		sparseMap: []sparseEntry{
   597  			{offset: 1, numBytes: 3},
   598  			{offset: 6, numBytes: -5},
   599  		},
   600  		sparseData: "abcde",
   601  		err:        ErrHeader,
   602  	}, {
   603  		realSize: 35,
   604  		sparseMap: []sparseEntry{
   605  			{offset: math.MaxInt64, numBytes: 3},
   606  			{offset: 6, numBytes: -5},
   607  		},
   608  		sparseData: "abcde",
   609  		err:        ErrHeader,
   610  	}, {
   611  		realSize: 10,
   612  		sparseMap: []sparseEntry{
   613  			{offset: 1, numBytes: 3},
   614  			{offset: 2, numBytes: 2},
   615  		},
   616  		sparseData: "abcde",
   617  		err:        ErrHeader,
   618  	}}
   619  
   620  	for i, v := range vectors {
   621  		r := bytes.NewReader([]byte(v.sparseData))
   622  		rfr := &regFileReader{r: r, nb: int64(len(v.sparseData))}
   623  
   624  		var sfr *sparseFileReader
   625  		var err error
   626  		var buf []byte
   627  
   628  		sfr, err = newSparseFileReader(rfr, v.sparseMap, v.realSize)
   629  		if err != nil {
   630  			goto fail
   631  		}
   632  		if sfr.numBytes() != int64(len(v.sparseData)) {
   633  			t.Errorf("test %d, numBytes() before reading: got %d, want %d", i, sfr.numBytes(), len(v.sparseData))
   634  		}
   635  		buf, err = ioutil.ReadAll(sfr)
   636  		if err != nil {
   637  			goto fail
   638  		}
   639  		if string(buf) != v.expected {
   640  			t.Errorf("test %d, ReadAll(): got %q, want %q", i, string(buf), v.expected)
   641  		}
   642  		if sfr.numBytes() != 0 {
   643  			t.Errorf("test %d, numBytes() after reading: got %d, want %d", i, sfr.numBytes(), 0)
   644  		}
   645  
   646  	fail:
   647  		if err != v.err {
   648  			t.Errorf("test %d, unexpected error: got %v, want %v", i, err, v.err)
   649  		}
   650  	}
   651  }
   652  
   653  func TestReadGNUSparseMap0x1(t *testing.T) {
   654  	const (
   655  		maxUint = ^uint(0)
   656  		maxInt  = int(maxUint >> 1)
   657  	)
   658  	var (
   659  		big1 = fmt.Sprintf("%d", int64(maxInt))
   660  		big2 = fmt.Sprintf("%d", (int64(maxInt)/2)+1)
   661  		big3 = fmt.Sprintf("%d", (int64(maxInt) / 3))
   662  	)
   663  
   664  	var vectors = []struct {
   665  		extHdrs   map[string]string // Input data
   666  		sparseMap []sparseEntry     // Expected sparse entries to be outputted
   667  		err       error             // Expected errors that may be raised
   668  	}{{
   669  		extHdrs: map[string]string{paxGNUSparseNumBlocks: "-4"},
   670  		err:     ErrHeader,
   671  	}, {
   672  		extHdrs: map[string]string{paxGNUSparseNumBlocks: "fee "},
   673  		err:     ErrHeader,
   674  	}, {
   675  		extHdrs: map[string]string{
   676  			paxGNUSparseNumBlocks: big1,
   677  			paxGNUSparseMap:       "0,5,10,5,20,5,30,5",
   678  		},
   679  		err: ErrHeader,
   680  	}, {
   681  		extHdrs: map[string]string{
   682  			paxGNUSparseNumBlocks: big2,
   683  			paxGNUSparseMap:       "0,5,10,5,20,5,30,5",
   684  		},
   685  		err: ErrHeader,
   686  	}, {
   687  		extHdrs: map[string]string{
   688  			paxGNUSparseNumBlocks: big3,
   689  			paxGNUSparseMap:       "0,5,10,5,20,5,30,5",
   690  		},
   691  		err: ErrHeader,
   692  	}, {
   693  		extHdrs: map[string]string{
   694  			paxGNUSparseNumBlocks: "4",
   695  			paxGNUSparseMap:       "0.5,5,10,5,20,5,30,5",
   696  		},
   697  		err: ErrHeader,
   698  	}, {
   699  		extHdrs: map[string]string{
   700  			paxGNUSparseNumBlocks: "4",
   701  			paxGNUSparseMap:       "0,5.5,10,5,20,5,30,5",
   702  		},
   703  		err: ErrHeader,
   704  	}, {
   705  		extHdrs: map[string]string{
   706  			paxGNUSparseNumBlocks: "4",
   707  			paxGNUSparseMap:       "0,fewafewa.5,fewafw,5,20,5,30,5",
   708  		},
   709  		err: ErrHeader,
   710  	}, {
   711  		extHdrs: map[string]string{
   712  			paxGNUSparseNumBlocks: "4",
   713  			paxGNUSparseMap:       "0,5,10,5,20,5,30,5",
   714  		},
   715  		sparseMap: []sparseEntry{{0, 5}, {10, 5}, {20, 5}, {30, 5}},
   716  	}}
   717  
   718  	for i, v := range vectors {
   719  		sp, err := readGNUSparseMap0x1(v.extHdrs)
   720  		if !reflect.DeepEqual(sp, v.sparseMap) && !(len(sp) == 0 && len(v.sparseMap) == 0) {
   721  			t.Errorf("test %d, readGNUSparseMap0x1(...): got %v, want %v", i, sp, v.sparseMap)
   722  		}
   723  		if err != v.err {
   724  			t.Errorf("test %d, unexpected error: got %v, want %v", i, err, v.err)
   725  		}
   726  	}
   727  }
   728  
   729  func TestReadGNUSparseMap1x0(t *testing.T) {
   730  	var sp = []sparseEntry{{1, 2}, {3, 4}}
   731  	for i := 0; i < 98; i++ {
   732  		sp = append(sp, sparseEntry{54321, 12345})
   733  	}
   734  
   735  	var vectors = []struct {
   736  		input     string        // Input data
   737  		sparseMap []sparseEntry // Expected sparse entries to be outputted
   738  		cnt       int           // Expected number of bytes read
   739  		err       error         // Expected errors that may be raised
   740  	}{{
   741  		input: "",
   742  		cnt:   0,
   743  		err:   io.ErrUnexpectedEOF,
   744  	}, {
   745  		input: "ab",
   746  		cnt:   2,
   747  		err:   io.ErrUnexpectedEOF,
   748  	}, {
   749  		input: strings.Repeat("\x00", 512),
   750  		cnt:   512,
   751  		err:   io.ErrUnexpectedEOF,
   752  	}, {
   753  		input: strings.Repeat("\x00", 511) + "\n",
   754  		cnt:   512,
   755  		err:   ErrHeader,
   756  	}, {
   757  		input: strings.Repeat("\n", 512),
   758  		cnt:   512,
   759  		err:   ErrHeader,
   760  	}, {
   761  		input:     "0\n" + strings.Repeat("\x00", 510) + strings.Repeat("a", 512),
   762  		sparseMap: []sparseEntry{},
   763  		cnt:       512,
   764  	}, {
   765  		input:     strings.Repeat("0", 512) + "0\n" + strings.Repeat("\x00", 510),
   766  		sparseMap: []sparseEntry{},
   767  		cnt:       1024,
   768  	}, {
   769  		input:     strings.Repeat("0", 1024) + "1\n2\n3\n" + strings.Repeat("\x00", 506),
   770  		sparseMap: []sparseEntry{{2, 3}},
   771  		cnt:       1536,
   772  	}, {
   773  		input: strings.Repeat("0", 1024) + "1\n2\n\n" + strings.Repeat("\x00", 509),
   774  		cnt:   1536,
   775  		err:   ErrHeader,
   776  	}, {
   777  		input: strings.Repeat("0", 1024) + "1\n2\n" + strings.Repeat("\x00", 508),
   778  		cnt:   1536,
   779  		err:   io.ErrUnexpectedEOF,
   780  	}, {
   781  		input: "-1\n2\n\n" + strings.Repeat("\x00", 506),
   782  		cnt:   512,
   783  		err:   ErrHeader,
   784  	}, {
   785  		input: "1\nk\n2\n" + strings.Repeat("\x00", 506),
   786  		cnt:   512,
   787  		err:   ErrHeader,
   788  	}, {
   789  		input:     "100\n1\n2\n3\n4\n" + strings.Repeat("54321\n0000000000000012345\n", 98) + strings.Repeat("\x00", 512),
   790  		cnt:       2560,
   791  		sparseMap: sp,
   792  	}}
   793  
   794  	for i, v := range vectors {
   795  		r := strings.NewReader(v.input)
   796  		sp, err := readGNUSparseMap1x0(r)
   797  		if !reflect.DeepEqual(sp, v.sparseMap) && !(len(sp) == 0 && len(v.sparseMap) == 0) {
   798  			t.Errorf("test %d, readGNUSparseMap1x0(...): got %v, want %v", i, sp, v.sparseMap)
   799  		}
   800  		if numBytes := len(v.input) - r.Len(); numBytes != v.cnt {
   801  			t.Errorf("test %d, bytes read: got %v, want %v", i, numBytes, v.cnt)
   802  		}
   803  		if err != v.err {
   804  			t.Errorf("test %d, unexpected error: got %v, want %v", i, err, v.err)
   805  		}
   806  	}
   807  }
   808  
   809  func TestUninitializedRead(t *testing.T) {
   810  	test := gnuTarTest
   811  	f, err := os.Open(test.file)
   812  	if err != nil {
   813  		t.Fatalf("Unexpected error: %v", err)
   814  	}
   815  	defer f.Close()
   816  
   817  	tr := NewReader(f)
   818  	_, err = tr.Read([]byte{})
   819  	if err == nil || err != io.EOF {
   820  		t.Errorf("Unexpected error: %v, wanted %v", err, io.EOF)
   821  	}
   822  
   823  }
   824  
   825  type reader struct{ io.Reader }
   826  type readSeeker struct{ io.ReadSeeker }
   827  type readBadSeeker struct{ io.ReadSeeker }
   828  
   829  func (rbs *readBadSeeker) Seek(int64, int) (int64, error) { return 0, fmt.Errorf("illegal seek") }
   830  
   831  // TestReadTruncation test the ending condition on various truncated files and
   832  // that truncated files are still detected even if the underlying io.Reader
   833  // satisfies io.Seeker.
   834  func TestReadTruncation(t *testing.T) {
   835  	var ss []string
   836  	for _, p := range []string{
   837  		"testdata/gnu.tar",
   838  		"testdata/ustar-file-reg.tar",
   839  		"testdata/pax-path-hdr.tar",
   840  		"testdata/sparse-formats.tar",
   841  	} {
   842  		buf, err := ioutil.ReadFile(p)
   843  		if err != nil {
   844  			t.Fatalf("unexpected error: %v", err)
   845  		}
   846  		ss = append(ss, string(buf))
   847  	}
   848  
   849  	data1, data2, pax, sparse := ss[0], ss[1], ss[2], ss[3]
   850  	data2 += strings.Repeat("\x00", 10*512)
   851  	trash := strings.Repeat("garbage ", 64) // Exactly 512 bytes
   852  
   853  	var vectors = []struct {
   854  		input string // Input stream
   855  		cnt   int    // Expected number of headers read
   856  		err   error  // Expected error outcome
   857  	}{
   858  		{"", 0, io.EOF}, // Empty file is a "valid" tar file
   859  		{data1[:511], 0, io.ErrUnexpectedEOF},
   860  		{data1[:512], 1, io.ErrUnexpectedEOF},
   861  		{data1[:1024], 1, io.EOF},
   862  		{data1[:1536], 2, io.ErrUnexpectedEOF},
   863  		{data1[:2048], 2, io.EOF},
   864  		{data1, 2, io.EOF},
   865  		{data1[:2048] + data2[:1536], 3, io.EOF},
   866  		{data2[:511], 0, io.ErrUnexpectedEOF},
   867  		{data2[:512], 1, io.ErrUnexpectedEOF},
   868  		{data2[:1195], 1, io.ErrUnexpectedEOF},
   869  		{data2[:1196], 1, io.EOF}, // Exact end of data and start of padding
   870  		{data2[:1200], 1, io.EOF},
   871  		{data2[:1535], 1, io.EOF},
   872  		{data2[:1536], 1, io.EOF}, // Exact end of padding
   873  		{data2[:1536] + trash[:1], 1, io.ErrUnexpectedEOF},
   874  		{data2[:1536] + trash[:511], 1, io.ErrUnexpectedEOF},
   875  		{data2[:1536] + trash, 1, ErrHeader},
   876  		{data2[:2048], 1, io.EOF}, // Exactly 1 empty block
   877  		{data2[:2048] + trash[:1], 1, io.ErrUnexpectedEOF},
   878  		{data2[:2048] + trash[:511], 1, io.ErrUnexpectedEOF},
   879  		{data2[:2048] + trash, 1, ErrHeader},
   880  		{data2[:2560], 1, io.EOF}, // Exactly 2 empty blocks (normal end-of-stream)
   881  		{data2[:2560] + trash[:1], 1, io.EOF},
   882  		{data2[:2560] + trash[:511], 1, io.EOF},
   883  		{data2[:2560] + trash, 1, io.EOF},
   884  		{data2[:3072], 1, io.EOF},
   885  		{pax, 0, io.EOF}, // PAX header without data is a "valid" tar file
   886  		{pax + trash[:1], 0, io.ErrUnexpectedEOF},
   887  		{pax + trash[:511], 0, io.ErrUnexpectedEOF},
   888  		{sparse[:511], 0, io.ErrUnexpectedEOF},
   889  		// TODO(dsnet): This should pass, but currently fails.
   890  		// {sparse[:512], 0, io.ErrUnexpectedEOF},
   891  		{sparse[:3584], 1, io.EOF},
   892  		{sparse[:9200], 1, io.EOF}, // Terminate in padding of sparse header
   893  		{sparse[:9216], 1, io.EOF},
   894  		{sparse[:9728], 2, io.ErrUnexpectedEOF},
   895  		{sparse[:10240], 2, io.EOF},
   896  		{sparse[:11264], 2, io.ErrUnexpectedEOF},
   897  		{sparse, 5, io.EOF},
   898  		{sparse + trash, 5, io.EOF},
   899  	}
   900  
   901  	for i, v := range vectors {
   902  		for j := 0; j < 6; j++ {
   903  			var tr *Reader
   904  			var s1, s2 string
   905  
   906  			switch j {
   907  			case 0:
   908  				tr = NewReader(&reader{strings.NewReader(v.input)})
   909  				s1, s2 = "io.Reader", "auto"
   910  			case 1:
   911  				tr = NewReader(&reader{strings.NewReader(v.input)})
   912  				s1, s2 = "io.Reader", "manual"
   913  			case 2:
   914  				tr = NewReader(&readSeeker{strings.NewReader(v.input)})
   915  				s1, s2 = "io.ReadSeeker", "auto"
   916  			case 3:
   917  				tr = NewReader(&readSeeker{strings.NewReader(v.input)})
   918  				s1, s2 = "io.ReadSeeker", "manual"
   919  			case 4:
   920  				tr = NewReader(&readBadSeeker{strings.NewReader(v.input)})
   921  				s1, s2 = "ReadBadSeeker", "auto"
   922  			case 5:
   923  				tr = NewReader(&readBadSeeker{strings.NewReader(v.input)})
   924  				s1, s2 = "ReadBadSeeker", "manual"
   925  			}
   926  
   927  			var cnt int
   928  			var err error
   929  			for {
   930  				if _, err = tr.Next(); err != nil {
   931  					break
   932  				}
   933  				cnt++
   934  				if s2 == "manual" {
   935  					if _, err = io.Copy(ioutil.Discard, tr); err != nil {
   936  						break
   937  					}
   938  				}
   939  			}
   940  			if err != v.err {
   941  				t.Errorf("test %d, NewReader(%s(...)) with %s discard: got %v, want %v",
   942  					i, s1, s2, err, v.err)
   943  			}
   944  			if cnt != v.cnt {
   945  				t.Errorf("test %d, NewReader(%s(...)) with %s discard: got %d headers, want %d headers",
   946  					i, s1, s2, cnt, v.cnt)
   947  			}
   948  		}
   949  	}
   950  }
   951  
   952  // TestReadHeaderOnly tests that Reader does not attempt to read special
   953  // header-only files.
   954  func TestReadHeaderOnly(t *testing.T) {
   955  	f, err := os.Open("testdata/hdr-only.tar")
   956  	if err != nil {
   957  		t.Fatalf("unexpected error: %v", err)
   958  	}
   959  	defer f.Close()
   960  
   961  	var hdrs []*Header
   962  	tr := NewReader(f)
   963  	for {
   964  		hdr, err := tr.Next()
   965  		if err == io.EOF {
   966  			break
   967  		}
   968  		if err != nil {
   969  			t.Errorf("Next(): got %v, want %v", err, nil)
   970  			continue
   971  		}
   972  		hdrs = append(hdrs, hdr)
   973  
   974  		// If a special flag, we should read nothing.
   975  		cnt, _ := io.ReadFull(tr, []byte{0})
   976  		if cnt > 0 && hdr.Typeflag != TypeReg {
   977  			t.Errorf("ReadFull(...): got %d bytes, want 0 bytes", cnt)
   978  		}
   979  	}
   980  
   981  	// File is crafted with 16 entries. The later 8 are identical to the first
   982  	// 8 except that the size is set.
   983  	if len(hdrs) != 16 {
   984  		t.Fatalf("len(hdrs): got %d, want %d", len(hdrs), 16)
   985  	}
   986  	for i := 0; i < 8; i++ {
   987  		var hdr1, hdr2 = hdrs[i+0], hdrs[i+8]
   988  		hdr1.Size, hdr2.Size = 0, 0
   989  		if !reflect.DeepEqual(*hdr1, *hdr2) {
   990  			t.Errorf("incorrect header:\ngot  %+v\nwant %+v", *hdr1, *hdr2)
   991  		}
   992  	}
   993  }