github.com/4ad/go@v0.0.0-20161219182952-69a12818b605/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  		// Matches the behavior of GNU, BSD, and STAR tar utilities.
   293  		file: "testdata/gnu-multi-hdrs.tar",
   294  		headers: []*Header{
   295  			{
   296  				Name:     "GNU2/GNU2/long-path-name",
   297  				Linkname: "GNU4/GNU4/long-linkpath-name",
   298  				ModTime:  time.Unix(0, 0),
   299  				Typeflag: '2',
   300  			},
   301  		},
   302  	},
   303  	{
   304  		// Matches the behavior of GNU and BSD tar utilities.
   305  		file: "testdata/pax-multi-hdrs.tar",
   306  		headers: []*Header{
   307  			{
   308  				Name:     "bar",
   309  				Linkname: "PAX4/PAX4/long-linkpath-name",
   310  				ModTime:  time.Unix(0, 0),
   311  				Typeflag: '2',
   312  			},
   313  		},
   314  	},
   315  	{
   316  		file: "testdata/neg-size.tar",
   317  		err:  ErrHeader,
   318  	},
   319  	{
   320  		file: "testdata/issue10968.tar",
   321  		err:  ErrHeader,
   322  	},
   323  	{
   324  		file: "testdata/issue11169.tar",
   325  		err:  ErrHeader,
   326  	},
   327  	{
   328  		file: "testdata/issue12435.tar",
   329  		err:  ErrHeader,
   330  	},
   331  }
   332  
   333  func TestReader(t *testing.T) {
   334  	for i, v := range untarTests {
   335  		f, err := os.Open(v.file)
   336  		if err != nil {
   337  			t.Errorf("file %s, test %d: unexpected error: %v", v.file, i, err)
   338  			continue
   339  		}
   340  		defer f.Close()
   341  
   342  		// Capture all headers and checksums.
   343  		var (
   344  			tr      = NewReader(f)
   345  			hdrs    []*Header
   346  			chksums []string
   347  			rdbuf   = make([]byte, 8)
   348  		)
   349  		for {
   350  			var hdr *Header
   351  			hdr, err = tr.Next()
   352  			if err != nil {
   353  				if err == io.EOF {
   354  					err = nil // Expected error
   355  				}
   356  				break
   357  			}
   358  			hdrs = append(hdrs, hdr)
   359  
   360  			if v.chksums == nil {
   361  				continue
   362  			}
   363  			h := md5.New()
   364  			_, err = io.CopyBuffer(h, tr, rdbuf) // Effectively an incremental read
   365  			if err != nil {
   366  				break
   367  			}
   368  			chksums = append(chksums, fmt.Sprintf("%x", h.Sum(nil)))
   369  		}
   370  
   371  		for j, hdr := range hdrs {
   372  			if j >= len(v.headers) {
   373  				t.Errorf("file %s, test %d, entry %d: unexpected header:\ngot %+v",
   374  					v.file, i, j, *hdr)
   375  				continue
   376  			}
   377  			if !reflect.DeepEqual(*hdr, *v.headers[j]) {
   378  				t.Errorf("file %s, test %d, entry %d: incorrect header:\ngot  %+v\nwant %+v",
   379  					v.file, i, j, *hdr, *v.headers[j])
   380  			}
   381  		}
   382  		if len(hdrs) != len(v.headers) {
   383  			t.Errorf("file %s, test %d: got %d headers, want %d headers",
   384  				v.file, i, len(hdrs), len(v.headers))
   385  		}
   386  
   387  		for j, sum := range chksums {
   388  			if j >= len(v.chksums) {
   389  				t.Errorf("file %s, test %d, entry %d: unexpected sum: got %s",
   390  					v.file, i, j, sum)
   391  				continue
   392  			}
   393  			if sum != v.chksums[j] {
   394  				t.Errorf("file %s, test %d, entry %d: incorrect checksum: got %s, want %s",
   395  					v.file, i, j, sum, v.chksums[j])
   396  			}
   397  		}
   398  
   399  		if err != v.err {
   400  			t.Errorf("file %s, test %d: unexpected error: got %v, want %v",
   401  				v.file, i, err, v.err)
   402  		}
   403  		f.Close()
   404  	}
   405  }
   406  
   407  func TestPartialRead(t *testing.T) {
   408  	f, err := os.Open("testdata/gnu.tar")
   409  	if err != nil {
   410  		t.Fatalf("Unexpected error: %v", err)
   411  	}
   412  	defer f.Close()
   413  
   414  	tr := NewReader(f)
   415  
   416  	// Read the first four bytes; Next() should skip the last byte.
   417  	hdr, err := tr.Next()
   418  	if err != nil || hdr == nil {
   419  		t.Fatalf("Didn't get first file: %v", err)
   420  	}
   421  	buf := make([]byte, 4)
   422  	if _, err := io.ReadFull(tr, buf); err != nil {
   423  		t.Fatalf("Unexpected error: %v", err)
   424  	}
   425  	if expected := []byte("Kilt"); !bytes.Equal(buf, expected) {
   426  		t.Errorf("Contents = %v, want %v", buf, expected)
   427  	}
   428  
   429  	// Second file
   430  	hdr, err = tr.Next()
   431  	if err != nil || hdr == nil {
   432  		t.Fatalf("Didn't get second file: %v", err)
   433  	}
   434  	buf = make([]byte, 6)
   435  	if _, err := io.ReadFull(tr, buf); err != nil {
   436  		t.Fatalf("Unexpected error: %v", err)
   437  	}
   438  	if expected := []byte("Google"); !bytes.Equal(buf, expected) {
   439  		t.Errorf("Contents = %v, want %v", buf, expected)
   440  	}
   441  }
   442  
   443  func TestParsePAXHeader(t *testing.T) {
   444  	paxTests := [][3]string{
   445  		{"a", "a=name", "10 a=name\n"}, // Test case involving multiple acceptable lengths
   446  		{"a", "a=name", "9 a=name\n"},  // Test case involving multiple acceptable length
   447  		{"mtime", "mtime=1350244992.023960108", "30 mtime=1350244992.023960108\n"}}
   448  	for _, test := range paxTests {
   449  		key, expected, raw := test[0], test[1], test[2]
   450  		reader := bytes.NewReader([]byte(raw))
   451  		headers, err := parsePAX(reader)
   452  		if err != nil {
   453  			t.Errorf("Couldn't parse correctly formatted headers: %v", err)
   454  			continue
   455  		}
   456  		if strings.EqualFold(headers[key], expected) {
   457  			t.Errorf("mtime header incorrectly parsed: got %s, wanted %s", headers[key], expected)
   458  			continue
   459  		}
   460  		trailer := make([]byte, 100)
   461  		n, err := reader.Read(trailer)
   462  		if err != io.EOF || n != 0 {
   463  			t.Error("Buffer wasn't consumed")
   464  		}
   465  	}
   466  	badHeaderTests := [][]byte{
   467  		[]byte("3 somelongkey=\n"),
   468  		[]byte("50 tooshort=\n"),
   469  	}
   470  	for _, test := range badHeaderTests {
   471  		if _, err := parsePAX(bytes.NewReader(test)); err != ErrHeader {
   472  			t.Fatal("Unexpected success when parsing bad header")
   473  		}
   474  	}
   475  }
   476  
   477  func TestParsePAXTime(t *testing.T) {
   478  	// Some valid PAX time values
   479  	timestamps := map[string]time.Time{
   480  		"1350244992.023960108":  time.Unix(1350244992, 23960108), // The common case
   481  		"1350244992.02396010":   time.Unix(1350244992, 23960100), // Lower precision value
   482  		"1350244992.0239601089": time.Unix(1350244992, 23960108), // Higher precision value
   483  		"1350244992":            time.Unix(1350244992, 0),        // Low precision value
   484  	}
   485  	for input, expected := range timestamps {
   486  		ts, err := parsePAXTime(input)
   487  		if err != nil {
   488  			t.Fatal(err)
   489  		}
   490  		if !ts.Equal(expected) {
   491  			t.Fatalf("Time parsing failure %s %s", ts, expected)
   492  		}
   493  	}
   494  }
   495  
   496  func TestMergePAX(t *testing.T) {
   497  	hdr := new(Header)
   498  	// Test a string, integer, and time based value.
   499  	headers := map[string]string{
   500  		"path":  "a/b/c",
   501  		"uid":   "1000",
   502  		"mtime": "1350244992.023960108",
   503  	}
   504  	err := mergePAX(hdr, headers)
   505  	if err != nil {
   506  		t.Fatal(err)
   507  	}
   508  	want := &Header{
   509  		Name:    "a/b/c",
   510  		Uid:     1000,
   511  		ModTime: time.Unix(1350244992, 23960108),
   512  	}
   513  	if !reflect.DeepEqual(hdr, want) {
   514  		t.Errorf("incorrect merge: got %+v, want %+v", hdr, want)
   515  	}
   516  }
   517  
   518  func TestSparseFileReader(t *testing.T) {
   519  	var vectors = []struct {
   520  		realSize   int64         // Real size of the output file
   521  		sparseMap  []sparseEntry // Input sparse map
   522  		sparseData string        // Input compact data
   523  		expected   string        // Expected output data
   524  		err        error         // Expected error outcome
   525  	}{{
   526  		realSize: 8,
   527  		sparseMap: []sparseEntry{
   528  			{offset: 0, numBytes: 2},
   529  			{offset: 5, numBytes: 3},
   530  		},
   531  		sparseData: "abcde",
   532  		expected:   "ab\x00\x00\x00cde",
   533  	}, {
   534  		realSize: 10,
   535  		sparseMap: []sparseEntry{
   536  			{offset: 0, numBytes: 2},
   537  			{offset: 5, numBytes: 3},
   538  		},
   539  		sparseData: "abcde",
   540  		expected:   "ab\x00\x00\x00cde\x00\x00",
   541  	}, {
   542  		realSize: 8,
   543  		sparseMap: []sparseEntry{
   544  			{offset: 1, numBytes: 3},
   545  			{offset: 6, numBytes: 2},
   546  		},
   547  		sparseData: "abcde",
   548  		expected:   "\x00abc\x00\x00de",
   549  	}, {
   550  		realSize: 8,
   551  		sparseMap: []sparseEntry{
   552  			{offset: 1, numBytes: 3},
   553  			{offset: 6, numBytes: 0},
   554  			{offset: 6, numBytes: 0},
   555  			{offset: 6, numBytes: 2},
   556  		},
   557  		sparseData: "abcde",
   558  		expected:   "\x00abc\x00\x00de",
   559  	}, {
   560  		realSize: 10,
   561  		sparseMap: []sparseEntry{
   562  			{offset: 1, numBytes: 3},
   563  			{offset: 6, numBytes: 2},
   564  		},
   565  		sparseData: "abcde",
   566  		expected:   "\x00abc\x00\x00de\x00\x00",
   567  	}, {
   568  		realSize: 10,
   569  		sparseMap: []sparseEntry{
   570  			{offset: 1, numBytes: 3},
   571  			{offset: 6, numBytes: 2},
   572  			{offset: 8, numBytes: 0},
   573  			{offset: 8, numBytes: 0},
   574  			{offset: 8, numBytes: 0},
   575  			{offset: 8, numBytes: 0},
   576  		},
   577  		sparseData: "abcde",
   578  		expected:   "\x00abc\x00\x00de\x00\x00",
   579  	}, {
   580  		realSize:   2,
   581  		sparseMap:  []sparseEntry{},
   582  		sparseData: "",
   583  		expected:   "\x00\x00",
   584  	}, {
   585  		realSize:  -2,
   586  		sparseMap: []sparseEntry{},
   587  		err:       ErrHeader,
   588  	}, {
   589  		realSize: -10,
   590  		sparseMap: []sparseEntry{
   591  			{offset: 1, numBytes: 3},
   592  			{offset: 6, numBytes: 2},
   593  		},
   594  		sparseData: "abcde",
   595  		err:        ErrHeader,
   596  	}, {
   597  		realSize: 10,
   598  		sparseMap: []sparseEntry{
   599  			{offset: 1, numBytes: 3},
   600  			{offset: 6, numBytes: 5},
   601  		},
   602  		sparseData: "abcde",
   603  		err:        ErrHeader,
   604  	}, {
   605  		realSize: 35,
   606  		sparseMap: []sparseEntry{
   607  			{offset: 1, numBytes: 3},
   608  			{offset: 6, numBytes: 5},
   609  		},
   610  		sparseData: "abcde",
   611  		err:        io.ErrUnexpectedEOF,
   612  	}, {
   613  		realSize: 35,
   614  		sparseMap: []sparseEntry{
   615  			{offset: 1, numBytes: 3},
   616  			{offset: 6, numBytes: -5},
   617  		},
   618  		sparseData: "abcde",
   619  		err:        ErrHeader,
   620  	}, {
   621  		realSize: 35,
   622  		sparseMap: []sparseEntry{
   623  			{offset: math.MaxInt64, numBytes: 3},
   624  			{offset: 6, numBytes: -5},
   625  		},
   626  		sparseData: "abcde",
   627  		err:        ErrHeader,
   628  	}, {
   629  		realSize: 10,
   630  		sparseMap: []sparseEntry{
   631  			{offset: 1, numBytes: 3},
   632  			{offset: 2, numBytes: 2},
   633  		},
   634  		sparseData: "abcde",
   635  		err:        ErrHeader,
   636  	}}
   637  
   638  	for i, v := range vectors {
   639  		r := bytes.NewReader([]byte(v.sparseData))
   640  		rfr := &regFileReader{r: r, nb: int64(len(v.sparseData))}
   641  
   642  		var sfr *sparseFileReader
   643  		var err error
   644  		var buf []byte
   645  
   646  		sfr, err = newSparseFileReader(rfr, v.sparseMap, v.realSize)
   647  		if err != nil {
   648  			goto fail
   649  		}
   650  		if sfr.numBytes() != int64(len(v.sparseData)) {
   651  			t.Errorf("test %d, numBytes() before reading: got %d, want %d", i, sfr.numBytes(), len(v.sparseData))
   652  		}
   653  		buf, err = ioutil.ReadAll(sfr)
   654  		if err != nil {
   655  			goto fail
   656  		}
   657  		if string(buf) != v.expected {
   658  			t.Errorf("test %d, ReadAll(): got %q, want %q", i, string(buf), v.expected)
   659  		}
   660  		if sfr.numBytes() != 0 {
   661  			t.Errorf("test %d, numBytes() after reading: got %d, want %d", i, sfr.numBytes(), 0)
   662  		}
   663  
   664  	fail:
   665  		if err != v.err {
   666  			t.Errorf("test %d, unexpected error: got %v, want %v", i, err, v.err)
   667  		}
   668  	}
   669  }
   670  
   671  func TestReadGNUSparseMap0x1(t *testing.T) {
   672  	const (
   673  		maxUint = ^uint(0)
   674  		maxInt  = int(maxUint >> 1)
   675  	)
   676  	var (
   677  		big1 = fmt.Sprintf("%d", int64(maxInt))
   678  		big2 = fmt.Sprintf("%d", (int64(maxInt)/2)+1)
   679  		big3 = fmt.Sprintf("%d", (int64(maxInt) / 3))
   680  	)
   681  
   682  	var vectors = []struct {
   683  		extHdrs   map[string]string // Input data
   684  		sparseMap []sparseEntry     // Expected sparse entries to be outputted
   685  		err       error             // Expected errors that may be raised
   686  	}{{
   687  		extHdrs: map[string]string{paxGNUSparseNumBlocks: "-4"},
   688  		err:     ErrHeader,
   689  	}, {
   690  		extHdrs: map[string]string{paxGNUSparseNumBlocks: "fee "},
   691  		err:     ErrHeader,
   692  	}, {
   693  		extHdrs: map[string]string{
   694  			paxGNUSparseNumBlocks: big1,
   695  			paxGNUSparseMap:       "0,5,10,5,20,5,30,5",
   696  		},
   697  		err: ErrHeader,
   698  	}, {
   699  		extHdrs: map[string]string{
   700  			paxGNUSparseNumBlocks: big2,
   701  			paxGNUSparseMap:       "0,5,10,5,20,5,30,5",
   702  		},
   703  		err: ErrHeader,
   704  	}, {
   705  		extHdrs: map[string]string{
   706  			paxGNUSparseNumBlocks: big3,
   707  			paxGNUSparseMap:       "0,5,10,5,20,5,30,5",
   708  		},
   709  		err: ErrHeader,
   710  	}, {
   711  		extHdrs: map[string]string{
   712  			paxGNUSparseNumBlocks: "4",
   713  			paxGNUSparseMap:       "0.5,5,10,5,20,5,30,5",
   714  		},
   715  		err: ErrHeader,
   716  	}, {
   717  		extHdrs: map[string]string{
   718  			paxGNUSparseNumBlocks: "4",
   719  			paxGNUSparseMap:       "0,5.5,10,5,20,5,30,5",
   720  		},
   721  		err: ErrHeader,
   722  	}, {
   723  		extHdrs: map[string]string{
   724  			paxGNUSparseNumBlocks: "4",
   725  			paxGNUSparseMap:       "0,fewafewa.5,fewafw,5,20,5,30,5",
   726  		},
   727  		err: ErrHeader,
   728  	}, {
   729  		extHdrs: map[string]string{
   730  			paxGNUSparseNumBlocks: "4",
   731  			paxGNUSparseMap:       "0,5,10,5,20,5,30,5",
   732  		},
   733  		sparseMap: []sparseEntry{{0, 5}, {10, 5}, {20, 5}, {30, 5}},
   734  	}}
   735  
   736  	for i, v := range vectors {
   737  		sp, err := readGNUSparseMap0x1(v.extHdrs)
   738  		if !reflect.DeepEqual(sp, v.sparseMap) && !(len(sp) == 0 && len(v.sparseMap) == 0) {
   739  			t.Errorf("test %d, readGNUSparseMap0x1(...): got %v, want %v", i, sp, v.sparseMap)
   740  		}
   741  		if err != v.err {
   742  			t.Errorf("test %d, unexpected error: got %v, want %v", i, err, v.err)
   743  		}
   744  	}
   745  }
   746  
   747  func TestReadGNUSparseMap1x0(t *testing.T) {
   748  	var sp = []sparseEntry{{1, 2}, {3, 4}}
   749  	for i := 0; i < 98; i++ {
   750  		sp = append(sp, sparseEntry{54321, 12345})
   751  	}
   752  
   753  	var vectors = []struct {
   754  		input     string        // Input data
   755  		sparseMap []sparseEntry // Expected sparse entries to be outputted
   756  		cnt       int           // Expected number of bytes read
   757  		err       error         // Expected errors that may be raised
   758  	}{{
   759  		input: "",
   760  		cnt:   0,
   761  		err:   io.ErrUnexpectedEOF,
   762  	}, {
   763  		input: "ab",
   764  		cnt:   2,
   765  		err:   io.ErrUnexpectedEOF,
   766  	}, {
   767  		input: strings.Repeat("\x00", 512),
   768  		cnt:   512,
   769  		err:   io.ErrUnexpectedEOF,
   770  	}, {
   771  		input: strings.Repeat("\x00", 511) + "\n",
   772  		cnt:   512,
   773  		err:   ErrHeader,
   774  	}, {
   775  		input: strings.Repeat("\n", 512),
   776  		cnt:   512,
   777  		err:   ErrHeader,
   778  	}, {
   779  		input:     "0\n" + strings.Repeat("\x00", 510) + strings.Repeat("a", 512),
   780  		sparseMap: []sparseEntry{},
   781  		cnt:       512,
   782  	}, {
   783  		input:     strings.Repeat("0", 512) + "0\n" + strings.Repeat("\x00", 510),
   784  		sparseMap: []sparseEntry{},
   785  		cnt:       1024,
   786  	}, {
   787  		input:     strings.Repeat("0", 1024) + "1\n2\n3\n" + strings.Repeat("\x00", 506),
   788  		sparseMap: []sparseEntry{{2, 3}},
   789  		cnt:       1536,
   790  	}, {
   791  		input: strings.Repeat("0", 1024) + "1\n2\n\n" + strings.Repeat("\x00", 509),
   792  		cnt:   1536,
   793  		err:   ErrHeader,
   794  	}, {
   795  		input: strings.Repeat("0", 1024) + "1\n2\n" + strings.Repeat("\x00", 508),
   796  		cnt:   1536,
   797  		err:   io.ErrUnexpectedEOF,
   798  	}, {
   799  		input: "-1\n2\n\n" + strings.Repeat("\x00", 506),
   800  		cnt:   512,
   801  		err:   ErrHeader,
   802  	}, {
   803  		input: "1\nk\n2\n" + strings.Repeat("\x00", 506),
   804  		cnt:   512,
   805  		err:   ErrHeader,
   806  	}, {
   807  		input:     "100\n1\n2\n3\n4\n" + strings.Repeat("54321\n0000000000000012345\n", 98) + strings.Repeat("\x00", 512),
   808  		cnt:       2560,
   809  		sparseMap: sp,
   810  	}}
   811  
   812  	for i, v := range vectors {
   813  		r := strings.NewReader(v.input)
   814  		sp, err := readGNUSparseMap1x0(r)
   815  		if !reflect.DeepEqual(sp, v.sparseMap) && !(len(sp) == 0 && len(v.sparseMap) == 0) {
   816  			t.Errorf("test %d, readGNUSparseMap1x0(...): got %v, want %v", i, sp, v.sparseMap)
   817  		}
   818  		if numBytes := len(v.input) - r.Len(); numBytes != v.cnt {
   819  			t.Errorf("test %d, bytes read: got %v, want %v", i, numBytes, v.cnt)
   820  		}
   821  		if err != v.err {
   822  			t.Errorf("test %d, unexpected error: got %v, want %v", i, err, v.err)
   823  		}
   824  	}
   825  }
   826  
   827  func TestUninitializedRead(t *testing.T) {
   828  	test := gnuTarTest
   829  	f, err := os.Open(test.file)
   830  	if err != nil {
   831  		t.Fatalf("Unexpected error: %v", err)
   832  	}
   833  	defer f.Close()
   834  
   835  	tr := NewReader(f)
   836  	_, err = tr.Read([]byte{})
   837  	if err == nil || err != io.EOF {
   838  		t.Errorf("Unexpected error: %v, wanted %v", err, io.EOF)
   839  	}
   840  
   841  }
   842  
   843  type reader struct{ io.Reader }
   844  type readSeeker struct{ io.ReadSeeker }
   845  type readBadSeeker struct{ io.ReadSeeker }
   846  
   847  func (rbs *readBadSeeker) Seek(int64, int) (int64, error) { return 0, fmt.Errorf("illegal seek") }
   848  
   849  // TestReadTruncation test the ending condition on various truncated files and
   850  // that truncated files are still detected even if the underlying io.Reader
   851  // satisfies io.Seeker.
   852  func TestReadTruncation(t *testing.T) {
   853  	var ss []string
   854  	for _, p := range []string{
   855  		"testdata/gnu.tar",
   856  		"testdata/ustar-file-reg.tar",
   857  		"testdata/pax-path-hdr.tar",
   858  		"testdata/sparse-formats.tar",
   859  	} {
   860  		buf, err := ioutil.ReadFile(p)
   861  		if err != nil {
   862  			t.Fatalf("unexpected error: %v", err)
   863  		}
   864  		ss = append(ss, string(buf))
   865  	}
   866  
   867  	data1, data2, pax, sparse := ss[0], ss[1], ss[2], ss[3]
   868  	data2 += strings.Repeat("\x00", 10*512)
   869  	trash := strings.Repeat("garbage ", 64) // Exactly 512 bytes
   870  
   871  	var vectors = []struct {
   872  		input string // Input stream
   873  		cnt   int    // Expected number of headers read
   874  		err   error  // Expected error outcome
   875  	}{
   876  		{"", 0, io.EOF}, // Empty file is a "valid" tar file
   877  		{data1[:511], 0, io.ErrUnexpectedEOF},
   878  		{data1[:512], 1, io.ErrUnexpectedEOF},
   879  		{data1[:1024], 1, io.EOF},
   880  		{data1[:1536], 2, io.ErrUnexpectedEOF},
   881  		{data1[:2048], 2, io.EOF},
   882  		{data1, 2, io.EOF},
   883  		{data1[:2048] + data2[:1536], 3, io.EOF},
   884  		{data2[:511], 0, io.ErrUnexpectedEOF},
   885  		{data2[:512], 1, io.ErrUnexpectedEOF},
   886  		{data2[:1195], 1, io.ErrUnexpectedEOF},
   887  		{data2[:1196], 1, io.EOF}, // Exact end of data and start of padding
   888  		{data2[:1200], 1, io.EOF},
   889  		{data2[:1535], 1, io.EOF},
   890  		{data2[:1536], 1, io.EOF}, // Exact end of padding
   891  		{data2[:1536] + trash[:1], 1, io.ErrUnexpectedEOF},
   892  		{data2[:1536] + trash[:511], 1, io.ErrUnexpectedEOF},
   893  		{data2[:1536] + trash, 1, ErrHeader},
   894  		{data2[:2048], 1, io.EOF}, // Exactly 1 empty block
   895  		{data2[:2048] + trash[:1], 1, io.ErrUnexpectedEOF},
   896  		{data2[:2048] + trash[:511], 1, io.ErrUnexpectedEOF},
   897  		{data2[:2048] + trash, 1, ErrHeader},
   898  		{data2[:2560], 1, io.EOF}, // Exactly 2 empty blocks (normal end-of-stream)
   899  		{data2[:2560] + trash[:1], 1, io.EOF},
   900  		{data2[:2560] + trash[:511], 1, io.EOF},
   901  		{data2[:2560] + trash, 1, io.EOF},
   902  		{data2[:3072], 1, io.EOF},
   903  		{pax, 0, io.EOF}, // PAX header without data is a "valid" tar file
   904  		{pax + trash[:1], 0, io.ErrUnexpectedEOF},
   905  		{pax + trash[:511], 0, io.ErrUnexpectedEOF},
   906  		{sparse[:511], 0, io.ErrUnexpectedEOF},
   907  		// TODO(dsnet): This should pass, but currently fails.
   908  		// {sparse[:512], 0, io.ErrUnexpectedEOF},
   909  		{sparse[:3584], 1, io.EOF},
   910  		{sparse[:9200], 1, io.EOF}, // Terminate in padding of sparse header
   911  		{sparse[:9216], 1, io.EOF},
   912  		{sparse[:9728], 2, io.ErrUnexpectedEOF},
   913  		{sparse[:10240], 2, io.EOF},
   914  		{sparse[:11264], 2, io.ErrUnexpectedEOF},
   915  		{sparse, 5, io.EOF},
   916  		{sparse + trash, 5, io.EOF},
   917  	}
   918  
   919  	for i, v := range vectors {
   920  		for j := 0; j < 6; j++ {
   921  			var tr *Reader
   922  			var s1, s2 string
   923  
   924  			switch j {
   925  			case 0:
   926  				tr = NewReader(&reader{strings.NewReader(v.input)})
   927  				s1, s2 = "io.Reader", "auto"
   928  			case 1:
   929  				tr = NewReader(&reader{strings.NewReader(v.input)})
   930  				s1, s2 = "io.Reader", "manual"
   931  			case 2:
   932  				tr = NewReader(&readSeeker{strings.NewReader(v.input)})
   933  				s1, s2 = "io.ReadSeeker", "auto"
   934  			case 3:
   935  				tr = NewReader(&readSeeker{strings.NewReader(v.input)})
   936  				s1, s2 = "io.ReadSeeker", "manual"
   937  			case 4:
   938  				tr = NewReader(&readBadSeeker{strings.NewReader(v.input)})
   939  				s1, s2 = "ReadBadSeeker", "auto"
   940  			case 5:
   941  				tr = NewReader(&readBadSeeker{strings.NewReader(v.input)})
   942  				s1, s2 = "ReadBadSeeker", "manual"
   943  			}
   944  
   945  			var cnt int
   946  			var err error
   947  			for {
   948  				if _, err = tr.Next(); err != nil {
   949  					break
   950  				}
   951  				cnt++
   952  				if s2 == "manual" {
   953  					if _, err = io.Copy(ioutil.Discard, tr); err != nil {
   954  						break
   955  					}
   956  				}
   957  			}
   958  			if err != v.err {
   959  				t.Errorf("test %d, NewReader(%s(...)) with %s discard: got %v, want %v",
   960  					i, s1, s2, err, v.err)
   961  			}
   962  			if cnt != v.cnt {
   963  				t.Errorf("test %d, NewReader(%s(...)) with %s discard: got %d headers, want %d headers",
   964  					i, s1, s2, cnt, v.cnt)
   965  			}
   966  		}
   967  	}
   968  }
   969  
   970  // TestReadHeaderOnly tests that Reader does not attempt to read special
   971  // header-only files.
   972  func TestReadHeaderOnly(t *testing.T) {
   973  	f, err := os.Open("testdata/hdr-only.tar")
   974  	if err != nil {
   975  		t.Fatalf("unexpected error: %v", err)
   976  	}
   977  	defer f.Close()
   978  
   979  	var hdrs []*Header
   980  	tr := NewReader(f)
   981  	for {
   982  		hdr, err := tr.Next()
   983  		if err == io.EOF {
   984  			break
   985  		}
   986  		if err != nil {
   987  			t.Errorf("Next(): got %v, want %v", err, nil)
   988  			continue
   989  		}
   990  		hdrs = append(hdrs, hdr)
   991  
   992  		// If a special flag, we should read nothing.
   993  		cnt, _ := io.ReadFull(tr, []byte{0})
   994  		if cnt > 0 && hdr.Typeflag != TypeReg {
   995  			t.Errorf("ReadFull(...): got %d bytes, want 0 bytes", cnt)
   996  		}
   997  	}
   998  
   999  	// File is crafted with 16 entries. The later 8 are identical to the first
  1000  	// 8 except that the size is set.
  1001  	if len(hdrs) != 16 {
  1002  		t.Fatalf("len(hdrs): got %d, want %d", len(hdrs), 16)
  1003  	}
  1004  	for i := 0; i < 8; i++ {
  1005  		var hdr1, hdr2 = hdrs[i+0], hdrs[i+8]
  1006  		hdr1.Size, hdr2.Size = 0, 0
  1007  		if !reflect.DeepEqual(*hdr1, *hdr2) {
  1008  			t.Errorf("incorrect header:\ngot  %+v\nwant %+v", *hdr1, *hdr2)
  1009  		}
  1010  	}
  1011  }
  1012  
  1013  func TestParsePAXRecord(t *testing.T) {
  1014  	var medName = strings.Repeat("CD", 50)
  1015  	var longName = strings.Repeat("AB", 100)
  1016  
  1017  	var vectors = []struct {
  1018  		input     string
  1019  		residual  string
  1020  		outputKey string
  1021  		outputVal string
  1022  		ok        bool
  1023  	}{
  1024  		{"6 k=v\n\n", "\n", "k", "v", true},
  1025  		{"19 path=/etc/hosts\n", "", "path", "/etc/hosts", true},
  1026  		{"210 path=" + longName + "\nabc", "abc", "path", longName, true},
  1027  		{"110 path=" + medName + "\n", "", "path", medName, true},
  1028  		{"9 foo=ba\n", "", "foo", "ba", true},
  1029  		{"11 foo=bar\n\x00", "\x00", "foo", "bar", true},
  1030  		{"18 foo=b=\nar=\n==\x00\n", "", "foo", "b=\nar=\n==\x00", true},
  1031  		{"27 foo=hello9 foo=ba\nworld\n", "", "foo", "hello9 foo=ba\nworld", true},
  1032  		{"27 ☺☻☹=日a本b語ç\nmeow mix", "meow mix", "☺☻☹", "日a本b語ç", true},
  1033  		{"17 \x00hello=\x00world\n", "", "\x00hello", "\x00world", true},
  1034  		{"1 k=1\n", "1 k=1\n", "", "", false},
  1035  		{"6 k~1\n", "6 k~1\n", "", "", false},
  1036  		{"6_k=1\n", "6_k=1\n", "", "", false},
  1037  		{"6 k=1 ", "6 k=1 ", "", "", false},
  1038  		{"632 k=1\n", "632 k=1\n", "", "", false},
  1039  		{"16 longkeyname=hahaha\n", "16 longkeyname=hahaha\n", "", "", false},
  1040  		{"3 somelongkey=\n", "3 somelongkey=\n", "", "", false},
  1041  		{"50 tooshort=\n", "50 tooshort=\n", "", "", false},
  1042  	}
  1043  
  1044  	for _, v := range vectors {
  1045  		key, val, res, err := parsePAXRecord(v.input)
  1046  		ok := (err == nil)
  1047  		if v.ok != ok {
  1048  			if v.ok {
  1049  				t.Errorf("parsePAXRecord(%q): got parsing failure, want success", v.input)
  1050  			} else {
  1051  				t.Errorf("parsePAXRecord(%q): got parsing success, want failure", v.input)
  1052  			}
  1053  		}
  1054  		if ok && (key != v.outputKey || val != v.outputVal) {
  1055  			t.Errorf("parsePAXRecord(%q): got (%q: %q), want (%q: %q)",
  1056  				v.input, key, val, v.outputKey, v.outputVal)
  1057  		}
  1058  		if res != v.residual {
  1059  			t.Errorf("parsePAXRecord(%q): got residual %q, want residual %q",
  1060  				v.input, res, v.residual)
  1061  		}
  1062  	}
  1063  }
  1064  
  1065  func TestParseNumeric(t *testing.T) {
  1066  	var vectors = []struct {
  1067  		input  string
  1068  		output int64
  1069  		ok     bool
  1070  	}{
  1071  		// Test base-256 (binary) encoded values.
  1072  		{"", 0, true},
  1073  		{"\x80", 0, true},
  1074  		{"\x80\x00", 0, true},
  1075  		{"\x80\x00\x00", 0, true},
  1076  		{"\xbf", (1 << 6) - 1, true},
  1077  		{"\xbf\xff", (1 << 14) - 1, true},
  1078  		{"\xbf\xff\xff", (1 << 22) - 1, true},
  1079  		{"\xff", -1, true},
  1080  		{"\xff\xff", -1, true},
  1081  		{"\xff\xff\xff", -1, true},
  1082  		{"\xc0", -1 * (1 << 6), true},
  1083  		{"\xc0\x00", -1 * (1 << 14), true},
  1084  		{"\xc0\x00\x00", -1 * (1 << 22), true},
  1085  		{"\x87\x76\xa2\x22\xeb\x8a\x72\x61", 537795476381659745, true},
  1086  		{"\x80\x00\x00\x00\x07\x76\xa2\x22\xeb\x8a\x72\x61", 537795476381659745, true},
  1087  		{"\xf7\x76\xa2\x22\xeb\x8a\x72\x61", -615126028225187231, true},
  1088  		{"\xff\xff\xff\xff\xf7\x76\xa2\x22\xeb\x8a\x72\x61", -615126028225187231, true},
  1089  		{"\x80\x7f\xff\xff\xff\xff\xff\xff\xff", math.MaxInt64, true},
  1090  		{"\x80\x80\x00\x00\x00\x00\x00\x00\x00", 0, false},
  1091  		{"\xff\x80\x00\x00\x00\x00\x00\x00\x00", math.MinInt64, true},
  1092  		{"\xff\x7f\xff\xff\xff\xff\xff\xff\xff", 0, false},
  1093  		{"\xf5\xec\xd1\xc7\x7e\x5f\x26\x48\x81\x9f\x8f\x9b", 0, false},
  1094  
  1095  		// Test base-8 (octal) encoded values.
  1096  		{"0000000\x00", 0, true},
  1097  		{" \x0000000\x00", 0, true},
  1098  		{" \x0000003\x00", 3, true},
  1099  		{"00000000227\x00", 0227, true},
  1100  		{"032033\x00 ", 032033, true},
  1101  		{"320330\x00 ", 0320330, true},
  1102  		{"0000660\x00 ", 0660, true},
  1103  		{"\x00 0000660\x00 ", 0660, true},
  1104  		{"0123456789abcdef", 0, false},
  1105  		{"0123456789\x00abcdef", 0, false},
  1106  		{"01234567\x0089abcdef", 342391, true},
  1107  		{"0123\x7e\x5f\x264123", 0, false},
  1108  	}
  1109  
  1110  	for _, v := range vectors {
  1111  		var p parser
  1112  		num := p.parseNumeric([]byte(v.input))
  1113  		ok := (p.err == nil)
  1114  		if v.ok != ok {
  1115  			if v.ok {
  1116  				t.Errorf("parseNumeric(%q): got parsing failure, want success", v.input)
  1117  			} else {
  1118  				t.Errorf("parseNumeric(%q): got parsing success, want failure", v.input)
  1119  			}
  1120  		}
  1121  		if ok && num != v.output {
  1122  			t.Errorf("parseNumeric(%q): got %d, want %d", v.input, num, v.output)
  1123  		}
  1124  	}
  1125  }