diff --git a/lib/zip/central_directory.rb b/lib/zip/central_directory.rb index 9975884c..5b64c7f5 100644 --- a/lib/zip/central_directory.rb +++ b/lib/zip/central_directory.rb @@ -124,10 +124,37 @@ def read_central_directory_entries(io) #:nodoc: end @entry_set = EntrySet.new @size.times do - @entry_set << Entry.read_c_dir_entry(io) + entry = Entry.read_c_dir_entry(io) + next unless entry + + offset = if entry.extra['Zip64'] + entry.extra['Zip64'].relative_header_offset + else + entry.local_header_offset + end + + unless offset.nil? + io_save = io.tell + io.seek(offset, IO::SEEK_SET) + entry.read_extra_field(read_local_extra_field(io)) + io.seek(io_save, IO::SEEK_SET) + end + + @entry_set << entry end end + def read_local_extra_field(io) + buf = io.read(::Zip::LOCAL_ENTRY_STATIC_HEADER_LENGTH) || '' + return '' unless buf.bytesize == ::Zip::LOCAL_ENTRY_STATIC_HEADER_LENGTH + + head, _, _, _, _, _, _, _, _, _, n_len, e_len = buf.unpack('VCCvvvvVVVvv') + return '' unless head == ::Zip::LOCAL_ENTRY_SIGNATURE + + io.seek(n_len, IO::SEEK_CUR) # Skip over the entry name. + io.read(e_len) + end + def read_from_stream(io) #:nodoc: buf = start_buf(io) if zip64_file?(buf) diff --git a/lib/zip/entry.rb b/lib/zip/entry.rb index a67c6568..bf47ad6d 100644 --- a/lib/zip/entry.rb +++ b/lib/zip/entry.rb @@ -271,12 +271,7 @@ def read_local_entry(io) #:nodoc:all raise ::Zip::Error, 'Truncated local zip entry header' end - if @extra.kind_of?(::Zip::ExtraField) - @extra.merge(extra) if extra - else - @extra = ::Zip::ExtraField.new(extra) - end - + read_extra_field(extra) parse_zip64_extra(true) @local_header_size = calculate_local_header_size end @@ -379,11 +374,11 @@ def check_c_dir_entry_comment_size raise ::Zip::Error, 'Truncated cdir zip entry header' end - def read_c_dir_extra_field(io) + def read_extra_field(buf) if @extra.kind_of?(::Zip::ExtraField) - @extra.merge(io.read(@extra_length)) + @extra.merge(buf) if buf else - @extra = ::Zip::ExtraField.new(io.read(@extra_length)) + @extra = ::Zip::ExtraField.new(buf) end end @@ -397,7 +392,7 @@ def read_c_dir_entry(io) #:nodoc:all if ::Zip.force_entry_names_encoding @name.force_encoding(::Zip.force_entry_names_encoding) end - read_c_dir_extra_field(io) + read_extra_field(io.read(@extra_length)) @comment = io.read(@comment_length) check_c_dir_entry_comment_size set_ftype_from_c_dir_entry diff --git a/lib/zip/extra_field/generic.rb b/lib/zip/extra_field/generic.rb index 5eb702d6..9237a1db 100644 --- a/lib/zip/extra_field/generic.rb +++ b/lib/zip/extra_field/generic.rb @@ -22,15 +22,6 @@ def initial_parse(binstr) [binstr[2, 2].unpack1('v'), binstr[4..-1]] end - def ==(other) - return false if self.class != other.class - - each do |k, v| - return false if v != other[k] - end - true - end - def to_local_bin s = pack_for_local self.class.const_get(:HEADER_ID) + [s.bytesize].pack('v') << s diff --git a/lib/zip/extra_field/unix.rb b/lib/zip/extra_field/unix.rb index 9a66c81d..d83087e4 100644 --- a/lib/zip/extra_field/unix.rb +++ b/lib/zip/extra_field/unix.rb @@ -20,8 +20,8 @@ def merge(binstr) return if !size || size == 0 uid, gid = content.unpack('vv') - @uid ||= uid - @gid ||= gid # rubocop:disable Naming/MemoizedInstanceVariableName + @uid = uid + @gid = gid end def ==(other) diff --git a/test/data/local_extra_field.zip b/test/data/local_extra_field.zip new file mode 100644 index 00000000..5a936e4c Binary files /dev/null and b/test/data/local_extra_field.zip differ diff --git a/test/extra_field_test.rb b/test/extra_field_test.rb index fa6e212d..52140a2d 100644 --- a/test/extra_field_test.rb +++ b/test/extra_field_test.rb @@ -17,6 +17,16 @@ def test_unknownfield assert_equal(extra.to_s, 'fooabarbaz') end + def test_bad_header_id + str = "ut\x5\0\x3\250$\r@" + ut = nil + assert_output('', /WARNING/) do + ut = ::Zip::ExtraField::UniversalTime.new(str) + end + assert_instance_of(::Zip::ExtraField::UniversalTime, ut) + assert_nil(ut.mtime) + end + def test_ntfs str = "\x0A\x00 \x00\x00\x00\x00\x00\x01\x00\x18\x00\xC0\x81\x17\xE8B\xCE\xCF\x01\xC0\x81\x17\xE8B\xCE\xCF\x01\xC0\x81\x17\xE8B\xCE\xCF\x01" extra = ::Zip::ExtraField.new(str) @@ -25,6 +35,8 @@ def test_ntfs assert_equal(t, extra['NTFS'].mtime) assert_equal(t, extra['NTFS'].atime) assert_equal(t, extra['NTFS'].ctime) + + assert_equal(str.force_encoding('BINARY'), extra.to_local_bin) end def test_merge @@ -73,4 +85,16 @@ def test_equality extra1.create('IUnix') assert_equal(extra1, extra3) end + + def test_read_local_extra_field + ::Zip::File.open('test/data/local_extra_field.zip') do |zf| + ['file1.txt', 'file2.txt'].each do |file| + entry = zf.get_entry(file) + + assert_instance_of(::Zip::ExtraField, entry.extra) + assert_equal(1_000, entry.extra['IUnix'].uid) + assert_equal(1_000, entry.extra['IUnix'].gid) + end + end + end end diff --git a/test/filesystem/file_nonmutating_test.rb b/test/filesystem/file_nonmutating_test.rb index 346d5a76..485298fa 100644 --- a/test/filesystem/file_nonmutating_test.rb +++ b/test/filesystem/file_nonmutating_test.rb @@ -295,8 +295,10 @@ def test_ctime end def test_atime - assert_nil(@zip_file.file.atime('file1')) - assert_nil(@zip_file.file.stat('file1').atime) + assert_equal(::Zip::DOSTime.at(1_027_694_306), + @zip_file.file.atime('file1')) + assert_equal(::Zip::DOSTime.at(1_027_694_306), + @zip_file.file.stat('file1').atime) end def test_ntfs_time diff --git a/test/filesystem/file_stat_test.rb b/test/filesystem/file_stat_test.rb index 05d7fff8..b8efe754 100644 --- a/test/filesystem/file_stat_test.rb +++ b/test/filesystem/file_stat_test.rb @@ -19,11 +19,11 @@ def test_ino end def test_uid - assert_equal(0, @zip_file.file.stat('file1').uid) + assert_equal(500, @zip_file.file.stat('file1').uid) end def test_gid - assert_equal(0, @zip_file.file.stat('file1').gid) + assert_equal(500, @zip_file.file.stat('file1').gid) end def test_ftype