authorgravatar for igor.anic@gmail.comIgor Anić <igor.anic@gmail.com> 2023-11-29 20:30:08+01:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-01-13 19:37:33-07:00
log48b160c1bf75f602acabc3b43eca56b8aa4abf4f
treefb23b9fa99d1ccc0d5548aca74c60500b9138513
parent16c40fc4713c195c7a6b8544c9dffbfc6201dc9d

tar: handle pax null attrs and pax attr ending


1 files changed, 57 insertions(+), 22 deletions(-)

lib/std/tar.zig+57-22
...@@ -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 }
169169
170 // Sum of all bytes in the header block. The chksum field is treated as if170 // 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 }
291291
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 };
419426
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}
592601
593test parsePaxAttribute {602fn noNull(str: []const u8) ![]const u8 {
603 if (std.mem.indexOfScalar(u8, str, 0)) |_| return error.InvalidPaxAttribute;
604 return str;
605}
606
607test "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}
609624
610const TestCase = struct {625const 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 fail718 .{
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.tar866 // 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{