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