| ... | ... | @@ -164,7 +164,7 @@ pub const Header = struct { |
| 164 | 164 | const ltrimmed = std.mem.trimLeft(u8, raw, "0 "); |
| 165 | 165 | const rtrimmed = std.mem.trimRight(u8, ltrimmed, " \x00"); |
| 166 | 166 | if (rtrimmed.len == 0) return 0; |
| 167 | | return std.fmt.parseInt(u64, rtrimmed, 8); |
| 167 | return std.fmt.parseInt(u64, rtrimmed, 8) catch return error.TarHeader; |
| 168 | 168 | } |
| 169 | 169 | |
| 170 | 170 | // Sum of all bytes in the header block. The chksum field is treated as if |
| ... | ... | @@ -289,6 +289,10 @@ fn BufferedReader(comptime ReaderType: type) type { |
| 289 | 289 | try self.reader.skip(len); |
| 290 | 290 | } |
| 291 | 291 | |
| 292 | fn byte(self: *@This()) u8 { |
| 293 | return self.reader.buffer[self.reader.start]; |
| 294 | } |
| 295 | |
| 292 | 296 | fn copy(self: *@This(), dst: []u8) ![]const u8 { |
| 293 | 297 | _ = try self.reader.copy(dst); |
| 294 | 298 | self.offset += dst.len; |
| ... | ... | @@ -416,21 +420,25 @@ fn Iterator(comptime ReaderType: type) type { |
| 416 | 420 | }, |
| 417 | 421 | .extended_header => { |
| 418 | 422 | if (file_size == 0) continue; |
| 423 | // TODO: ovo resetiranje je nezgodno |
| 424 | self.attrs.free(); |
| 425 | file = File{ .reader = &self.reader }; |
| 419 | 426 | |
| 420 | 427 | var rdr = self.reader.sliceReader(file_size, false); |
| 421 | 428 | while (try rdr.next()) |slice| { |
| 422 | 429 | const attr = try parsePaxAttribute(slice, rdr.remainingSize()); |
| 423 | 430 | try rdr.advance(attr.value_off); |
| 424 | 431 | if (attr.is("path")) { |
| 425 | | file.name = try rdr.copy(try self.attrs.alloc(attr.value_len)); |
| 432 | file.name = try noNull(try rdr.copy(try self.attrs.alloc(attr.value_len))); |
| 426 | 433 | } else if (attr.is("linkpath")) { |
| 427 | | file.link_name = try rdr.copy(try self.attrs.alloc(attr.value_len)); |
| 434 | file.link_name = try noNull(try rdr.copy(try self.attrs.alloc(attr.value_len))); |
| 428 | 435 | } else if (attr.is("size")) { |
| 429 | 436 | var buf = [_]u8{'0'} ** 32; |
| 430 | 437 | file.size = try std.fmt.parseInt(usize, try rdr.copy(buf[0..attr.value_len]), 10); |
| 431 | 438 | } else { |
| 432 | 439 | try rdr.advance(attr.value_len); |
| 433 | 440 | } |
| 441 | if (rdr.byte() != '\n') return error.InvalidPaxAttribute; |
| 434 | 442 | try rdr.advance(1); |
| 435 | 443 | } |
| 436 | 444 | try self.reader.skipPadding(file_size); |
| ... | ... | @@ -582,15 +590,21 @@ fn parsePaxAttribute(data: []const u8, max_size: usize) !PaxAttributeInfo { |
| 582 | 590 | if (kv_size > max_size) { |
| 583 | 591 | return error.InvalidPaxAttribute; |
| 584 | 592 | } |
| 593 | const key = data[pos_space + 1 .. pos_equals]; |
| 585 | 594 | return .{ |
| 586 | 595 | .size = kv_size, |
| 587 | | .key = data[pos_space + 1 .. pos_equals], |
| 596 | .key = try noNull(key), |
| 588 | 597 | .value_off = pos_equals + 1, |
| 589 | 598 | .value_len = kv_size - pos_equals - 2, |
| 590 | 599 | }; |
| 591 | 600 | } |
| 592 | 601 | |
| 593 | | test parsePaxAttribute { |
| 602 | fn noNull(str: []const u8) ![]const u8 { |
| 603 | if (std.mem.indexOfScalar(u8, str, 0)) |_| return error.InvalidPaxAttribute; |
| 604 | return str; |
| 605 | } |
| 606 | |
| 607 | test "parsePaxAttribute" { |
| 594 | 608 | const expectEqual = std.testing.expectEqual; |
| 595 | 609 | const expectEqualStrings = std.testing.expectEqualStrings; |
| 596 | 610 | const expectError = std.testing.expectError; |
| ... | ... | @@ -605,6 +619,7 @@ test parsePaxAttribute { |
| 605 | 619 | try expectEqual(attr_info, try parsePaxAttribute(header, 1012)); |
| 606 | 620 | try expectError(error.InvalidPaxAttribute, parsePaxAttribute(header, 1010)); |
| 607 | 621 | try expectError(error.InvalidPaxAttribute, parsePaxAttribute("", 0)); |
| 622 | try expectError(error.InvalidPaxAttribute, parsePaxAttribute("13 pa\x00th=abc\n", 1024)); // null in key |
| 608 | 623 | } |
| 609 | 624 | |
| 610 | 625 | const TestCase = struct { |
| ... | ... | @@ -633,12 +648,10 @@ test "Go test cases" { |
| 633 | 648 | .{ |
| 634 | 649 | .name = "small.txt", |
| 635 | 650 | .size = 5, |
| 636 | | .file_type = .normal, |
| 637 | 651 | }, |
| 638 | 652 | .{ |
| 639 | 653 | .name = "small2.txt", |
| 640 | 654 | .size = 11, |
| 641 | | .file_type = .normal, |
| 642 | 655 | }, |
| 643 | 656 | }, |
| 644 | 657 | .chksums = &[_][]const u8{ |
| ... | ... | @@ -656,12 +669,10 @@ test "Go test cases" { |
| 656 | 669 | .{ |
| 657 | 670 | .name = "small.txt", |
| 658 | 671 | .size = 5, |
| 659 | | .file_type = .normal, |
| 660 | 672 | }, |
| 661 | 673 | .{ |
| 662 | 674 | .name = "small2.txt", |
| 663 | 675 | .size = 11, |
| 664 | | .file_type = .normal, |
| 665 | 676 | }, |
| 666 | 677 | }, |
| 667 | 678 | .chksums = &[_][]const u8{ |
| ... | ... | @@ -675,12 +686,10 @@ test "Go test cases" { |
| 675 | 686 | .{ |
| 676 | 687 | .name = "small.txt", |
| 677 | 688 | .size = 5, |
| 678 | | .file_type = .normal, |
| 679 | 689 | }, |
| 680 | 690 | .{ |
| 681 | 691 | .name = "small2.txt", |
| 682 | 692 | .size = 11, |
| 683 | | .file_type = .normal, |
| 684 | 693 | }, |
| 685 | 694 | }, |
| 686 | 695 | .chksums = &[_][]const u8{ |
| ... | ... | @@ -694,7 +703,6 @@ test "Go test cases" { |
| 694 | 703 | .{ |
| 695 | 704 | .name = "a/123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100", |
| 696 | 705 | .size = 7, |
| 697 | | .file_type = .normal, |
| 698 | 706 | }, |
| 699 | 707 | .{ |
| 700 | 708 | .name = "a/b", |
| ... | ... | @@ -707,18 +715,25 @@ test "Go test cases" { |
| 707 | 715 | "3c382e8f5b6631aa2db52643912ffd4a", |
| 708 | 716 | }, |
| 709 | 717 | }, |
| 710 | | // TODO: this should fail |
| 711 | | // .{ |
| 712 | | // .path = "pax-bad-hdr-file.tar", |
| 713 | | // .err = error.TarBadHeader, |
| 714 | | // }, |
| 718 | .{ |
| 719 | // pax attribute don't end with \n |
| 720 | .path = "pax-bad-hdr-file.tar", |
| 721 | // .files = &[_]TestCase.File{ |
| 722 | // .{ |
| 723 | // .name = "PAX1/PAX1/long-path-name", |
| 724 | // .size = 684, |
| 725 | // }, |
| 726 | // }, |
| 727 | .err = error.InvalidPaxAttribute, |
| 728 | }, |
| 729 | // |
| 715 | 730 | // .{ |
| 716 | 731 | // .path = "pax-bad-mtime-file.tar", |
| 717 | 732 | // .err = error.TarBadHeader, |
| 718 | 733 | // }, |
| 719 | 734 | // |
| 720 | | // TODO: giving wrong result because we are not reading pax size header |
| 721 | 735 | .{ |
| 736 | // size is in pax attribute |
| 722 | 737 | .path = "pax-pos-size-file.tar", |
| 723 | 738 | .files = &[_]TestCase.File{ |
| 724 | 739 | .{ |
| ... | ... | @@ -799,9 +814,17 @@ test "Go test cases" { |
| 799 | 814 | .path = "gnu-incremental.tar", |
| 800 | 815 | .err = error.TarUnsupportedFileType, |
| 801 | 816 | }, |
| 802 | | // .{ |
| 803 | | // .path = "pax-multi-hdrs.tar", |
| 804 | | // }, |
| 817 | .{ |
| 818 | // should use values only from last pax header |
| 819 | .path = "pax-multi-hdrs.tar", |
| 820 | .files = &[_]TestCase.File{ |
| 821 | .{ |
| 822 | .name = "bar", |
| 823 | .link_name = "PAX4/PAX4/long-linkpath-name", |
| 824 | .file_type = .symbolic_link, |
| 825 | }, |
| 826 | }, |
| 827 | }, |
| 805 | 828 | // .{ |
| 806 | 829 | // .path = "gnu-long-nul.tar", |
| 807 | 830 | // .files = &[_]TestCase.File{ |
| ... | ... | @@ -827,8 +850,20 @@ test "Go test cases" { |
| 827 | 850 | }, |
| 828 | 851 | }, |
| 829 | 852 | }, |
| 853 | .{ |
| 854 | .path = "neg-size.tar", |
| 855 | .err = error.TarHeader, |
| 856 | }, |
| 857 | .{ |
| 858 | .path = "pax-nul-path.tar", |
| 859 | .err = error.InvalidPaxAttribute, |
| 860 | }, |
| 861 | .{ |
| 862 | .path = "pax-nul-xattrs.tar", |
| 863 | .err = error.InvalidPaxAttribute, |
| 864 | }, |
| 830 | 865 | // TODO some files with errors: |
| 831 | | // pax-nul-xattrs.tar, pax-nul-path.tar, neg-size.tar, issue10968.tar, issue11169.tar, issue12435.tar |
| 866 | // issue10968.tar, issue11169.tar, issue12435.tar |
| 832 | 867 | .{ |
| 833 | 868 | .path = "trailing-slash.tar", |
| 834 | 869 | .files = &[_]TestCase.File{ |