Skip to content

Commit

Permalink
Only use the Zip64 CDIR end locator if needed.
Browse files Browse the repository at this point in the history
Previously the central directory Zip64 data was written even if it wasn't
strictly needed. The standard allows for entries to include Zip64 data
(say, if they are streamed and their size is unknown when writing the file
data) without needing any Zip64 data in the central directory. So now we
only write central directory Zip64 data if there are over 65535 files or
the file data is huge.
  • Loading branch information
hainesr committed Nov 17, 2022
1 parent 1aac89b commit c9ddf28
Show file tree
Hide file tree
Showing 2 changed files with 13 additions and 34 deletions.
12 changes: 4 additions & 8 deletions lib/zip/central_directory.rb
Original file line number Diff line number Diff line change
Expand Up @@ -44,14 +44,10 @@ def write_to_stream(io) #:nodoc:
@entry_set.each { |entry| entry.write_c_dir_entry(io) }
eocd_offset = io.tell
cdir_size = eocd_offset - cdir_offset
if ::Zip.write_zip64_support
need_zip64_eocd = cdir_offset > 0xFFFFFFFF || cdir_size > 0xFFFFFFFF \
|| @entry_set.size > 0xFFFF
need_zip64_eocd ||= @entry_set.any?(&:zip64?)
if need_zip64_eocd
write_64_e_o_c_d(io, cdir_offset, cdir_size)
write_64_eocd_locator(io, eocd_offset)
end
if Zip.write_zip64_support &&
(cdir_offset > 0xFFFFFFFF || cdir_size > 0xFFFFFFFF || @entry_set.size > 0xFFFF)
write_64_e_o_c_d(io, cdir_offset, cdir_size)
write_64_eocd_locator(io, eocd_offset)
end
write_e_o_c_d(io, cdir_offset, cdir_size)
end
Expand Down
35 changes: 9 additions & 26 deletions test/central_directory_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -87,43 +87,26 @@ def test_write_to_stream
assert_equal(cdir.entries.sort, cdir_readback.entries.sort)
end

def test_write64_to_stream
entries = [
::Zip::Entry.new(
'file.zip', 'file1-little', comment: 'comment1', size: 200,
compressed_size: 200, crc: 101,
compression_method: ::Zip::Entry::STORED
),
::Zip::Entry.new(
'file.zip', 'file2-big', comment: 'comment2',
size: 20_000_000_000, compressed_size: 18_000_000_000, crc: 102
),
::Zip::Entry.new(
'file.zip', 'file3-alsobig', comment: 'comment3',
size: 21_000_000_000, compressed_size: 15_000_000_000, crc: 103
),
::Zip::Entry.new(
'file.zip', 'file4-little', comment: 'comment4',
size: 121, compressed_size: 100, crc: 104
)
]
def test_write64_to_stream_65536_entries
skip unless ENV['FULL_ZIP64_TEST']

[0, 250, 18_000_000_300, 33_000_000_350].each_with_index do |offset, index|
entries[index].local_header_offset = offset
entries = []
0x10000.times do |i|
entries << Zip::Entry.new('file.zip', "#{i}.txt")
end

cdir = ::Zip::CentralDirectory.new(entries, 'zip comment')
cdir = Zip::CentralDirectory.new(entries)
File.open('test/data/generated/cdir64test.bin', 'wb') do |f|
cdir.write_to_stream(f)
end

cdir_readback = ::Zip::CentralDirectory.new
cdir_readback = Zip::CentralDirectory.new
File.open('test/data/generated/cdir64test.bin', 'rb') do |f|
cdir_readback.read_from_stream(f)
end

assert_equal(cdir.entries.sort, cdir_readback.entries.sort)
assert_equal(::Zip::VERSION_NEEDED_TO_EXTRACT_ZIP64, cdir_readback.instance_variable_get(:@version_needed_for_extract))
assert_equal(0x10000, cdir_readback.size)
assert_equal(Zip::VERSION_NEEDED_TO_EXTRACT_ZIP64, cdir_readback.instance_variable_get(:@version_needed_for_extract))
end

def test_equality
Expand Down

0 comments on commit c9ddf28

Please sign in to comment.