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