github.com/eun/go@v0.0.0-20170811110501-92cfd07a6cfd/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 // BSD tar v3.1.2 and GNU tar v1.27.1 both rejects PAX records 355 // with NULs in the key. 356 file: "testdata/pax-nul-xattrs.tar", 357 err: ErrHeader, 358 }, { 359 // BSD tar v3.1.2 rejects a PAX path with NUL in the value, while 360 // GNU tar v1.27.1 simply truncates at first NUL. 361 // We emulate the behavior of BSD since it is strange doing NUL 362 // truncations since PAX records are length-prefix strings instead 363 // of NUL-terminated C-strings. 364 file: "testdata/pax-nul-path.tar", 365 err: ErrHeader, 366 }, { 367 file: "testdata/neg-size.tar", 368 err: ErrHeader, 369 }, { 370 file: "testdata/issue10968.tar", 371 err: ErrHeader, 372 }, { 373 file: "testdata/issue11169.tar", 374 err: ErrHeader, 375 }, { 376 file: "testdata/issue12435.tar", 377 err: ErrHeader, 378 }, { 379 // Ensure that we can read back the original Header as written with 380 // a buggy pre-Go1.8 tar.Writer. 381 file: "testdata/invalid-go17.tar", 382 headers: []*Header{{ 383 Name: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/foo", 384 Uid: 010000000, 385 ModTime: time.Unix(0, 0), 386 }}, 387 }} 388 389 for _, v := range vectors { 390 t.Run(path.Base(v.file), func(t *testing.T) { 391 f, err := os.Open(v.file) 392 if err != nil { 393 t.Fatalf("unexpected error: %v", err) 394 } 395 defer f.Close() 396 397 // Capture all headers and checksums. 398 var ( 399 tr = NewReader(f) 400 hdrs []*Header 401 chksums []string 402 rdbuf = make([]byte, 8) 403 ) 404 for { 405 var hdr *Header 406 hdr, err = tr.Next() 407 if err != nil { 408 if err == io.EOF { 409 err = nil // Expected error 410 } 411 break 412 } 413 hdrs = append(hdrs, hdr) 414 415 if v.chksums == nil { 416 continue 417 } 418 h := md5.New() 419 _, err = io.CopyBuffer(h, tr, rdbuf) // Effectively an incremental read 420 if err != nil { 421 break 422 } 423 chksums = append(chksums, fmt.Sprintf("%x", h.Sum(nil))) 424 } 425 426 for i, hdr := range hdrs { 427 if i >= len(v.headers) { 428 t.Fatalf("entry %d: unexpected header:\ngot %+v", i, *hdr) 429 continue 430 } 431 if !reflect.DeepEqual(*hdr, *v.headers[i]) { 432 t.Fatalf("entry %d: incorrect header:\ngot %+v\nwant %+v", i, *hdr, *v.headers[i]) 433 } 434 } 435 if len(hdrs) != len(v.headers) { 436 t.Fatalf("got %d headers, want %d headers", len(hdrs), len(v.headers)) 437 } 438 439 for i, sum := range chksums { 440 if i >= len(v.chksums) { 441 t.Fatalf("entry %d: unexpected sum: got %s", i, sum) 442 continue 443 } 444 if sum != v.chksums[i] { 445 t.Fatalf("entry %d: incorrect checksum: got %s, want %s", i, sum, v.chksums[i]) 446 } 447 } 448 449 if err != v.err { 450 t.Fatalf("unexpected error: got %v, want %v", err, v.err) 451 } 452 f.Close() 453 }) 454 } 455 } 456 457 func TestPartialRead(t *testing.T) { 458 type testCase struct { 459 cnt int // Number of bytes to read 460 output string // Expected value of string read 461 } 462 vectors := []struct { 463 file string 464 cases []testCase 465 }{{ 466 file: "testdata/gnu.tar", 467 cases: []testCase{ 468 {4, "Kilt"}, 469 {6, "Google"}, 470 }, 471 }, { 472 file: "testdata/sparse-formats.tar", 473 cases: []testCase{ 474 {2, "\x00G"}, 475 {4, "\x00G\x00o"}, 476 {6, "\x00G\x00o\x00G"}, 477 {8, "\x00G\x00o\x00G\x00o"}, 478 {4, "end\n"}, 479 }, 480 }} 481 482 for _, v := range vectors { 483 t.Run(path.Base(v.file), func(t *testing.T) { 484 f, err := os.Open(v.file) 485 if err != nil { 486 t.Fatalf("Open() error: %v", err) 487 } 488 defer f.Close() 489 490 tr := NewReader(f) 491 for i, tc := range v.cases { 492 hdr, err := tr.Next() 493 if err != nil || hdr == nil { 494 t.Fatalf("entry %d, Next(): got %v, want %v", i, err, nil) 495 } 496 buf := make([]byte, tc.cnt) 497 if _, err := io.ReadFull(tr, buf); err != nil { 498 t.Fatalf("entry %d, ReadFull(): got %v, want %v", i, err, nil) 499 } 500 if string(buf) != tc.output { 501 t.Fatalf("entry %d, ReadFull(): got %q, want %q", i, string(buf), tc.output) 502 } 503 } 504 505 if _, err := tr.Next(); err != io.EOF { 506 t.Fatalf("Next(): got %v, want EOF", err) 507 } 508 }) 509 } 510 } 511 512 func TestSparseFileReader(t *testing.T) { 513 vectors := []struct { 514 realSize int64 // Real size of the output file 515 sparseMap []sparseEntry // Input sparse map 516 sparseData string // Input compact data 517 expected string // Expected output data 518 err error // Expected error outcome 519 }{{ 520 realSize: 8, 521 sparseMap: []sparseEntry{ 522 {offset: 0, numBytes: 2}, 523 {offset: 5, numBytes: 3}, 524 }, 525 sparseData: "abcde", 526 expected: "ab\x00\x00\x00cde", 527 }, { 528 realSize: 10, 529 sparseMap: []sparseEntry{ 530 {offset: 0, numBytes: 2}, 531 {offset: 5, numBytes: 3}, 532 }, 533 sparseData: "abcde", 534 expected: "ab\x00\x00\x00cde\x00\x00", 535 }, { 536 realSize: 8, 537 sparseMap: []sparseEntry{ 538 {offset: 1, numBytes: 3}, 539 {offset: 6, numBytes: 2}, 540 }, 541 sparseData: "abcde", 542 expected: "\x00abc\x00\x00de", 543 }, { 544 realSize: 8, 545 sparseMap: []sparseEntry{ 546 {offset: 1, numBytes: 3}, 547 {offset: 6, numBytes: 0}, 548 {offset: 6, numBytes: 0}, 549 {offset: 6, numBytes: 2}, 550 }, 551 sparseData: "abcde", 552 expected: "\x00abc\x00\x00de", 553 }, { 554 realSize: 10, 555 sparseMap: []sparseEntry{ 556 {offset: 1, numBytes: 3}, 557 {offset: 6, numBytes: 2}, 558 }, 559 sparseData: "abcde", 560 expected: "\x00abc\x00\x00de\x00\x00", 561 }, { 562 realSize: 10, 563 sparseMap: []sparseEntry{ 564 {offset: 1, numBytes: 3}, 565 {offset: 6, numBytes: 2}, 566 {offset: 8, numBytes: 0}, 567 {offset: 8, numBytes: 0}, 568 {offset: 8, numBytes: 0}, 569 {offset: 8, numBytes: 0}, 570 }, 571 sparseData: "abcde", 572 expected: "\x00abc\x00\x00de\x00\x00", 573 }, { 574 realSize: 2, 575 sparseMap: []sparseEntry{}, 576 sparseData: "", 577 expected: "\x00\x00", 578 }, { 579 realSize: -2, 580 sparseMap: []sparseEntry{}, 581 err: ErrHeader, 582 }, { 583 realSize: -10, 584 sparseMap: []sparseEntry{ 585 {offset: 1, numBytes: 3}, 586 {offset: 6, numBytes: 2}, 587 }, 588 sparseData: "abcde", 589 err: ErrHeader, 590 }, { 591 realSize: 10, 592 sparseMap: []sparseEntry{ 593 {offset: 1, numBytes: 3}, 594 {offset: 6, numBytes: 5}, 595 }, 596 sparseData: "abcde", 597 err: ErrHeader, 598 }, { 599 realSize: 35, 600 sparseMap: []sparseEntry{ 601 {offset: 1, numBytes: 3}, 602 {offset: 6, numBytes: 5}, 603 }, 604 sparseData: "abcde", 605 err: io.ErrUnexpectedEOF, 606 }, { 607 realSize: 35, 608 sparseMap: []sparseEntry{ 609 {offset: 1, numBytes: 3}, 610 {offset: 6, numBytes: -5}, 611 }, 612 sparseData: "abcde", 613 err: ErrHeader, 614 }, { 615 realSize: 35, 616 sparseMap: []sparseEntry{ 617 {offset: math.MaxInt64, numBytes: 3}, 618 {offset: 6, numBytes: -5}, 619 }, 620 sparseData: "abcde", 621 err: ErrHeader, 622 }, { 623 realSize: 10, 624 sparseMap: []sparseEntry{ 625 {offset: 1, numBytes: 3}, 626 {offset: 2, numBytes: 2}, 627 }, 628 sparseData: "abcde", 629 err: ErrHeader, 630 }} 631 632 for i, v := range vectors { 633 r := bytes.NewReader([]byte(v.sparseData)) 634 rfr := ®FileReader{r: r, nb: int64(len(v.sparseData))} 635 636 var ( 637 sfr *sparseFileReader 638 err error 639 buf []byte 640 ) 641 642 sfr, err = newSparseFileReader(rfr, v.sparseMap, v.realSize) 643 if err != nil { 644 goto fail 645 } 646 if sfr.numBytes() != int64(len(v.sparseData)) { 647 t.Errorf("test %d, numBytes() before reading: got %d, want %d", i, sfr.numBytes(), len(v.sparseData)) 648 } 649 buf, err = ioutil.ReadAll(sfr) 650 if err != nil { 651 goto fail 652 } 653 if string(buf) != v.expected { 654 t.Errorf("test %d, ReadAll(): got %q, want %q", i, string(buf), v.expected) 655 } 656 if sfr.numBytes() != 0 { 657 t.Errorf("test %d, numBytes() after reading: got %d, want %d", i, sfr.numBytes(), 0) 658 } 659 660 fail: 661 if err != v.err { 662 t.Errorf("test %d, unexpected error: got %v, want %v", i, err, v.err) 663 } 664 } 665 } 666 667 func TestReadOldGNUSparseMap(t *testing.T) { 668 const ( 669 t00 = "00000000000\x0000000000000\x00" 670 t11 = "00000000001\x0000000000001\x00" 671 t12 = "00000000001\x0000000000002\x00" 672 t21 = "00000000002\x0000000000001\x00" 673 ) 674 675 mkBlk := func(size, sp0, sp1, sp2, sp3, ext string, format int) *block { 676 var blk block 677 copy(blk.GNU().RealSize(), size) 678 copy(blk.GNU().Sparse().Entry(0), sp0) 679 copy(blk.GNU().Sparse().Entry(1), sp1) 680 copy(blk.GNU().Sparse().Entry(2), sp2) 681 copy(blk.GNU().Sparse().Entry(3), sp3) 682 copy(blk.GNU().Sparse().IsExtended(), ext) 683 if format != formatUnknown { 684 blk.SetFormat(format) 685 } 686 return &blk 687 } 688 689 vectors := []struct { 690 data string // Input data 691 rawHdr *block // Input raw header 692 want []sparseEntry // Expected sparse entries to be outputted 693 err error // Expected error to be returned 694 }{ 695 {"", mkBlk("", "", "", "", "", "", formatUnknown), nil, ErrHeader}, 696 {"", mkBlk("1234", "fewa", "", "", "", "", formatGNU), nil, ErrHeader}, 697 {"", mkBlk("0031", "", "", "", "", "", formatGNU), nil, nil}, 698 {"", mkBlk("1234", t00, t11, "", "", "", formatGNU), 699 []sparseEntry{{0, 0}, {1, 1}}, nil}, 700 {"", mkBlk("1234", t11, t12, t21, t11, "", formatGNU), 701 []sparseEntry{{1, 1}, {1, 2}, {2, 1}, {1, 1}}, nil}, 702 {"", mkBlk("1234", t11, t12, t21, t11, "\x80", formatGNU), 703 []sparseEntry{}, io.ErrUnexpectedEOF}, 704 {t11 + t11, 705 mkBlk("1234", t11, t12, t21, t11, "\x80", formatGNU), 706 []sparseEntry{}, io.ErrUnexpectedEOF}, 707 {t11 + t21 + strings.Repeat("\x00", 512), 708 mkBlk("1234", t11, t12, t21, t11, "\x80", formatGNU), 709 []sparseEntry{{1, 1}, {1, 2}, {2, 1}, {1, 1}, {1, 1}, {2, 1}}, nil}, 710 } 711 712 for i, v := range vectors { 713 tr := Reader{r: strings.NewReader(v.data)} 714 hdr := new(Header) 715 got, err := tr.readOldGNUSparseMap(hdr, v.rawHdr) 716 if !reflect.DeepEqual(got, v.want) && !(len(got) == 0 && len(v.want) == 0) { 717 t.Errorf("test %d, readOldGNUSparseMap(...): got %v, want %v", i, got, v.want) 718 } 719 if err != v.err { 720 t.Errorf("test %d, unexpected error: got %v, want %v", i, err, v.err) 721 } 722 } 723 } 724 725 func TestReadGNUSparseMap0x1(t *testing.T) { 726 const ( 727 maxUint = ^uint(0) 728 maxInt = int(maxUint >> 1) 729 ) 730 var ( 731 big1 = fmt.Sprintf("%d", int64(maxInt)) 732 big2 = fmt.Sprintf("%d", (int64(maxInt)/2)+1) 733 big3 = fmt.Sprintf("%d", (int64(maxInt) / 3)) 734 ) 735 736 vectors := []struct { 737 extHdrs map[string]string // Input data 738 sparseMap []sparseEntry // Expected sparse entries to be outputted 739 err error // Expected errors that may be raised 740 }{{ 741 extHdrs: map[string]string{paxGNUSparseNumBlocks: "-4"}, 742 err: ErrHeader, 743 }, { 744 extHdrs: map[string]string{paxGNUSparseNumBlocks: "fee "}, 745 err: ErrHeader, 746 }, { 747 extHdrs: map[string]string{ 748 paxGNUSparseNumBlocks: big1, 749 paxGNUSparseMap: "0,5,10,5,20,5,30,5", 750 }, 751 err: ErrHeader, 752 }, { 753 extHdrs: map[string]string{ 754 paxGNUSparseNumBlocks: big2, 755 paxGNUSparseMap: "0,5,10,5,20,5,30,5", 756 }, 757 err: ErrHeader, 758 }, { 759 extHdrs: map[string]string{ 760 paxGNUSparseNumBlocks: big3, 761 paxGNUSparseMap: "0,5,10,5,20,5,30,5", 762 }, 763 err: ErrHeader, 764 }, { 765 extHdrs: map[string]string{ 766 paxGNUSparseNumBlocks: "4", 767 paxGNUSparseMap: "0.5,5,10,5,20,5,30,5", 768 }, 769 err: ErrHeader, 770 }, { 771 extHdrs: map[string]string{ 772 paxGNUSparseNumBlocks: "4", 773 paxGNUSparseMap: "0,5.5,10,5,20,5,30,5", 774 }, 775 err: ErrHeader, 776 }, { 777 extHdrs: map[string]string{ 778 paxGNUSparseNumBlocks: "4", 779 paxGNUSparseMap: "0,fewafewa.5,fewafw,5,20,5,30,5", 780 }, 781 err: ErrHeader, 782 }, { 783 extHdrs: map[string]string{ 784 paxGNUSparseNumBlocks: "4", 785 paxGNUSparseMap: "0,5,10,5,20,5,30,5", 786 }, 787 sparseMap: []sparseEntry{{0, 5}, {10, 5}, {20, 5}, {30, 5}}, 788 }} 789 790 for i, v := range vectors { 791 sp, err := readGNUSparseMap0x1(v.extHdrs) 792 if !reflect.DeepEqual(sp, v.sparseMap) && !(len(sp) == 0 && len(v.sparseMap) == 0) { 793 t.Errorf("test %d, readGNUSparseMap0x1(...): got %v, want %v", i, sp, v.sparseMap) 794 } 795 if err != v.err { 796 t.Errorf("test %d, unexpected error: got %v, want %v", i, err, v.err) 797 } 798 } 799 } 800 801 func TestReadGNUSparseMap1x0(t *testing.T) { 802 sp := []sparseEntry{{1, 2}, {3, 4}} 803 for i := 0; i < 98; i++ { 804 sp = append(sp, sparseEntry{54321, 12345}) 805 } 806 807 vectors := []struct { 808 input string // Input data 809 sparseMap []sparseEntry // Expected sparse entries to be outputted 810 cnt int // Expected number of bytes read 811 err error // Expected errors that may be raised 812 }{{ 813 input: "", 814 cnt: 0, 815 err: io.ErrUnexpectedEOF, 816 }, { 817 input: "ab", 818 cnt: 2, 819 err: io.ErrUnexpectedEOF, 820 }, { 821 input: strings.Repeat("\x00", 512), 822 cnt: 512, 823 err: io.ErrUnexpectedEOF, 824 }, { 825 input: strings.Repeat("\x00", 511) + "\n", 826 cnt: 512, 827 err: ErrHeader, 828 }, { 829 input: strings.Repeat("\n", 512), 830 cnt: 512, 831 err: ErrHeader, 832 }, { 833 input: "0\n" + strings.Repeat("\x00", 510) + strings.Repeat("a", 512), 834 sparseMap: []sparseEntry{}, 835 cnt: 512, 836 }, { 837 input: strings.Repeat("0", 512) + "0\n" + strings.Repeat("\x00", 510), 838 sparseMap: []sparseEntry{}, 839 cnt: 1024, 840 }, { 841 input: strings.Repeat("0", 1024) + "1\n2\n3\n" + strings.Repeat("\x00", 506), 842 sparseMap: []sparseEntry{{2, 3}}, 843 cnt: 1536, 844 }, { 845 input: strings.Repeat("0", 1024) + "1\n2\n\n" + strings.Repeat("\x00", 509), 846 cnt: 1536, 847 err: ErrHeader, 848 }, { 849 input: strings.Repeat("0", 1024) + "1\n2\n" + strings.Repeat("\x00", 508), 850 cnt: 1536, 851 err: io.ErrUnexpectedEOF, 852 }, { 853 input: "-1\n2\n\n" + strings.Repeat("\x00", 506), 854 cnt: 512, 855 err: ErrHeader, 856 }, { 857 input: "1\nk\n2\n" + strings.Repeat("\x00", 506), 858 cnt: 512, 859 err: ErrHeader, 860 }, { 861 input: "100\n1\n2\n3\n4\n" + strings.Repeat("54321\n0000000000000012345\n", 98) + strings.Repeat("\x00", 512), 862 cnt: 2560, 863 sparseMap: sp, 864 }} 865 866 for i, v := range vectors { 867 r := strings.NewReader(v.input) 868 sp, err := readGNUSparseMap1x0(r) 869 if !reflect.DeepEqual(sp, v.sparseMap) && !(len(sp) == 0 && len(v.sparseMap) == 0) { 870 t.Errorf("test %d, readGNUSparseMap1x0(...): got %v, want %v", i, sp, v.sparseMap) 871 } 872 if numBytes := len(v.input) - r.Len(); numBytes != v.cnt { 873 t.Errorf("test %d, bytes read: got %v, want %v", i, numBytes, v.cnt) 874 } 875 if err != v.err { 876 t.Errorf("test %d, unexpected error: got %v, want %v", i, err, v.err) 877 } 878 } 879 } 880 881 func TestUninitializedRead(t *testing.T) { 882 f, err := os.Open("testdata/gnu.tar") 883 if err != nil { 884 t.Fatalf("Unexpected error: %v", err) 885 } 886 defer f.Close() 887 888 tr := NewReader(f) 889 _, err = tr.Read([]byte{}) 890 if err == nil || err != io.EOF { 891 t.Errorf("Unexpected error: %v, wanted %v", err, io.EOF) 892 } 893 894 } 895 896 type reader struct{ io.Reader } 897 type readSeeker struct{ io.ReadSeeker } 898 type readBadSeeker struct{ io.ReadSeeker } 899 900 func (rbs *readBadSeeker) Seek(int64, int) (int64, error) { return 0, fmt.Errorf("illegal seek") } 901 902 // TestReadTruncation test the ending condition on various truncated files and 903 // that truncated files are still detected even if the underlying io.Reader 904 // satisfies io.Seeker. 905 func TestReadTruncation(t *testing.T) { 906 var ss []string 907 for _, p := range []string{ 908 "testdata/gnu.tar", 909 "testdata/ustar-file-reg.tar", 910 "testdata/pax-path-hdr.tar", 911 "testdata/sparse-formats.tar", 912 } { 913 buf, err := ioutil.ReadFile(p) 914 if err != nil { 915 t.Fatalf("unexpected error: %v", err) 916 } 917 ss = append(ss, string(buf)) 918 } 919 920 data1, data2, pax, sparse := ss[0], ss[1], ss[2], ss[3] 921 data2 += strings.Repeat("\x00", 10*512) 922 trash := strings.Repeat("garbage ", 64) // Exactly 512 bytes 923 924 vectors := []struct { 925 input string // Input stream 926 cnt int // Expected number of headers read 927 err error // Expected error outcome 928 }{ 929 {"", 0, io.EOF}, // Empty file is a "valid" tar file 930 {data1[:511], 0, io.ErrUnexpectedEOF}, 931 {data1[:512], 1, io.ErrUnexpectedEOF}, 932 {data1[:1024], 1, io.EOF}, 933 {data1[:1536], 2, io.ErrUnexpectedEOF}, 934 {data1[:2048], 2, io.EOF}, 935 {data1, 2, io.EOF}, 936 {data1[:2048] + data2[:1536], 3, io.EOF}, 937 {data2[:511], 0, io.ErrUnexpectedEOF}, 938 {data2[:512], 1, io.ErrUnexpectedEOF}, 939 {data2[:1195], 1, io.ErrUnexpectedEOF}, 940 {data2[:1196], 1, io.EOF}, // Exact end of data and start of padding 941 {data2[:1200], 1, io.EOF}, 942 {data2[:1535], 1, io.EOF}, 943 {data2[:1536], 1, io.EOF}, // Exact end of padding 944 {data2[:1536] + trash[:1], 1, io.ErrUnexpectedEOF}, 945 {data2[:1536] + trash[:511], 1, io.ErrUnexpectedEOF}, 946 {data2[:1536] + trash, 1, ErrHeader}, 947 {data2[:2048], 1, io.EOF}, // Exactly 1 empty block 948 {data2[:2048] + trash[:1], 1, io.ErrUnexpectedEOF}, 949 {data2[:2048] + trash[:511], 1, io.ErrUnexpectedEOF}, 950 {data2[:2048] + trash, 1, ErrHeader}, 951 {data2[:2560], 1, io.EOF}, // Exactly 2 empty blocks (normal end-of-stream) 952 {data2[:2560] + trash[:1], 1, io.EOF}, 953 {data2[:2560] + trash[:511], 1, io.EOF}, 954 {data2[:2560] + trash, 1, io.EOF}, 955 {data2[:3072], 1, io.EOF}, 956 {pax, 0, io.EOF}, // PAX header without data is a "valid" tar file 957 {pax + trash[:1], 0, io.ErrUnexpectedEOF}, 958 {pax + trash[:511], 0, io.ErrUnexpectedEOF}, 959 {sparse[:511], 0, io.ErrUnexpectedEOF}, 960 {sparse[:512], 0, io.ErrUnexpectedEOF}, 961 {sparse[:3584], 1, io.EOF}, 962 {sparse[:9200], 1, io.EOF}, // Terminate in padding of sparse header 963 {sparse[:9216], 1, io.EOF}, 964 {sparse[:9728], 2, io.ErrUnexpectedEOF}, 965 {sparse[:10240], 2, io.EOF}, 966 {sparse[:11264], 2, io.ErrUnexpectedEOF}, 967 {sparse, 5, io.EOF}, 968 {sparse + trash, 5, io.EOF}, 969 } 970 971 for i, v := range vectors { 972 for j := 0; j < 6; j++ { 973 var tr *Reader 974 var s1, s2 string 975 976 switch j { 977 case 0: 978 tr = NewReader(&reader{strings.NewReader(v.input)}) 979 s1, s2 = "io.Reader", "auto" 980 case 1: 981 tr = NewReader(&reader{strings.NewReader(v.input)}) 982 s1, s2 = "io.Reader", "manual" 983 case 2: 984 tr = NewReader(&readSeeker{strings.NewReader(v.input)}) 985 s1, s2 = "io.ReadSeeker", "auto" 986 case 3: 987 tr = NewReader(&readSeeker{strings.NewReader(v.input)}) 988 s1, s2 = "io.ReadSeeker", "manual" 989 case 4: 990 tr = NewReader(&readBadSeeker{strings.NewReader(v.input)}) 991 s1, s2 = "ReadBadSeeker", "auto" 992 case 5: 993 tr = NewReader(&readBadSeeker{strings.NewReader(v.input)}) 994 s1, s2 = "ReadBadSeeker", "manual" 995 } 996 997 var cnt int 998 var err error 999 for { 1000 if _, err = tr.Next(); err != nil { 1001 break 1002 } 1003 cnt++ 1004 if s2 == "manual" { 1005 if _, err = io.Copy(ioutil.Discard, tr); err != nil { 1006 break 1007 } 1008 } 1009 } 1010 if err != v.err { 1011 t.Errorf("test %d, NewReader(%s(...)) with %s discard: got %v, want %v", 1012 i, s1, s2, err, v.err) 1013 } 1014 if cnt != v.cnt { 1015 t.Errorf("test %d, NewReader(%s(...)) with %s discard: got %d headers, want %d headers", 1016 i, s1, s2, cnt, v.cnt) 1017 } 1018 } 1019 } 1020 } 1021 1022 // TestReadHeaderOnly tests that Reader does not attempt to read special 1023 // header-only files. 1024 func TestReadHeaderOnly(t *testing.T) { 1025 f, err := os.Open("testdata/hdr-only.tar") 1026 if err != nil { 1027 t.Fatalf("unexpected error: %v", err) 1028 } 1029 defer f.Close() 1030 1031 var hdrs []*Header 1032 tr := NewReader(f) 1033 for { 1034 hdr, err := tr.Next() 1035 if err == io.EOF { 1036 break 1037 } 1038 if err != nil { 1039 t.Errorf("Next(): got %v, want %v", err, nil) 1040 continue 1041 } 1042 hdrs = append(hdrs, hdr) 1043 1044 // If a special flag, we should read nothing. 1045 cnt, _ := io.ReadFull(tr, []byte{0}) 1046 if cnt > 0 && hdr.Typeflag != TypeReg { 1047 t.Errorf("ReadFull(...): got %d bytes, want 0 bytes", cnt) 1048 } 1049 } 1050 1051 // File is crafted with 16 entries. The later 8 are identical to the first 1052 // 8 except that the size is set. 1053 if len(hdrs) != 16 { 1054 t.Fatalf("len(hdrs): got %d, want %d", len(hdrs), 16) 1055 } 1056 for i := 0; i < 8; i++ { 1057 hdr1, hdr2 := hdrs[i+0], hdrs[i+8] 1058 hdr1.Size, hdr2.Size = 0, 0 1059 if !reflect.DeepEqual(*hdr1, *hdr2) { 1060 t.Errorf("incorrect header:\ngot %+v\nwant %+v", *hdr1, *hdr2) 1061 } 1062 } 1063 } 1064 1065 func TestMergePAX(t *testing.T) { 1066 vectors := []struct { 1067 in map[string]string 1068 want *Header 1069 ok bool 1070 }{{ 1071 in: map[string]string{ 1072 "path": "a/b/c", 1073 "uid": "1000", 1074 "mtime": "1350244992.023960108", 1075 }, 1076 want: &Header{ 1077 Name: "a/b/c", 1078 Uid: 1000, 1079 ModTime: time.Unix(1350244992, 23960108), 1080 }, 1081 ok: true, 1082 }, { 1083 in: map[string]string{ 1084 "gid": "gtgergergersagersgers", 1085 }, 1086 }, { 1087 in: map[string]string{ 1088 "missing": "missing", 1089 "SCHILY.xattr.key": "value", 1090 }, 1091 want: &Header{ 1092 Xattrs: map[string]string{"key": "value"}, 1093 }, 1094 ok: true, 1095 }} 1096 1097 for i, v := range vectors { 1098 got := new(Header) 1099 err := mergePAX(got, v.in) 1100 if v.ok && !reflect.DeepEqual(*got, *v.want) { 1101 t.Errorf("test %d, mergePAX(...):\ngot %+v\nwant %+v", i, *got, *v.want) 1102 } 1103 if ok := err == nil; ok != v.ok { 1104 t.Errorf("test %d, mergePAX(...): got %v, want %v", i, ok, v.ok) 1105 } 1106 } 1107 } 1108 1109 func TestParsePAX(t *testing.T) { 1110 vectors := []struct { 1111 in string 1112 want map[string]string 1113 ok bool 1114 }{ 1115 {"", nil, true}, 1116 {"6 k=1\n", map[string]string{"k": "1"}, true}, 1117 {"10 a=name\n", map[string]string{"a": "name"}, true}, 1118 {"9 a=name\n", map[string]string{"a": "name"}, true}, 1119 {"30 mtime=1350244992.023960108\n", map[string]string{"mtime": "1350244992.023960108"}, true}, 1120 {"3 somelongkey=\n", nil, false}, 1121 {"50 tooshort=\n", nil, false}, 1122 {"13 key1=haha\n13 key2=nana\n13 key3=kaka\n", 1123 map[string]string{"key1": "haha", "key2": "nana", "key3": "kaka"}, true}, 1124 {"13 key1=val1\n13 key2=val2\n8 key1=\n", 1125 map[string]string{"key2": "val2"}, true}, 1126 {"22 GNU.sparse.size=10\n26 GNU.sparse.numblocks=2\n" + 1127 "23 GNU.sparse.offset=1\n25 GNU.sparse.numbytes=2\n" + 1128 "23 GNU.sparse.offset=3\n25 GNU.sparse.numbytes=4\n", 1129 map[string]string{paxGNUSparseSize: "10", paxGNUSparseNumBlocks: "2", paxGNUSparseMap: "1,2,3,4"}, true}, 1130 {"22 GNU.sparse.size=10\n26 GNU.sparse.numblocks=1\n" + 1131 "25 GNU.sparse.numbytes=2\n23 GNU.sparse.offset=1\n", 1132 nil, false}, 1133 {"22 GNU.sparse.size=10\n26 GNU.sparse.numblocks=1\n" + 1134 "25 GNU.sparse.offset=1,2\n25 GNU.sparse.numbytes=2\n", 1135 nil, false}, 1136 } 1137 1138 for i, v := range vectors { 1139 r := strings.NewReader(v.in) 1140 got, err := parsePAX(r) 1141 if !reflect.DeepEqual(got, v.want) && !(len(got) == 0 && len(v.want) == 0) { 1142 t.Errorf("test %d, parsePAX(...):\ngot %v\nwant %v", i, got, v.want) 1143 } 1144 if ok := err == nil; ok != v.ok { 1145 t.Errorf("test %d, parsePAX(...): got %v, want %v", i, ok, v.ok) 1146 } 1147 } 1148 }