| ... | ... | @@ -215,7 +215,7 @@ const FileExtents = struct { |
| 215 | 215 | local_file_header_offset: u64, |
| 216 | 216 | }; |
| 217 | 217 | |
| 218 | | fn readZip64FileExtents(header: CentralDirectoryFileHeader, extents: *FileExtents, data: []u8) !void { |
| 218 | fn readZip64FileExtents(comptime T: type, header: T, extents: *FileExtents, data: []u8) !void { |
| 219 | 219 | var data_offset: usize = 0; |
| 220 | 220 | if (isMaxInt(header.uncompressed_size)) { |
| 221 | 221 | if (data_offset + 8 > data.len) |
| ... | ... | @@ -229,22 +229,28 @@ fn readZip64FileExtents(header: CentralDirectoryFileHeader, extents: *FileExtent |
| 229 | 229 | extents.compressed_size = std.mem.readInt(u64, data[data_offset..][0..8], .little); |
| 230 | 230 | data_offset += 8; |
| 231 | 231 | } |
| 232 | | if (isMaxInt(header.local_file_header_offset)) { |
| 233 | | if (data_offset + 8 > data.len) |
| 234 | | return error.ZipBadCd64Size; |
| 235 | | extents.local_file_header_offset = std.mem.readInt(u64, data[data_offset..][0..8], .little); |
| 236 | | data_offset += 8; |
| 237 | | } |
| 238 | | if (isMaxInt(header.disk_number)) { |
| 239 | | if (data_offset + 4 > data.len) |
| 240 | | return error.ZipInvalid; |
| 241 | | const disk_number = std.mem.readInt(u32, data[data_offset..][0..4], .little); |
| 242 | | if (disk_number != 0) |
| 243 | | return error.ZipMultiDiskUnsupported; |
| 244 | | data_offset += 4; |
| 232 | |
| 233 | switch (T) { |
| 234 | CentralDirectoryFileHeader => { |
| 235 | if (isMaxInt(header.local_file_header_offset)) { |
| 236 | if (data_offset + 8 > data.len) |
| 237 | return error.ZipBadCd64Size; |
| 238 | extents.local_file_header_offset = std.mem.readInt(u64, data[data_offset..][0..8], .little); |
| 239 | data_offset += 8; |
| 240 | } |
| 241 | if (isMaxInt(header.disk_number)) { |
| 242 | if (data_offset + 4 > data.len) |
| 243 | return error.ZipInvalid; |
| 244 | const disk_number = std.mem.readInt(u32, data[data_offset..][0..4], .little); |
| 245 | if (disk_number != 0) |
| 246 | return error.ZipMultiDiskUnsupported; |
| 247 | data_offset += 4; |
| 248 | } |
| 249 | if (data_offset > data.len) |
| 250 | return error.ZipBadCd64Size; |
| 251 | }, |
| 252 | else => {}, |
| 245 | 253 | } |
| 246 | | if (data_offset > data.len) |
| 247 | | return error.ZipBadCd64Size; |
| 248 | 254 | } |
| 249 | 255 | |
| 250 | 256 | pub fn Iterator(comptime SeekableStream: type) type { |
| ... | ... | @@ -394,7 +400,7 @@ pub fn Iterator(comptime SeekableStream: type) type { |
| 394 | 400 | return error.ZipBadExtraFieldSize; |
| 395 | 401 | const data = extra[extra_offset + 4 .. end]; |
| 396 | 402 | switch (@as(ExtraHeader, @enumFromInt(header_id))) { |
| 397 | | .zip64_info => try readZip64FileExtents(header, &extents, data), |
| 403 | .zip64_info => try readZip64FileExtents(CentralDirectoryFileHeader, header, &extents, data), |
| 398 | 404 | else => {}, // ignore |
| 399 | 405 | } |
| 400 | 406 | extra_offset = end; |
| ... | ... | @@ -466,12 +472,45 @@ pub fn Iterator(comptime SeekableStream: type) type { |
| 466 | 472 | return error.ZipMismatchFlags; |
| 467 | 473 | if (local_header.crc32 != 0 and local_header.crc32 != self.crc32) |
| 468 | 474 | return error.ZipMismatchCrc32; |
| 469 | | if (local_header.compressed_size != 0 and |
| 470 | | local_header.compressed_size != self.compressed_size) |
| 475 | var extents: FileExtents = .{ |
| 476 | .uncompressed_size = local_header.uncompressed_size, |
| 477 | .compressed_size = local_header.compressed_size, |
| 478 | .local_file_header_offset = 0, |
| 479 | }; |
| 480 | if (local_header.extra_len > 0) { |
| 481 | var extra_buf: [std.math.maxInt(u16)]u8 = undefined; |
| 482 | const extra = extra_buf[0..local_header.extra_len]; |
| 483 | |
| 484 | { |
| 485 | try stream.seekTo(self.file_offset + @sizeOf(LocalFileHeader) + local_header.filename_len); |
| 486 | const len = try stream.context.reader().readAll(extra); |
| 487 | if (len != extra.len) |
| 488 | return error.ZipTruncated; |
| 489 | } |
| 490 | |
| 491 | var extra_offset: usize = 0; |
| 492 | while (extra_offset + 4 <= local_header.extra_len) { |
| 493 | const header_id = std.mem.readInt(u16, extra[extra_offset..][0..2], .little); |
| 494 | const data_size = std.mem.readInt(u16, extra[extra_offset..][2..4], .little); |
| 495 | const end = extra_offset + 4 + data_size; |
| 496 | if (end > local_header.extra_len) |
| 497 | return error.ZipBadExtraFieldSize; |
| 498 | const data = extra[extra_offset + 4 .. end]; |
| 499 | switch (@as(ExtraHeader, @enumFromInt(header_id))) { |
| 500 | .zip64_info => try readZip64FileExtents(LocalFileHeader, local_header, &extents, data), |
| 501 | else => {}, // ignore |
| 502 | } |
| 503 | extra_offset = end; |
| 504 | } |
| 505 | } |
| 506 | |
| 507 | if (extents.compressed_size != 0 and |
| 508 | extents.compressed_size != self.compressed_size) |
| 471 | 509 | return error.ZipMismatchCompLen; |
| 472 | | if (local_header.uncompressed_size != 0 and |
| 473 | | local_header.uncompressed_size != self.uncompressed_size) |
| 510 | if (extents.uncompressed_size != 0 and |
| 511 | extents.uncompressed_size != self.uncompressed_size) |
| 474 | 512 | return error.ZipMismatchUncompLen; |
| 513 | |
| 475 | 514 | if (local_header.filename_len != self.filename_len) |
| 476 | 515 | return error.ZipMismatchFilenameLen; |
| 477 | 516 | |
| ... | ... | @@ -695,6 +734,20 @@ test "zip64" { |
| 695 | 734 | .central_directory_offset = std.math.maxInt(u32), // trigger zip64 |
| 696 | 735 | }, |
| 697 | 736 | }); |
| 737 | try testZip(.{}, &test_files, .{ |
| 738 | .end = .{ |
| 739 | .zip64 = .{}, |
| 740 | .central_directory_offset = std.math.maxInt(u32), // trigger zip64 |
| 741 | }, |
| 742 | .local_header = .{ |
| 743 | .zip64 = .{ // trigger local header zip64 |
| 744 | .data_size = 16, |
| 745 | }, |
| 746 | .compressed_size = std.math.maxInt(u32), |
| 747 | .uncompressed_size = std.math.maxInt(u32), |
| 748 | .extra_len = 20, |
| 749 | }, |
| 750 | }); |
| 698 | 751 | } |
| 699 | 752 | |
| 700 | 753 | test "bad zip files" { |