| ... | ... | @@ -140,11 +140,25 @@ pub const Header = struct { |
| 140 | 140 | } |
| 141 | 141 | |
| 142 | 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 | 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 | 164 | pub fn chksum(header: Header) !u64 { |
| ... | ... | @@ -170,22 +184,6 @@ pub const Header = struct { |
| 170 | 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 | 187 | fn octal(header: Header, start: usize, len: usize) !u64 { |
| 190 | 188 | const raw = header.bytes[start..][0..len]; |
| 191 | 189 | // Zero-filled octal number in ASCII. Each numeric field of width w |
| ... | ... | @@ -756,3 +754,63 @@ test "tar PaxIterator" { |
| 756 | 754 | test { |
| 757 | 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 | } |