| ... | ... | @@ -3,25 +3,6 @@ const block = @import("xz/block.zig"); |
| 3 | 3 | const Allocator = std.mem.Allocator; |
| 4 | 4 | const Crc32 = std.hash.Crc32; |
| 5 | 5 | |
| 6 | | pub const Flags = packed struct(u16) { |
| 7 | | reserved1: u8, |
| 8 | | check_kind: Check, |
| 9 | | reserved2: u4, |
| 10 | | }; |
| 11 | | |
| 12 | | pub const Header = extern struct { |
| 13 | | magic: [6]u8, |
| 14 | | flags: Flags, |
| 15 | | crc32: u32, |
| 16 | | }; |
| 17 | | |
| 18 | | pub const Footer = extern struct { |
| 19 | | crc32: u32, |
| 20 | | backward_size: u32, |
| 21 | | flags: Flags, |
| 22 | | magic: [2]u8, |
| 23 | | }; |
| 24 | | |
| 25 | 6 | pub const Check = enum(u4) { |
| 26 | 7 | none = 0x00, |
| 27 | 8 | crc32 = 0x01, |
| ... | ... | @@ -30,6 +11,20 @@ pub const Check = enum(u4) { |
| 30 | 11 | _, |
| 31 | 12 | }; |
| 32 | 13 | |
| 14 | fn readStreamFlags(reader: anytype, check: *Check) !void { |
| 15 | var bit_reader = std.io.bitReader(.Little, reader); |
| 16 | |
| 17 | const reserved1 = try bit_reader.readBitsNoEof(u8, 8); |
| 18 | if (reserved1 != 0) |
| 19 | return error.CorruptInput; |
| 20 | |
| 21 | check.* = @intToEnum(Check, try bit_reader.readBitsNoEof(u4, 4)); |
| 22 | |
| 23 | const reserved2 = try bit_reader.readBitsNoEof(u4, 4); |
| 24 | if (reserved2 != 0) |
| 25 | return error.CorruptInput; |
| 26 | } |
| 27 | |
| 33 | 28 | pub fn decompress(allocator: Allocator, reader: anytype) !Decompress(@TypeOf(reader)) { |
| 34 | 29 | return Decompress(@TypeOf(reader)).init(allocator, reader); |
| 35 | 30 | } |
| ... | ... | @@ -46,21 +41,24 @@ pub fn Decompress(comptime ReaderType: type) type { |
| 46 | 41 | in_reader: ReaderType, |
| 47 | 42 | |
| 48 | 43 | fn init(allocator: Allocator, source: ReaderType) !Self { |
| 49 | | const header = try source.readStruct(Header); |
| 50 | | |
| 51 | | if (!std.mem.eql(u8, &header.magic, &.{ 0xFD, '7', 'z', 'X', 'Z', 0x00 })) |
| 44 | const magic = try source.readBytesNoEof(6); |
| 45 | if (!std.mem.eql(u8, &magic, &.{ 0xFD, '7', 'z', 'X', 'Z', 0x00 })) |
| 52 | 46 | return error.BadHeader; |
| 53 | 47 | |
| 54 | | if (header.flags.reserved1 != 0 or header.flags.reserved2 != 0) |
| 55 | | return error.BadHeader; |
| 48 | var check: Check = undefined; |
| 49 | const hash_a = blk: { |
| 50 | var hasher = std.compress.hashedReader(source, Crc32.init()); |
| 51 | try readStreamFlags(hasher.reader(), &check); |
| 52 | break :blk hasher.hasher.final(); |
| 53 | }; |
| 56 | 54 | |
| 57 | | const hash = Crc32.hash(std.mem.asBytes(&header.flags)); |
| 58 | | if (hash != header.crc32) |
| 55 | const hash_b = try source.readIntLittle(u32); |
| 56 | if (hash_a != hash_b) |
| 59 | 57 | return error.WrongChecksum; |
| 60 | 58 | |
| 61 | 59 | return Self{ |
| 62 | 60 | .allocator = allocator, |
| 63 | | .block_decoder = try block.decoder(allocator, source, header.flags.check_kind), |
| 61 | .block_decoder = try block.decoder(allocator, source, check), |
| 64 | 62 | .in_reader = source, |
| 65 | 63 | }; |
| 66 | 64 | } |
| ... | ... | @@ -114,22 +112,27 @@ pub fn Decompress(comptime ReaderType: type) type { |
| 114 | 112 | break :blk counter.bytes_read; |
| 115 | 113 | }; |
| 116 | 114 | |
| 117 | | const footer = try self.in_reader.readStruct(Footer); |
| 118 | | const backward_size = (footer.backward_size + 1) * 4; |
| 119 | | if (backward_size != index_size) |
| 120 | | return error.CorruptInput; |
| 115 | const hash_a = try self.in_reader.readIntLittle(u32); |
| 121 | 116 | |
| 122 | | if (footer.flags.reserved1 != 0 or footer.flags.reserved2 != 0) |
| 123 | | return error.CorruptInput; |
| 117 | const hash_b = blk: { |
| 118 | var hasher = std.compress.hashedReader(self.in_reader, Crc32.init()); |
| 119 | const hashed_reader = hasher.reader(); |
| 120 | |
| 121 | const backward_size = (try hashed_reader.readIntLittle(u32) + 1) * 4; |
| 122 | if (backward_size != index_size) |
| 123 | return error.CorruptInput; |
| 124 | |
| 125 | var check: Check = undefined; |
| 126 | try readStreamFlags(hashed_reader, &check); |
| 127 | |
| 128 | break :blk hasher.hasher.final(); |
| 129 | }; |
| 124 | 130 | |
| 125 | | var hasher = Crc32.init(); |
| 126 | | hasher.update(std.mem.asBytes(&footer.backward_size)); |
| 127 | | hasher.update(std.mem.asBytes(&footer.flags)); |
| 128 | | const hash = hasher.final(); |
| 129 | | if (hash != footer.crc32) |
| 131 | if (hash_a != hash_b) |
| 130 | 132 | return error.WrongChecksum; |
| 131 | 133 | |
| 132 | | if (!std.mem.eql(u8, &footer.magic, &.{ 'Y', 'Z' })) |
| 134 | const magic = try self.in_reader.readBytesNoEof(2); |
| 135 | if (!std.mem.eql(u8, &magic, &.{ 'Y', 'Z' })) |
| 133 | 136 | return error.CorruptInput; |
| 134 | 137 | |
| 135 | 138 | return 0; |