@@ -215,7 +215,7 @@ const FileExtents = struct {
215215 local_file_header_offset : u64 ,
216216};
217217
218- fn readZip64FileExtents (header : CentralDirectoryFileHeader , extents : * FileExtents , data : []u8 ) ! void {
218+ fn readZip64FileExtents (comptime T : type , header : T , extents : * FileExtents , data : []u8 ) ! void {
219219 var data_offset : usize = 0 ;
220220 if (isMaxInt (header .uncompressed_size )) {
221221 if (data_offset + 8 > data .len )
@@ -229,22 +229,28 @@ fn readZip64FileExtents(header: CentralDirectoryFileHeader, extents: *FileExtent
229229 extents .compressed_size = std .mem .readInt (u64 , data [data_offset .. ][0.. 8], .little );
230230 data_offset += 8 ;
231231 }
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 = > {},
245253 }
246- if (data_offset > data .len )
247- return error .ZipBadCd64Size ;
248254}
249255
250256pub fn Iterator (comptime SeekableStream : type ) type {
@@ -394,7 +400,7 @@ pub fn Iterator(comptime SeekableStream: type) type {
394400 return error .ZipBadExtraFieldSize ;
395401 const data = extra [extra_offset + 4 .. end ];
396402 switch (@as (ExtraHeader , @enumFromInt (header_id ))) {
397- .zip64_info = > try readZip64FileExtents (header , & extents , data ),
403+ .zip64_info = > try readZip64FileExtents (CentralDirectoryFileHeader , header , & extents , data ),
398404 else = > {}, // ignore
399405 }
400406 extra_offset = end ;
@@ -466,12 +472,45 @@ pub fn Iterator(comptime SeekableStream: type) type {
466472 return error .ZipMismatchFlags ;
467473 if (local_header .crc32 != 0 and local_header .crc32 != self .crc32 )
468474 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 )
471509 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 )
474512 return error .ZipMismatchUncompLen ;
513+
475514 if (local_header .filename_len != self .filename_len )
476515 return error .ZipMismatchFilenameLen ;
477516
@@ -695,6 +734,20 @@ test "zip64" {
695734 .central_directory_offset = std .math .maxInt (u32 ), // trigger zip64
696735 },
697736 });
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+ });
698751}
699752
700753test "bad zip files" {
0 commit comments