| ... | @@ -164,7 +164,7 @@ pub const Header = struct { | ... | @@ -164,7 +164,7 @@ pub const Header = struct { |
| 164 | const ltrimmed = std.mem.trimLeft(u8, raw, "0 "); | 164 | const ltrimmed = std.mem.trimLeft(u8, raw, "0 "); |
| 165 | const rtrimmed = std.mem.trimRight(u8, ltrimmed, " \x00"); | 165 | const rtrimmed = std.mem.trimRight(u8, ltrimmed, " \x00"); |
| 166 | if (rtrimmed.len == 0) return 0; | 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 | // Sum of all bytes in the header block. The chksum field is treated as if | 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,6 +289,10 @@ fn BufferedReader(comptime ReaderType: type) type { |
| 289 | try self.reader.skip(len); | 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 | fn copy(self: *@This(), dst: []u8) ![]const u8 { | 296 | fn copy(self: *@This(), dst: []u8) ![]const u8 { |
| 293 | _ = try self.reader.copy(dst); | 297 | _ = try self.reader.copy(dst); |
| 294 | self.offset += dst.len; | 298 | self.offset += dst.len; |
| ... | @@ -416,21 +420,25 @@ fn Iterator(comptime ReaderType: type) type { | ... | @@ -416,21 +420,25 @@ fn Iterator(comptime ReaderType: type) type { |
| 416 | }, | 420 | }, |
| 417 | .extended_header => { | 421 | .extended_header => { |
| 418 | if (file_size == 0) continue; | 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 | var rdr = self.reader.sliceReader(file_size, false); | 427 | var rdr = self.reader.sliceReader(file_size, false); |
| 421 | while (try rdr.next()) |slice| { | 428 | while (try rdr.next()) |slice| { |
| 422 | const attr = try parsePaxAttribute(slice, rdr.remainingSize()); | 429 | const attr = try parsePaxAttribute(slice, rdr.remainingSize()); |
| 423 | try rdr.advance(attr.value_off); | 430 | try rdr.advance(attr.value_off); |
| 424 | if (attr.is("path")) { | 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 | } else if (attr.is("linkpath")) { | 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 | } else if (attr.is("size")) { | 435 | } else if (attr.is("size")) { |
| 429 | var buf = [_]u8{'0'} ** 32; | 436 | var buf = [_]u8{'0'} ** 32; |
| 430 | file.size = try std.fmt.parseInt(usize, try rdr.copy(buf[0..attr.value_len]), 10); | 437 | file.size = try std.fmt.parseInt(usize, try rdr.copy(buf[0..attr.value_len]), 10); |
| 431 | } else { | 438 | } else { |
| 432 | try rdr.advance(attr.value_len); | 439 | try rdr.advance(attr.value_len); |
| 433 | } | 440 | } |
| | 441 | if (rdr.byte() != '\n') return error.InvalidPaxAttribute; |
| 434 | try rdr.advance(1); | 442 | try rdr.advance(1); |
| 435 | } | 443 | } |
| 436 | try self.reader.skipPadding(file_size); | 444 | try self.reader.skipPadding(file_size); |
| ... | @@ -582,15 +590,21 @@ fn parsePaxAttribute(data: []const u8, max_size: usize) !PaxAttributeInfo { | ... | @@ -582,15 +590,21 @@ fn parsePaxAttribute(data: []const u8, max_size: usize) !PaxAttributeInfo { |
| 582 | if (kv_size > max_size) { | 590 | if (kv_size > max_size) { |
| 583 | return error.InvalidPaxAttribute; | 591 | return error.InvalidPaxAttribute; |
| 584 | } | 592 | } |
| | 593 | const key = data[pos_space + 1 .. pos_equals]; |
| 585 | return .{ | 594 | return .{ |
| 586 | .size = kv_size, | 595 | .size = kv_size, |
| 587 | .key = data[pos_space + 1 .. pos_equals], | 596 | .key = try noNull(key), |
| 588 | .value_off = pos_equals + 1, | 597 | .value_off = pos_equals + 1, |
| 589 | .value_len = kv_size - pos_equals - 2, | 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 | const expectEqual = std.testing.expectEqual; | 608 | const expectEqual = std.testing.expectEqual; |
| 595 | const expectEqualStrings = std.testing.expectEqualStrings; | 609 | const expectEqualStrings = std.testing.expectEqualStrings; |
| 596 | const expectError = std.testing.expectError; | 610 | const expectError = std.testing.expectError; |
| ... | @@ -605,6 +619,7 @@ test parsePaxAttribute { | ... | @@ -605,6 +619,7 @@ test parsePaxAttribute { |
| 605 | try expectEqual(attr_info, try parsePaxAttribute(header, 1012)); | 619 | try expectEqual(attr_info, try parsePaxAttribute(header, 1012)); |
| 606 | try expectError(error.InvalidPaxAttribute, parsePaxAttribute(header, 1010)); | 620 | try expectError(error.InvalidPaxAttribute, parsePaxAttribute(header, 1010)); |
| 607 | try expectError(error.InvalidPaxAttribute, parsePaxAttribute("", 0)); | 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 | const TestCase = struct { | 625 | const TestCase = struct { |
| ... | @@ -633,12 +648,10 @@ test "Go test cases" { | ... | @@ -633,12 +648,10 @@ test "Go test cases" { |
| 633 | .{ | 648 | .{ |
| 634 | .name = "small.txt", | 649 | .name = "small.txt", |
| 635 | .size = 5, | 650 | .size = 5, |
| 636 | .file_type = .normal, | | |
| 637 | }, | 651 | }, |
| 638 | .{ | 652 | .{ |
| 639 | .name = "small2.txt", | 653 | .name = "small2.txt", |
| 640 | .size = 11, | 654 | .size = 11, |
| 641 | .file_type = .normal, | | |
| 642 | }, | 655 | }, |
| 643 | }, | 656 | }, |
| 644 | .chksums = &[_][]const u8{ | 657 | .chksums = &[_][]const u8{ |
| ... | @@ -656,12 +669,10 @@ test "Go test cases" { | ... | @@ -656,12 +669,10 @@ test "Go test cases" { |
| 656 | .{ | 669 | .{ |
| 657 | .name = "small.txt", | 670 | .name = "small.txt", |
| 658 | .size = 5, | 671 | .size = 5, |
| 659 | .file_type = .normal, | | |
| 660 | }, | 672 | }, |
| 661 | .{ | 673 | .{ |
| 662 | .name = "small2.txt", | 674 | .name = "small2.txt", |
| 663 | .size = 11, | 675 | .size = 11, |
| 664 | .file_type = .normal, | | |
| 665 | }, | 676 | }, |
| 666 | }, | 677 | }, |
| 667 | .chksums = &[_][]const u8{ | 678 | .chksums = &[_][]const u8{ |
| ... | @@ -675,12 +686,10 @@ test "Go test cases" { | ... | @@ -675,12 +686,10 @@ test "Go test cases" { |
| 675 | .{ | 686 | .{ |
| 676 | .name = "small.txt", | 687 | .name = "small.txt", |
| 677 | .size = 5, | 688 | .size = 5, |
| 678 | .file_type = .normal, | | |
| 679 | }, | 689 | }, |
| 680 | .{ | 690 | .{ |
| 681 | .name = "small2.txt", | 691 | .name = "small2.txt", |
| 682 | .size = 11, | 692 | .size = 11, |
| 683 | .file_type = .normal, | | |
| 684 | }, | 693 | }, |
| 685 | }, | 694 | }, |
| 686 | .chksums = &[_][]const u8{ | 695 | .chksums = &[_][]const u8{ |
| ... | @@ -694,7 +703,6 @@ test "Go test cases" { | ... | @@ -694,7 +703,6 @@ test "Go test cases" { |
| 694 | .{ | 703 | .{ |
| 695 | .name = "a/123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100", | 704 | .name = "a/123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100", |
| 696 | .size = 7, | 705 | .size = 7, |
| 697 | .file_type = .normal, | | |
| 698 | }, | 706 | }, |
| 699 | .{ | 707 | .{ |
| 700 | .name = "a/b", | 708 | .name = "a/b", |
| ... | @@ -707,18 +715,25 @@ test "Go test cases" { | ... | @@ -707,18 +715,25 @@ test "Go test cases" { |
| 707 | "3c382e8f5b6631aa2db52643912ffd4a", | 715 | "3c382e8f5b6631aa2db52643912ffd4a", |
| 708 | }, | 716 | }, |
| 709 | }, | 717 | }, |
| 710 | // TODO: this should fail | 718 | .{ |
| 711 | // .{ | 719 | // pax attribute don't end with \n |
| 712 | // .path = "pax-bad-hdr-file.tar", | 720 | .path = "pax-bad-hdr-file.tar", |
| 713 | // .err = error.TarBadHeader, | 721 | // .files = &[_]TestCase.File{ |
| 714 | // }, | 722 | // .{ |
| | 723 | // .name = "PAX1/PAX1/long-path-name", |
| | 724 | // .size = 684, |
| | 725 | // }, |
| | 726 | // }, |
| | 727 | .err = error.InvalidPaxAttribute, |
| | 728 | }, |
| | 729 | // |
| 715 | // .{ | 730 | // .{ |
| 716 | // .path = "pax-bad-mtime-file.tar", | 731 | // .path = "pax-bad-mtime-file.tar", |
| 717 | // .err = error.TarBadHeader, | 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 | .path = "pax-pos-size-file.tar", | 737 | .path = "pax-pos-size-file.tar", |
| 723 | .files = &[_]TestCase.File{ | 738 | .files = &[_]TestCase.File{ |
| 724 | .{ | 739 | .{ |
| ... | @@ -799,9 +814,17 @@ test "Go test cases" { | ... | @@ -799,9 +814,17 @@ test "Go test cases" { |
| 799 | .path = "gnu-incremental.tar", | 814 | .path = "gnu-incremental.tar", |
| 800 | .err = error.TarUnsupportedFileType, | 815 | .err = error.TarUnsupportedFileType, |
| 801 | }, | 816 | }, |
| 802 | // .{ | 817 | .{ |
| 803 | // .path = "pax-multi-hdrs.tar", | 818 | // should use values only from last pax header |
| 804 | // }, | 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 | // .path = "gnu-long-nul.tar", | 829 | // .path = "gnu-long-nul.tar", |
| 807 | // .files = &[_]TestCase.File{ | 830 | // .files = &[_]TestCase.File{ |
| ... | @@ -827,8 +850,20 @@ test "Go test cases" { | ... | @@ -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 | // TODO some files with errors: | 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 | .path = "trailing-slash.tar", | 868 | .path = "trailing-slash.tar", |
| 834 | .files = &[_]TestCase.File{ | 869 | .files = &[_]TestCase.File{ |