github.com/karrick/go@v0.0.0-20170817181416-d5b0ec858b37/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  	"path"
    16  	"reflect"
    17  	"strings"
    18  	"testing"
    19  	"time"
    20  )
    21  
    22  func TestReader(t *testing.T) {
    23  	vectors := []struct {
    24  		file    string    // Test input file
    25  		headers []*Header // Expected output headers
    26  		chksums []string  // MD5 checksum of files, leave as nil if not checked
    27  		err     error     // Expected error to occur
    28  	}{{
    29  		file: "testdata/gnu.tar",
    30  		headers: []*Header{{
    31  			Name:     "small.txt",
    32  			Mode:     0640,
    33  			Uid:      73025,
    34  			Gid:      5000,
    35  			Size:     5,
    36  			ModTime:  time.Unix(1244428340, 0),
    37  			Typeflag: '0',
    38  			Uname:    "dsymonds",
    39  			Gname:    "eng",
    40  		}, {
    41  			Name:     "small2.txt",
    42  			Mode:     0640,
    43  			Uid:      73025,
    44  			Gid:      5000,
    45  			Size:     11,
    46  			ModTime:  time.Unix(1244436044, 0),
    47  			Typeflag: '0',
    48  			Uname:    "dsymonds",
    49  			Gname:    "eng",
    50  		}},
    51  		chksums: []string{
    52  			"e38b27eaccb4391bdec553a7f3ae6b2f",
    53  			"c65bd2e50a56a2138bf1716f2fd56fe9",
    54  		},
    55  	}, {
    56  		file: "testdata/sparse-formats.tar",
    57  		headers: []*Header{{
    58  			Name:     "sparse-gnu",
    59  			Mode:     420,
    60  			Uid:      1000,
    61  			Gid:      1000,
    62  			Size:     200,
    63  			ModTime:  time.Unix(1392395740, 0),
    64  			Typeflag: 0x53,
    65  			Linkname: "",
    66  			Uname:    "david",
    67  			Gname:    "david",
    68  			Devmajor: 0,
    69  			Devminor: 0,
    70  		}, {
    71  			Name:     "sparse-posix-0.0",
    72  			Mode:     420,
    73  			Uid:      1000,
    74  			Gid:      1000,
    75  			Size:     200,
    76  			ModTime:  time.Unix(1392342187, 0),
    77  			Typeflag: 0x30,
    78  			Linkname: "",
    79  			Uname:    "david",
    80  			Gname:    "david",
    81  			Devmajor: 0,
    82  			Devminor: 0,
    83  		}, {
    84  			Name:     "sparse-posix-0.1",
    85  			Mode:     420,
    86  			Uid:      1000,
    87  			Gid:      1000,
    88  			Size:     200,
    89  			ModTime:  time.Unix(1392340456, 0),
    90  			Typeflag: 0x30,
    91  			Linkname: "",
    92  			Uname:    "david",
    93  			Gname:    "david",
    94  			Devmajor: 0,
    95  			Devminor: 0,
    96  		}, {
    97  			Name:     "sparse-posix-1.0",
    98  			Mode:     420,
    99  			Uid:      1000,
   100  			Gid:      1000,
   101  			Size:     200,
   102  			ModTime:  time.Unix(1392337404, 0),
   103  			Typeflag: 0x30,
   104  			Linkname: "",
   105  			Uname:    "david",
   106  			Gname:    "david",
   107  			Devmajor: 0,
   108  			Devminor: 0,
   109  		}, {
   110  			Name:     "end",
   111  			Mode:     420,
   112  			Uid:      1000,
   113  			Gid:      1000,
   114  			Size:     4,
   115  			ModTime:  time.Unix(1392398319, 0),
   116  			Typeflag: 0x30,
   117  			Linkname: "",
   118  			Uname:    "david",
   119  			Gname:    "david",
   120  			Devmajor: 0,
   121  			Devminor: 0,
   122  		}},
   123  		chksums: []string{
   124  			"6f53234398c2449fe67c1812d993012f",
   125  			"6f53234398c2449fe67c1812d993012f",
   126  			"6f53234398c2449fe67c1812d993012f",
   127  			"6f53234398c2449fe67c1812d993012f",
   128  			"b0061974914468de549a2af8ced10316",
   129  		},
   130  	}, {
   131  		file: "testdata/star.tar",
   132  		headers: []*Header{{
   133  			Name:       "small.txt",
   134  			Mode:       0640,
   135  			Uid:        73025,
   136  			Gid:        5000,
   137  			Size:       5,
   138  			ModTime:    time.Unix(1244592783, 0),
   139  			Typeflag:   '0',
   140  			Uname:      "dsymonds",
   141  			Gname:      "eng",
   142  			AccessTime: time.Unix(1244592783, 0),
   143  			ChangeTime: time.Unix(1244592783, 0),
   144  		}, {
   145  			Name:       "small2.txt",
   146  			Mode:       0640,
   147  			Uid:        73025,
   148  			Gid:        5000,
   149  			Size:       11,
   150  			ModTime:    time.Unix(1244592783, 0),
   151  			Typeflag:   '0',
   152  			Uname:      "dsymonds",
   153  			Gname:      "eng",
   154  			AccessTime: time.Unix(1244592783, 0),
   155  			ChangeTime: time.Unix(1244592783, 0),
   156  		}},
   157  	}, {
   158  		file: "testdata/v7.tar",
   159  		headers: []*Header{{
   160  			Name:     "small.txt",
   161  			Mode:     0444,
   162  			Uid:      73025,
   163  			Gid:      5000,
   164  			Size:     5,
   165  			ModTime:  time.Unix(1244593104, 0),
   166  			Typeflag: '\x00',
   167  		}, {
   168  			Name:     "small2.txt",
   169  			Mode:     0444,
   170  			Uid:      73025,
   171  			Gid:      5000,
   172  			Size:     11,
   173  			ModTime:  time.Unix(1244593104, 0),
   174  			Typeflag: '\x00',
   175  		}},
   176  	}, {
   177  		file: "testdata/pax.tar",
   178  		headers: []*Header{{
   179  			Name:       "a/123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100",
   180  			Mode:       0664,
   181  			Uid:        1000,
   182  			Gid:        1000,
   183  			Uname:      "shane",
   184  			Gname:      "shane",
   185  			Size:       7,
   186  			ModTime:    time.Unix(1350244992, 23960108),
   187  			ChangeTime: time.Unix(1350244992, 23960108),
   188  			AccessTime: time.Unix(1350244992, 23960108),
   189  			Typeflag:   TypeReg,
   190  		}, {
   191  			Name:       "a/b",
   192  			Mode:       0777,
   193  			Uid:        1000,
   194  			Gid:        1000,
   195  			Uname:      "shane",
   196  			Gname:      "shane",
   197  			Size:       0,
   198  			ModTime:    time.Unix(1350266320, 910238425),
   199  			ChangeTime: time.Unix(1350266320, 910238425),
   200  			AccessTime: time.Unix(1350266320, 910238425),
   201  			Typeflag:   TypeSymlink,
   202  			Linkname:   "123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100",
   203  		}},
   204  	}, {
   205  		file: "testdata/pax-bad-hdr-file.tar",
   206  		err:  ErrHeader,
   207  	}, {
   208  		file: "testdata/pax-bad-mtime-file.tar",
   209  		err:  ErrHeader,
   210  	}, {
   211  		file: "testdata/pax-pos-size-file.tar",
   212  		headers: []*Header{{
   213  			Name:     "foo",
   214  			Mode:     0640,
   215  			Uid:      319973,
   216  			Gid:      5000,
   217  			Size:     999,
   218  			ModTime:  time.Unix(1442282516, 0),
   219  			Typeflag: '0',
   220  			Uname:    "joetsai",
   221  			Gname:    "eng",
   222  		}},
   223  		chksums: []string{
   224  			"0afb597b283fe61b5d4879669a350556",
   225  		},
   226  	}, {
   227  		file: "testdata/nil-uid.tar", // golang.org/issue/5290
   228  		headers: []*Header{{
   229  			Name:     "P1050238.JPG.log",
   230  			Mode:     0664,
   231  			Uid:      0,
   232  			Gid:      0,
   233  			Size:     14,
   234  			ModTime:  time.Unix(1365454838, 0),
   235  			Typeflag: TypeReg,
   236  			Linkname: "",
   237  			Uname:    "eyefi",
   238  			Gname:    "eyefi",
   239  			Devmajor: 0,
   240  			Devminor: 0,
   241  		}},
   242  	}, {
   243  		file: "testdata/xattrs.tar",
   244  		headers: []*Header{{
   245  			Name:       "small.txt",
   246  			Mode:       0644,
   247  			Uid:        1000,
   248  			Gid:        10,
   249  			Size:       5,
   250  			ModTime:    time.Unix(1386065770, 448252320),
   251  			Typeflag:   '0',
   252  			Uname:      "alex",
   253  			Gname:      "wheel",
   254  			AccessTime: time.Unix(1389782991, 419875220),
   255  			ChangeTime: time.Unix(1389782956, 794414986),
   256  			Xattrs: map[string]string{
   257  				"user.key":  "value",
   258  				"user.key2": "value2",
   259  				// Interestingly, selinux encodes the terminating null inside the xattr
   260  				"security.selinux": "unconfined_u:object_r:default_t:s0\x00",
   261  			},
   262  		}, {
   263  			Name:       "small2.txt",
   264  			Mode:       0644,
   265  			Uid:        1000,
   266  			Gid:        10,
   267  			Size:       11,
   268  			ModTime:    time.Unix(1386065770, 449252304),
   269  			Typeflag:   '0',
   270  			Uname:      "alex",
   271  			Gname:      "wheel",
   272  			AccessTime: time.Unix(1389782991, 419875220),
   273  			ChangeTime: time.Unix(1386065770, 449252304),
   274  			Xattrs: map[string]string{
   275  				"security.selinux": "unconfined_u:object_r:default_t:s0\x00",
   276  			},
   277  		}},
   278  	}, {
   279  		// Matches the behavior of GNU, BSD, and STAR tar utilities.
   280  		file: "testdata/gnu-multi-hdrs.tar",
   281  		headers: []*Header{{
   282  			Name:     "GNU2/GNU2/long-path-name",
   283  			Linkname: "GNU4/GNU4/long-linkpath-name",
   284  			ModTime:  time.Unix(0, 0),
   285  			Typeflag: '2',
   286  		}},
   287  	}, {
   288  		// GNU tar file with atime and ctime fields set.
   289  		// Created with the GNU tar v1.27.1.
   290  		//	tar --incremental -S -cvf gnu-incremental.tar test2
   291  		file: "testdata/gnu-incremental.tar",
   292  		headers: []*Header{{
   293  			Name:       "test2/",
   294  			Mode:       16877,
   295  			Uid:        1000,
   296  			Gid:        1000,
   297  			Size:       14,
   298  			ModTime:    time.Unix(1441973427, 0),
   299  			Typeflag:   'D',
   300  			Uname:      "rawr",
   301  			Gname:      "dsnet",
   302  			AccessTime: time.Unix(1441974501, 0),
   303  			ChangeTime: time.Unix(1441973436, 0),
   304  		}, {
   305  			Name:       "test2/foo",
   306  			Mode:       33188,
   307  			Uid:        1000,
   308  			Gid:        1000,
   309  			Size:       64,
   310  			ModTime:    time.Unix(1441973363, 0),
   311  			Typeflag:   '0',
   312  			Uname:      "rawr",
   313  			Gname:      "dsnet",
   314  			AccessTime: time.Unix(1441974501, 0),
   315  			ChangeTime: time.Unix(1441973436, 0),
   316  		}, {
   317  			Name:       "test2/sparse",
   318  			Mode:       33188,
   319  			Uid:        1000,
   320  			Gid:        1000,
   321  			Size:       536870912,
   322  			ModTime:    time.Unix(1441973427, 0),
   323  			Typeflag:   'S',
   324  			Uname:      "rawr",
   325  			Gname:      "dsnet",
   326  			AccessTime: time.Unix(1441991948, 0),
   327  			ChangeTime: time.Unix(1441973436, 0),
   328  		}},
   329  	}, {
   330  		// Matches the behavior of GNU and BSD tar utilities.
   331  		file: "testdata/pax-multi-hdrs.tar",
   332  		headers: []*Header{{
   333  			Name:     "bar",
   334  			Linkname: "PAX4/PAX4/long-linkpath-name",
   335  			ModTime:  time.Unix(0, 0),
   336  			Typeflag: '2',
   337  		}},
   338  	}, {
   339  		// Both BSD and GNU tar truncate long names at first NUL even
   340  		// if there is data following that NUL character.
   341  		// This is reasonable as GNU long names are C-strings.
   342  		file: "testdata/gnu-long-nul.tar",
   343  		headers: []*Header{{
   344  			Name:     "0123456789",
   345  			Mode:     0644,
   346  			Uid:      1000,
   347  			Gid:      1000,
   348  			ModTime:  time.Unix(1486082191, 0),
   349  			Typeflag: '0',
   350  			Uname:    "rawr",
   351  			Gname:    "dsnet",
   352  		}},
   353  	}, {
   354  		// This archive was generated by Writer but is readable by both
   355  		// GNU and BSD tar utilities.
   356  		// The archive generated by GNU is nearly byte-for-byte identical
   357  		// to the Go version except the Go version sets a negative Devminor
   358  		// just to force the GNU format.
   359  		file: "testdata/gnu-utf8.tar",
   360  		headers: []*Header{{
   361  			Name: "☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹",
   362  			Mode: 0644,
   363  			Uid:  1000, Gid: 1000,
   364  			ModTime:  time.Unix(0, 0),
   365  			Typeflag: '0',
   366  			Uname:    "☺",
   367  			Gname:    "⚹",
   368  			Devminor: -1,
   369  		}},
   370  	}, {
   371  		// This archive was generated by Writer but is readable by both
   372  		// GNU and BSD tar utilities.
   373  		// The archive generated by GNU is nearly byte-for-byte identical
   374  		// to the Go version except the Go version sets a negative Devminor
   375  		// just to force the GNU format.
   376  		file: "testdata/gnu-not-utf8.tar",
   377  		headers: []*Header{{
   378  			Name:     "hi\x80\x81\x82\x83bye",
   379  			Mode:     0644,
   380  			Uid:      1000,
   381  			Gid:      1000,
   382  			ModTime:  time.Unix(0, 0),
   383  			Typeflag: '0',
   384  			Uname:    "rawr",
   385  			Gname:    "dsnet",
   386  			Devminor: -1,
   387  		}},
   388  	}, {
   389  		// BSD tar v3.1.2 and GNU tar v1.27.1 both rejects PAX records
   390  		// with NULs in the key.
   391  		file: "testdata/pax-nul-xattrs.tar",
   392  		err:  ErrHeader,
   393  	}, {
   394  		// BSD tar v3.1.2 rejects a PAX path with NUL in the value, while
   395  		// GNU tar v1.27.1 simply truncates at first NUL.
   396  		// We emulate the behavior of BSD since it is strange doing NUL
   397  		// truncations since PAX records are length-prefix strings instead
   398  		// of NUL-terminated C-strings.
   399  		file: "testdata/pax-nul-path.tar",
   400  		err:  ErrHeader,
   401  	}, {
   402  		file: "testdata/neg-size.tar",
   403  		err:  ErrHeader,
   404  	}, {
   405  		file: "testdata/issue10968.tar",
   406  		err:  ErrHeader,
   407  	}, {
   408  		file: "testdata/issue11169.tar",
   409  		err:  ErrHeader,
   410  	}, {
   411  		file: "testdata/issue12435.tar",
   412  		err:  ErrHeader,
   413  	}, {
   414  		// Ensure that we can read back the original Header as written with
   415  		// a buggy pre-Go1.8 tar.Writer.
   416  		file: "testdata/invalid-go17.tar",
   417  		headers: []*Header{{
   418  			Name:    "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/foo",
   419  			Uid:     010000000,
   420  			ModTime: time.Unix(0, 0),
   421  		}},
   422  	}, {
   423  		// USTAR archive with a regular entry with non-zero device numbers.
   424  		file: "testdata/ustar-file-devs.tar",
   425  		headers: []*Header{{
   426  			Name:     "file",
   427  			Mode:     0644,
   428  			Typeflag: '0',
   429  			ModTime:  time.Unix(0, 0),
   430  			Devmajor: 1,
   431  			Devminor: 1,
   432  		}},
   433  	}}
   434  
   435  	for _, v := range vectors {
   436  		t.Run(path.Base(v.file), func(t *testing.T) {
   437  			f, err := os.Open(v.file)
   438  			if err != nil {
   439  				t.Fatalf("unexpected error: %v", err)
   440  			}
   441  			defer f.Close()
   442  
   443  			// Capture all headers and checksums.
   444  			var (
   445  				tr      = NewReader(f)
   446  				hdrs    []*Header
   447  				chksums []string
   448  				rdbuf   = make([]byte, 8)
   449  			)
   450  			for {
   451  				var hdr *Header
   452  				hdr, err = tr.Next()
   453  				if err != nil {
   454  					if err == io.EOF {
   455  						err = nil // Expected error
   456  					}
   457  					break
   458  				}
   459  				hdrs = append(hdrs, hdr)
   460  
   461  				if v.chksums == nil {
   462  					continue
   463  				}
   464  				h := md5.New()
   465  				_, err = io.CopyBuffer(h, tr, rdbuf) // Effectively an incremental read
   466  				if err != nil {
   467  					break
   468  				}
   469  				chksums = append(chksums, fmt.Sprintf("%x", h.Sum(nil)))
   470  			}
   471  
   472  			for i, hdr := range hdrs {
   473  				if i >= len(v.headers) {
   474  					t.Fatalf("entry %d: unexpected header:\ngot %+v", i, *hdr)
   475  					continue
   476  				}
   477  				if !reflect.DeepEqual(*hdr, *v.headers[i]) {
   478  					t.Fatalf("entry %d: incorrect header:\ngot  %+v\nwant %+v", i, *hdr, *v.headers[i])
   479  				}
   480  			}
   481  			if len(hdrs) != len(v.headers) {
   482  				t.Fatalf("got %d headers, want %d headers", len(hdrs), len(v.headers))
   483  			}
   484  
   485  			for i, sum := range chksums {
   486  				if i >= len(v.chksums) {
   487  					t.Fatalf("entry %d: unexpected sum: got %s", i, sum)
   488  					continue
   489  				}
   490  				if sum != v.chksums[i] {
   491  					t.Fatalf("entry %d: incorrect checksum: got %s, want %s", i, sum, v.chksums[i])
   492  				}
   493  			}
   494  
   495  			if err != v.err {
   496  				t.Fatalf("unexpected error: got %v, want %v", err, v.err)
   497  			}
   498  			f.Close()
   499  		})
   500  	}
   501  }
   502  
   503  func TestPartialRead(t *testing.T) {
   504  	type testCase struct {
   505  		cnt    int    // Number of bytes to read
   506  		output string // Expected value of string read
   507  	}
   508  	vectors := []struct {
   509  		file  string
   510  		cases []testCase
   511  	}{{
   512  		file: "testdata/gnu.tar",
   513  		cases: []testCase{
   514  			{4, "Kilt"},
   515  			{6, "Google"},
   516  		},
   517  	}, {
   518  		file: "testdata/sparse-formats.tar",
   519  		cases: []testCase{
   520  			{2, "\x00G"},
   521  			{4, "\x00G\x00o"},
   522  			{6, "\x00G\x00o\x00G"},
   523  			{8, "\x00G\x00o\x00G\x00o"},
   524  			{4, "end\n"},
   525  		},
   526  	}}
   527  
   528  	for _, v := range vectors {
   529  		t.Run(path.Base(v.file), func(t *testing.T) {
   530  			f, err := os.Open(v.file)
   531  			if err != nil {
   532  				t.Fatalf("Open() error: %v", err)
   533  			}
   534  			defer f.Close()
   535  
   536  			tr := NewReader(f)
   537  			for i, tc := range v.cases {
   538  				hdr, err := tr.Next()
   539  				if err != nil || hdr == nil {
   540  					t.Fatalf("entry %d, Next(): got %v, want %v", i, err, nil)
   541  				}
   542  				buf := make([]byte, tc.cnt)
   543  				if _, err := io.ReadFull(tr, buf); err != nil {
   544  					t.Fatalf("entry %d, ReadFull(): got %v, want %v", i, err, nil)
   545  				}
   546  				if string(buf) != tc.output {
   547  					t.Fatalf("entry %d, ReadFull(): got %q, want %q", i, string(buf), tc.output)
   548  				}
   549  			}
   550  
   551  			if _, err := tr.Next(); err != io.EOF {
   552  				t.Fatalf("Next(): got %v, want EOF", err)
   553  			}
   554  		})
   555  	}
   556  }
   557  
   558  func TestSparseFileReader(t *testing.T) {
   559  	vectors := []struct {
   560  		realSize   int64         // Real size of the output file
   561  		sparseMap  []sparseEntry // Input sparse map
   562  		sparseData string        // Input compact data
   563  		expected   string        // Expected output data
   564  		err        error         // Expected error outcome
   565  	}{{
   566  		realSize: 8,
   567  		sparseMap: []sparseEntry{
   568  			{offset: 0, numBytes: 2},
   569  			{offset: 5, numBytes: 3},
   570  		},
   571  		sparseData: "abcde",
   572  		expected:   "ab\x00\x00\x00cde",
   573  	}, {
   574  		realSize: 10,
   575  		sparseMap: []sparseEntry{
   576  			{offset: 0, numBytes: 2},
   577  			{offset: 5, numBytes: 3},
   578  		},
   579  		sparseData: "abcde",
   580  		expected:   "ab\x00\x00\x00cde\x00\x00",
   581  	}, {
   582  		realSize: 8,
   583  		sparseMap: []sparseEntry{
   584  			{offset: 1, numBytes: 3},
   585  			{offset: 6, numBytes: 2},
   586  		},
   587  		sparseData: "abcde",
   588  		expected:   "\x00abc\x00\x00de",
   589  	}, {
   590  		realSize: 8,
   591  		sparseMap: []sparseEntry{
   592  			{offset: 1, numBytes: 3},
   593  			{offset: 6, numBytes: 0},
   594  			{offset: 6, numBytes: 0},
   595  			{offset: 6, numBytes: 2},
   596  		},
   597  		sparseData: "abcde",
   598  		expected:   "\x00abc\x00\x00de",
   599  	}, {
   600  		realSize: 10,
   601  		sparseMap: []sparseEntry{
   602  			{offset: 1, numBytes: 3},
   603  			{offset: 6, numBytes: 2},
   604  		},
   605  		sparseData: "abcde",
   606  		expected:   "\x00abc\x00\x00de\x00\x00",
   607  	}, {
   608  		realSize: 10,
   609  		sparseMap: []sparseEntry{
   610  			{offset: 1, numBytes: 3},
   611  			{offset: 6, numBytes: 2},
   612  			{offset: 8, numBytes: 0},
   613  			{offset: 8, numBytes: 0},
   614  			{offset: 8, numBytes: 0},
   615  			{offset: 8, numBytes: 0},
   616  		},
   617  		sparseData: "abcde",
   618  		expected:   "\x00abc\x00\x00de\x00\x00",
   619  	}, {
   620  		realSize:   2,
   621  		sparseMap:  []sparseEntry{},
   622  		sparseData: "",
   623  		expected:   "\x00\x00",
   624  	}, {
   625  		realSize:  -2,
   626  		sparseMap: []sparseEntry{},
   627  		err:       ErrHeader,
   628  	}, {
   629  		realSize: -10,
   630  		sparseMap: []sparseEntry{
   631  			{offset: 1, numBytes: 3},
   632  			{offset: 6, numBytes: 2},
   633  		},
   634  		sparseData: "abcde",
   635  		err:        ErrHeader,
   636  	}, {
   637  		realSize: 10,
   638  		sparseMap: []sparseEntry{
   639  			{offset: 1, numBytes: 3},
   640  			{offset: 6, numBytes: 5},
   641  		},
   642  		sparseData: "abcde",
   643  		err:        ErrHeader,
   644  	}, {
   645  		realSize: 35,
   646  		sparseMap: []sparseEntry{
   647  			{offset: 1, numBytes: 3},
   648  			{offset: 6, numBytes: 5},
   649  		},
   650  		sparseData: "abcde",
   651  		err:        io.ErrUnexpectedEOF,
   652  	}, {
   653  		realSize: 35,
   654  		sparseMap: []sparseEntry{
   655  			{offset: 1, numBytes: 3},
   656  			{offset: 6, numBytes: -5},
   657  		},
   658  		sparseData: "abcde",
   659  		err:        ErrHeader,
   660  	}, {
   661  		realSize: 35,
   662  		sparseMap: []sparseEntry{
   663  			{offset: math.MaxInt64, numBytes: 3},
   664  			{offset: 6, numBytes: -5},
   665  		},
   666  		sparseData: "abcde",
   667  		err:        ErrHeader,
   668  	}, {
   669  		realSize: 10,
   670  		sparseMap: []sparseEntry{
   671  			{offset: 1, numBytes: 3},
   672  			{offset: 2, numBytes: 2},
   673  		},
   674  		sparseData: "abcde",
   675  		err:        ErrHeader,
   676  	}}
   677  
   678  	for i, v := range vectors {
   679  		r := bytes.NewReader([]byte(v.sparseData))
   680  		rfr := &regFileReader{r: r, nb: int64(len(v.sparseData))}
   681  
   682  		var (
   683  			sfr *sparseFileReader
   684  			err error
   685  			buf []byte
   686  		)
   687  
   688  		sfr, err = newSparseFileReader(rfr, v.sparseMap, v.realSize)
   689  		if err != nil {
   690  			goto fail
   691  		}
   692  		if sfr.numBytes() != int64(len(v.sparseData)) {
   693  			t.Errorf("test %d, numBytes() before reading: got %d, want %d", i, sfr.numBytes(), len(v.sparseData))
   694  		}
   695  		buf, err = ioutil.ReadAll(sfr)
   696  		if err != nil {
   697  			goto fail
   698  		}
   699  		if string(buf) != v.expected {
   700  			t.Errorf("test %d, ReadAll(): got %q, want %q", i, string(buf), v.expected)
   701  		}
   702  		if sfr.numBytes() != 0 {
   703  			t.Errorf("test %d, numBytes() after reading: got %d, want %d", i, sfr.numBytes(), 0)
   704  		}
   705  
   706  	fail:
   707  		if err != v.err {
   708  			t.Errorf("test %d, unexpected error: got %v, want %v", i, err, v.err)
   709  		}
   710  	}
   711  }
   712  
   713  func TestReadOldGNUSparseMap(t *testing.T) {
   714  	const (
   715  		t00 = "00000000000\x0000000000000\x00"
   716  		t11 = "00000000001\x0000000000001\x00"
   717  		t12 = "00000000001\x0000000000002\x00"
   718  		t21 = "00000000002\x0000000000001\x00"
   719  	)
   720  
   721  	mkBlk := func(size, sp0, sp1, sp2, sp3, ext string, format int) *block {
   722  		var blk block
   723  		copy(blk.GNU().RealSize(), size)
   724  		copy(blk.GNU().Sparse().Entry(0), sp0)
   725  		copy(blk.GNU().Sparse().Entry(1), sp1)
   726  		copy(blk.GNU().Sparse().Entry(2), sp2)
   727  		copy(blk.GNU().Sparse().Entry(3), sp3)
   728  		copy(blk.GNU().Sparse().IsExtended(), ext)
   729  		if format != formatUnknown {
   730  			blk.SetFormat(format)
   731  		}
   732  		return &blk
   733  	}
   734  
   735  	vectors := []struct {
   736  		data   string        // Input data
   737  		rawHdr *block        // Input raw header
   738  		want   []sparseEntry // Expected sparse entries to be outputted
   739  		err    error         // Expected error to be returned
   740  	}{
   741  		{"", mkBlk("", "", "", "", "", "", formatUnknown), nil, ErrHeader},
   742  		{"", mkBlk("1234", "fewa", "", "", "", "", formatGNU), nil, ErrHeader},
   743  		{"", mkBlk("0031", "", "", "", "", "", formatGNU), nil, nil},
   744  		{"", mkBlk("1234", t00, t11, "", "", "", formatGNU),
   745  			[]sparseEntry{{0, 0}, {1, 1}}, nil},
   746  		{"", mkBlk("1234", t11, t12, t21, t11, "", formatGNU),
   747  			[]sparseEntry{{1, 1}, {1, 2}, {2, 1}, {1, 1}}, nil},
   748  		{"", mkBlk("1234", t11, t12, t21, t11, "\x80", formatGNU),
   749  			[]sparseEntry{}, io.ErrUnexpectedEOF},
   750  		{t11 + t11,
   751  			mkBlk("1234", t11, t12, t21, t11, "\x80", formatGNU),
   752  			[]sparseEntry{}, io.ErrUnexpectedEOF},
   753  		{t11 + t21 + strings.Repeat("\x00", 512),
   754  			mkBlk("1234", t11, t12, t21, t11, "\x80", formatGNU),
   755  			[]sparseEntry{{1, 1}, {1, 2}, {2, 1}, {1, 1}, {1, 1}, {2, 1}}, nil},
   756  	}
   757  
   758  	for i, v := range vectors {
   759  		tr := Reader{r: strings.NewReader(v.data)}
   760  		hdr := new(Header)
   761  		got, err := tr.readOldGNUSparseMap(hdr, v.rawHdr)
   762  		if !reflect.DeepEqual(got, v.want) && !(len(got) == 0 && len(v.want) == 0) {
   763  			t.Errorf("test %d, readOldGNUSparseMap(...): got %v, want %v", i, got, v.want)
   764  		}
   765  		if err != v.err {
   766  			t.Errorf("test %d, unexpected error: got %v, want %v", i, err, v.err)
   767  		}
   768  	}
   769  }
   770  
   771  func TestReadGNUSparseMap0x1(t *testing.T) {
   772  	const (
   773  		maxUint = ^uint(0)
   774  		maxInt  = int(maxUint >> 1)
   775  	)
   776  	var (
   777  		big1 = fmt.Sprintf("%d", int64(maxInt))
   778  		big2 = fmt.Sprintf("%d", (int64(maxInt)/2)+1)
   779  		big3 = fmt.Sprintf("%d", (int64(maxInt) / 3))
   780  	)
   781  
   782  	vectors := []struct {
   783  		extHdrs   map[string]string // Input data
   784  		sparseMap []sparseEntry     // Expected sparse entries to be outputted
   785  		err       error             // Expected errors that may be raised
   786  	}{{
   787  		extHdrs: map[string]string{paxGNUSparseNumBlocks: "-4"},
   788  		err:     ErrHeader,
   789  	}, {
   790  		extHdrs: map[string]string{paxGNUSparseNumBlocks: "fee "},
   791  		err:     ErrHeader,
   792  	}, {
   793  		extHdrs: map[string]string{
   794  			paxGNUSparseNumBlocks: big1,
   795  			paxGNUSparseMap:       "0,5,10,5,20,5,30,5",
   796  		},
   797  		err: ErrHeader,
   798  	}, {
   799  		extHdrs: map[string]string{
   800  			paxGNUSparseNumBlocks: big2,
   801  			paxGNUSparseMap:       "0,5,10,5,20,5,30,5",
   802  		},
   803  		err: ErrHeader,
   804  	}, {
   805  		extHdrs: map[string]string{
   806  			paxGNUSparseNumBlocks: big3,
   807  			paxGNUSparseMap:       "0,5,10,5,20,5,30,5",
   808  		},
   809  		err: ErrHeader,
   810  	}, {
   811  		extHdrs: map[string]string{
   812  			paxGNUSparseNumBlocks: "4",
   813  			paxGNUSparseMap:       "0.5,5,10,5,20,5,30,5",
   814  		},
   815  		err: ErrHeader,
   816  	}, {
   817  		extHdrs: map[string]string{
   818  			paxGNUSparseNumBlocks: "4",
   819  			paxGNUSparseMap:       "0,5.5,10,5,20,5,30,5",
   820  		},
   821  		err: ErrHeader,
   822  	}, {
   823  		extHdrs: map[string]string{
   824  			paxGNUSparseNumBlocks: "4",
   825  			paxGNUSparseMap:       "0,fewafewa.5,fewafw,5,20,5,30,5",
   826  		},
   827  		err: ErrHeader,
   828  	}, {
   829  		extHdrs: map[string]string{
   830  			paxGNUSparseNumBlocks: "4",
   831  			paxGNUSparseMap:       "0,5,10,5,20,5,30,5",
   832  		},
   833  		sparseMap: []sparseEntry{{0, 5}, {10, 5}, {20, 5}, {30, 5}},
   834  	}}
   835  
   836  	for i, v := range vectors {
   837  		sp, err := readGNUSparseMap0x1(v.extHdrs)
   838  		if !reflect.DeepEqual(sp, v.sparseMap) && !(len(sp) == 0 && len(v.sparseMap) == 0) {
   839  			t.Errorf("test %d, readGNUSparseMap0x1(...): got %v, want %v", i, sp, v.sparseMap)
   840  		}
   841  		if err != v.err {
   842  			t.Errorf("test %d, unexpected error: got %v, want %v", i, err, v.err)
   843  		}
   844  	}
   845  }
   846  
   847  func TestReadGNUSparseMap1x0(t *testing.T) {
   848  	sp := []sparseEntry{{1, 2}, {3, 4}}
   849  	for i := 0; i < 98; i++ {
   850  		sp = append(sp, sparseEntry{54321, 12345})
   851  	}
   852  
   853  	vectors := []struct {
   854  		input     string        // Input data
   855  		sparseMap []sparseEntry // Expected sparse entries to be outputted
   856  		cnt       int           // Expected number of bytes read
   857  		err       error         // Expected errors that may be raised
   858  	}{{
   859  		input: "",
   860  		cnt:   0,
   861  		err:   io.ErrUnexpectedEOF,
   862  	}, {
   863  		input: "ab",
   864  		cnt:   2,
   865  		err:   io.ErrUnexpectedEOF,
   866  	}, {
   867  		input: strings.Repeat("\x00", 512),
   868  		cnt:   512,
   869  		err:   io.ErrUnexpectedEOF,
   870  	}, {
   871  		input: strings.Repeat("\x00", 511) + "\n",
   872  		cnt:   512,
   873  		err:   ErrHeader,
   874  	}, {
   875  		input: strings.Repeat("\n", 512),
   876  		cnt:   512,
   877  		err:   ErrHeader,
   878  	}, {
   879  		input:     "0\n" + strings.Repeat("\x00", 510) + strings.Repeat("a", 512),
   880  		sparseMap: []sparseEntry{},
   881  		cnt:       512,
   882  	}, {
   883  		input:     strings.Repeat("0", 512) + "0\n" + strings.Repeat("\x00", 510),
   884  		sparseMap: []sparseEntry{},
   885  		cnt:       1024,
   886  	}, {
   887  		input:     strings.Repeat("0", 1024) + "1\n2\n3\n" + strings.Repeat("\x00", 506),
   888  		sparseMap: []sparseEntry{{2, 3}},
   889  		cnt:       1536,
   890  	}, {
   891  		input: strings.Repeat("0", 1024) + "1\n2\n\n" + strings.Repeat("\x00", 509),
   892  		cnt:   1536,
   893  		err:   ErrHeader,
   894  	}, {
   895  		input: strings.Repeat("0", 1024) + "1\n2\n" + strings.Repeat("\x00", 508),
   896  		cnt:   1536,
   897  		err:   io.ErrUnexpectedEOF,
   898  	}, {
   899  		input: "-1\n2\n\n" + strings.Repeat("\x00", 506),
   900  		cnt:   512,
   901  		err:   ErrHeader,
   902  	}, {
   903  		input: "1\nk\n2\n" + strings.Repeat("\x00", 506),
   904  		cnt:   512,
   905  		err:   ErrHeader,
   906  	}, {
   907  		input:     "100\n1\n2\n3\n4\n" + strings.Repeat("54321\n0000000000000012345\n", 98) + strings.Repeat("\x00", 512),
   908  		cnt:       2560,
   909  		sparseMap: sp,
   910  	}}
   911  
   912  	for i, v := range vectors {
   913  		r := strings.NewReader(v.input)
   914  		sp, err := readGNUSparseMap1x0(r)
   915  		if !reflect.DeepEqual(sp, v.sparseMap) && !(len(sp) == 0 && len(v.sparseMap) == 0) {
   916  			t.Errorf("test %d, readGNUSparseMap1x0(...): got %v, want %v", i, sp, v.sparseMap)
   917  		}
   918  		if numBytes := len(v.input) - r.Len(); numBytes != v.cnt {
   919  			t.Errorf("test %d, bytes read: got %v, want %v", i, numBytes, v.cnt)
   920  		}
   921  		if err != v.err {
   922  			t.Errorf("test %d, unexpected error: got %v, want %v", i, err, v.err)
   923  		}
   924  	}
   925  }
   926  
   927  func TestUninitializedRead(t *testing.T) {
   928  	f, err := os.Open("testdata/gnu.tar")
   929  	if err != nil {
   930  		t.Fatalf("Unexpected error: %v", err)
   931  	}
   932  	defer f.Close()
   933  
   934  	tr := NewReader(f)
   935  	_, err = tr.Read([]byte{})
   936  	if err == nil || err != io.EOF {
   937  		t.Errorf("Unexpected error: %v, wanted %v", err, io.EOF)
   938  	}
   939  
   940  }
   941  
   942  type reader struct{ io.Reader }
   943  type readSeeker struct{ io.ReadSeeker }
   944  type readBadSeeker struct{ io.ReadSeeker }
   945  
   946  func (rbs *readBadSeeker) Seek(int64, int) (int64, error) { return 0, fmt.Errorf("illegal seek") }
   947  
   948  // TestReadTruncation test the ending condition on various truncated files and
   949  // that truncated files are still detected even if the underlying io.Reader
   950  // satisfies io.Seeker.
   951  func TestReadTruncation(t *testing.T) {
   952  	var ss []string
   953  	for _, p := range []string{
   954  		"testdata/gnu.tar",
   955  		"testdata/ustar-file-reg.tar",
   956  		"testdata/pax-path-hdr.tar",
   957  		"testdata/sparse-formats.tar",
   958  	} {
   959  		buf, err := ioutil.ReadFile(p)
   960  		if err != nil {
   961  			t.Fatalf("unexpected error: %v", err)
   962  		}
   963  		ss = append(ss, string(buf))
   964  	}
   965  
   966  	data1, data2, pax, sparse := ss[0], ss[1], ss[2], ss[3]
   967  	data2 += strings.Repeat("\x00", 10*512)
   968  	trash := strings.Repeat("garbage ", 64) // Exactly 512 bytes
   969  
   970  	vectors := []struct {
   971  		input string // Input stream
   972  		cnt   int    // Expected number of headers read
   973  		err   error  // Expected error outcome
   974  	}{
   975  		{"", 0, io.EOF}, // Empty file is a "valid" tar file
   976  		{data1[:511], 0, io.ErrUnexpectedEOF},
   977  		{data1[:512], 1, io.ErrUnexpectedEOF},
   978  		{data1[:1024], 1, io.EOF},
   979  		{data1[:1536], 2, io.ErrUnexpectedEOF},
   980  		{data1[:2048], 2, io.EOF},
   981  		{data1, 2, io.EOF},
   982  		{data1[:2048] + data2[:1536], 3, io.EOF},
   983  		{data2[:511], 0, io.ErrUnexpectedEOF},
   984  		{data2[:512], 1, io.ErrUnexpectedEOF},
   985  		{data2[:1195], 1, io.ErrUnexpectedEOF},
   986  		{data2[:1196], 1, io.EOF}, // Exact end of data and start of padding
   987  		{data2[:1200], 1, io.EOF},
   988  		{data2[:1535], 1, io.EOF},
   989  		{data2[:1536], 1, io.EOF}, // Exact end of padding
   990  		{data2[:1536] + trash[:1], 1, io.ErrUnexpectedEOF},
   991  		{data2[:1536] + trash[:511], 1, io.ErrUnexpectedEOF},
   992  		{data2[:1536] + trash, 1, ErrHeader},
   993  		{data2[:2048], 1, io.EOF}, // Exactly 1 empty block
   994  		{data2[:2048] + trash[:1], 1, io.ErrUnexpectedEOF},
   995  		{data2[:2048] + trash[:511], 1, io.ErrUnexpectedEOF},
   996  		{data2[:2048] + trash, 1, ErrHeader},
   997  		{data2[:2560], 1, io.EOF}, // Exactly 2 empty blocks (normal end-of-stream)
   998  		{data2[:2560] + trash[:1], 1, io.EOF},
   999  		{data2[:2560] + trash[:511], 1, io.EOF},
  1000  		{data2[:2560] + trash, 1, io.EOF},
  1001  		{data2[:3072], 1, io.EOF},
  1002  		{pax, 0, io.EOF}, // PAX header without data is a "valid" tar file
  1003  		{pax + trash[:1], 0, io.ErrUnexpectedEOF},
  1004  		{pax + trash[:511], 0, io.ErrUnexpectedEOF},
  1005  		{sparse[:511], 0, io.ErrUnexpectedEOF},
  1006  		{sparse[:512], 0, io.ErrUnexpectedEOF},
  1007  		{sparse[:3584], 1, io.EOF},
  1008  		{sparse[:9200], 1, io.EOF}, // Terminate in padding of sparse header
  1009  		{sparse[:9216], 1, io.EOF},
  1010  		{sparse[:9728], 2, io.ErrUnexpectedEOF},
  1011  		{sparse[:10240], 2, io.EOF},
  1012  		{sparse[:11264], 2, io.ErrUnexpectedEOF},
  1013  		{sparse, 5, io.EOF},
  1014  		{sparse + trash, 5, io.EOF},
  1015  	}
  1016  
  1017  	for i, v := range vectors {
  1018  		for j := 0; j < 6; j++ {
  1019  			var tr *Reader
  1020  			var s1, s2 string
  1021  
  1022  			switch j {
  1023  			case 0:
  1024  				tr = NewReader(&reader{strings.NewReader(v.input)})
  1025  				s1, s2 = "io.Reader", "auto"
  1026  			case 1:
  1027  				tr = NewReader(&reader{strings.NewReader(v.input)})
  1028  				s1, s2 = "io.Reader", "manual"
  1029  			case 2:
  1030  				tr = NewReader(&readSeeker{strings.NewReader(v.input)})
  1031  				s1, s2 = "io.ReadSeeker", "auto"
  1032  			case 3:
  1033  				tr = NewReader(&readSeeker{strings.NewReader(v.input)})
  1034  				s1, s2 = "io.ReadSeeker", "manual"
  1035  			case 4:
  1036  				tr = NewReader(&readBadSeeker{strings.NewReader(v.input)})
  1037  				s1, s2 = "ReadBadSeeker", "auto"
  1038  			case 5:
  1039  				tr = NewReader(&readBadSeeker{strings.NewReader(v.input)})
  1040  				s1, s2 = "ReadBadSeeker", "manual"
  1041  			}
  1042  
  1043  			var cnt int
  1044  			var err error
  1045  			for {
  1046  				if _, err = tr.Next(); err != nil {
  1047  					break
  1048  				}
  1049  				cnt++
  1050  				if s2 == "manual" {
  1051  					if _, err = io.Copy(ioutil.Discard, tr); err != nil {
  1052  						break
  1053  					}
  1054  				}
  1055  			}
  1056  			if err != v.err {
  1057  				t.Errorf("test %d, NewReader(%s(...)) with %s discard: got %v, want %v",
  1058  					i, s1, s2, err, v.err)
  1059  			}
  1060  			if cnt != v.cnt {
  1061  				t.Errorf("test %d, NewReader(%s(...)) with %s discard: got %d headers, want %d headers",
  1062  					i, s1, s2, cnt, v.cnt)
  1063  			}
  1064  		}
  1065  	}
  1066  }
  1067  
  1068  // TestReadHeaderOnly tests that Reader does not attempt to read special
  1069  // header-only files.
  1070  func TestReadHeaderOnly(t *testing.T) {
  1071  	f, err := os.Open("testdata/hdr-only.tar")
  1072  	if err != nil {
  1073  		t.Fatalf("unexpected error: %v", err)
  1074  	}
  1075  	defer f.Close()
  1076  
  1077  	var hdrs []*Header
  1078  	tr := NewReader(f)
  1079  	for {
  1080  		hdr, err := tr.Next()
  1081  		if err == io.EOF {
  1082  			break
  1083  		}
  1084  		if err != nil {
  1085  			t.Errorf("Next(): got %v, want %v", err, nil)
  1086  			continue
  1087  		}
  1088  		hdrs = append(hdrs, hdr)
  1089  
  1090  		// If a special flag, we should read nothing.
  1091  		cnt, _ := io.ReadFull(tr, []byte{0})
  1092  		if cnt > 0 && hdr.Typeflag != TypeReg {
  1093  			t.Errorf("ReadFull(...): got %d bytes, want 0 bytes", cnt)
  1094  		}
  1095  	}
  1096  
  1097  	// File is crafted with 16 entries. The later 8 are identical to the first
  1098  	// 8 except that the size is set.
  1099  	if len(hdrs) != 16 {
  1100  		t.Fatalf("len(hdrs): got %d, want %d", len(hdrs), 16)
  1101  	}
  1102  	for i := 0; i < 8; i++ {
  1103  		hdr1, hdr2 := hdrs[i+0], hdrs[i+8]
  1104  		hdr1.Size, hdr2.Size = 0, 0
  1105  		if !reflect.DeepEqual(*hdr1, *hdr2) {
  1106  			t.Errorf("incorrect header:\ngot  %+v\nwant %+v", *hdr1, *hdr2)
  1107  		}
  1108  	}
  1109  }
  1110  
  1111  func TestMergePAX(t *testing.T) {
  1112  	vectors := []struct {
  1113  		in   map[string]string
  1114  		want *Header
  1115  		ok   bool
  1116  	}{{
  1117  		in: map[string]string{
  1118  			"path":  "a/b/c",
  1119  			"uid":   "1000",
  1120  			"mtime": "1350244992.023960108",
  1121  		},
  1122  		want: &Header{
  1123  			Name:    "a/b/c",
  1124  			Uid:     1000,
  1125  			ModTime: time.Unix(1350244992, 23960108),
  1126  		},
  1127  		ok: true,
  1128  	}, {
  1129  		in: map[string]string{
  1130  			"gid": "gtgergergersagersgers",
  1131  		},
  1132  	}, {
  1133  		in: map[string]string{
  1134  			"missing":          "missing",
  1135  			"SCHILY.xattr.key": "value",
  1136  		},
  1137  		want: &Header{
  1138  			Xattrs: map[string]string{"key": "value"},
  1139  		},
  1140  		ok: true,
  1141  	}}
  1142  
  1143  	for i, v := range vectors {
  1144  		got := new(Header)
  1145  		err := mergePAX(got, v.in)
  1146  		if v.ok && !reflect.DeepEqual(*got, *v.want) {
  1147  			t.Errorf("test %d, mergePAX(...):\ngot  %+v\nwant %+v", i, *got, *v.want)
  1148  		}
  1149  		if ok := err == nil; ok != v.ok {
  1150  			t.Errorf("test %d, mergePAX(...): got %v, want %v", i, ok, v.ok)
  1151  		}
  1152  	}
  1153  }
  1154  
  1155  func TestParsePAX(t *testing.T) {
  1156  	vectors := []struct {
  1157  		in   string
  1158  		want map[string]string
  1159  		ok   bool
  1160  	}{
  1161  		{"", nil, true},
  1162  		{"6 k=1\n", map[string]string{"k": "1"}, true},
  1163  		{"10 a=name\n", map[string]string{"a": "name"}, true},
  1164  		{"9 a=name\n", map[string]string{"a": "name"}, true},
  1165  		{"30 mtime=1350244992.023960108\n", map[string]string{"mtime": "1350244992.023960108"}, true},
  1166  		{"3 somelongkey=\n", nil, false},
  1167  		{"50 tooshort=\n", nil, false},
  1168  		{"13 key1=haha\n13 key2=nana\n13 key3=kaka\n",
  1169  			map[string]string{"key1": "haha", "key2": "nana", "key3": "kaka"}, true},
  1170  		{"13 key1=val1\n13 key2=val2\n8 key1=\n",
  1171  			map[string]string{"key2": "val2"}, true},
  1172  		{"22 GNU.sparse.size=10\n26 GNU.sparse.numblocks=2\n" +
  1173  			"23 GNU.sparse.offset=1\n25 GNU.sparse.numbytes=2\n" +
  1174  			"23 GNU.sparse.offset=3\n25 GNU.sparse.numbytes=4\n",
  1175  			map[string]string{paxGNUSparseSize: "10", paxGNUSparseNumBlocks: "2", paxGNUSparseMap: "1,2,3,4"}, true},
  1176  		{"22 GNU.sparse.size=10\n26 GNU.sparse.numblocks=1\n" +
  1177  			"25 GNU.sparse.numbytes=2\n23 GNU.sparse.offset=1\n",
  1178  			nil, false},
  1179  		{"22 GNU.sparse.size=10\n26 GNU.sparse.numblocks=1\n" +
  1180  			"25 GNU.sparse.offset=1,2\n25 GNU.sparse.numbytes=2\n",
  1181  			nil, false},
  1182  	}
  1183  
  1184  	for i, v := range vectors {
  1185  		r := strings.NewReader(v.in)
  1186  		got, err := parsePAX(r)
  1187  		if !reflect.DeepEqual(got, v.want) && !(len(got) == 0 && len(v.want) == 0) {
  1188  			t.Errorf("test %d, parsePAX(...):\ngot  %v\nwant %v", i, got, v.want)
  1189  		}
  1190  		if ok := err == nil; ok != v.ok {
  1191  			t.Errorf("test %d, parsePAX(...): got %v, want %v", i, ok, v.ok)
  1192  		}
  1193  	}
  1194  }