| ... | @@ -140,11 +140,25 @@ pub const Header = struct { | ... | @@ -140,11 +140,25 @@ pub const Header = struct { |
| 140 | } | 140 | } |
| 141 | | 141 | |
| 142 | pub fn mode(header: Header) !u32 { | 142 | pub fn mode(header: Header) !u32 { |
| 143 | return @intCast(try header.numeric(100, 8)); | 143 | return @intCast(try header.octal(100, 8)); |
| 144 | } | 144 | } |
| 145 | | 145 | |
| 146 | pub fn size(header: Header) !u64 { | 146 | pub fn size(header: Header) !u64 { |
| 147 | return header.numeric(124, 12); | 147 | const start = 124; |
| | 148 | const len = 12; |
| | 149 | const raw = header.bytes[start..][0..len]; |
| | 150 | // If the leading byte is 0xff (255), all the bytes of the field |
| | 151 | // (including the leading byte) are concatenated in big-endian order, |
| | 152 | // with the result being a negative number expressed in two’s |
| | 153 | // complement form. |
| | 154 | if (raw[0] == 0xff) return error.TarNumericValueNegative; |
| | 155 | // If the leading byte is 0x80 (128), the non-leading bytes of the |
| | 156 | // field are concatenated in big-endian order. |
| | 157 | if (raw[0] == 0x80) { |
| | 158 | if (raw[1] != 0 or raw[2] != 0 or raw[3] != 0) return error.TarNumericValueTooBig; |
| | 159 | return std.mem.readInt(u64, raw[4..12], .big); |
| | 160 | } |
| | 161 | return try header.octal(start, len); |
| 148 | } | 162 | } |
| 149 | | 163 | |
| 150 | pub fn chksum(header: Header) !u64 { | 164 | pub fn chksum(header: Header) !u64 { |
| ... | @@ -170,22 +184,6 @@ pub const Header = struct { | ... | @@ -170,22 +184,6 @@ pub const Header = struct { |
| 170 | return nullStr(header.bytes[start .. start + len]); | 184 | return nullStr(header.bytes[start .. start + len]); |
| 171 | } | 185 | } |
| 172 | | 186 | |
| 173 | fn numeric(header: Header, start: usize, len: usize) !u64 { | | |
| 174 | const raw = header.bytes[start..][0..len]; | | |
| 175 | // If the leading byte is 0xff (255), all the bytes of the field | | |
| 176 | // (including the leading byte) are concatenated in big-endian order, | | |
| 177 | // with the result being a negative number expressed in two’s | | |
| 178 | // complement form. | | |
| 179 | if (raw[0] == 0xff) return error.TarNumericValueNegative; | | |
| 180 | // If the leading byte is 0x80 (128), the non-leading bytes of the | | |
| 181 | // field are concatenated in big-endian order. | | |
| 182 | if (raw[0] == 0x80) { | | |
| 183 | if (raw[1] + raw[2] + raw[3] != 0) return error.TarNumericValueTooBig; | | |
| 184 | return std.mem.readInt(u64, raw[4..12], .big); | | |
| 185 | } | | |
| 186 | return try header.octal(start, len); | | |
| 187 | } | | |
| 188 | | | |
| 189 | fn octal(header: Header, start: usize, len: usize) !u64 { | 187 | fn octal(header: Header, start: usize, len: usize) !u64 { |
| 190 | const raw = header.bytes[start..][0..len]; | 188 | const raw = header.bytes[start..][0..len]; |
| 191 | // Zero-filled octal number in ASCII. Each numeric field of width w | 189 | // Zero-filled octal number in ASCII. Each numeric field of width w |
| ... | @@ -756,3 +754,63 @@ test "tar PaxIterator" { | ... | @@ -756,3 +754,63 @@ test "tar PaxIterator" { |
| 756 | test { | 754 | test { |
| 757 | _ = @import("tar/test.zig"); | 755 | _ = @import("tar/test.zig"); |
| 758 | } | 756 | } |
| | 757 | |
| | 758 | test "tar header parse size" { |
| | 759 | const cases = [_]struct { |
| | 760 | in: []const u8, |
| | 761 | want: u64 = 0, |
| | 762 | err: ?anyerror = null, |
| | 763 | }{ |
| | 764 | // Test base-256 (binary) encoded values. |
| | 765 | .{ .in = "", .want = 0 }, |
| | 766 | .{ .in = "\x80", .want = 0 }, |
| | 767 | .{ .in = "\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01", .want = 1 }, |
| | 768 | .{ .in = "\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x02", .want = 0x0102 }, |
| | 769 | .{ .in = "\x80\x00\x00\x00\x01\x02\x03\x04\x05\x06\x07\x08", .want = 0x0102030405060708 }, |
| | 770 | .{ .in = "\x80\x00\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09", .err = error.TarNumericValueTooBig }, |
| | 771 | .{ .in = "\x80\x00\x00\x00\x07\x76\xa2\x22\xeb\x8a\x72\x61", .want = 537795476381659745 }, |
| | 772 | .{ .in = "\x80\x80\x80\x00\x01\x02\x03\x04\x05\x06\x07\x08", .err = error.TarNumericValueTooBig }, |
| | 773 | |
| | 774 | // // Test base-8 (octal) encoded values. |
| | 775 | .{ .in = "00000000227\x00", .want = 0o227 }, |
| | 776 | .{ .in = " 000000227\x00", .want = 0o227 }, |
| | 777 | .{ .in = "00000000228\x00", .err = error.TarHeader }, |
| | 778 | .{ .in = "11111111111\x00", .want = 0o11111111111 }, |
| | 779 | }; |
| | 780 | |
| | 781 | for (cases) |case| { |
| | 782 | var bytes = [_]u8{0} ** Header.SIZE; |
| | 783 | @memcpy(bytes[124 .. 124 + case.in.len], case.in); |
| | 784 | var header = Header{ .bytes = &bytes }; |
| | 785 | if (case.err) |err| { |
| | 786 | try std.testing.expectError(err, header.size()); |
| | 787 | } else { |
| | 788 | try std.testing.expectEqual(case.want, try header.size()); |
| | 789 | } |
| | 790 | } |
| | 791 | } |
| | 792 | |
| | 793 | test "tar header parse mode" { |
| | 794 | const cases = [_]struct { |
| | 795 | in: []const u8, |
| | 796 | want: u64 = 0, |
| | 797 | err: ?anyerror = null, |
| | 798 | }{ |
| | 799 | .{ .in = "0000644\x00", .want = 0o644 }, |
| | 800 | .{ .in = "0000777\x00", .want = 0o777 }, |
| | 801 | .{ .in = "7777777\x00", .want = 0o7777777 }, |
| | 802 | .{ .in = "7777778\x00", .err = error.TarHeader }, |
| | 803 | .{ .in = "77777777", .want = 0o77777777 }, |
| | 804 | .{ .in = "777777777777", .want = 0o77777777 }, |
| | 805 | }; |
| | 806 | for (cases) |case| { |
| | 807 | var bytes = [_]u8{0} ** Header.SIZE; |
| | 808 | @memcpy(bytes[100 .. 100 + case.in.len], case.in); |
| | 809 | var header = Header{ .bytes = &bytes }; |
| | 810 | if (case.err) |err| { |
| | 811 | try std.testing.expectError(err, header.mode()); |
| | 812 | } else { |
| | 813 | try std.testing.expectEqual(case.want, try header.mode()); |
| | 814 | } |
| | 815 | } |
| | 816 | } |