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 := ®FileReader{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 }