| ... | @@ -3,25 +3,6 @@ const block = @import("xz/block.zig"); | ... | @@ -3,25 +3,6 @@ const block = @import("xz/block.zig"); |
| 3 | const Allocator = std.mem.Allocator; | 3 | const Allocator = std.mem.Allocator; |
| 4 | const Crc32 = std.hash.Crc32; | 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 | pub const Check = enum(u4) { | 6 | pub const Check = enum(u4) { |
| 26 | none = 0x00, | 7 | none = 0x00, |
| 27 | crc32 = 0x01, | 8 | crc32 = 0x01, |
| ... | @@ -30,6 +11,20 @@ pub const Check = enum(u4) { | ... | @@ -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 | pub fn decompress(allocator: Allocator, reader: anytype) !Decompress(@TypeOf(reader)) { | 28 | pub fn decompress(allocator: Allocator, reader: anytype) !Decompress(@TypeOf(reader)) { |
| 34 | return Decompress(@TypeOf(reader)).init(allocator, reader); | 29 | return Decompress(@TypeOf(reader)).init(allocator, reader); |
| 35 | } | 30 | } |
| ... | @@ -46,21 +41,24 @@ pub fn Decompress(comptime ReaderType: type) type { | ... | @@ -46,21 +41,24 @@ pub fn Decompress(comptime ReaderType: type) type { |
| 46 | in_reader: ReaderType, | 41 | in_reader: ReaderType, |
| 47 | | 42 | |
| 48 | fn init(allocator: Allocator, source: ReaderType) !Self { | 43 | fn init(allocator: Allocator, source: ReaderType) !Self { |
| 49 | const header = try source.readStruct(Header); | 44 | const magic = try source.readBytesNoEof(6); |
| 50 | | 45 | if (!std.mem.eql(u8, &magic, &.{ 0xFD, '7', 'z', 'X', 'Z', 0x00 })) |
| 51 | if (!std.mem.eql(u8, &header.magic, &.{ 0xFD, '7', 'z', 'X', 'Z', 0x00 })) | | |
| 52 | return error.BadHeader; | 46 | return error.BadHeader; |
| 53 | | 47 | |
| 54 | if (header.flags.reserved1 != 0 or header.flags.reserved2 != 0) | 48 | var check: Check = undefined; |
| 55 | return error.BadHeader; | 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)); | 55 | const hash_b = try source.readIntLittle(u32); |
| 58 | if (hash != header.crc32) | 56 | if (hash_a != hash_b) |
| 59 | return error.WrongChecksum; | 57 | return error.WrongChecksum; |
| 60 | | 58 | |
| 61 | return Self{ | 59 | return Self{ |
| 62 | .allocator = allocator, | 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 | .in_reader = source, | 62 | .in_reader = source, |
| 65 | }; | 63 | }; |
| 66 | } | 64 | } |
| ... | @@ -114,22 +112,27 @@ pub fn Decompress(comptime ReaderType: type) type { | ... | @@ -114,22 +112,27 @@ pub fn Decompress(comptime ReaderType: type) type { |
| 114 | break :blk counter.bytes_read; | 112 | break :blk counter.bytes_read; |
| 115 | }; | 113 | }; |
| 116 | | 114 | |
| 117 | const footer = try self.in_reader.readStruct(Footer); | 115 | const hash_a = try self.in_reader.readIntLittle(u32); |
| 118 | const backward_size = (footer.backward_size + 1) * 4; | | |
| 119 | if (backward_size != index_size) | | |
| 120 | return error.CorruptInput; | | |
| 121 | | 116 | |
| 122 | if (footer.flags.reserved1 != 0 or footer.flags.reserved2 != 0) | 117 | const hash_b = blk: { |
| 123 | return error.CorruptInput; | 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(); | 131 | if (hash_a != hash_b) |
| 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) | | |
| 130 | return error.WrongChecksum; | 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 | return error.CorruptInput; | 136 | return error.CorruptInput; |
| 134 | | 137 | |
| 135 | return 0; | 138 | return 0; |