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 func TestReader(t *testing.T) { 22 vectors := []struct { 23 file string // Test input file 24 headers []*Header // Expected output headers 25 chksums []string // MD5 checksum of files, leave as nil if not checked 26 err error // Expected error to occur 27 }{{ 28 file: "testdata/gnu.tar", 29 headers: []*Header{{ 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 Name: "small2.txt", 41 Mode: 0640, 42 Uid: 73025, 43 Gid: 5000, 44 Size: 11, 45 ModTime: time.Unix(1244436044, 0), 46 Typeflag: '0', 47 Uname: "dsymonds", 48 Gname: "eng", 49 }}, 50 chksums: []string{ 51 "e38b27eaccb4391bdec553a7f3ae6b2f", 52 "c65bd2e50a56a2138bf1716f2fd56fe9", 53 }, 54 }, { 55 file: "testdata/sparse-formats.tar", 56 headers: []*Header{{ 57 Name: "sparse-gnu", 58 Mode: 420, 59 Uid: 1000, 60 Gid: 1000, 61 Size: 200, 62 ModTime: time.Unix(1392395740, 0), 63 Typeflag: 0x53, 64 Linkname: "", 65 Uname: "david", 66 Gname: "david", 67 Devmajor: 0, 68 Devminor: 0, 69 }, { 70 Name: "sparse-posix-0.0", 71 Mode: 420, 72 Uid: 1000, 73 Gid: 1000, 74 Size: 200, 75 ModTime: time.Unix(1392342187, 0), 76 Typeflag: 0x30, 77 Linkname: "", 78 Uname: "david", 79 Gname: "david", 80 Devmajor: 0, 81 Devminor: 0, 82 }, { 83 Name: "sparse-posix-0.1", 84 Mode: 420, 85 Uid: 1000, 86 Gid: 1000, 87 Size: 200, 88 ModTime: time.Unix(1392340456, 0), 89 Typeflag: 0x30, 90 Linkname: "", 91 Uname: "david", 92 Gname: "david", 93 Devmajor: 0, 94 Devminor: 0, 95 }, { 96 Name: "sparse-posix-1.0", 97 Mode: 420, 98 Uid: 1000, 99 Gid: 1000, 100 Size: 200, 101 ModTime: time.Unix(1392337404, 0), 102 Typeflag: 0x30, 103 Linkname: "", 104 Uname: "david", 105 Gname: "david", 106 Devmajor: 0, 107 Devminor: 0, 108 }, { 109 Name: "end", 110 Mode: 420, 111 Uid: 1000, 112 Gid: 1000, 113 Size: 4, 114 ModTime: time.Unix(1392398319, 0), 115 Typeflag: 0x30, 116 Linkname: "", 117 Uname: "david", 118 Gname: "david", 119 Devmajor: 0, 120 Devminor: 0, 121 }}, 122 chksums: []string{ 123 "6f53234398c2449fe67c1812d993012f", 124 "6f53234398c2449fe67c1812d993012f", 125 "6f53234398c2449fe67c1812d993012f", 126 "6f53234398c2449fe67c1812d993012f", 127 "b0061974914468de549a2af8ced10316", 128 }, 129 }, { 130 file: "testdata/star.tar", 131 headers: []*Header{{ 132 Name: "small.txt", 133 Mode: 0640, 134 Uid: 73025, 135 Gid: 5000, 136 Size: 5, 137 ModTime: time.Unix(1244592783, 0), 138 Typeflag: '0', 139 Uname: "dsymonds", 140 Gname: "eng", 141 AccessTime: time.Unix(1244592783, 0), 142 ChangeTime: time.Unix(1244592783, 0), 143 }, { 144 Name: "small2.txt", 145 Mode: 0640, 146 Uid: 73025, 147 Gid: 5000, 148 Size: 11, 149 ModTime: time.Unix(1244592783, 0), 150 Typeflag: '0', 151 Uname: "dsymonds", 152 Gname: "eng", 153 AccessTime: time.Unix(1244592783, 0), 154 ChangeTime: time.Unix(1244592783, 0), 155 }}, 156 }, { 157 file: "testdata/v7.tar", 158 headers: []*Header{{ 159 Name: "small.txt", 160 Mode: 0444, 161 Uid: 73025, 162 Gid: 5000, 163 Size: 5, 164 ModTime: time.Unix(1244593104, 0), 165 Typeflag: '\x00', 166 }, { 167 Name: "small2.txt", 168 Mode: 0444, 169 Uid: 73025, 170 Gid: 5000, 171 Size: 11, 172 ModTime: time.Unix(1244593104, 0), 173 Typeflag: '\x00', 174 }}, 175 }, { 176 file: "testdata/pax.tar", 177 headers: []*Header{{ 178 Name: "a/123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100", 179 Mode: 0664, 180 Uid: 1000, 181 Gid: 1000, 182 Uname: "shane", 183 Gname: "shane", 184 Size: 7, 185 ModTime: time.Unix(1350244992, 23960108), 186 ChangeTime: time.Unix(1350244992, 23960108), 187 AccessTime: time.Unix(1350244992, 23960108), 188 Typeflag: TypeReg, 189 }, { 190 Name: "a/b", 191 Mode: 0777, 192 Uid: 1000, 193 Gid: 1000, 194 Uname: "shane", 195 Gname: "shane", 196 Size: 0, 197 ModTime: time.Unix(1350266320, 910238425), 198 ChangeTime: time.Unix(1350266320, 910238425), 199 AccessTime: time.Unix(1350266320, 910238425), 200 Typeflag: TypeSymlink, 201 Linkname: "123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100", 202 }}, 203 }, { 204 file: "testdata/pax-bad-hdr-file.tar", 205 err: ErrHeader, 206 }, { 207 file: "testdata/pax-bad-mtime-file.tar", 208 err: ErrHeader, 209 }, { 210 file: "testdata/pax-pos-size-file.tar", 211 headers: []*Header{{ 212 Name: "foo", 213 Mode: 0640, 214 Uid: 319973, 215 Gid: 5000, 216 Size: 999, 217 ModTime: time.Unix(1442282516, 0), 218 Typeflag: '0', 219 Uname: "joetsai", 220 Gname: "eng", 221 }}, 222 chksums: []string{ 223 "0afb597b283fe61b5d4879669a350556", 224 }, 225 }, { 226 file: "testdata/nil-uid.tar", // golang.org/issue/5290 227 headers: []*Header{{ 228 Name: "P1050238.JPG.log", 229 Mode: 0664, 230 Uid: 0, 231 Gid: 0, 232 Size: 14, 233 ModTime: time.Unix(1365454838, 0), 234 Typeflag: TypeReg, 235 Linkname: "", 236 Uname: "eyefi", 237 Gname: "eyefi", 238 Devmajor: 0, 239 Devminor: 0, 240 }}, 241 }, { 242 file: "testdata/xattrs.tar", 243 headers: []*Header{{ 244 Name: "small.txt", 245 Mode: 0644, 246 Uid: 1000, 247 Gid: 10, 248 Size: 5, 249 ModTime: time.Unix(1386065770, 448252320), 250 Typeflag: '0', 251 Uname: "alex", 252 Gname: "wheel", 253 AccessTime: time.Unix(1389782991, 419875220), 254 ChangeTime: time.Unix(1389782956, 794414986), 255 Xattrs: map[string]string{ 256 "user.key": "value", 257 "user.key2": "value2", 258 // Interestingly, selinux encodes the terminating null inside the xattr 259 "security.selinux": "unconfined_u:object_r:default_t:s0\x00", 260 }, 261 }, { 262 Name: "small2.txt", 263 Mode: 0644, 264 Uid: 1000, 265 Gid: 10, 266 Size: 11, 267 ModTime: time.Unix(1386065770, 449252304), 268 Typeflag: '0', 269 Uname: "alex", 270 Gname: "wheel", 271 AccessTime: time.Unix(1389782991, 419875220), 272 ChangeTime: time.Unix(1386065770, 449252304), 273 Xattrs: map[string]string{ 274 "security.selinux": "unconfined_u:object_r:default_t:s0\x00", 275 }, 276 }}, 277 }, { 278 // Matches the behavior of GNU, BSD, and STAR tar utilities. 279 file: "testdata/gnu-multi-hdrs.tar", 280 headers: []*Header{{ 281 Name: "GNU2/GNU2/long-path-name", 282 Linkname: "GNU4/GNU4/long-linkpath-name", 283 ModTime: time.Unix(0, 0), 284 Typeflag: '2', 285 }}, 286 }, { 287 // GNU tar file with atime and ctime fields set. 288 // Created with the GNU tar v1.27.1. 289 // tar --incremental -S -cvf gnu-incremental.tar test2 290 file: "testdata/gnu-incremental.tar", 291 headers: []*Header{{ 292 Name: "test2/", 293 Mode: 16877, 294 Uid: 1000, 295 Gid: 1000, 296 Size: 14, 297 ModTime: time.Unix(1441973427, 0), 298 Typeflag: 'D', 299 Uname: "rawr", 300 Gname: "dsnet", 301 AccessTime: time.Unix(1441974501, 0), 302 ChangeTime: time.Unix(1441973436, 0), 303 }, { 304 Name: "test2/foo", 305 Mode: 33188, 306 Uid: 1000, 307 Gid: 1000, 308 Size: 64, 309 ModTime: time.Unix(1441973363, 0), 310 Typeflag: '0', 311 Uname: "rawr", 312 Gname: "dsnet", 313 AccessTime: time.Unix(1441974501, 0), 314 ChangeTime: time.Unix(1441973436, 0), 315 }, { 316 Name: "test2/sparse", 317 Mode: 33188, 318 Uid: 1000, 319 Gid: 1000, 320 Size: 536870912, 321 ModTime: time.Unix(1441973427, 0), 322 Typeflag: 'S', 323 Uname: "rawr", 324 Gname: "dsnet", 325 AccessTime: time.Unix(1441991948, 0), 326 ChangeTime: time.Unix(1441973436, 0), 327 }}, 328 }, { 329 // Matches the behavior of GNU and BSD tar utilities. 330 file: "testdata/pax-multi-hdrs.tar", 331 headers: []*Header{{ 332 Name: "bar", 333 Linkname: "PAX4/PAX4/long-linkpath-name", 334 ModTime: time.Unix(0, 0), 335 Typeflag: '2', 336 }}, 337 }, { 338 file: "testdata/neg-size.tar", 339 err: ErrHeader, 340 }, { 341 file: "testdata/issue10968.tar", 342 err: ErrHeader, 343 }, { 344 file: "testdata/issue11169.tar", 345 err: ErrHeader, 346 }, { 347 file: "testdata/issue12435.tar", 348 err: ErrHeader, 349 }} 350 351 for i, v := range vectors { 352 f, err := os.Open(v.file) 353 if err != nil { 354 t.Errorf("file %s, test %d: unexpected error: %v", v.file, i, err) 355 continue 356 } 357 defer f.Close() 358 359 // Capture all headers and checksums. 360 var ( 361 tr = NewReader(f) 362 hdrs []*Header 363 chksums []string 364 rdbuf = make([]byte, 8) 365 ) 366 for { 367 var hdr *Header 368 hdr, err = tr.Next() 369 if err != nil { 370 if err == io.EOF { 371 err = nil // Expected error 372 } 373 break 374 } 375 hdrs = append(hdrs, hdr) 376 377 if v.chksums == nil { 378 continue 379 } 380 h := md5.New() 381 _, err = io.CopyBuffer(h, tr, rdbuf) // Effectively an incremental read 382 if err != nil { 383 break 384 } 385 chksums = append(chksums, fmt.Sprintf("%x", h.Sum(nil))) 386 } 387 388 for j, hdr := range hdrs { 389 if j >= len(v.headers) { 390 t.Errorf("file %s, test %d, entry %d: unexpected header:\ngot %+v", 391 v.file, i, j, *hdr) 392 continue 393 } 394 if !reflect.DeepEqual(*hdr, *v.headers[j]) { 395 t.Errorf("file %s, test %d, entry %d: incorrect header:\ngot %+v\nwant %+v", 396 v.file, i, j, *hdr, *v.headers[j]) 397 } 398 } 399 if len(hdrs) != len(v.headers) { 400 t.Errorf("file %s, test %d: got %d headers, want %d headers", 401 v.file, i, len(hdrs), len(v.headers)) 402 } 403 404 for j, sum := range chksums { 405 if j >= len(v.chksums) { 406 t.Errorf("file %s, test %d, entry %d: unexpected sum: got %s", 407 v.file, i, j, sum) 408 continue 409 } 410 if sum != v.chksums[j] { 411 t.Errorf("file %s, test %d, entry %d: incorrect checksum: got %s, want %s", 412 v.file, i, j, sum, v.chksums[j]) 413 } 414 } 415 416 if err != v.err { 417 t.Errorf("file %s, test %d: unexpected error: got %v, want %v", 418 v.file, i, err, v.err) 419 } 420 f.Close() 421 } 422 } 423 424 func TestPartialRead(t *testing.T) { 425 f, err := os.Open("testdata/gnu.tar") 426 if err != nil { 427 t.Fatalf("Unexpected error: %v", err) 428 } 429 defer f.Close() 430 431 tr := NewReader(f) 432 433 // Read the first four bytes; Next() should skip the last byte. 434 hdr, err := tr.Next() 435 if err != nil || hdr == nil { 436 t.Fatalf("Didn't get first file: %v", err) 437 } 438 buf := make([]byte, 4) 439 if _, err := io.ReadFull(tr, buf); err != nil { 440 t.Fatalf("Unexpected error: %v", err) 441 } 442 if expected := []byte("Kilt"); !bytes.Equal(buf, expected) { 443 t.Errorf("Contents = %v, want %v", buf, expected) 444 } 445 446 // Second file 447 hdr, err = tr.Next() 448 if err != nil || hdr == nil { 449 t.Fatalf("Didn't get second file: %v", err) 450 } 451 buf = make([]byte, 6) 452 if _, err := io.ReadFull(tr, buf); err != nil { 453 t.Fatalf("Unexpected error: %v", err) 454 } 455 if expected := []byte("Google"); !bytes.Equal(buf, expected) { 456 t.Errorf("Contents = %v, want %v", buf, expected) 457 } 458 } 459 460 func TestSparseFileReader(t *testing.T) { 461 vectors := []struct { 462 realSize int64 // Real size of the output file 463 sparseMap []sparseEntry // Input sparse map 464 sparseData string // Input compact data 465 expected string // Expected output data 466 err error // Expected error outcome 467 }{{ 468 realSize: 8, 469 sparseMap: []sparseEntry{ 470 {offset: 0, numBytes: 2}, 471 {offset: 5, numBytes: 3}, 472 }, 473 sparseData: "abcde", 474 expected: "ab\x00\x00\x00cde", 475 }, { 476 realSize: 10, 477 sparseMap: []sparseEntry{ 478 {offset: 0, numBytes: 2}, 479 {offset: 5, numBytes: 3}, 480 }, 481 sparseData: "abcde", 482 expected: "ab\x00\x00\x00cde\x00\x00", 483 }, { 484 realSize: 8, 485 sparseMap: []sparseEntry{ 486 {offset: 1, numBytes: 3}, 487 {offset: 6, numBytes: 2}, 488 }, 489 sparseData: "abcde", 490 expected: "\x00abc\x00\x00de", 491 }, { 492 realSize: 8, 493 sparseMap: []sparseEntry{ 494 {offset: 1, numBytes: 3}, 495 {offset: 6, numBytes: 0}, 496 {offset: 6, numBytes: 0}, 497 {offset: 6, numBytes: 2}, 498 }, 499 sparseData: "abcde", 500 expected: "\x00abc\x00\x00de", 501 }, { 502 realSize: 10, 503 sparseMap: []sparseEntry{ 504 {offset: 1, numBytes: 3}, 505 {offset: 6, numBytes: 2}, 506 }, 507 sparseData: "abcde", 508 expected: "\x00abc\x00\x00de\x00\x00", 509 }, { 510 realSize: 10, 511 sparseMap: []sparseEntry{ 512 {offset: 1, numBytes: 3}, 513 {offset: 6, numBytes: 2}, 514 {offset: 8, numBytes: 0}, 515 {offset: 8, numBytes: 0}, 516 {offset: 8, numBytes: 0}, 517 {offset: 8, numBytes: 0}, 518 }, 519 sparseData: "abcde", 520 expected: "\x00abc\x00\x00de\x00\x00", 521 }, { 522 realSize: 2, 523 sparseMap: []sparseEntry{}, 524 sparseData: "", 525 expected: "\x00\x00", 526 }, { 527 realSize: -2, 528 sparseMap: []sparseEntry{}, 529 err: ErrHeader, 530 }, { 531 realSize: -10, 532 sparseMap: []sparseEntry{ 533 {offset: 1, numBytes: 3}, 534 {offset: 6, numBytes: 2}, 535 }, 536 sparseData: "abcde", 537 err: ErrHeader, 538 }, { 539 realSize: 10, 540 sparseMap: []sparseEntry{ 541 {offset: 1, numBytes: 3}, 542 {offset: 6, numBytes: 5}, 543 }, 544 sparseData: "abcde", 545 err: ErrHeader, 546 }, { 547 realSize: 35, 548 sparseMap: []sparseEntry{ 549 {offset: 1, numBytes: 3}, 550 {offset: 6, numBytes: 5}, 551 }, 552 sparseData: "abcde", 553 err: io.ErrUnexpectedEOF, 554 }, { 555 realSize: 35, 556 sparseMap: []sparseEntry{ 557 {offset: 1, numBytes: 3}, 558 {offset: 6, numBytes: -5}, 559 }, 560 sparseData: "abcde", 561 err: ErrHeader, 562 }, { 563 realSize: 35, 564 sparseMap: []sparseEntry{ 565 {offset: math.MaxInt64, numBytes: 3}, 566 {offset: 6, numBytes: -5}, 567 }, 568 sparseData: "abcde", 569 err: ErrHeader, 570 }, { 571 realSize: 10, 572 sparseMap: []sparseEntry{ 573 {offset: 1, numBytes: 3}, 574 {offset: 2, numBytes: 2}, 575 }, 576 sparseData: "abcde", 577 err: ErrHeader, 578 }} 579 580 for i, v := range vectors { 581 r := bytes.NewReader([]byte(v.sparseData)) 582 rfr := ®FileReader{r: r, nb: int64(len(v.sparseData))} 583 584 var ( 585 sfr *sparseFileReader 586 err error 587 buf []byte 588 ) 589 590 sfr, err = newSparseFileReader(rfr, v.sparseMap, v.realSize) 591 if err != nil { 592 goto fail 593 } 594 if sfr.numBytes() != int64(len(v.sparseData)) { 595 t.Errorf("test %d, numBytes() before reading: got %d, want %d", i, sfr.numBytes(), len(v.sparseData)) 596 } 597 buf, err = ioutil.ReadAll(sfr) 598 if err != nil { 599 goto fail 600 } 601 if string(buf) != v.expected { 602 t.Errorf("test %d, ReadAll(): got %q, want %q", i, string(buf), v.expected) 603 } 604 if sfr.numBytes() != 0 { 605 t.Errorf("test %d, numBytes() after reading: got %d, want %d", i, sfr.numBytes(), 0) 606 } 607 608 fail: 609 if err != v.err { 610 t.Errorf("test %d, unexpected error: got %v, want %v", i, err, v.err) 611 } 612 } 613 } 614 615 func TestReadOldGNUSparseMap(t *testing.T) { 616 const ( 617 t00 = "00000000000\x0000000000000\x00" 618 t11 = "00000000001\x0000000000001\x00" 619 t12 = "00000000001\x0000000000002\x00" 620 t21 = "00000000002\x0000000000001\x00" 621 ) 622 623 mkBlk := func(size, sp0, sp1, sp2, sp3, ext string, format int) *block { 624 var blk block 625 copy(blk.GNU().RealSize(), size) 626 copy(blk.GNU().Sparse().Entry(0), sp0) 627 copy(blk.GNU().Sparse().Entry(1), sp1) 628 copy(blk.GNU().Sparse().Entry(2), sp2) 629 copy(blk.GNU().Sparse().Entry(3), sp3) 630 copy(blk.GNU().Sparse().IsExtended(), ext) 631 if format != formatUnknown { 632 blk.SetFormat(format) 633 } 634 return &blk 635 } 636 637 vectors := []struct { 638 data string // Input data 639 rawHdr *block // Input raw header 640 want []sparseEntry // Expected sparse entries to be outputted 641 err error // Expected error to be returned 642 }{ 643 {"", mkBlk("", "", "", "", "", "", formatUnknown), nil, ErrHeader}, 644 {"", mkBlk("1234", "fewa", "", "", "", "", formatGNU), nil, ErrHeader}, 645 {"", mkBlk("0031", "", "", "", "", "", formatGNU), nil, nil}, 646 {"", mkBlk("1234", t00, t11, "", "", "", formatGNU), 647 []sparseEntry{{0, 0}, {1, 1}}, nil}, 648 {"", mkBlk("1234", t11, t12, t21, t11, "", formatGNU), 649 []sparseEntry{{1, 1}, {1, 2}, {2, 1}, {1, 1}}, nil}, 650 {"", mkBlk("1234", t11, t12, t21, t11, "\x80", formatGNU), 651 []sparseEntry{}, io.ErrUnexpectedEOF}, 652 {t11 + t11, 653 mkBlk("1234", t11, t12, t21, t11, "\x80", formatGNU), 654 []sparseEntry{}, io.ErrUnexpectedEOF}, 655 {t11 + t21 + strings.Repeat("\x00", 512), 656 mkBlk("1234", t11, t12, t21, t11, "\x80", formatGNU), 657 []sparseEntry{{1, 1}, {1, 2}, {2, 1}, {1, 1}, {1, 1}, {2, 1}}, nil}, 658 } 659 660 for i, v := range vectors { 661 tr := Reader{r: strings.NewReader(v.data)} 662 hdr := new(Header) 663 got, err := tr.readOldGNUSparseMap(hdr, v.rawHdr) 664 if !reflect.DeepEqual(got, v.want) && !(len(got) == 0 && len(v.want) == 0) { 665 t.Errorf("test %d, readOldGNUSparseMap(...): got %v, want %v", i, got, v.want) 666 } 667 if err != v.err { 668 t.Errorf("test %d, unexpected error: got %v, want %v", i, err, v.err) 669 } 670 } 671 } 672 673 func TestReadGNUSparseMap0x1(t *testing.T) { 674 const ( 675 maxUint = ^uint(0) 676 maxInt = int(maxUint >> 1) 677 ) 678 var ( 679 big1 = fmt.Sprintf("%d", int64(maxInt)) 680 big2 = fmt.Sprintf("%d", (int64(maxInt)/2)+1) 681 big3 = fmt.Sprintf("%d", (int64(maxInt) / 3)) 682 ) 683 684 vectors := []struct { 685 extHdrs map[string]string // Input data 686 sparseMap []sparseEntry // Expected sparse entries to be outputted 687 err error // Expected errors that may be raised 688 }{{ 689 extHdrs: map[string]string{paxGNUSparseNumBlocks: "-4"}, 690 err: ErrHeader, 691 }, { 692 extHdrs: map[string]string{paxGNUSparseNumBlocks: "fee "}, 693 err: ErrHeader, 694 }, { 695 extHdrs: map[string]string{ 696 paxGNUSparseNumBlocks: big1, 697 paxGNUSparseMap: "0,5,10,5,20,5,30,5", 698 }, 699 err: ErrHeader, 700 }, { 701 extHdrs: map[string]string{ 702 paxGNUSparseNumBlocks: big2, 703 paxGNUSparseMap: "0,5,10,5,20,5,30,5", 704 }, 705 err: ErrHeader, 706 }, { 707 extHdrs: map[string]string{ 708 paxGNUSparseNumBlocks: big3, 709 paxGNUSparseMap: "0,5,10,5,20,5,30,5", 710 }, 711 err: ErrHeader, 712 }, { 713 extHdrs: map[string]string{ 714 paxGNUSparseNumBlocks: "4", 715 paxGNUSparseMap: "0.5,5,10,5,20,5,30,5", 716 }, 717 err: ErrHeader, 718 }, { 719 extHdrs: map[string]string{ 720 paxGNUSparseNumBlocks: "4", 721 paxGNUSparseMap: "0,5.5,10,5,20,5,30,5", 722 }, 723 err: ErrHeader, 724 }, { 725 extHdrs: map[string]string{ 726 paxGNUSparseNumBlocks: "4", 727 paxGNUSparseMap: "0,fewafewa.5,fewafw,5,20,5,30,5", 728 }, 729 err: ErrHeader, 730 }, { 731 extHdrs: map[string]string{ 732 paxGNUSparseNumBlocks: "4", 733 paxGNUSparseMap: "0,5,10,5,20,5,30,5", 734 }, 735 sparseMap: []sparseEntry{{0, 5}, {10, 5}, {20, 5}, {30, 5}}, 736 }} 737 738 for i, v := range vectors { 739 sp, err := readGNUSparseMap0x1(v.extHdrs) 740 if !reflect.DeepEqual(sp, v.sparseMap) && !(len(sp) == 0 && len(v.sparseMap) == 0) { 741 t.Errorf("test %d, readGNUSparseMap0x1(...): got %v, want %v", i, sp, v.sparseMap) 742 } 743 if err != v.err { 744 t.Errorf("test %d, unexpected error: got %v, want %v", i, err, v.err) 745 } 746 } 747 } 748 749 func TestReadGNUSparseMap1x0(t *testing.T) { 750 sp := []sparseEntry{{1, 2}, {3, 4}} 751 for i := 0; i < 98; i++ { 752 sp = append(sp, sparseEntry{54321, 12345}) 753 } 754 755 vectors := []struct { 756 input string // Input data 757 sparseMap []sparseEntry // Expected sparse entries to be outputted 758 cnt int // Expected number of bytes read 759 err error // Expected errors that may be raised 760 }{{ 761 input: "", 762 cnt: 0, 763 err: io.ErrUnexpectedEOF, 764 }, { 765 input: "ab", 766 cnt: 2, 767 err: io.ErrUnexpectedEOF, 768 }, { 769 input: strings.Repeat("\x00", 512), 770 cnt: 512, 771 err: io.ErrUnexpectedEOF, 772 }, { 773 input: strings.Repeat("\x00", 511) + "\n", 774 cnt: 512, 775 err: ErrHeader, 776 }, { 777 input: strings.Repeat("\n", 512), 778 cnt: 512, 779 err: ErrHeader, 780 }, { 781 input: "0\n" + strings.Repeat("\x00", 510) + strings.Repeat("a", 512), 782 sparseMap: []sparseEntry{}, 783 cnt: 512, 784 }, { 785 input: strings.Repeat("0", 512) + "0\n" + strings.Repeat("\x00", 510), 786 sparseMap: []sparseEntry{}, 787 cnt: 1024, 788 }, { 789 input: strings.Repeat("0", 1024) + "1\n2\n3\n" + strings.Repeat("\x00", 506), 790 sparseMap: []sparseEntry{{2, 3}}, 791 cnt: 1536, 792 }, { 793 input: strings.Repeat("0", 1024) + "1\n2\n\n" + strings.Repeat("\x00", 509), 794 cnt: 1536, 795 err: ErrHeader, 796 }, { 797 input: strings.Repeat("0", 1024) + "1\n2\n" + strings.Repeat("\x00", 508), 798 cnt: 1536, 799 err: io.ErrUnexpectedEOF, 800 }, { 801 input: "-1\n2\n\n" + strings.Repeat("\x00", 506), 802 cnt: 512, 803 err: ErrHeader, 804 }, { 805 input: "1\nk\n2\n" + strings.Repeat("\x00", 506), 806 cnt: 512, 807 err: ErrHeader, 808 }, { 809 input: "100\n1\n2\n3\n4\n" + strings.Repeat("54321\n0000000000000012345\n", 98) + strings.Repeat("\x00", 512), 810 cnt: 2560, 811 sparseMap: sp, 812 }} 813 814 for i, v := range vectors { 815 r := strings.NewReader(v.input) 816 sp, err := readGNUSparseMap1x0(r) 817 if !reflect.DeepEqual(sp, v.sparseMap) && !(len(sp) == 0 && len(v.sparseMap) == 0) { 818 t.Errorf("test %d, readGNUSparseMap1x0(...): got %v, want %v", i, sp, v.sparseMap) 819 } 820 if numBytes := len(v.input) - r.Len(); numBytes != v.cnt { 821 t.Errorf("test %d, bytes read: got %v, want %v", i, numBytes, v.cnt) 822 } 823 if err != v.err { 824 t.Errorf("test %d, unexpected error: got %v, want %v", i, err, v.err) 825 } 826 } 827 } 828 829 func TestUninitializedRead(t *testing.T) { 830 f, err := os.Open("testdata/gnu.tar") 831 if err != nil { 832 t.Fatalf("Unexpected error: %v", err) 833 } 834 defer f.Close() 835 836 tr := NewReader(f) 837 _, err = tr.Read([]byte{}) 838 if err == nil || err != io.EOF { 839 t.Errorf("Unexpected error: %v, wanted %v", err, io.EOF) 840 } 841 842 } 843 844 type reader struct{ io.Reader } 845 type readSeeker struct{ io.ReadSeeker } 846 type readBadSeeker struct{ io.ReadSeeker } 847 848 func (rbs *readBadSeeker) Seek(int64, int) (int64, error) { return 0, fmt.Errorf("illegal seek") } 849 850 // TestReadTruncation test the ending condition on various truncated files and 851 // that truncated files are still detected even if the underlying io.Reader 852 // satisfies io.Seeker. 853 func TestReadTruncation(t *testing.T) { 854 var ss []string 855 for _, p := range []string{ 856 "testdata/gnu.tar", 857 "testdata/ustar-file-reg.tar", 858 "testdata/pax-path-hdr.tar", 859 "testdata/sparse-formats.tar", 860 } { 861 buf, err := ioutil.ReadFile(p) 862 if err != nil { 863 t.Fatalf("unexpected error: %v", err) 864 } 865 ss = append(ss, string(buf)) 866 } 867 868 data1, data2, pax, sparse := ss[0], ss[1], ss[2], ss[3] 869 data2 += strings.Repeat("\x00", 10*512) 870 trash := strings.Repeat("garbage ", 64) // Exactly 512 bytes 871 872 vectors := []struct { 873 input string // Input stream 874 cnt int // Expected number of headers read 875 err error // Expected error outcome 876 }{ 877 {"", 0, io.EOF}, // Empty file is a "valid" tar file 878 {data1[:511], 0, io.ErrUnexpectedEOF}, 879 {data1[:512], 1, io.ErrUnexpectedEOF}, 880 {data1[:1024], 1, io.EOF}, 881 {data1[:1536], 2, io.ErrUnexpectedEOF}, 882 {data1[:2048], 2, io.EOF}, 883 {data1, 2, io.EOF}, 884 {data1[:2048] + data2[:1536], 3, io.EOF}, 885 {data2[:511], 0, io.ErrUnexpectedEOF}, 886 {data2[:512], 1, io.ErrUnexpectedEOF}, 887 {data2[:1195], 1, io.ErrUnexpectedEOF}, 888 {data2[:1196], 1, io.EOF}, // Exact end of data and start of padding 889 {data2[:1200], 1, io.EOF}, 890 {data2[:1535], 1, io.EOF}, 891 {data2[:1536], 1, io.EOF}, // Exact end of padding 892 {data2[:1536] + trash[:1], 1, io.ErrUnexpectedEOF}, 893 {data2[:1536] + trash[:511], 1, io.ErrUnexpectedEOF}, 894 {data2[:1536] + trash, 1, ErrHeader}, 895 {data2[:2048], 1, io.EOF}, // Exactly 1 empty block 896 {data2[:2048] + trash[:1], 1, io.ErrUnexpectedEOF}, 897 {data2[:2048] + trash[:511], 1, io.ErrUnexpectedEOF}, 898 {data2[:2048] + trash, 1, ErrHeader}, 899 {data2[:2560], 1, io.EOF}, // Exactly 2 empty blocks (normal end-of-stream) 900 {data2[:2560] + trash[:1], 1, io.EOF}, 901 {data2[:2560] + trash[:511], 1, io.EOF}, 902 {data2[:2560] + trash, 1, io.EOF}, 903 {data2[:3072], 1, io.EOF}, 904 {pax, 0, io.EOF}, // PAX header without data is a "valid" tar file 905 {pax + trash[:1], 0, io.ErrUnexpectedEOF}, 906 {pax + trash[:511], 0, io.ErrUnexpectedEOF}, 907 {sparse[:511], 0, io.ErrUnexpectedEOF}, 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 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 TestMergePAX(t *testing.T) { 1014 vectors := []struct { 1015 in map[string]string 1016 want *Header 1017 ok bool 1018 }{{ 1019 in: map[string]string{ 1020 "path": "a/b/c", 1021 "uid": "1000", 1022 "mtime": "1350244992.023960108", 1023 }, 1024 want: &Header{ 1025 Name: "a/b/c", 1026 Uid: 1000, 1027 ModTime: time.Unix(1350244992, 23960108), 1028 }, 1029 ok: true, 1030 }, { 1031 in: map[string]string{ 1032 "gid": "gtgergergersagersgers", 1033 }, 1034 }, { 1035 in: map[string]string{ 1036 "missing": "missing", 1037 "SCHILY.xattr.key": "value", 1038 }, 1039 want: &Header{ 1040 Xattrs: map[string]string{"key": "value"}, 1041 }, 1042 ok: true, 1043 }} 1044 1045 for i, v := range vectors { 1046 got := new(Header) 1047 err := mergePAX(got, v.in) 1048 if v.ok && !reflect.DeepEqual(*got, *v.want) { 1049 t.Errorf("test %d, mergePAX(...):\ngot %+v\nwant %+v", i, *got, *v.want) 1050 } 1051 if ok := err == nil; ok != v.ok { 1052 t.Errorf("test %d, mergePAX(...): got %v, want %v", i, ok, v.ok) 1053 } 1054 } 1055 } 1056 1057 func TestParsePAX(t *testing.T) { 1058 vectors := []struct { 1059 in string 1060 want map[string]string 1061 ok bool 1062 }{ 1063 {"", nil, true}, 1064 {"6 k=1\n", map[string]string{"k": "1"}, true}, 1065 {"10 a=name\n", map[string]string{"a": "name"}, true}, 1066 {"9 a=name\n", map[string]string{"a": "name"}, true}, 1067 {"30 mtime=1350244992.023960108\n", map[string]string{"mtime": "1350244992.023960108"}, true}, 1068 {"3 somelongkey=\n", nil, false}, 1069 {"50 tooshort=\n", nil, false}, 1070 {"13 key1=haha\n13 key2=nana\n13 key3=kaka\n", 1071 map[string]string{"key1": "haha", "key2": "nana", "key3": "kaka"}, true}, 1072 {"13 key1=val1\n13 key2=val2\n8 key1=\n", 1073 map[string]string{"key2": "val2"}, true}, 1074 {"22 GNU.sparse.size=10\n26 GNU.sparse.numblocks=2\n" + 1075 "23 GNU.sparse.offset=1\n25 GNU.sparse.numbytes=2\n" + 1076 "23 GNU.sparse.offset=3\n25 GNU.sparse.numbytes=4\n", 1077 map[string]string{paxGNUSparseSize: "10", paxGNUSparseNumBlocks: "2", paxGNUSparseMap: "1,2,3,4"}, true}, 1078 {"22 GNU.sparse.size=10\n26 GNU.sparse.numblocks=1\n" + 1079 "25 GNU.sparse.numbytes=2\n23 GNU.sparse.offset=1\n", 1080 nil, false}, 1081 {"22 GNU.sparse.size=10\n26 GNU.sparse.numblocks=1\n" + 1082 "25 GNU.sparse.offset=1,2\n25 GNU.sparse.numbytes=2\n", 1083 nil, false}, 1084 } 1085 1086 for i, v := range vectors { 1087 r := strings.NewReader(v.in) 1088 got, err := parsePAX(r) 1089 if !reflect.DeepEqual(got, v.want) && !(len(got) == 0 && len(v.want) == 0) { 1090 t.Errorf("test %d, parsePAX(...):\ngot %v\nwant %v", i, got, v.want) 1091 } 1092 if ok := err == nil; ok != v.ok { 1093 t.Errorf("test %d, parsePAX(...): got %v, want %v", i, ok, v.ok) 1094 } 1095 } 1096 } 1097