github.com/bgentry/go@v0.0.0-20150121062915-6cf5a733d54d/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 "os" 14 "reflect" 15 "strings" 16 "testing" 17 "time" 18 ) 19 20 type untarTest struct { 21 file string 22 headers []*Header 23 cksums []string 24 } 25 26 var gnuTarTest = &untarTest{ 27 file: "testdata/gnu.tar", 28 headers: []*Header{ 29 { 30 Name: "small.txt", 31 Mode: 0640, 32 Uid: 73025, 33 Gid: 5000, 34 Size: 5, 35 ModTime: time.Unix(1244428340, 0), 36 Typeflag: '0', 37 Uname: "dsymonds", 38 Gname: "eng", 39 }, 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 }, 52 cksums: []string{ 53 "e38b27eaccb4391bdec553a7f3ae6b2f", 54 "c65bd2e50a56a2138bf1716f2fd56fe9", 55 }, 56 } 57 58 var sparseTarTest = &untarTest{ 59 file: "testdata/sparse-formats.tar", 60 headers: []*Header{ 61 { 62 Name: "sparse-gnu", 63 Mode: 420, 64 Uid: 1000, 65 Gid: 1000, 66 Size: 200, 67 ModTime: time.Unix(1392395740, 0), 68 Typeflag: 0x53, 69 Linkname: "", 70 Uname: "david", 71 Gname: "david", 72 Devmajor: 0, 73 Devminor: 0, 74 }, 75 { 76 Name: "sparse-posix-0.0", 77 Mode: 420, 78 Uid: 1000, 79 Gid: 1000, 80 Size: 200, 81 ModTime: time.Unix(1392342187, 0), 82 Typeflag: 0x30, 83 Linkname: "", 84 Uname: "david", 85 Gname: "david", 86 Devmajor: 0, 87 Devminor: 0, 88 }, 89 { 90 Name: "sparse-posix-0.1", 91 Mode: 420, 92 Uid: 1000, 93 Gid: 1000, 94 Size: 200, 95 ModTime: time.Unix(1392340456, 0), 96 Typeflag: 0x30, 97 Linkname: "", 98 Uname: "david", 99 Gname: "david", 100 Devmajor: 0, 101 Devminor: 0, 102 }, 103 { 104 Name: "sparse-posix-1.0", 105 Mode: 420, 106 Uid: 1000, 107 Gid: 1000, 108 Size: 200, 109 ModTime: time.Unix(1392337404, 0), 110 Typeflag: 0x30, 111 Linkname: "", 112 Uname: "david", 113 Gname: "david", 114 Devmajor: 0, 115 Devminor: 0, 116 }, 117 { 118 Name: "end", 119 Mode: 420, 120 Uid: 1000, 121 Gid: 1000, 122 Size: 4, 123 ModTime: time.Unix(1392398319, 0), 124 Typeflag: 0x30, 125 Linkname: "", 126 Uname: "david", 127 Gname: "david", 128 Devmajor: 0, 129 Devminor: 0, 130 }, 131 }, 132 cksums: []string{ 133 "6f53234398c2449fe67c1812d993012f", 134 "6f53234398c2449fe67c1812d993012f", 135 "6f53234398c2449fe67c1812d993012f", 136 "6f53234398c2449fe67c1812d993012f", 137 "b0061974914468de549a2af8ced10316", 138 }, 139 } 140 141 var untarTests = []*untarTest{ 142 gnuTarTest, 143 sparseTarTest, 144 { 145 file: "testdata/star.tar", 146 headers: []*Header{ 147 { 148 Name: "small.txt", 149 Mode: 0640, 150 Uid: 73025, 151 Gid: 5000, 152 Size: 5, 153 ModTime: time.Unix(1244592783, 0), 154 Typeflag: '0', 155 Uname: "dsymonds", 156 Gname: "eng", 157 AccessTime: time.Unix(1244592783, 0), 158 ChangeTime: time.Unix(1244592783, 0), 159 }, 160 { 161 Name: "small2.txt", 162 Mode: 0640, 163 Uid: 73025, 164 Gid: 5000, 165 Size: 11, 166 ModTime: time.Unix(1244592783, 0), 167 Typeflag: '0', 168 Uname: "dsymonds", 169 Gname: "eng", 170 AccessTime: time.Unix(1244592783, 0), 171 ChangeTime: time.Unix(1244592783, 0), 172 }, 173 }, 174 }, 175 { 176 file: "testdata/v7.tar", 177 headers: []*Header{ 178 { 179 Name: "small.txt", 180 Mode: 0444, 181 Uid: 73025, 182 Gid: 5000, 183 Size: 5, 184 ModTime: time.Unix(1244593104, 0), 185 Typeflag: '\x00', 186 }, 187 { 188 Name: "small2.txt", 189 Mode: 0444, 190 Uid: 73025, 191 Gid: 5000, 192 Size: 11, 193 ModTime: time.Unix(1244593104, 0), 194 Typeflag: '\x00', 195 }, 196 }, 197 }, 198 { 199 file: "testdata/pax.tar", 200 headers: []*Header{ 201 { 202 Name: "a/123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100", 203 Mode: 0664, 204 Uid: 1000, 205 Gid: 1000, 206 Uname: "shane", 207 Gname: "shane", 208 Size: 7, 209 ModTime: time.Unix(1350244992, 23960108), 210 ChangeTime: time.Unix(1350244992, 23960108), 211 AccessTime: time.Unix(1350244992, 23960108), 212 Typeflag: TypeReg, 213 }, 214 { 215 Name: "a/b", 216 Mode: 0777, 217 Uid: 1000, 218 Gid: 1000, 219 Uname: "shane", 220 Gname: "shane", 221 Size: 0, 222 ModTime: time.Unix(1350266320, 910238425), 223 ChangeTime: time.Unix(1350266320, 910238425), 224 AccessTime: time.Unix(1350266320, 910238425), 225 Typeflag: TypeSymlink, 226 Linkname: "123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100", 227 }, 228 }, 229 }, 230 { 231 file: "testdata/nil-uid.tar", // golang.org/issue/5290 232 headers: []*Header{ 233 { 234 Name: "P1050238.JPG.log", 235 Mode: 0664, 236 Uid: 0, 237 Gid: 0, 238 Size: 14, 239 ModTime: time.Unix(1365454838, 0), 240 Typeflag: TypeReg, 241 Linkname: "", 242 Uname: "eyefi", 243 Gname: "eyefi", 244 Devmajor: 0, 245 Devminor: 0, 246 }, 247 }, 248 }, 249 { 250 file: "testdata/xattrs.tar", 251 headers: []*Header{ 252 { 253 Name: "small.txt", 254 Mode: 0644, 255 Uid: 1000, 256 Gid: 10, 257 Size: 5, 258 ModTime: time.Unix(1386065770, 448252320), 259 Typeflag: '0', 260 Uname: "alex", 261 Gname: "wheel", 262 AccessTime: time.Unix(1389782991, 419875220), 263 ChangeTime: time.Unix(1389782956, 794414986), 264 Xattrs: map[string]string{ 265 "user.key": "value", 266 "user.key2": "value2", 267 // Interestingly, selinux encodes the terminating null inside the xattr 268 "security.selinux": "unconfined_u:object_r:default_t:s0\x00", 269 }, 270 }, 271 { 272 Name: "small2.txt", 273 Mode: 0644, 274 Uid: 1000, 275 Gid: 10, 276 Size: 11, 277 ModTime: time.Unix(1386065770, 449252304), 278 Typeflag: '0', 279 Uname: "alex", 280 Gname: "wheel", 281 AccessTime: time.Unix(1389782991, 419875220), 282 ChangeTime: time.Unix(1386065770, 449252304), 283 Xattrs: map[string]string{ 284 "security.selinux": "unconfined_u:object_r:default_t:s0\x00", 285 }, 286 }, 287 }, 288 }, 289 } 290 291 func TestReader(t *testing.T) { 292 testLoop: 293 for i, test := range untarTests { 294 f, err := os.Open(test.file) 295 if err != nil { 296 t.Errorf("test %d: Unexpected error: %v", i, err) 297 continue 298 } 299 defer f.Close() 300 tr := NewReader(f) 301 for j, header := range test.headers { 302 hdr, err := tr.Next() 303 if err != nil || hdr == nil { 304 t.Errorf("test %d, entry %d: Didn't get entry: %v", i, j, err) 305 f.Close() 306 continue testLoop 307 } 308 if !reflect.DeepEqual(*hdr, *header) { 309 t.Errorf("test %d, entry %d: Incorrect header:\nhave %+v\nwant %+v", 310 i, j, *hdr, *header) 311 } 312 } 313 hdr, err := tr.Next() 314 if err == io.EOF { 315 continue testLoop 316 } 317 if hdr != nil || err != nil { 318 t.Errorf("test %d: Unexpected entry or error: hdr=%v err=%v", i, hdr, err) 319 } 320 } 321 } 322 323 func TestPartialRead(t *testing.T) { 324 f, err := os.Open("testdata/gnu.tar") 325 if err != nil { 326 t.Fatalf("Unexpected error: %v", err) 327 } 328 defer f.Close() 329 330 tr := NewReader(f) 331 332 // Read the first four bytes; Next() should skip the last byte. 333 hdr, err := tr.Next() 334 if err != nil || hdr == nil { 335 t.Fatalf("Didn't get first file: %v", err) 336 } 337 buf := make([]byte, 4) 338 if _, err := io.ReadFull(tr, buf); err != nil { 339 t.Fatalf("Unexpected error: %v", err) 340 } 341 if expected := []byte("Kilt"); !bytes.Equal(buf, expected) { 342 t.Errorf("Contents = %v, want %v", buf, expected) 343 } 344 345 // Second file 346 hdr, err = tr.Next() 347 if err != nil || hdr == nil { 348 t.Fatalf("Didn't get second file: %v", err) 349 } 350 buf = make([]byte, 6) 351 if _, err := io.ReadFull(tr, buf); err != nil { 352 t.Fatalf("Unexpected error: %v", err) 353 } 354 if expected := []byte("Google"); !bytes.Equal(buf, expected) { 355 t.Errorf("Contents = %v, want %v", buf, expected) 356 } 357 } 358 359 func TestIncrementalRead(t *testing.T) { 360 test := gnuTarTest 361 f, err := os.Open(test.file) 362 if err != nil { 363 t.Fatalf("Unexpected error: %v", err) 364 } 365 defer f.Close() 366 367 tr := NewReader(f) 368 369 headers := test.headers 370 cksums := test.cksums 371 nread := 0 372 373 // loop over all files 374 for ; ; nread++ { 375 hdr, err := tr.Next() 376 if hdr == nil || err == io.EOF { 377 break 378 } 379 380 // check the header 381 if !reflect.DeepEqual(*hdr, *headers[nread]) { 382 t.Errorf("Incorrect header:\nhave %+v\nwant %+v", 383 *hdr, headers[nread]) 384 } 385 386 // read file contents in little chunks EOF, 387 // checksumming all the way 388 h := md5.New() 389 rdbuf := make([]uint8, 8) 390 for { 391 nr, err := tr.Read(rdbuf) 392 if err == io.EOF { 393 break 394 } 395 if err != nil { 396 t.Errorf("Read: unexpected error %v\n", err) 397 break 398 } 399 h.Write(rdbuf[0:nr]) 400 } 401 // verify checksum 402 have := fmt.Sprintf("%x", h.Sum(nil)) 403 want := cksums[nread] 404 if want != have { 405 t.Errorf("Bad checksum on file %s:\nhave %+v\nwant %+v", hdr.Name, have, want) 406 } 407 } 408 if nread != len(headers) { 409 t.Errorf("Didn't process all files\nexpected: %d\nprocessed %d\n", len(headers), nread) 410 } 411 } 412 413 func TestNonSeekable(t *testing.T) { 414 test := gnuTarTest 415 f, err := os.Open(test.file) 416 if err != nil { 417 t.Fatalf("Unexpected error: %v", err) 418 } 419 defer f.Close() 420 421 type readerOnly struct { 422 io.Reader 423 } 424 tr := NewReader(readerOnly{f}) 425 nread := 0 426 427 for ; ; nread++ { 428 _, err := tr.Next() 429 if err == io.EOF { 430 break 431 } 432 if err != nil { 433 t.Fatalf("Unexpected error: %v", err) 434 } 435 } 436 437 if nread != len(test.headers) { 438 t.Errorf("Didn't process all files\nexpected: %d\nprocessed %d\n", len(test.headers), nread) 439 } 440 } 441 442 func TestParsePAXHeader(t *testing.T) { 443 paxTests := [][3]string{ 444 {"a", "a=name", "10 a=name\n"}, // Test case involving multiple acceptable lengths 445 {"a", "a=name", "9 a=name\n"}, // Test case involving multiple acceptable length 446 {"mtime", "mtime=1350244992.023960108", "30 mtime=1350244992.023960108\n"}} 447 for _, test := range paxTests { 448 key, expected, raw := test[0], test[1], test[2] 449 reader := bytes.NewReader([]byte(raw)) 450 headers, err := parsePAX(reader) 451 if err != nil { 452 t.Errorf("Couldn't parse correctly formatted headers: %v", err) 453 continue 454 } 455 if strings.EqualFold(headers[key], expected) { 456 t.Errorf("mtime header incorrectly parsed: got %s, wanted %s", headers[key], expected) 457 continue 458 } 459 trailer := make([]byte, 100) 460 n, err := reader.Read(trailer) 461 if err != io.EOF || n != 0 { 462 t.Error("Buffer wasn't consumed") 463 } 464 } 465 badHeader := bytes.NewReader([]byte("3 somelongkey=")) 466 if _, err := parsePAX(badHeader); err != ErrHeader { 467 t.Fatal("Unexpected success when parsing bad header") 468 } 469 } 470 471 func TestParsePAXTime(t *testing.T) { 472 // Some valid PAX time values 473 timestamps := map[string]time.Time{ 474 "1350244992.023960108": time.Unix(1350244992, 23960108), // The common case 475 "1350244992.02396010": time.Unix(1350244992, 23960100), // Lower precision value 476 "1350244992.0239601089": time.Unix(1350244992, 23960108), // Higher precision value 477 "1350244992": time.Unix(1350244992, 0), // Low precision value 478 } 479 for input, expected := range timestamps { 480 ts, err := parsePAXTime(input) 481 if err != nil { 482 t.Fatal(err) 483 } 484 if !ts.Equal(expected) { 485 t.Fatalf("Time parsing failure %s %s", ts, expected) 486 } 487 } 488 } 489 490 func TestMergePAX(t *testing.T) { 491 hdr := new(Header) 492 // Test a string, integer, and time based value. 493 headers := map[string]string{ 494 "path": "a/b/c", 495 "uid": "1000", 496 "mtime": "1350244992.023960108", 497 } 498 err := mergePAX(hdr, headers) 499 if err != nil { 500 t.Fatal(err) 501 } 502 want := &Header{ 503 Name: "a/b/c", 504 Uid: 1000, 505 ModTime: time.Unix(1350244992, 23960108), 506 } 507 if !reflect.DeepEqual(hdr, want) { 508 t.Errorf("incorrect merge: got %+v, want %+v", hdr, want) 509 } 510 } 511 512 func TestSparseEndToEnd(t *testing.T) { 513 test := sparseTarTest 514 f, err := os.Open(test.file) 515 if err != nil { 516 t.Fatalf("Unexpected error: %v", err) 517 } 518 defer f.Close() 519 520 tr := NewReader(f) 521 522 headers := test.headers 523 cksums := test.cksums 524 nread := 0 525 526 // loop over all files 527 for ; ; nread++ { 528 hdr, err := tr.Next() 529 if hdr == nil || err == io.EOF { 530 break 531 } 532 533 // check the header 534 if !reflect.DeepEqual(*hdr, *headers[nread]) { 535 t.Errorf("Incorrect header:\nhave %+v\nwant %+v", 536 *hdr, headers[nread]) 537 } 538 539 // read and checksum the file data 540 h := md5.New() 541 _, err = io.Copy(h, tr) 542 if err != nil { 543 t.Fatalf("Unexpected error: %v", err) 544 } 545 546 // verify checksum 547 have := fmt.Sprintf("%x", h.Sum(nil)) 548 want := cksums[nread] 549 if want != have { 550 t.Errorf("Bad checksum on file %s:\nhave %+v\nwant %+v", hdr.Name, have, want) 551 } 552 } 553 if nread != len(headers) { 554 t.Errorf("Didn't process all files\nexpected: %d\nprocessed %d\n", len(headers), nread) 555 } 556 } 557 558 type sparseFileReadTest struct { 559 sparseData []byte 560 sparseMap []sparseEntry 561 realSize int64 562 expected []byte 563 } 564 565 var sparseFileReadTests = []sparseFileReadTest{ 566 { 567 sparseData: []byte("abcde"), 568 sparseMap: []sparseEntry{ 569 {offset: 0, numBytes: 2}, 570 {offset: 5, numBytes: 3}, 571 }, 572 realSize: 8, 573 expected: []byte("ab\x00\x00\x00cde"), 574 }, 575 { 576 sparseData: []byte("abcde"), 577 sparseMap: []sparseEntry{ 578 {offset: 0, numBytes: 2}, 579 {offset: 5, numBytes: 3}, 580 }, 581 realSize: 10, 582 expected: []byte("ab\x00\x00\x00cde\x00\x00"), 583 }, 584 { 585 sparseData: []byte("abcde"), 586 sparseMap: []sparseEntry{ 587 {offset: 1, numBytes: 3}, 588 {offset: 6, numBytes: 2}, 589 }, 590 realSize: 8, 591 expected: []byte("\x00abc\x00\x00de"), 592 }, 593 { 594 sparseData: []byte("abcde"), 595 sparseMap: []sparseEntry{ 596 {offset: 1, numBytes: 3}, 597 {offset: 6, numBytes: 2}, 598 }, 599 realSize: 10, 600 expected: []byte("\x00abc\x00\x00de\x00\x00"), 601 }, 602 { 603 sparseData: []byte(""), 604 sparseMap: nil, 605 realSize: 2, 606 expected: []byte("\x00\x00"), 607 }, 608 } 609 610 func TestSparseFileReader(t *testing.T) { 611 for i, test := range sparseFileReadTests { 612 r := bytes.NewReader(test.sparseData) 613 nb := int64(r.Len()) 614 sfr := &sparseFileReader{ 615 rfr: ®FileReader{r: r, nb: nb}, 616 sp: test.sparseMap, 617 pos: 0, 618 tot: test.realSize, 619 } 620 if sfr.numBytes() != nb { 621 t.Errorf("test %d: Before reading, sfr.numBytes() = %d, want %d", i, sfr.numBytes(), nb) 622 } 623 buf, err := ioutil.ReadAll(sfr) 624 if err != nil { 625 t.Errorf("test %d: Unexpected error: %v", i, err) 626 } 627 if e := test.expected; !bytes.Equal(buf, e) { 628 t.Errorf("test %d: Contents = %v, want %v", i, buf, e) 629 } 630 if sfr.numBytes() != 0 { 631 t.Errorf("test %d: After draining the reader, numBytes() was nonzero", i) 632 } 633 } 634 } 635 636 func TestSparseIncrementalRead(t *testing.T) { 637 sparseMap := []sparseEntry{{10, 2}} 638 sparseData := []byte("Go") 639 expected := "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00Go\x00\x00\x00\x00\x00\x00\x00\x00" 640 641 r := bytes.NewReader(sparseData) 642 nb := int64(r.Len()) 643 sfr := &sparseFileReader{ 644 rfr: ®FileReader{r: r, nb: nb}, 645 sp: sparseMap, 646 pos: 0, 647 tot: int64(len(expected)), 648 } 649 650 // We'll read the data 6 bytes at a time, with a hole of size 10 at 651 // the beginning and one of size 8 at the end. 652 var outputBuf bytes.Buffer 653 buf := make([]byte, 6) 654 for { 655 n, err := sfr.Read(buf) 656 if err == io.EOF { 657 break 658 } 659 if err != nil { 660 t.Errorf("Read: unexpected error %v\n", err) 661 } 662 if n > 0 { 663 _, err := outputBuf.Write(buf[:n]) 664 if err != nil { 665 t.Errorf("Write: unexpected error %v\n", err) 666 } 667 } 668 } 669 got := outputBuf.String() 670 if got != expected { 671 t.Errorf("Contents = %v, want %v", got, expected) 672 } 673 } 674 675 func TestReadGNUSparseMap0x1(t *testing.T) { 676 headers := map[string]string{ 677 paxGNUSparseNumBlocks: "4", 678 paxGNUSparseMap: "0,5,10,5,20,5,30,5", 679 } 680 expected := []sparseEntry{ 681 {offset: 0, numBytes: 5}, 682 {offset: 10, numBytes: 5}, 683 {offset: 20, numBytes: 5}, 684 {offset: 30, numBytes: 5}, 685 } 686 687 sp, err := readGNUSparseMap0x1(headers) 688 if err != nil { 689 t.Errorf("Unexpected error: %v", err) 690 } 691 if !reflect.DeepEqual(sp, expected) { 692 t.Errorf("Incorrect sparse map: got %v, wanted %v", sp, expected) 693 } 694 } 695 696 func TestReadGNUSparseMap1x0(t *testing.T) { 697 // This test uses lots of holes so the sparse header takes up more than two blocks 698 numEntries := 100 699 expected := make([]sparseEntry, 0, numEntries) 700 sparseMap := new(bytes.Buffer) 701 702 fmt.Fprintf(sparseMap, "%d\n", numEntries) 703 for i := 0; i < numEntries; i++ { 704 offset := int64(2048 * i) 705 numBytes := int64(1024) 706 expected = append(expected, sparseEntry{offset: offset, numBytes: numBytes}) 707 fmt.Fprintf(sparseMap, "%d\n%d\n", offset, numBytes) 708 } 709 710 // Make the header the smallest multiple of blockSize that fits the sparseMap 711 headerBlocks := (sparseMap.Len() + blockSize - 1) / blockSize 712 bufLen := blockSize * headerBlocks 713 buf := make([]byte, bufLen) 714 copy(buf, sparseMap.Bytes()) 715 716 // Get an reader to read the sparse map 717 r := bytes.NewReader(buf) 718 719 // Read the sparse map 720 sp, err := readGNUSparseMap1x0(r) 721 if err != nil { 722 t.Errorf("Unexpected error: %v", err) 723 } 724 if !reflect.DeepEqual(sp, expected) { 725 t.Errorf("Incorrect sparse map: got %v, wanted %v", sp, expected) 726 } 727 } 728 729 func TestUninitializedRead(t *testing.T) { 730 test := gnuTarTest 731 f, err := os.Open(test.file) 732 if err != nil { 733 t.Fatalf("Unexpected error: %v", err) 734 } 735 defer f.Close() 736 737 tr := NewReader(f) 738 _, err = tr.Read([]byte{}) 739 if err == nil || err != io.EOF { 740 t.Errorf("Unexpected error: %v, wanted %v", err, io.EOF) 741 } 742 743 }