From f37e679e7caa111ccc7f29c6b7fc6bf99b263f23 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Thu, 20 Feb 2020 14:58:32 +0000 Subject: [PATCH 001/311] Add a rubocop job to the Travis config. --- .travis.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.travis.yml b/.travis.yml index b903c3b3..969d4c0e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,6 +17,10 @@ matrix: - rvm: jruby-head jdk: openjdk11 - rvm: rbx-4 + - name: Rubocop + script: + - bundle info rubocop + - bundle exec rubocop allow_failures: - rvm: ruby-head - rvm: rbx-4 From ee06a8038aac41ba6128f658b7c9ddc3130d4fe3 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Thu, 20 Feb 2020 15:26:18 +0000 Subject: [PATCH 002/311] Bump Rubocop version and lock it down at patch level. Now we're using Rubocop in CI we need to prevent CI from dragging in a newer version than that which we are expecting. This will avoid unexpected fails from the linting stage. --- .rubocop.yml | 6 ++++++ .rubocop_todo.yml | 4 ++++ rubyzip.gemspec | 2 +- 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/.rubocop.yml b/.rubocop.yml index 3fd8ffae..ef285c5e 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -54,6 +54,12 @@ Metrics/MethodLength: Style/ClassCheck: EnforcedStyle: kind_of? +Style/HashEachMethods: + Enabled: true + +Style/HashTransformValues: + Enabled: true + # Allow this multi-line block chain as it actually reads better # than the alternatives. Style/MultilineBlockChain: diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 7776a745..1debf8fe 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -77,6 +77,10 @@ Style/FormatStringToken: Style/FrozenStringLiteralComment: Enabled: false +# This one has to be off until our base ruby is at least 2.5. +Style/HashTransformKeys: + Enabled: false + # Offense count: 17 # Cop supports --auto-correct. Style/IfUnlessModifier: diff --git a/rubyzip.gemspec b/rubyzip.gemspec index 2e7cbf78..24e9ecf3 100644 --- a/rubyzip.gemspec +++ b/rubyzip.gemspec @@ -25,5 +25,5 @@ Gem::Specification.new do |s| s.add_development_dependency 'minitest', '~> 5.4' s.add_development_dependency 'pry', '~> 0.10' s.add_development_dependency 'rake', '~> 12.3', '>= 12.3.3' - s.add_development_dependency 'rubocop', '~> 0.79' + s.add_development_dependency 'rubocop', '~> 0.80.1' end From 0c34cc18144784f16e79764ce9a649759e200f5f Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Thu, 20 Feb 2020 15:42:39 +0000 Subject: [PATCH 003/311] Explicitly set Rubocop ruby version to 2.4. --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 969d4c0e..a140064c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,6 +18,7 @@ matrix: jdk: openjdk11 - rvm: rbx-4 - name: Rubocop + rvm: 2.4 script: - bundle info rubocop - bundle exec rubocop From 2f311541dafea15573fa20487ec6db7c4f0fdb42 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sat, 14 Mar 2020 17:54:08 +0000 Subject: [PATCH 004/311] Update the auto-config for Rubocop 0.80. --- .rubocop_todo.yml | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 1debf8fe..831139d6 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -1,35 +1,35 @@ # This configuration was generated by # `rubocop --auto-gen-config` -# on 2020-02-08 14:58:51 +0000 using RuboCop version 0.79.0. +# on 2020-03-14 17:50:29 +0000 using RuboCop version 0.80.0. # The point is for the user to remove these configuration records # one by one as the offenses are removed from the code base. # Note that changes in the inspected code, or installation of new # versions of RuboCop, may require this file to be generated again. -# Offense count: 15 +# Offense count: 5 # Configuration parameters: CountComments. Metrics/ClassLength: - Max: 580 + Max: 570 # Offense count: 26 Metrics/CyclomaticComplexity: Max: 14 -# Offense count: 120 +# Offense count: 44 # Configuration parameters: CountComments, ExcludedMethods. Metrics/MethodLength: - Max: 30 + Max: 29 # Offense count: 2 # Configuration parameters: CountKeywordArgs. Metrics/ParameterLists: Max: 10 -# Offense count: 21 +# Offense count: 20 Metrics/PerceivedComplexity: Max: 15 -# Offense count: 9 +# Offense count: 8 Naming/AccessorMethodName: Exclude: - 'lib/zip/entry.rb' @@ -60,7 +60,7 @@ Style/ClassAndModuleChildren: - 'lib/zip/extra_field/zip64.rb' - 'lib/zip/extra_field/zip64_placeholder.rb' -# Offense count: 26 +# Offense count: 25 Style/Documentation: Enabled: false @@ -70,10 +70,10 @@ Style/Documentation: Style/FormatStringToken: EnforcedStyle: unannotated -# Offense count: 95 +# Offense count: 96 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle. -# SupportedStyles: always, never +# SupportedStyles: always, always_true, never Style/FrozenStringLiteralComment: Enabled: false @@ -81,15 +81,13 @@ Style/FrozenStringLiteralComment: Style/HashTransformKeys: Enabled: false -# Offense count: 17 +# Offense count: 13 # Cop supports --auto-correct. Style/IfUnlessModifier: Exclude: - 'lib/zip/entry.rb' - - 'lib/zip/extra_field/generic.rb' - 'lib/zip/file.rb' - 'lib/zip/filesystem.rb' - - 'lib/zip/input_stream.rb' - 'lib/zip/pass_thru_decompressor.rb' - 'lib/zip/streamable_stream.rb' From 382cc84915e9a4a5fff7e85b3999910d81a0c359 Mon Sep 17 00:00:00 2001 From: John Lees-Miller Date: Sat, 4 Apr 2020 10:11:06 +0100 Subject: [PATCH 005/311] Update changelog for #444 --- Changelog.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Changelog.md b/Changelog.md index 5131d210..10fc371d 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,5 +1,9 @@ # X.X.X (Next) +Tooling: + +- Update rubocop again and run it in CI. [#444](https://github.com/rubyzip/rubyzip/pull/444) + # 2.3.0 (2020-03-14) - Fix frozen string literal error [#431](https://github.com/rubyzip/rubyzip/pull/431) From 262ba001a4a0cd2b32956b1a4a597ce7c32c85e7 Mon Sep 17 00:00:00 2001 From: Lucas Kanashiro Date: Wed, 25 Mar 2020 14:45:09 -0300 Subject: [PATCH 006/311] test/file_extract_test.rb: fix test_extract_incorrect_size in s390x arch Using the current pack directives makes test_extract_incorrect_size fail in s390x architecture because of the endian probably. This is the output of the command executed by the test in amd64: irb(main):001:0> [501, 500000, 1].pack('LLS') => "\xF5\x01\x00\x00 \xA1\a\x00\x01\x00" And the output of the same command in s390x: irb(main):001:0> [501, 500000, 1].pack('LLS') => "\x00\x00\x01\xF5\x00\a\xA1 \x00\x01" Changing to 'VVv' pack directives like is used in lib/zib/entry.rb fixes the test in s390x and does not add a regression in amd64. --- test/file_extract_test.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/file_extract_test.rb b/test/file_extract_test.rb index 0e697187..d8306b35 100644 --- a/test/file_extract_test.rb +++ b/test/file_extract_test.rb @@ -110,8 +110,8 @@ def test_extract_incorrect_size assert_equal true_size, a_entry.size end - true_size_bytes = [compressed_size, true_size, file_name.size].pack('LLS') - fake_size_bytes = [compressed_size, fake_size, file_name.size].pack('LLS') + true_size_bytes = [compressed_size, true_size, file_name.size].pack('VVv') + fake_size_bytes = [compressed_size, fake_size, file_name.size].pack('VVv') data = File.binread(real_zip) assert data.include?(true_size_bytes) From b653d57635cc5da067b24737ce9f6ad84ce47799 Mon Sep 17 00:00:00 2001 From: John Lees-Miller Date: Sat, 11 Apr 2020 10:37:05 +0100 Subject: [PATCH 007/311] Update changelog for #445 --- Changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/Changelog.md b/Changelog.md index 10fc371d..cb4a893e 100644 --- a/Changelog.md +++ b/Changelog.md @@ -3,6 +3,7 @@ Tooling: - Update rubocop again and run it in CI. [#444](https://github.com/rubyzip/rubyzip/pull/444) +- Fix a test that was incorrect on big-endian architectures. [#445](https://github.com/rubyzip/rubyzip/pull/445) # 2.3.0 (2020-03-14) From 5af76cecb51e618ded61bba7a9e4078b4df7b921 Mon Sep 17 00:00:00 2001 From: Joni Lahtinen Date: Wed, 29 Apr 2020 16:38:33 +0300 Subject: [PATCH 008/311] Use Zlib::SYNC_FLUSH so buffer does not grow until finished With JRuby implementation deflate would always return empty string without Zlib::SYNC_FLUSH. That can cause memory problems when large files are in deflate buffer as whole and red there with finish call at once. --- lib/zip/deflater.rb | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/zip/deflater.rb b/lib/zip/deflater.rb index 8509cf47..b197f5ca 100644 --- a/lib/zip/deflater.rb +++ b/lib/zip/deflater.rb @@ -13,7 +13,7 @@ def <<(data) val = data.to_s @crc = Zlib.crc32(val, @crc) @size += val.bytesize - buffer = @zlib_deflater.deflate(data) + buffer = @zlib_deflater.deflate(data, Zlib::SYNC_FLUSH) if buffer.empty? @output_stream else @@ -22,7 +22,9 @@ def <<(data) end def finish - @output_stream << @encrypter.encrypt(@zlib_deflater.finish) until @zlib_deflater.finished? + buffer = @zlib_deflater.finish + @output_stream << @encrypter.encrypt(buffer) unless buffer.empty? + @zlib_deflater.close end attr_reader :size, :crc From 99d8f59eaf8baebf971fb603b437d24a26d93c3a Mon Sep 17 00:00:00 2001 From: Joni Lahtinen Date: Wed, 29 Apr 2020 16:41:21 +0300 Subject: [PATCH 009/311] Change encryption_test less implementation specific Seems like zlib deflate compress differently when Zlib::SYNC_FLUSH is used. --- test/encryption_test.rb | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/test/encryption_test.rb b/test/encryption_test.rb index d3ed5ffb..d06c4712 100644 --- a/test/encryption_test.rb +++ b/test/encryption_test.rb @@ -14,20 +14,28 @@ def teardown end def test_encrypt - test_file = ::File.open(ENCRYPT_ZIP_TEST_FILE, 'rb').read - - @rand = [250, 143, 107, 13, 143, 22, 155, 75, 228, 150, 12] - @output = ::Zip::DOSTime.stub(:now, ::Zip::DOSTime.new(2014, 12, 17, 15, 56, 24)) do - Random.stub(:rand, ->(_range) { @rand.shift }) do - Zip::OutputStream.write_buffer(::StringIO.new(''), Zip::TraditionalEncrypter.new('password')) do |zos| - zos.put_next_entry('file1.txt') - zos.write ::File.open(INPUT_FILE1).read - end.string - end + content = File.open(INPUT_FILE1, 'r').read + test_filename = 'top_secret_file.txt' + + password = 'swordfish' + + encrypted_zip = Zip::OutputStream.write_buffer(::StringIO.new(''), Zip::TraditionalEncrypter.new(password)) do |out| + out.put_next_entry(test_filename) + out.write content + end + + Zip::InputStream.open(encrypted_zip, 0, Zip::TraditionalDecrypter.new(password)) do |zis| + entry = zis.get_next_entry + assert_equal test_filename, entry.name + assert_equal 1327, entry.size + assert_equal content, zis.read end - @output.unpack('C*').each_with_index do |c, i| - assert_equal test_file[i].ord, c + assert_raises(Zip::DecompressionError) do + Zip::InputStream.open(encrypted_zip, 0, Zip::TraditionalDecrypter.new(password + 'wrong')) do |zis| + zis.get_next_entry + assert_equal content, zis.read + end end end From 0fee529de577ab81c0cce9dd6f12e97db07ccc28 Mon Sep 17 00:00:00 2001 From: Joni Lahtinen Date: Wed, 29 Apr 2020 17:06:29 +0300 Subject: [PATCH 010/311] Use guard instead of if else construct --- lib/zip/deflater.rb | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/lib/zip/deflater.rb b/lib/zip/deflater.rb index b197f5ca..9f5371c8 100644 --- a/lib/zip/deflater.rb +++ b/lib/zip/deflater.rb @@ -14,11 +14,9 @@ def <<(data) @crc = Zlib.crc32(val, @crc) @size += val.bytesize buffer = @zlib_deflater.deflate(data, Zlib::SYNC_FLUSH) - if buffer.empty? - @output_stream - else - @output_stream << @encrypter.encrypt(buffer) - end + return @output_stream if buffer.empty? + + @output_stream << @encrypter.encrypt(buffer) end def finish From cf73152560ec10c5c2cfd43afe82e996ef13006a Mon Sep 17 00:00:00 2001 From: Igor Victor Date: Thu, 13 Aug 2020 17:37:09 +0100 Subject: [PATCH 011/311] enable truffle ruby in Travis CI @eregon enabling truffle ruby support for rubyzip --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index a140064c..953a930b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,6 +7,7 @@ rvm: - 2.6 - 2.7 - ruby-head + - truffleruby-head matrix: fast_finish: true include: From e4f6d5ec56c45e17521516c517cfe60a4fdb32ef Mon Sep 17 00:00:00 2001 From: Igor Victor Date: Mon, 24 Aug 2020 13:19:53 +0200 Subject: [PATCH 012/311] Allow failures for truffleruby-head and truffleruby --- .travis.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.travis.yml b/.travis.yml index 953a930b..c73a251b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,6 +8,7 @@ rvm: - 2.7 - ruby-head - truffleruby-head + - truffleruby matrix: fast_finish: true include: @@ -27,6 +28,8 @@ matrix: - rvm: ruby-head - rvm: rbx-4 - rvm: jruby-head + - rvm: truffleruby-head + - rvm: truffleruby before_install: - gem --version before_script: From 0790695e8c3f10120143e8e1d4305f5a37032fcd Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sun, 3 May 2020 11:34:47 +0100 Subject: [PATCH 013/311] Clean up OutputStream#init_next_entry. There's no need for this private method to specify a default. --- lib/zip/output_stream.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/zip/output_stream.rb b/lib/zip/output_stream.rb index 266083cd..f46f3271 100644 --- a/lib/zip/output_stream.rb +++ b/lib/zip/output_stream.rb @@ -142,7 +142,7 @@ def finalize_current_entry @compressor = ::Zip::NullCompressor.instance end - def init_next_entry(entry, level = Zip.default_compression) + def init_next_entry(entry, level) finalize_current_entry @entry_set << entry entry.write_local_entry(@output_stream) From ef520b4b946f232b09fa8d1ae0db02899016c714 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sun, 3 May 2020 14:54:19 +0100 Subject: [PATCH 014/311] Add `compression_level` to the Entry API. Allow an Entry to specify a compression level and pass this down to the underlying OutputStream infrastructure. OutputStream has been able to specify a compression level for a while but this has, up until now, only ever been set to the default. This fundamentally changes the API so will need a major version bump. --- .rubocop_todo.yml | 2 +- lib/zip/entry.rb | 12 +++++++++--- lib/zip/file.rb | 2 +- lib/zip/output_stream.rb | 1 + test/entry_test.rb | 17 +++++++++-------- test/file_test.rb | 4 ++-- test/local_entry_test.rb | 3 ++- test/output_stream_test.rb | 2 +- test/test_helper.rb | 1 + 9 files changed, 27 insertions(+), 17 deletions(-) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 831139d6..ae0c7049 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -9,7 +9,7 @@ # Offense count: 5 # Configuration parameters: CountComments. Metrics/ClassLength: - Max: 570 + Max: 580 # Offense count: 26 Metrics/CyclomaticComplexity: diff --git a/lib/zip/entry.rb b/lib/zip/entry.rb index a67c6568..e2160432 100644 --- a/lib/zip/entry.rb +++ b/lib/zip/entry.rb @@ -14,6 +14,7 @@ class Entry :unix_uid, :unix_gid, :unix_perms, :dirty attr_reader :ftype, :filepath # :nodoc: + attr_writer :compression_level # :nodoc: def set_default_vars_values @local_header_offset = 0 @@ -66,8 +67,9 @@ def initialize(*args) @compressed_size = args[4] || 0 @crc = args[5] || 0 @compression_method = args[6] || ::Zip::Entry::DEFLATED - @size = args[7] || 0 - @time = args[8] || ::Zip::DOSTime.now + @compression_level = args[7] || ::Zip.default_compression + @size = args[8] || 0 + @time = args[9] || ::Zip::DOSTime.now @ftype = name_is_directory? ? :directory : :file @extra = ::Zip::ExtraField.new(@extra.to_s) unless @extra.kind_of?(::Zip::ExtraField) @@ -579,7 +581,11 @@ def write_to_zip_output_stream(zip_output_stream) #:nodoc:all if @ftype == :directory zip_output_stream.put_next_entry(self, nil, nil, ::Zip::Entry::STORED) elsif @filepath - zip_output_stream.put_next_entry(self, nil, nil, compression_method || ::Zip::Entry::DEFLATED) + zip_output_stream.put_next_entry( + self, nil, nil, + compression_method || ::Zip::Entry::DEFLATED, + @compression_level || ::Zip.default_compression + ) get_input_stream { |is| ::Zip::IOExtras.copy_stream(zip_output_stream, is) } else zip_output_stream.copy_raw_entry(self) diff --git a/lib/zip/file.rb b/lib/zip/file.rb index 999d9728..f5bae24a 100644 --- a/lib/zip/file.rb +++ b/lib/zip/file.rb @@ -273,7 +273,7 @@ def get_output_stream(entry, permission_int = nil, comment = nil, if entry.kind_of?(Entry) entry else - Entry.new(@name, entry.to_s, comment, extra, compressed_size, crc, compression_method, size, time) + Entry.new(@name, entry.to_s, comment, extra, compressed_size, crc, compression_method, ::Zip.default_compression, size, time) end if new_entry.directory? raise ArgumentError, diff --git a/lib/zip/output_stream.rb b/lib/zip/output_stream.rb index f46f3271..4168e60b 100644 --- a/lib/zip/output_stream.rb +++ b/lib/zip/output_stream.rb @@ -102,6 +102,7 @@ def put_next_entry(entry_name, comment = nil, extra = nil, compression_method = new_entry.extra = extra.kind_of?(ExtraField) ? extra : ExtraField.new(extra.to_s) end new_entry.compression_method = compression_method unless compression_method.nil? + new_entry.compression_level = level unless level.nil? init_next_entry(new_entry, level) @current_entry = new_entry end diff --git a/test/entry_test.rb b/test/entry_test.rb index 8daf7adc..541ba41f 100644 --- a/test/entry_test.rb +++ b/test/entry_test.rb @@ -11,6 +11,7 @@ def test_constructor_and_getters TEST_COMPRESSED_SIZE, TEST_CRC, TEST_COMPRESSIONMETHOD, + TEST_COMPRESSIONLEVEL, TEST_SIZE, TEST_TIME) @@ -41,28 +42,28 @@ def test_is_directory_and_is_file def test_equality entry1 = ::Zip::Entry.new('file.zip', 'name', 'isNotCompared', 'something extra', 123, 1234, - ::Zip::Entry::DEFLATED, 10_000) + ::Zip::Entry::DEFLATED, ::Zip.default_compression, 10_000) entry2 = ::Zip::Entry.new('file.zip', 'name', 'isNotComparedXXX', 'something extra', 123, 1234, - ::Zip::Entry::DEFLATED, 10_000) + ::Zip::Entry::DEFLATED, ::Zip.default_compression, 10_000) entry3 = ::Zip::Entry.new('file.zip', 'name2', 'isNotComparedXXX', 'something extra', 123, 1234, - ::Zip::Entry::DEFLATED, 10_000) + ::Zip::Entry::DEFLATED, ::Zip.default_compression, 10_000) entry4 = ::Zip::Entry.new('file.zip', 'name2', 'isNotComparedXXX', 'something extraXX', 123, 1234, - ::Zip::Entry::DEFLATED, 10_000) + ::Zip::Entry::DEFLATED, ::Zip.default_compression, 10_000) entry5 = ::Zip::Entry.new('file.zip', 'name2', 'isNotComparedXXX', 'something extraXX', 12, 1234, - ::Zip::Entry::DEFLATED, 10_000) + ::Zip::Entry::DEFLATED, ::Zip.default_compression, 10_000) entry6 = ::Zip::Entry.new('file.zip', 'name2', 'isNotComparedXXX', 'something extraXX', 12, 123, - ::Zip::Entry::DEFLATED, 10_000) + ::Zip::Entry::DEFLATED, ::Zip.default_compression, 10_000) entry7 = ::Zip::Entry.new('file.zip', 'name2', 'isNotComparedXXX', 'something extraXX', 12, 123, - ::Zip::Entry::STORED, 10_000) + ::Zip::Entry::STORED, ::Zip.default_compression, 10_000) entry8 = ::Zip::Entry.new('file.zip', 'name2', 'isNotComparedXXX', 'something extraXX', 12, 123, - ::Zip::Entry::STORED, 100_000) + ::Zip::Entry::STORED, ::Zip.default_compression, 100_000) assert_equal(entry1, entry1) assert_equal(entry1, entry2) diff --git a/test/file_test.rb b/test/file_test.rb index c11af675..8a5dd432 100644 --- a/test/file_test.rb +++ b/test/file_test.rb @@ -87,8 +87,8 @@ def test_get_output_stream assert_equal(custom_entry_args[2], entry.compressed_size) assert_equal(custom_entry_args[3], entry.crc) assert_equal(custom_entry_args[4], entry.compression_method) - assert_equal(custom_entry_args[5], entry.size) - assert_equal(custom_entry_args[6], entry.time) + assert_equal(custom_entry_args[6], entry.size) + assert_equal(custom_entry_args[7], entry.time) zf.get_output_stream('entry.bin') do |os| os.write(::File.open('test/data/generated/5entry.zip', 'rb').read) diff --git a/test/local_entry_test.rb b/test/local_entry_test.rb index 58bcda74..28148039 100644 --- a/test/local_entry_test.rb +++ b/test/local_entry_test.rb @@ -94,7 +94,8 @@ def test_write_64entry ::Zip.write_zip64_support = true entry = ::Zip::Entry.new('bigfile.zip', 'entry_name', 'my little equine', 'malformed extra field because why not', - 0x7766554433221100, 0xDEADBEEF, ::Zip::Entry::DEFLATED, + 0x7766554433221100, 0xDEADBEEF, + ::Zip::Entry::DEFLATED, ::Zip.default_compression, 0x9988776655443322) write_to_file(LEH_FILE, CEH_FILE, entry) local_entry, central_entry = read_from_file(LEH_FILE, CEH_FILE) diff --git a/test/output_stream_test.rb b/test/output_stream_test.rb index b2f64ab9..79fb8f71 100644 --- a/test/output_stream_test.rb +++ b/test/output_stream_test.rb @@ -92,7 +92,7 @@ def test_put_next_entry def test_put_next_entry_using_zip_entry_creates_entries_with_correct_timestamps file = ::File.open('test/data/file2.txt', 'rb') ::Zip::OutputStream.open(TEST_ZIP.zip_name) do |zos| - zip_entry = ::Zip::Entry.new(zos, file.path, '', '', 0, 0, ::Zip::Entry::DEFLATED, 0, ::Zip::DOSTime.at(file.mtime)) + zip_entry = ::Zip::Entry.new(zos, file.path, '', '', 0, 0, ::Zip::Entry::DEFLATED, ::Zip.default_compression, 0, ::Zip::DOSTime.at(file.mtime)) zos.put_next_entry(zip_entry) zos << file.read end diff --git a/test/test_helper.rb b/test/test_helper.rb index 598736e6..973b4abd 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -218,6 +218,7 @@ module ZipEntryData TEST_CRC = 325_324 TEST_EXTRA = 'Some data here' TEST_COMPRESSIONMETHOD = ::Zip::Entry::DEFLATED + TEST_COMPRESSIONLEVEL = ::Zip.default_compression TEST_NAME = 'entry name' TEST_SIZE = 8432 TEST_ISDIRECTORY = false From 14451e63e70058242d8cd5680eb15c8777104d2c Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sun, 24 May 2020 11:52:41 +0100 Subject: [PATCH 015/311] Add setting a compression level to the File options. It looks like it needs to be surfaced in `add` and `get_output_stream`. The compression level defaults to whatever the global default is unless it is overridden on opening the Zip::File. Also needed to reorder some of the requires in the top-level module file now that we are using defaults in the File class. --- .rubocop_todo.yml | 4 +-- lib/zip/file.rb | 17 +++++++--- test/file_test.rb | 81 +++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 92 insertions(+), 10 deletions(-) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index ae0c7049..04879430 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -18,12 +18,12 @@ Metrics/CyclomaticComplexity: # Offense count: 44 # Configuration parameters: CountComments, ExcludedMethods. Metrics/MethodLength: - Max: 29 + Max: 32 # Offense count: 2 # Configuration parameters: CountKeywordArgs. Metrics/ParameterLists: - Max: 10 + Max: 12 # Offense count: 20 Metrics/PerceivedComplexity: diff --git a/lib/zip/file.rb b/lib/zip/file.rb index f5bae24a..600e6160 100644 --- a/lib/zip/file.rb +++ b/lib/zip/file.rb @@ -75,7 +75,9 @@ class File < CentralDirectory # a new archive if it doesn't exist already. def initialize(path_or_io, create = false, buffer = false, options = {}) super() - options = DEFAULT_OPTIONS.merge(options) + options = DEFAULT_OPTIONS + .merge(compression_level: ::Zip.default_compression) + .merge(options) @name = path_or_io.respond_to?(:path) ? path_or_io.path : path_or_io @comment = '' @create = create ? true : false # allow any truthy value to mean true @@ -111,6 +113,7 @@ def initialize(path_or_io, create = false, buffer = false, options = {}) @restore_ownership = options[:restore_ownership] @restore_permissions = options[:restore_permissions] @restore_times = options[:restore_times] + @compression_level = options[:compression_level] end class << self @@ -266,14 +269,14 @@ def get_input_stream(entry, &a_proc) # File.open method. def get_output_stream(entry, permission_int = nil, comment = nil, extra = nil, compressed_size = nil, crc = nil, - compression_method = nil, size = nil, time = nil, - &a_proc) + compression_method = nil, compression_level = nil, + size = nil, time = nil, &a_proc) new_entry = if entry.kind_of?(Entry) entry else - Entry.new(@name, entry.to_s, comment, extra, compressed_size, crc, compression_method, ::Zip.default_compression, size, time) + Entry.new(@name, entry.to_s, comment, extra, compressed_size, crc, compression_method, compression_level, size, time) end if new_entry.directory? raise ArgumentError, @@ -299,7 +302,11 @@ def read(entry) def add(entry, src_path, &continue_on_exists_proc) continue_on_exists_proc ||= proc { ::Zip.continue_on_exists_proc } check_entry_exists(entry, continue_on_exists_proc, 'add') - new_entry = entry.kind_of?(::Zip::Entry) ? entry : ::Zip::Entry.new(@name, entry.to_s) + new_entry = if entry.kind_of?(::Zip::Entry) + entry + else + ::Zip::Entry.new(@name, entry.to_s, nil, nil, 0, 0, ::Zip::Entry::DEFLATED, @compression_level) + end new_entry.gather_fileinfo_from_srcpath(src_path) new_entry.dirty = true @entry_set << new_entry diff --git a/test/file_test.rb b/test/file_test.rb index 8a5dd432..1d05007b 100644 --- a/test/file_test.rb +++ b/test/file_test.rb @@ -8,7 +8,7 @@ class ZipFileTest < MiniTest::Test OK_DELETE_MOVED_FILE = 'test/data/generated/okToDeleteMoved.txt' def teardown - ::Zip.write_zip64_support = false + ::Zip.reset! end def test_create_from_scratch_to_buffer @@ -77,7 +77,7 @@ def test_get_output_stream assert_equal(count + 1, zf.size) assert_equal('Putting stuff in data/generated/empty.txt', zf.read('test/data/generated/empty.txt')) - custom_entry_args = [TEST_COMMENT, TEST_EXTRA, TEST_COMPRESSED_SIZE, TEST_CRC, ::Zip::Entry::STORED, TEST_SIZE, TEST_TIME] + custom_entry_args = [TEST_COMMENT, TEST_EXTRA, TEST_COMPRESSED_SIZE, TEST_CRC, ::Zip::Entry::STORED, ::Zlib::BEST_SPEED, TEST_SIZE, TEST_TIME] zf.get_output_stream('entry_with_custom_args.txt', nil, *custom_entry_args) do |os| os.write 'Some data' end @@ -188,7 +188,7 @@ def test_cleans_up_tempfiles_after_close assert_equal(false, File.exist?(@tempfile_path)) end - def test_add + def test_add_default_compression src_file = 'test/data/file2.txt' entry_name = 'newEntryName.rb' assert(::File.exist?(src_file)) @@ -197,9 +197,84 @@ def test_add zf.close zf_read = ::Zip::File.new(EMPTY_FILENAME) + entry = zf_read.entries.first assert_equal('', zf_read.comment) assert_equal(1, zf_read.entries.length) assert_equal(entry_name, zf_read.entries.first.name) + assert_equal(File.size(src_file), entry.size) + assert_equal(8_764, entry.compressed_size) + AssertEntry.assert_contents(src_file, + zf_read.get_input_stream(entry_name, &:read)) + end + + def test_add_best_compression + src_file = 'test/data/file2.txt' + entry_name = 'newEntryName.rb' + assert(::File.exist?(src_file)) + zf = ::Zip::File.new(EMPTY_FILENAME, ::Zip::File::CREATE, false, { compression_level: Zlib::BEST_COMPRESSION }) + zf.add(entry_name, src_file) + zf.close + + zf_read = ::Zip::File.new(EMPTY_FILENAME) + entry = zf_read.entries.first + assert_equal(1, zf_read.entries.length) + assert_equal(File.size(src_file), entry.size) + assert_equal(8_658, entry.compressed_size) + AssertEntry.assert_contents(src_file, + zf_read.get_input_stream(entry_name, &:read)) + end + + def test_add_best_compression_as_default + ::Zip.default_compression = Zlib::BEST_COMPRESSION + + src_file = 'test/data/file2.txt' + entry_name = 'newEntryName.rb' + assert(::File.exist?(src_file)) + zf = ::Zip::File.new(EMPTY_FILENAME, ::Zip::File::CREATE) + zf.add(entry_name, src_file) + zf.close + + zf_read = ::Zip::File.new(EMPTY_FILENAME) + entry = zf_read.entries.first + assert_equal(1, zf_read.entries.length) + assert_equal(File.size(src_file), entry.size) + assert_equal(8_658, entry.compressed_size) + AssertEntry.assert_contents(src_file, + zf_read.get_input_stream(entry_name, &:read)) + end + + def test_add_best_speed + src_file = 'test/data/file2.txt' + entry_name = 'newEntryName.rb' + assert(::File.exist?(src_file)) + zf = ::Zip::File.new(EMPTY_FILENAME, ::Zip::File::CREATE, false, { compression_level: Zlib::BEST_SPEED }) + zf.add(entry_name, src_file) + zf.close + + zf_read = ::Zip::File.new(EMPTY_FILENAME) + entry = zf_read.entries.first + assert_equal(1, zf_read.entries.length) + assert_equal(File.size(src_file), entry.size) + assert_equal(10_938, entry.compressed_size) + AssertEntry.assert_contents(src_file, + zf_read.get_input_stream(entry_name, &:read)) + end + + def test_add_best_speed_as_default + ::Zip.default_compression = Zlib::BEST_SPEED + + src_file = 'test/data/file2.txt' + entry_name = 'newEntryName.rb' + assert(::File.exist?(src_file)) + zf = ::Zip::File.new(EMPTY_FILENAME, ::Zip::File::CREATE) + zf.add(entry_name, src_file) + zf.close + + zf_read = ::Zip::File.new(EMPTY_FILENAME) + entry = zf_read.entries.first + assert_equal(1, zf_read.entries.length) + assert_equal(File.size(src_file), entry.size) + assert_equal(10_938, entry.compressed_size) AssertEntry.assert_contents(src_file, zf_read.get_input_stream(entry_name, &:read)) end From d4bc24dcb38a7b0832fe39d700ea8f88c336da22 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sun, 24 May 2020 18:50:18 +0100 Subject: [PATCH 016/311] Clean up `OutputStream` internals. There was some fairly odd stuff going on in `put_next_entry` that allowed for data within an `Entry` to be overridden and prevented an `Entry` from being a single point of truth. Fixing this also simplifies the code within `File` and still passes all tests. Also, fixing the above means we can stop passing the compression level around as a parameter and use the value stored in each `Entry` directly. Let's keep `compression_level` out of the `Entry` public API though as it only makes sense when writing an `Entry`: there doesn't seem to be an obvious way to read what level of compression was used when reading an `Entry` from a zip file. --- lib/zip/entry.rb | 16 +++++++--------- lib/zip/output_stream.rb | 19 +++++++------------ test/file_test.rb | 1 + 3 files changed, 15 insertions(+), 21 deletions(-) diff --git a/lib/zip/entry.rb b/lib/zip/entry.rb index e2160432..dc29a579 100644 --- a/lib/zip/entry.rb +++ b/lib/zip/entry.rb @@ -13,8 +13,7 @@ class Entry :restore_times, :restore_permissions, :restore_ownership, :unix_uid, :unix_gid, :unix_perms, :dirty - attr_reader :ftype, :filepath # :nodoc: - attr_writer :compression_level # :nodoc: + attr_reader :compression_level, :ftype, :filepath # :nodoc: def set_default_vars_values @local_header_offset = 0 @@ -579,14 +578,13 @@ def gather_fileinfo_from_srcpath(src_path) # :nodoc: def write_to_zip_output_stream(zip_output_stream) #:nodoc:all if @ftype == :directory - zip_output_stream.put_next_entry(self, nil, nil, ::Zip::Entry::STORED) + @compression_method = ::Zip::Entry::STORED + zip_output_stream.put_next_entry(self) elsif @filepath - zip_output_stream.put_next_entry( - self, nil, nil, - compression_method || ::Zip::Entry::DEFLATED, - @compression_level || ::Zip.default_compression - ) - get_input_stream { |is| ::Zip::IOExtras.copy_stream(zip_output_stream, is) } + zip_output_stream.put_next_entry(self) + get_input_stream do |is| + ::Zip::IOExtras.copy_stream(zip_output_stream, is) + end else zip_output_stream.copy_raw_entry(self) end diff --git a/lib/zip/output_stream.rb b/lib/zip/output_stream.rb index 4168e60b..d4ca32c2 100644 --- a/lib/zip/output_stream.rb +++ b/lib/zip/output_stream.rb @@ -95,15 +95,10 @@ def put_next_entry(entry_name, comment = nil, extra = nil, compression_method = new_entry = if entry_name.kind_of?(Entry) entry_name else - Entry.new(@file_name, entry_name.to_s) + Entry.new(@file_name, entry_name.to_s, comment, extra, 0, 0, compression_method, level) end - new_entry.comment = comment unless comment.nil? - unless extra.nil? - new_entry.extra = extra.kind_of?(ExtraField) ? extra : ExtraField.new(extra.to_s) - end - new_entry.compression_method = compression_method unless compression_method.nil? - new_entry.compression_level = level unless level.nil? - init_next_entry(new_entry, level) + + init_next_entry(new_entry) @current_entry = new_entry end @@ -143,19 +138,19 @@ def finalize_current_entry @compressor = ::Zip::NullCompressor.instance end - def init_next_entry(entry, level) + def init_next_entry(entry) finalize_current_entry @entry_set << entry entry.write_local_entry(@output_stream) @encrypter.reset! @output_stream << @encrypter.header(entry.mtime) - @compressor = get_compressor(entry, level) + @compressor = get_compressor(entry) end - def get_compressor(entry, level) + def get_compressor(entry) case entry.compression_method when Entry::DEFLATED - ::Zip::Deflater.new(@output_stream, level, @encrypter) + ::Zip::Deflater.new(@output_stream, entry.compression_level, @encrypter) when Entry::STORED ::Zip::PassThruCompressor.new(@output_stream) else diff --git a/test/file_test.rb b/test/file_test.rb index 1d05007b..0d72a1d3 100644 --- a/test/file_test.rb +++ b/test/file_test.rb @@ -87,6 +87,7 @@ def test_get_output_stream assert_equal(custom_entry_args[2], entry.compressed_size) assert_equal(custom_entry_args[3], entry.crc) assert_equal(custom_entry_args[4], entry.compression_method) + assert_equal(custom_entry_args[5], entry.compression_level) assert_equal(custom_entry_args[6], entry.size) assert_equal(custom_entry_args[7], entry.time) From 072fa27e786cb01e570f6e5b4e2ce4cfd1bcb945 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sun, 7 Jun 2020 17:38:46 +0100 Subject: [PATCH 017/311] Refactor `Entry#compression_method` access. As per the conversation here [1], make `compression_method` a method and enforce the correct type of compression for directories by default. [1] https://github.com/rubyzip/rubyzip/pull/448#discussion_r436268506 --- .rubocop_todo.yml | 2 +- lib/zip/entry.rb | 25 ++++++++++++++++--------- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 04879430..04be2680 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -13,7 +13,7 @@ Metrics/ClassLength: # Offense count: 26 Metrics/CyclomaticComplexity: - Max: 14 + Max: 15 # Offense count: 44 # Configuration parameters: CountComments, ExcludedMethods. diff --git a/lib/zip/entry.rb b/lib/zip/entry.rb index dc29a579..810567be 100644 --- a/lib/zip/entry.rb +++ b/lib/zip/entry.rb @@ -6,7 +6,7 @@ class Entry # Language encoding flag (EFS) bit EFS = 0b100000000000 - attr_accessor :comment, :compressed_size, :crc, :extra, :compression_method, + attr_accessor :comment, :compressed_size, :crc, :extra, :name, :size, :local_header_offset, :zipfile, :fstype, :external_file_attributes, :internal_file_attributes, :gp_flags, :header_signature, :follow_symlinks, @@ -53,24 +53,24 @@ def check_name(name) end def initialize(*args) - name = args[1] || '' - check_name(name) + @name = args[1] || '' + check_name(@name) set_default_vars_values @fstype = ::Zip::RUNNING_ON_WINDOWS ? ::Zip::FSTYPE_FAT : ::Zip::FSTYPE_UNIX + @ftype = name_is_directory? ? :directory : :file @zipfile = args[0] || '' - @name = name @comment = args[2] || '' @extra = args[3] || '' @compressed_size = args[4] || 0 @crc = args[5] || 0 - @compression_method = args[6] || ::Zip::Entry::DEFLATED + @compression_method = + (@ftype == :directory ? STORED : args[6] || DEFLATED) @compression_level = args[7] || ::Zip.default_compression @size = args[8] || 0 @time = args[9] || ::Zip::DOSTime.now - @ftype = name_is_directory? ? :directory : :file @extra = ::Zip::ExtraField.new(@extra.to_s) unless @extra.kind_of?(::Zip::ExtraField) end @@ -104,6 +104,14 @@ def time=(value) @time = value end + def compression_method + @ftype == :directory ? STORED : @compression_method + end + + def compression_method=(method) + @compression_method = (@ftype == :directory ? STORED : method) + end + def file_type_is?(type) raise InternalError, "current filetype is unknown: #{inspect}" unless @ftype @@ -287,7 +295,7 @@ def pack_local_entry [::Zip::LOCAL_ENTRY_SIGNATURE, @version_needed_to_extract, # version needed to extract @gp_flags, # @gp_flags - @compression_method, + compression_method, @time.to_binary_dos_time, # @last_mod_time @time.to_binary_dos_date, # @last_mod_date @crc, @@ -453,7 +461,7 @@ def pack_c_dir_entry @fstype, # filesystem type @version_needed_to_extract, # @versionNeededToExtract @gp_flags, # @gp_flags - @compression_method, + compression_method, @time.to_binary_dos_time, # @last_mod_time @time.to_binary_dos_date, # @last_mod_date @crc, @@ -578,7 +586,6 @@ def gather_fileinfo_from_srcpath(src_path) # :nodoc: def write_to_zip_output_stream(zip_output_stream) #:nodoc:all if @ftype == :directory - @compression_method = ::Zip::Entry::STORED zip_output_stream.put_next_entry(self) elsif @filepath zip_output_stream.put_next_entry(self) From 2775f529b4c3e3b9d37733cf67798e3431cab78d Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sat, 13 Jun 2020 17:33:39 +0100 Subject: [PATCH 018/311] Set the compression level general purpose flags. --- .rubocop_todo.yml | 2 +- lib/zip/entry.rb | 22 ++++++++++++++++++++++ test/entry_test.rb | 26 ++++++++++++++++++++++++++ 3 files changed, 49 insertions(+), 1 deletion(-) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 04be2680..0a045c53 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -9,7 +9,7 @@ # Offense count: 5 # Configuration parameters: CountComments. Metrics/ClassLength: - Max: 580 + Max: 610 # Offense count: 26 Metrics/CyclomaticComplexity: diff --git a/lib/zip/entry.rb b/lib/zip/entry.rb index 810567be..5aec84bd 100644 --- a/lib/zip/entry.rb +++ b/lib/zip/entry.rb @@ -72,6 +72,7 @@ def initialize(*args) @time = args[9] || ::Zip::DOSTime.now @extra = ::Zip::ExtraField.new(@extra.to_s) unless @extra.kind_of?(::Zip::ExtraField) + set_compression_level_flags end def encrypted? @@ -689,6 +690,27 @@ def data_descriptor_size (@gp_flags & 0x0008) > 0 ? 16 : 0 end + # For DEFLATED compression *only*: set the general purpose flags 1 and 2 to + # indicate compression level. This seems to be mainly cosmetic but they are + # generally set by other tools - including in docx files. It is these flags + # that are used by commandline tools (and elsewhere) to give an indication + # of how compressed a file is. See the PKWARE APPNOTE for more information: + # https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT + # + # It's safe to simply OR these flags here as compression_level is read only. + def set_compression_level_flags + return unless compression_method == DEFLATED + + case @compression_level + when 1 + @gp_flags |= 0b110 + when 2 + @gp_flags |= 0b100 + when 8, 9 + @gp_flags |= 0b010 + end + end + # create a zip64 extra information field if we need one def prep_zip64_extra(for_local_header) #:nodoc:all return unless ::Zip.write_zip64_support diff --git a/test/entry_test.rb b/test/entry_test.rb index 541ba41f..aabb2fdb 100644 --- a/test/entry_test.rb +++ b/test/entry_test.rb @@ -170,4 +170,30 @@ def test_incomplete? entry.gp_flags = 0 assert_equal(false, entry.incomplete?) end + + def test_compression_level_flags + [ + [Zip.default_compression, 0], + [0, 0], + [1, 6], + [2, 4], + [3, 0], + [7, 0], + [8, 2], + [9, 2] + ].each do |level, flags| + # Check flags are set correctly when DEFLATED is specified. + e_def = Zip::Entry.new('', '', '', '', 0, 0, Zip::Entry::DEFLATED, level) + assert_equal(flags, e_def.gp_flags & 0b110) + + # Check that flags are not set when STORED is specified. + e_sto = Zip::Entry.new('', '', '', '', 0, 0, Zip::Entry::STORED, level) + assert_equal(0, e_sto.gp_flags & 0b110) + end + + # Check that a directory entry's flags are not set, even if DEFLATED + # is specified. + e_dir = Zip::Entry.new('', 'd/', '', '', 0, 0, Zip::Entry::DEFLATED, 1) + assert_equal(0, e_dir.gp_flags & 0b110) + end end From e4ceedaa5805e2716ba3cfe304d36a7ed422f227 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sun, 2 Aug 2020 17:51:08 +0100 Subject: [PATCH 019/311] Use keyword arguments for the `Entry` initializer. This greatly simplifies the creation of `Entry` objects when only a couple of fields are not set to their defaults, while at the same time allowing an `Entry` to be fully configured at creation time if appropriate. This fundamentally changes the `Entry` API and means that some convenience methods in `OutputStream` and `File` have needed to be refactored. --- lib/zip/entry.rb | 34 ++++++---- lib/zip/file.rb | 16 ++++- lib/zip/output_stream.rb | 11 ++- test/central_directory_test.rb | 85 ++++++++++++++++------- test/entry_set_test.rb | 28 +++++--- test/entry_test.rb | 119 ++++++++++++++++++++------------- test/file_test.rb | 5 +- test/local_entry_test.rb | 26 ++++--- test/output_stream_test.rb | 4 +- 9 files changed, 215 insertions(+), 113 deletions(-) diff --git a/lib/zip/entry.rb b/lib/zip/entry.rb index 5aec84bd..0e2dfd8c 100644 --- a/lib/zip/entry.rb +++ b/lib/zip/entry.rb @@ -52,26 +52,32 @@ def check_name(name) raise ::Zip::EntryNameError, "Illegal ZipEntry name '#{name}', name must not start with /" end - def initialize(*args) - @name = args[1] || '' + def initialize( + zipfile = '', name = '', + comment: '', size: 0, compressed_size: 0, crc: 0, + compression_method: DEFLATED, + compression_level: ::Zip.default_compression, + time: ::Zip::DOSTime.now, extra: ::Zip::ExtraField.new + ) + @name = name check_name(@name) set_default_vars_values @fstype = ::Zip::RUNNING_ON_WINDOWS ? ::Zip::FSTYPE_FAT : ::Zip::FSTYPE_UNIX @ftype = name_is_directory? ? :directory : :file - @zipfile = args[0] || '' - @comment = args[2] || '' - @extra = args[3] || '' - @compressed_size = args[4] || 0 - @crc = args[5] || 0 - @compression_method = - (@ftype == :directory ? STORED : args[6] || DEFLATED) - @compression_level = args[7] || ::Zip.default_compression - @size = args[8] || 0 - @time = args[9] || ::Zip::DOSTime.now - - @extra = ::Zip::ExtraField.new(@extra.to_s) unless @extra.kind_of?(::Zip::ExtraField) + @zipfile = zipfile + @comment = comment + @compression_method = compression_method + @compression_level = compression_level + + @compressed_size = compressed_size + @crc = crc + @size = size + @time = time + @extra = + extra.kind_of?(ExtraField) ? extra : ExtraField.new(extra.to_s) + set_compression_level_flags end diff --git a/lib/zip/file.rb b/lib/zip/file.rb index 600e6160..c96d6615 100644 --- a/lib/zip/file.rb +++ b/lib/zip/file.rb @@ -276,7 +276,12 @@ def get_output_stream(entry, permission_int = nil, comment = nil, if entry.kind_of?(Entry) entry else - Entry.new(@name, entry.to_s, comment, extra, compressed_size, crc, compression_method, compression_level, size, time) + Entry.new( + @name, entry.to_s, comment: comment, extra: extra, + compressed_size: compressed_size, crc: crc, size: size, + compression_method: compression_method, + compression_level: compression_level, time: time + ) end if new_entry.directory? raise ArgumentError, @@ -305,7 +310,10 @@ def add(entry, src_path, &continue_on_exists_proc) new_entry = if entry.kind_of?(::Zip::Entry) entry else - ::Zip::Entry.new(@name, entry.to_s, nil, nil, 0, 0, ::Zip::Entry::DEFLATED, @compression_level) + ::Zip::Entry.new( + @name, entry.to_s, + compression_level: @compression_level + ) end new_entry.gather_fileinfo_from_srcpath(src_path) new_entry.dirty = true @@ -315,7 +323,9 @@ def add(entry, src_path, &continue_on_exists_proc) # Convenience method for adding the contents of a file to the archive # in Stored format (uncompressed) def add_stored(entry, src_path, &continue_on_exists_proc) - entry = ::Zip::Entry.new(@name, entry.to_s, nil, nil, nil, nil, ::Zip::Entry::STORED) + entry = ::Zip::Entry.new( + @name, entry.to_s, compression_method: ::Zip::Entry::STORED + ) add(entry, src_path, &continue_on_exists_proc) end diff --git a/lib/zip/output_stream.rb b/lib/zip/output_stream.rb index d4ca32c2..65b7d620 100644 --- a/lib/zip/output_stream.rb +++ b/lib/zip/output_stream.rb @@ -89,13 +89,20 @@ def close_buffer # Closes the current entry and opens a new for writing. # +entry+ can be a ZipEntry object or a string. - def put_next_entry(entry_name, comment = nil, extra = nil, compression_method = Entry::DEFLATED, level = Zip.default_compression) + def put_next_entry( + entry_name, comment = '', extra = ExtraField.new, + compression_method = Entry::DEFLATED, level = Zip.default_compression + ) raise Error, 'zip stream is closed' if @closed new_entry = if entry_name.kind_of?(Entry) entry_name else - Entry.new(@file_name, entry_name.to_s, comment, extra, 0, 0, compression_method, level) + Entry.new( + @file_name, entry_name.to_s, comment: comment, + extra: extra, compression_method: compression_method, + compression_level: level + ) end init_next_entry(new_entry) diff --git a/test/central_directory_test.rb b/test/central_directory_test.rb index c4f7afa0..b2fce5f1 100644 --- a/test/central_directory_test.rb +++ b/test/central_directory_test.rb @@ -38,9 +38,14 @@ def test_read_from_truncated_zip_file end def test_write_to_stream - entries = [::Zip::Entry.new('file.zip', 'flimse', 'myComment', 'somethingExtra'), - ::Zip::Entry.new('file.zip', 'secondEntryName'), - ::Zip::Entry.new('file.zip', 'lastEntry.txt', 'Has a comment too')] + entries = [ + ::Zip::Entry.new( + 'file.zip', 'flimse', + comment: 'myComment', extra: 'somethingExtra' + ), + ::Zip::Entry.new('file.zip', 'secondEntryName'), + ::Zip::Entry.new('file.zip', 'lastEntry.txt', comment: 'Has a comment') + ] cdir = ::Zip::CentralDirectory.new(entries, 'my zip comment') File.open('test/data/generated/cdirtest.bin', 'wb') do |f| @@ -57,10 +62,26 @@ def test_write_to_stream def test_write64_to_stream ::Zip.write_zip64_support = true - entries = [::Zip::Entry.new('file.zip', 'file1-little', 'comment1', '', 200, 101, ::Zip::Entry::STORED, 200), - ::Zip::Entry.new('file.zip', 'file2-big', 'comment2', '', 18_000_000_000, 102, ::Zip::Entry::DEFLATED, 20_000_000_000), - ::Zip::Entry.new('file.zip', 'file3-alsobig', 'comment3', '', 15_000_000_000, 103, ::Zip::Entry::DEFLATED, 21_000_000_000), - ::Zip::Entry.new('file.zip', 'file4-little', 'comment4', '', 100, 104, ::Zip::Entry::DEFLATED, 121)] + 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 + ) + ] + [0, 250, 18_000_000_300, 33_000_000_350].each_with_index do |offset, index| entries[index].local_header_offset = offset end @@ -80,25 +101,37 @@ def test_write64_to_stream end def test_equality - cdir1 = ::Zip::CentralDirectory.new([::Zip::Entry.new('file.zip', 'flimse', nil, - 'somethingExtra'), - ::Zip::Entry.new('file.zip', 'secondEntryName'), - ::Zip::Entry.new('file.zip', 'lastEntry.txt')], - 'my zip comment') - cdir2 = ::Zip::CentralDirectory.new([::Zip::Entry.new('file.zip', 'flimse', nil, - 'somethingExtra'), - ::Zip::Entry.new('file.zip', 'secondEntryName'), - ::Zip::Entry.new('file.zip', 'lastEntry.txt')], - 'my zip comment') - cdir3 = ::Zip::CentralDirectory.new([::Zip::Entry.new('file.zip', 'flimse', nil, - 'somethingExtra'), - ::Zip::Entry.new('file.zip', 'secondEntryName'), - ::Zip::Entry.new('file.zip', 'lastEntry.txt')], - 'comment?') - cdir4 = ::Zip::CentralDirectory.new([::Zip::Entry.new('file.zip', 'flimse', nil, - 'somethingExtra'), - ::Zip::Entry.new('file.zip', 'lastEntry.txt')], - 'comment?') + cdir1 = ::Zip::CentralDirectory.new( + [ + ::Zip::Entry.new('file.zip', 'flimse', extra: 'somethingExtra'), + ::Zip::Entry.new('file.zip', 'secondEntryName'), + ::Zip::Entry.new('file.zip', 'lastEntry.txt') + ], + 'my zip comment' + ) + cdir2 = ::Zip::CentralDirectory.new( + [ + ::Zip::Entry.new('file.zip', 'flimse', extra: 'somethingExtra'), + ::Zip::Entry.new('file.zip', 'secondEntryName'), + ::Zip::Entry.new('file.zip', 'lastEntry.txt') + ], + 'my zip comment' + ) + cdir3 = ::Zip::CentralDirectory.new( + [ + ::Zip::Entry.new('file.zip', 'flimse', extra: 'somethingExtra'), + ::Zip::Entry.new('file.zip', 'secondEntryName'), + ::Zip::Entry.new('file.zip', 'lastEntry.txt') + ], + 'comment?' + ) + cdir4 = ::Zip::CentralDirectory.new( + [ + ::Zip::Entry.new('file.zip', 'flimse', extra: 'somethingExtra'), + ::Zip::Entry.new('file.zip', 'lastEntry.txt') + ], + 'comment?' + ) assert_equal(cdir1, cdir1) assert_equal(cdir1, cdir2) diff --git a/test/entry_set_test.rb b/test/entry_set_test.rb index 4f137902..fd038deb 100644 --- a/test/entry_set_test.rb +++ b/test/entry_set_test.rb @@ -2,12 +2,12 @@ class ZipEntrySetTest < MiniTest::Test ZIP_ENTRIES = [ - ::Zip::Entry.new('zipfile.zip', 'name1', 'comment1'), - ::Zip::Entry.new('zipfile.zip', 'name3', 'comment1'), - ::Zip::Entry.new('zipfile.zip', 'name2', 'comment1'), - ::Zip::Entry.new('zipfile.zip', 'name4', 'comment1'), - ::Zip::Entry.new('zipfile.zip', 'name5', 'comment1'), - ::Zip::Entry.new('zipfile.zip', 'name6', 'comment1') + ::Zip::Entry.new('zipfile.zip', 'name1', comment: 'comment1'), + ::Zip::Entry.new('zipfile.zip', 'name3', comment: 'comment1'), + ::Zip::Entry.new('zipfile.zip', 'name2', comment: 'comment1'), + ::Zip::Entry.new('zipfile.zip', 'name4', comment: 'comment1'), + ::Zip::Entry.new('zipfile.zip', 'name5', comment: 'comment1'), + ::Zip::Entry.new('zipfile.zip', 'name6', comment: 'comment1') ] def setup @@ -20,13 +20,17 @@ def teardown def test_include assert(@zip_entry_set.include?(ZIP_ENTRIES.first)) - assert(!@zip_entry_set.include?(::Zip::Entry.new('different.zip', 'different', 'aComment'))) + assert( + !@zip_entry_set.include?( + ::Zip::Entry.new('different.zip', 'different', comment: 'aComment') + ) + ) end def test_size assert_equal(ZIP_ENTRIES.size, @zip_entry_set.size) assert_equal(ZIP_ENTRIES.size, @zip_entry_set.length) - @zip_entry_set << ::Zip::Entry.new('a', 'b', 'c') + @zip_entry_set << ::Zip::Entry.new('a', 'b', comment: 'c') assert_equal(ZIP_ENTRIES.size + 1, @zip_entry_set.length) end @@ -66,7 +70,9 @@ def test_entries end def test_find_entry - entries = [::Zip::Entry.new('zipfile.zip', 'MiXeDcAsEnAmE', 'comment1')] + entries = [ + ::Zip::Entry.new('zipfile.zip', 'MiXeDcAsEnAmE', comment: 'comment1') + ] ::Zip.case_insensitive_match = true zip_entry_set = ::Zip::EntrySet.new(entries) @@ -96,7 +102,9 @@ def test_entries_sorted_in_each end def test_compound - new_entry = ::Zip::Entry.new('zf.zip', 'new entry', "new entry's comment") + new_entry = ::Zip::Entry.new( + 'zf.zip', 'new entry', comment: "new entry's comment" + ) assert_equal(ZIP_ENTRIES.size, @zip_entry_set.size) @zip_entry_set << new_entry assert_equal(ZIP_ENTRIES.size + 1, @zip_entry_set.size) diff --git a/test/entry_test.rb b/test/entry_test.rb index aabb2fdb..6df0964c 100644 --- a/test/entry_test.rb +++ b/test/entry_test.rb @@ -4,16 +4,14 @@ class ZipEntryTest < MiniTest::Test include ZipEntryData def test_constructor_and_getters - entry = ::Zip::Entry.new(TEST_ZIPFILE, - TEST_NAME, - TEST_COMMENT, - TEST_EXTRA, - TEST_COMPRESSED_SIZE, - TEST_CRC, - TEST_COMPRESSIONMETHOD, - TEST_COMPRESSIONLEVEL, - TEST_SIZE, - TEST_TIME) + entry = ::Zip::Entry.new( + TEST_ZIPFILE, TEST_NAME, + comment: TEST_COMMENT, extra: TEST_EXTRA, + compressed_size: TEST_COMPRESSED_SIZE, + crc: TEST_CRC, size: TEST_SIZE, time: TEST_TIME, + compression_method: TEST_COMPRESSIONMETHOD, + compression_level: TEST_COMPRESSIONLEVEL + ) assert_equal(TEST_COMMENT, entry.comment) assert_equal(TEST_COMPRESSED_SIZE, entry.compressed_size) @@ -40,30 +38,54 @@ def test_is_directory_and_is_file end def test_equality - entry1 = ::Zip::Entry.new('file.zip', 'name', 'isNotCompared', - 'something extra', 123, 1234, - ::Zip::Entry::DEFLATED, ::Zip.default_compression, 10_000) - entry2 = ::Zip::Entry.new('file.zip', 'name', 'isNotComparedXXX', - 'something extra', 123, 1234, - ::Zip::Entry::DEFLATED, ::Zip.default_compression, 10_000) - entry3 = ::Zip::Entry.new('file.zip', 'name2', 'isNotComparedXXX', - 'something extra', 123, 1234, - ::Zip::Entry::DEFLATED, ::Zip.default_compression, 10_000) - entry4 = ::Zip::Entry.new('file.zip', 'name2', 'isNotComparedXXX', - 'something extraXX', 123, 1234, - ::Zip::Entry::DEFLATED, ::Zip.default_compression, 10_000) - entry5 = ::Zip::Entry.new('file.zip', 'name2', 'isNotComparedXXX', - 'something extraXX', 12, 1234, - ::Zip::Entry::DEFLATED, ::Zip.default_compression, 10_000) - entry6 = ::Zip::Entry.new('file.zip', 'name2', 'isNotComparedXXX', - 'something extraXX', 12, 123, - ::Zip::Entry::DEFLATED, ::Zip.default_compression, 10_000) - entry7 = ::Zip::Entry.new('file.zip', 'name2', 'isNotComparedXXX', - 'something extraXX', 12, 123, - ::Zip::Entry::STORED, ::Zip.default_compression, 10_000) - entry8 = ::Zip::Entry.new('file.zip', 'name2', 'isNotComparedXXX', - 'something extraXX', 12, 123, - ::Zip::Entry::STORED, ::Zip.default_compression, 100_000) + entry1 = ::Zip::Entry.new( + 'file.zip', 'name', + comment: 'isNotCompared', extra: 'something extra', + compressed_size: 123, crc: 1234, size: 10_000 + ) + + entry2 = ::Zip::Entry.new( + 'file.zip', 'name', + comment: 'isNotComparedXXX', extra: 'something extra', + compressed_size: 123, crc: 1234, size: 10_000 + ) + + entry3 = ::Zip::Entry.new( + 'file.zip', 'name2', + comment: 'isNotComparedXXX', extra: 'something extra', + compressed_size: 123, crc: 1234, size: 10_000 + ) + + entry4 = ::Zip::Entry.new( + 'file.zip', 'name2', + comment: 'isNotComparedXXX', extra: 'something extraXX', + compressed_size: 123, crc: 1234, size: 10_000 + ) + + entry5 = ::Zip::Entry.new( + 'file.zip', 'name2', + comment: 'isNotComparedXXX', extra: 'something extraXX', + compressed_size: 12, crc: 1234, size: 10_000 + ) + + entry6 = ::Zip::Entry.new( + 'file.zip', 'name2', + comment: 'isNotComparedXXX', extra: 'something extraXX', + compressed_size: 12, crc: 123, size: 10_000 + ) + + entry7 = ::Zip::Entry.new( + 'file.zip', 'name2', comment: 'isNotComparedXXX', + extra: 'something extraXX', compressed_size: 12, crc: 123, size: 10_000, + compression_method: ::Zip::Entry::STORED + ) + + entry8 = ::Zip::Entry.new( + 'file.zip', 'name2', + comment: 'isNotComparedXXX', extra: 'something extraXX', + compressed_size: 12, crc: 123, size: 100_000, + compression_method: ::Zip::Entry::STORED + ) assert_equal(entry1, entry1) assert_equal(entry1, entry2) @@ -131,13 +153,11 @@ def test_store_file_without_compression end zipfile = Zip::File.open('/tmp/no_compress.zip', Zip::File::CREATE) - mimetype_entry = Zip::Entry.new(zipfile, # @zipfile - 'mimetype', # @name - '', # @comment - '', # @extra - 0, # @compressed_size - 0, # @crc - Zip::Entry::STORED) # @comppressed_method + mimetype_entry = Zip::Entry.new( + zipfile, # @zipfile + 'mimetype', # @name + compression_method: Zip::Entry::STORED + ) zipfile.add(mimetype_entry, 'test/data/mimetype') @@ -182,18 +202,27 @@ def test_compression_level_flags [8, 2], [9, 2] ].each do |level, flags| - # Check flags are set correctly when DEFLATED is specified. - e_def = Zip::Entry.new('', '', '', '', 0, 0, Zip::Entry::DEFLATED, level) + # Check flags are set correctly when DEFLATED is (implicitly) specified. + e_def = Zip::Entry.new( + '', '', + compression_level: level + ) assert_equal(flags, e_def.gp_flags & 0b110) # Check that flags are not set when STORED is specified. - e_sto = Zip::Entry.new('', '', '', '', 0, 0, Zip::Entry::STORED, level) + e_sto = Zip::Entry.new( + '', '', + compression_method: Zip::Entry::STORED, + compression_level: level + ) assert_equal(0, e_sto.gp_flags & 0b110) end # Check that a directory entry's flags are not set, even if DEFLATED # is specified. - e_dir = Zip::Entry.new('', 'd/', '', '', 0, 0, Zip::Entry::DEFLATED, 1) + e_dir = Zip::Entry.new( + '', 'd/', compression_method: Zip::Entry::DEFLATED, compression_level: 1 + ) assert_equal(0, e_dir.gp_flags & 0b110) end end diff --git a/test/file_test.rb b/test/file_test.rb index 0d72a1d3..6c52adfc 100644 --- a/test/file_test.rb +++ b/test/file_test.rb @@ -77,7 +77,10 @@ def test_get_output_stream assert_equal(count + 1, zf.size) assert_equal('Putting stuff in data/generated/empty.txt', zf.read('test/data/generated/empty.txt')) - custom_entry_args = [TEST_COMMENT, TEST_EXTRA, TEST_COMPRESSED_SIZE, TEST_CRC, ::Zip::Entry::STORED, ::Zlib::BEST_SPEED, TEST_SIZE, TEST_TIME] + custom_entry_args = [ + TEST_COMMENT, TEST_EXTRA, TEST_COMPRESSED_SIZE, TEST_CRC, + ::Zip::Entry::STORED, ::Zlib::BEST_SPEED, TEST_SIZE, TEST_TIME + ] zf.get_output_stream('entry_with_custom_args.txt', nil, *custom_entry_args) do |os| os.write 'Some data' end diff --git a/test/local_entry_test.rb b/test/local_entry_test.rb index 28148039..3a1c91ff 100644 --- a/test/local_entry_test.rb +++ b/test/local_entry_test.rb @@ -53,9 +53,11 @@ def test_read_local_entry_from_truncated_zip_file end def test_write_entry - entry = ::Zip::Entry.new('file.zip', 'entry_name', 'my little comment', - 'thisIsSomeExtraInformation', 100, 987_654, - ::Zip::Entry::DEFLATED, 400) + entry = ::Zip::Entry.new( + 'file.zip', 'entry_name', comment: 'my little comment', size: 400, + extra: 'thisIsSomeExtraInformation', compressed_size: 100, crc: 987_654 + ) + write_to_file(LEH_FILE, CEH_FILE, entry) local_entry, central_entry = read_from_file(LEH_FILE, CEH_FILE) assert( @@ -68,9 +70,10 @@ def test_write_entry def test_write_entry_with_zip64 ::Zip.write_zip64_support = true - entry = ::Zip::Entry.new('file.zip', 'entry_name', 'my little comment', - 'thisIsSomeExtraInformation', 100, 987_654, - ::Zip::Entry::DEFLATED, 400) + entry = ::Zip::Entry.new( + 'file.zip', 'entry_name', comment: 'my little comment', size: 400, + extra: 'thisIsSomeExtraInformation', compressed_size: 100, crc: 987_654 + ) write_to_file(LEH_FILE, CEH_FILE, entry) local_entry, central_entry = read_from_file(LEH_FILE, CEH_FILE) @@ -92,11 +95,12 @@ def test_write_entry_with_zip64 def test_write_64entry ::Zip.write_zip64_support = true - entry = ::Zip::Entry.new('bigfile.zip', 'entry_name', 'my little equine', - 'malformed extra field because why not', - 0x7766554433221100, 0xDEADBEEF, - ::Zip::Entry::DEFLATED, ::Zip.default_compression, - 0x9988776655443322) + entry = ::Zip::Entry.new( + 'bigfile.zip', 'entry_name', comment: 'my little equine', + extra: 'malformed extra field because why not', size: 0x9988776655443322, + compressed_size: 0x7766554433221100, crc: 0xDEADBEEF + ) + write_to_file(LEH_FILE, CEH_FILE, entry) local_entry, central_entry = read_from_file(LEH_FILE, CEH_FILE) compare_local_entry_headers(entry, local_entry) diff --git a/test/output_stream_test.rb b/test/output_stream_test.rb index 79fb8f71..c89cbbcc 100644 --- a/test/output_stream_test.rb +++ b/test/output_stream_test.rb @@ -92,7 +92,9 @@ def test_put_next_entry def test_put_next_entry_using_zip_entry_creates_entries_with_correct_timestamps file = ::File.open('test/data/file2.txt', 'rb') ::Zip::OutputStream.open(TEST_ZIP.zip_name) do |zos| - zip_entry = ::Zip::Entry.new(zos, file.path, '', '', 0, 0, ::Zip::Entry::DEFLATED, ::Zip.default_compression, 0, ::Zip::DOSTime.at(file.mtime)) + zip_entry = ::Zip::Entry.new( + zos, file.path, time: ::Zip::DOSTime.at(file.mtime) + ) zos.put_next_entry(zip_entry) zos << file.read end From 156b0f3dee1dbb9f7c4dbc0012251990aec1da02 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Mon, 15 Jun 2020 00:16:17 +0100 Subject: [PATCH 020/311] Tidy up accessors in `Entry`. Remove a load of accessors from the 'public' API by not documenting them, and remove access to `header_signature` completely. Also, `Entry#extra` has been created to allow extra fields to be set correctly after initialization. --- lib/zip/entry.rb | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/lib/zip/entry.rb b/lib/zip/entry.rb index 0e2dfd8c..32c0b165 100644 --- a/lib/zip/entry.rb +++ b/lib/zip/entry.rb @@ -6,14 +6,14 @@ class Entry # Language encoding flag (EFS) bit EFS = 0b100000000000 - attr_accessor :comment, :compressed_size, :crc, :extra, - :name, :size, :local_header_offset, :zipfile, :fstype, :external_file_attributes, - :internal_file_attributes, - :gp_flags, :header_signature, :follow_symlinks, - :restore_times, :restore_permissions, :restore_ownership, - :unix_uid, :unix_gid, :unix_perms, - :dirty - attr_reader :compression_level, :ftype, :filepath # :nodoc: + attr_accessor :comment, :compressed_size, :follow_symlinks, :name, + :restore_ownership, :restore_permissions, :restore_times, + :size, :unix_gid, :unix_perms, :unix_uid, :zipfile + + attr_accessor :crc, :dirty, :external_file_attributes, :fstype, :gp_flags, + :internal_file_attributes, :local_header_offset # :nodoc: + + attr_reader :extra, :compression_level, :ftype, :filepath # :nodoc: def set_default_vars_values @local_header_offset = 0 @@ -89,6 +89,14 @@ def incomplete? gp_flags & 8 == 8 end + def extra=(field) + @extra = if field.nil? + ExtraField.new + else + field.kind_of?(ExtraField) ? field : ExtraField.new(field.to_s) + end + end + def time if @extra['UniversalTime'] @extra['UniversalTime'].mtime @@ -384,7 +392,7 @@ def check_c_dir_entry_static_header_length(buf) end def check_c_dir_entry_signature - return if header_signature == ::Zip::CENTRAL_DIRECTORY_ENTRY_SIGNATURE + return if @header_signature == ::Zip::CENTRAL_DIRECTORY_ENTRY_SIGNATURE raise Error, "Zip local header magic not found at location '#{local_header_offset}'" end From 0620fba13dd75ddb0d4d4d670db6bdae7a04fd98 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sat, 20 Jun 2020 16:03:46 +0100 Subject: [PATCH 021/311] Don't use raw numbers for Entry compression types. Constants for Store and Deflate are already available, so use them. It might be sensible to remove these local versions, but they do have their uses as a shortened form. --- lib/zip/entry.rb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/zip/entry.rb b/lib/zip/entry.rb index 32c0b165..48b88cd5 100644 --- a/lib/zip/entry.rb +++ b/lib/zip/entry.rb @@ -1,8 +1,9 @@ require 'pathname' module Zip class Entry - STORED = 0 - DEFLATED = 8 + STORED = ::Zip::COMPRESSION_METHOD_STORE + DEFLATED = ::Zip::COMPRESSION_METHOD_DEFLATE + # Language encoding flag (EFS) bit EFS = 0b100000000000 From 5201cd2ea35bdd1087e39b39a5b53ee80ab5adfd Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sat, 20 Jun 2020 17:34:57 +0100 Subject: [PATCH 022/311] Make sure tests that change Zip defaults reset properly. --- test/case_sensitivity_test.rb | 2 +- test/deflater_test.rb | 4 ++++ test/encryption_test.rb | 3 +-- test/entry_test.rb | 4 ++++ test/local_entry_test.rb | 2 +- test/unicode_file_names_and_comments_test.rb | 4 ++++ 6 files changed, 15 insertions(+), 4 deletions(-) diff --git a/test/case_sensitivity_test.rb b/test/case_sensitivity_test.rb index 1c89551a..fdbee8e3 100644 --- a/test/case_sensitivity_test.rb +++ b/test/case_sensitivity_test.rb @@ -7,7 +7,7 @@ class ZipCaseSensitivityTest < MiniTest::Test ['test/data/file2.txt', 'testFILE.rb']] def teardown - ::Zip.case_insensitive_match = false + ::Zip.reset! end # Ensure that everything functions normally when +case_insensitive_match = false+ diff --git a/test/deflater_test.rb b/test/deflater_test.rb index 2506f920..35d7b0c6 100644 --- a/test/deflater_test.rb +++ b/test/deflater_test.rb @@ -8,6 +8,10 @@ class DeflaterTest < MiniTest::Test DEFAULT_COMP_FILE = 'test/data/generated/compressiontest_default_compression.bin' NO_COMP_FILE = 'test/data/generated/compressiontest_no_compression.bin' + def teardown + Zip.reset! + end + def test_output_operator txt = load_file('test/data/file2.txt') deflate(txt, DEFLATER_TEST_FILE) diff --git a/test/encryption_test.rb b/test/encryption_test.rb index d3ed5ffb..7cb39ca4 100644 --- a/test/encryption_test.rb +++ b/test/encryption_test.rb @@ -5,12 +5,11 @@ class EncryptionTest < MiniTest::Test INPUT_FILE1 = 'test/data/file1.txt' def setup - @default_compression = Zip.default_compression Zip.default_compression = ::Zlib::DEFAULT_COMPRESSION end def teardown - Zip.default_compression = @default_compression + Zip.reset! end def test_encrypt diff --git a/test/entry_test.rb b/test/entry_test.rb index 6df0964c..bf9efa3a 100644 --- a/test/entry_test.rb +++ b/test/entry_test.rb @@ -3,6 +3,10 @@ class ZipEntryTest < MiniTest::Test include ZipEntryData + def teardown + ::Zip.reset! + end + def test_constructor_and_getters entry = ::Zip::Entry.new( TEST_ZIPFILE, TEST_NAME, diff --git a/test/local_entry_test.rb b/test/local_entry_test.rb index 3a1c91ff..1317fb45 100644 --- a/test/local_entry_test.rb +++ b/test/local_entry_test.rb @@ -5,7 +5,7 @@ class ZipLocalEntryTest < MiniTest::Test LEH_FILE = 'test/data/generated/localEntryHeader.bin' def teardown - ::Zip.write_zip64_support = false + ::Zip.reset! end def test_read_local_entry_header_of_first_test_zip_entry diff --git a/test/unicode_file_names_and_comments_test.rb b/test/unicode_file_names_and_comments_test.rb index 4d2fc20f..7950e289 100644 --- a/test/unicode_file_names_and_comments_test.rb +++ b/test/unicode_file_names_and_comments_test.rb @@ -3,6 +3,10 @@ class ZipUnicodeFileNamesAndComments < MiniTest::Test FILENAME = File.join(File.dirname(__FILE__), 'test1.zip') + def teardown + ::Zip.reset! + end + def test_unicode_file_name file_entrys = ['текстовыйфайл.txt', 'Résumé.txt', '슬레이어스휘.txt'] directory_entrys = ['папка/текстовыйфайл.txt', 'Résumé/Résumé.txt', '슬레이어스휘/슬레이어스휘.txt'] From cf3f4339f6dc048ea61c854cc2e4588c7a0f39bc Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sat, 20 Jun 2020 17:36:17 +0100 Subject: [PATCH 023/311] Make sure that compression method is STORE for level 0. Whatever the compression method that is set by the user, if the compression level is set to 0 (no compression), then the entry should be STORED. This mimics commandline tool behaviour and matches user expectations. --- lib/zip/entry.rb | 4 +++- test/entry_test.rb | 25 +++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/lib/zip/entry.rb b/lib/zip/entry.rb index 48b88cd5..d036bb7d 100644 --- a/lib/zip/entry.rb +++ b/lib/zip/entry.rb @@ -121,7 +121,9 @@ def time=(value) end def compression_method - @ftype == :directory ? STORED : @compression_method + return STORED if @ftype == :directory || @compression_level == 0 + + @compression_method end def compression_method=(method) diff --git a/test/entry_test.rb b/test/entry_test.rb index bf9efa3a..dda0a7ba 100644 --- a/test/entry_test.rb +++ b/test/entry_test.rb @@ -229,4 +229,29 @@ def test_compression_level_flags ) assert_equal(0, e_dir.gp_flags & 0b110) end + + def test_compression_method_reader + [ + [Zip.default_compression, Zip::Entry::DEFLATED], + [0, Zip::Entry::STORED], + [1, Zip::Entry::DEFLATED], + [9, Zip::Entry::DEFLATED] + ].each do |level, method| + # Check that the correct method is returned when DEFLATED is specified. + entry = Zip::Entry.new(compression_level: level) + assert_equal(method, entry.compression_method) + end + + # Check that the correct method is returned when STORED is specified. + entry = Zip::Entry.new( + compression_method: Zip::Entry::STORED, compression_level: 1 + ) + assert_equal(Zip::Entry::STORED, entry.compression_method) + + # Check that directories are always STORED, whatever level is specified. + entry = Zip::Entry.new( + '', 'd/', compression_method: Zip::Entry::DEFLATED, compression_level: 1 + ) + assert_equal(Zip::Entry::STORED, entry.compression_method) + end end From f1dd724a3ab30190cc9422855e5eb5cf6f02645b Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Fri, 7 Aug 2020 19:45:56 +0100 Subject: [PATCH 024/311] Use constants for the compression level gp flags. --- lib/zip/entry.rb | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/lib/zip/entry.rb b/lib/zip/entry.rb index d036bb7d..c56dd052 100644 --- a/lib/zip/entry.rb +++ b/lib/zip/entry.rb @@ -7,6 +7,11 @@ class Entry # Language encoding flag (EFS) bit EFS = 0b100000000000 + # Compression level flags (used as part of the gp flags). + COMPRESSION_LEVEL_SUPERFAST_GPFLAG = 0b110 + COMPRESSION_LEVEL_FAST_GPFLAG = 0b100 + COMPRESSION_LEVEL_MAX_GPFLAG = 0b010 + attr_accessor :comment, :compressed_size, :follow_symlinks, :name, :restore_ownership, :restore_permissions, :restore_times, :size, :unix_gid, :unix_perms, :unix_uid, :zipfile @@ -720,11 +725,11 @@ def set_compression_level_flags case @compression_level when 1 - @gp_flags |= 0b110 + @gp_flags |= COMPRESSION_LEVEL_SUPERFAST_GPFLAG when 2 - @gp_flags |= 0b100 + @gp_flags |= COMPRESSION_LEVEL_FAST_GPFLAG when 8, 9 - @gp_flags |= 0b010 + @gp_flags |= COMPRESSION_LEVEL_MAX_GPFLAG end end From 2ff313da26c0936e526a1cc04862d25623da267f Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sun, 13 Sep 2020 16:45:10 +0100 Subject: [PATCH 025/311] Update README instructions for setting compression_level. --- README.md | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 059f22d1..2d59eb7f 100644 --- a/README.md +++ b/README.md @@ -288,15 +288,25 @@ Zip.validate_entry_sizes = false Note that if you use the lower level `Zip::InputStream` interface, `rubyzip` does *not* check the entry `size`s. In this case, the caller is responsible for making sure it does not read more data than expected from the input stream. -### Default Compression +### Compression level -You can set the default compression level like so: +When adding entries to a zip archive you can set the compression level to trade-off compressed size against compression speed. By default this is set to the same as the underlying Zlib library's default (`Zlib::DEFAULT_COMPRESSION`), which is somewhere in the middle. + +You can configure the default compression level with: ```ruby -Zip.default_compression = Zlib::DEFAULT_COMPRESSION +Zip.default_compression = X ``` -It defaults to `Zlib::DEFAULT_COMPRESSION`. Possible values are `Zlib::BEST_COMPRESSION`, `Zlib::DEFAULT_COMPRESSION` and `Zlib::NO_COMPRESSION` +Where X is an integer between 0 and 9, inclusive. If this option is set to 0 (`Zlib::NO_COMPRESSION`) then entries will be stored in the zip archive uncompressed. A value of 1 (`Zlib::BEST_SPEED`) gives the fastest compression and 9 (`Zlib::BEST_COMPRESSION`) gives the smallest compressed file size. + +This can also be set for each archive as an option to `Zip::File`: + +```ruby +Zip::File.open('foo.zip', Zip::File::CREATE, {compression_level: 9}) do |zip| + zip.add ... +end +``` ### Zip64 Support From 8816279fe04db2f8a87655e19a9341778942bbae Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sun, 13 Sep 2020 16:58:06 +0100 Subject: [PATCH 026/311] Update the 'Developing' instructions in the README. --- README.md | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 2d59eb7f..f8958480 100644 --- a/README.md +++ b/README.md @@ -333,13 +333,22 @@ You can set multiple settings at the same time by using a block: ## Developing -To run the test you need to do this: +Install the dependencies: -``` +```shell bundle install +``` + +Run the tests with `rake`: + +```shell rake ``` +Please also run `rubocop` over your changes. + +Our CI is here: https://travis-ci.org/github/rubyzip/rubyzip. Please note that `rubocop` is run as part of the CI configuration and will fail a build if errors are found. + ## Website and Project Home http://github.com/rubyzip/rubyzip From 834ff70c4d6511dc85e3d4d198a4f7cdb09f655d Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sun, 13 Sep 2020 17:13:02 +0100 Subject: [PATCH 027/311] Updated and cleaned-up authors in the README. --- README.md | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index f8958480..fe1fd79d 100644 --- a/README.md +++ b/README.md @@ -357,15 +357,22 @@ http://rdoc.info/github/rubyzip/rubyzip/master/frames ## Authors -Alexander Simonov ( alex at simonov.me) +See https://github.com/rubyzip/rubyzip/graphs/contributors for a comprehensive list. -Alan Harper ( alan at aussiegeek.net) +### Current contributors -Thomas Sondergaard (thomas at sondergaard.cc) +* Robert Haines (@hainesr) +* John Lees-Miller (@jdleesmiller) -Technorama Ltd. (oss-ruby-zip at technorama.net) +### Past contributors -extra-field support contributed by Tatsuki Sugiura (sugi at nemui.org) +* Pavel Lobashov (@ShockwaveNN) +* Oleksandr Simonov (@simonoff) +* Alan Harper (@aussiegeek) + +### Original author + +* Thomas Sondergaard ## License From f0714137f6314a3c38a914c58e099f9ac1af1b82 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sun, 13 Sep 2020 17:17:05 +0100 Subject: [PATCH 028/311] Update Changelog.md with an entry for this PR. --- Changelog.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Changelog.md b/Changelog.md index cb4a893e..ec6ae1fd 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,5 +1,7 @@ # X.X.X (Next) +- Set compression level on a per-zipfile basis. [#448](https://github.com/rubyzip/rubyzip/pull/448) + Tooling: - Update rubocop again and run it in CI. [#444](https://github.com/rubyzip/rubyzip/pull/444) From b00c47a0471f64b379e2c1c5ece6a75875f42ec2 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sun, 20 Sep 2020 14:41:24 +0100 Subject: [PATCH 029/311] Add a failing test for reading local extra fields. --- test/data/local_extra_field.zip | Bin 0 -> 9466 bytes test/extra_field_test.rb | 12 ++++++++++++ 2 files changed, 12 insertions(+) create mode 100644 test/data/local_extra_field.zip diff --git a/test/data/local_extra_field.zip b/test/data/local_extra_field.zip new file mode 100644 index 0000000000000000000000000000000000000000..5a936e4c351f0676c63eaeaf8bd39453ba5c2022 GIT binary patch literal 9466 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 MJpS`L()_pjUtRzq-T(jq literal 0 HcmV?d00001 diff --git a/test/extra_field_test.rb b/test/extra_field_test.rb index fa6e212d..69704890 100644 --- a/test/extra_field_test.rb +++ b/test/extra_field_test.rb @@ -73,4 +73,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 From fe1d3c8da0591a4a802f711b4a9770f34899132f Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sun, 20 Sep 2020 15:06:04 +0100 Subject: [PATCH 030/311] Fix reading Ux extra field. As previously implemented the `uid` and `gid` fields could only ever be read as 0, because they were being initialized to zero and then memoization (`@uid ||= uid`) was used to 'save' the new value. Using `nil` as the initial value for either of these fields breaks so many tests, so I have fixed this by not using memoization instead. This is safe because it is only the local extra field that holds these values for this type of extra field. --- lib/zip/extra_field/unix.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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) From f742994cf25bd4c3fb66f69b9ecc012f3f51c214 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sun, 20 Sep 2020 15:18:34 +0100 Subject: [PATCH 031/311] Abstract out reading extra fields in Entry. Remove some (almost) duplicated code and get ready for the real fix. --- lib/zip/entry.rb | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) 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 From a9628ef9d5ee9f85d7559a658732964fca2a8580 Mon Sep 17 00:00:00 2001 From: Kentaro Hayashi Date: Fri, 25 Sep 2020 16:17:01 +0900 Subject: [PATCH 032/311] Use correct SPDX license identifier The valid SPDX license is "BSD-2-Clause" instead of "BSD 2-Clause". ref. https://spdx.org/licenses/ --- rubyzip.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rubyzip.gemspec b/rubyzip.gemspec index 24e9ecf3..67de78dc 100644 --- a/rubyzip.gemspec +++ b/rubyzip.gemspec @@ -12,7 +12,7 @@ Gem::Specification.new do |s| s.summary = 'rubyzip is a ruby module for reading and writing zip files' s.files = Dir.glob('{samples,lib}/**/*.rb') + %w[README.md TODO Rakefile] s.require_paths = ['lib'] - s.license = 'BSD 2-Clause' + s.license = 'BSD-2-Clause' s.metadata = { 'bug_tracker_uri' => 'https://github.com/rubyzip/rubyzip/issues', 'changelog_uri' => "https://github.com/rubyzip/rubyzip/blob/v#{s.version}/Changelog.md", From c2b9aa2893c03537e9a985b8410d9bb1eeeba8e2 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sun, 20 Sep 2020 18:11:49 +0100 Subject: [PATCH 033/311] Correctly read extra fields when opening a zip file. Previously, only the extra fields stored in the central directory were being read in. In reality it is often the case that the extra field in the central directory is just a marker, and the full data is in the local header. So we need to read both in and merge the two into the final correct extra field. This merging infrastructure was already implemented in the extra field code but we never actually read the local extra fields in until now. Reading the central directory headers and local entry headers seems rather fragile, so we can't just read one over the other and hope to end up with a correctly merged set of extra fields because this breaks other things. So we need to specifically read the local extra field data and merge just those bits. This commit also fixes a couple of tests that were 'broken' by us now reading extra fields in correctly! --- lib/zip/central_directory.rb | 29 +++++++++++++++++++++++- test/filesystem/file_nonmutating_test.rb | 6 +++-- test/filesystem/file_stat_test.rb | 4 ++-- 3 files changed, 34 insertions(+), 5 deletions(-) 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/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 From a668fd14d2047b27ba9fa5d39e7e7f91971a4084 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Mon, 21 Sep 2020 09:34:14 +0100 Subject: [PATCH 034/311] Test reading an extra field with a bad header ID. --- test/extra_field_test.rb | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/test/extra_field_test.rb b/test/extra_field_test.rb index 69704890..6f2ec65b 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) From 8bafcbbc4db64ba7e9f6d7e2fc59cb3ac87dc685 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Mon, 21 Sep 2020 09:51:47 +0100 Subject: [PATCH 035/311] Remove dead code in extra_field/generic.rb (`==`). From what I can tell this was erroneously copied out of extra_field.rb during a refactor. It attempts to compare a non-existent Hash that used to be inherited before the refactor. If this code had been left within ExtraField it would make more sense, but as it's not needed there either let's just remove it. See 20d79feb995728c6b791580cc82f62f2be82606e for the refactor. --- lib/zip/extra_field/generic.rb | 9 --------- 1 file changed, 9 deletions(-) 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 From 0235e76baecd1d952e44319db906775156675664 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Mon, 21 Sep 2020 10:50:22 +0100 Subject: [PATCH 036/311] Test packing the NTFS extra field. --- test/extra_field_test.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/extra_field_test.rb b/test/extra_field_test.rb index 6f2ec65b..52140a2d 100644 --- a/test/extra_field_test.rb +++ b/test/extra_field_test.rb @@ -35,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 From ac89366902df4dff7ea4d2384d565787f5c08b02 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sun, 8 Nov 2020 17:19:49 +0000 Subject: [PATCH 037/311] Failing test to catch error on read after readline. --- test/input_stream_test.rb | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/test/input_stream_test.rb b/test/input_stream_test.rb index 773ee6b5..34042ba9 100644 --- a/test/input_stream_test.rb +++ b/test/input_stream_test.rb @@ -179,4 +179,14 @@ def test_ungetc assert_equal('$VERBOSE =', zis.read(10)) end end + + def test_readline_then_read + ::Zip::InputStream.open(TestZipFile::TEST_ZIP2.zip_name) do |zis| + zis.get_next_entry + assert_equal("#!/usr/bin/env ruby\n", zis.readline) + refute(zis.eof?) + refute_empty(zis.read) # Also should not raise an error. + assert(zis.eof?) + end + end end From 2ea805c9513e5a1976b17c309ccdcbd15a4f71f0 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sun, 8 Nov 2020 17:20:53 +0000 Subject: [PATCH 038/311] Check `number_of_bytes` before comparison in read. If an input stream has been read from, and left some data in the internal buffer, then a subsequent `read`, with no amount of bytes to be read having been specified, will raise an error when comparing to `nil`. This fix checks that the number of bytes specified in the `read` is not `nil` before comparing with the size of the internal buffer. Fixes #461. --- lib/zip/ioextras/abstract_input_stream.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/zip/ioextras/abstract_input_stream.rb b/lib/zip/ioextras/abstract_input_stream.rb index 8392d240..848dcae3 100644 --- a/lib/zip/ioextras/abstract_input_stream.rb +++ b/lib/zip/ioextras/abstract_input_stream.rb @@ -19,7 +19,7 @@ def initialize def read(number_of_bytes = nil, buf = '') tbuf = if @output_buffer.bytesize > 0 - if number_of_bytes <= @output_buffer.bytesize + if number_of_bytes && number_of_bytes <= @output_buffer.bytesize @output_buffer.slice!(0, number_of_bytes) else number_of_bytes -= @output_buffer.bytesize if number_of_bytes From 0a6037b0ad067c8a90021609c86f42c6963c4566 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sun, 8 Nov 2020 17:51:57 +0000 Subject: [PATCH 039/311] Update Changelog.md. --- Changelog.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Changelog.md b/Changelog.md index cb4a893e..ab862d3f 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,5 +1,7 @@ # X.X.X (Next) +- Fix input stream partial read error. [#462](https://github.com/rubyzip/rubyzip/pull/462) + Tooling: - Update rubocop again and run it in CI. [#444](https://github.com/rubyzip/rubyzip/pull/444) From 5a4d1d8b6b9b556d05114ee8f005b20579e0cf8a Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sat, 28 Nov 2020 21:19:58 +0000 Subject: [PATCH 040/311] Replace and deprecate `Zip::DOSTime#dos_equals`. Having a specific 'does this instance equal another instance' method is kind of annoying and breaks a number of things. Most obviously it breaks comparing to `nil`: `nil.dos_equals(other)` will fail where `nil == other` does not. So this commit overrides `<=>` in `Zip::DOSTime` and deprecates `dos_equals`. --- lib/zip/dos_time.rb | 9 +++++++-- lib/zip/entry.rb | 3 +-- test/output_stream_test.rb | 2 +- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/lib/zip/dos_time.rb b/lib/zip/dos_time.rb index 1d77aa40..326645ad 100644 --- a/lib/zip/dos_time.rb +++ b/lib/zip/dos_time.rb @@ -24,9 +24,14 @@ def to_binary_dos_date ((year - 1980) << 9) end - # Dos time is only stored with two seconds accuracy def dos_equals(other) - to_i / 2 == other.to_i / 2 + warn 'Zip::DOSTime#dos_equals is deprecated. Use `==` instead.' + self == other + end + + # Dos time is only stored with two seconds accuracy. + def <=>(other) + (to_i / 2) <=> (other.to_i / 2) end # Create a DOSTime instance from a vanilla Time instance. diff --git a/lib/zip/entry.rb b/lib/zip/entry.rb index a67c6568..783a3b17 100644 --- a/lib/zip/entry.rb +++ b/lib/zip/entry.rb @@ -503,10 +503,9 @@ def ==(other) return false unless other.class == self.class # Compares contents of local entry and exposed fields - keys_equal = %w[compression_method crc compressed_size size name extra filepath].all? do |k| + %w[compression_method crc compressed_size size name extra filepath time].all? do |k| other.__send__(k.to_sym) == __send__(k.to_sym) end - keys_equal && time.dos_equals(other.time) end def <=>(other) diff --git a/test/output_stream_test.rb b/test/output_stream_test.rb index b2f64ab9..f86eb2ec 100644 --- a/test/output_stream_test.rb +++ b/test/output_stream_test.rb @@ -100,7 +100,7 @@ def test_put_next_entry_using_zip_entry_creates_entries_with_correct_timestamps ::Zip::InputStream.open(TEST_ZIP.zip_name) do |io| while (entry = io.get_next_entry) # Compare DOS Times, since they are stored with two seconds accuracy - assert(::Zip::DOSTime.at(file.mtime).dos_equals(::Zip::DOSTime.at(entry.mtime))) + assert(::Zip::DOSTime.at(file.mtime) == ::Zip::DOSTime.at(entry.mtime)) end end end From 4dc308caed51193b82edee4b2d118ac266cef866 Mon Sep 17 00:00:00 2001 From: Brian Buchalter Date: Tue, 26 Jan 2021 04:34:45 -0700 Subject: [PATCH 041/311] Prefer OUTPUT_FIELD_SEPARATOR to $, --- test/ioextras/abstract_output_stream_test.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/ioextras/abstract_output_stream_test.rb b/test/ioextras/abstract_output_stream_test.rb index 9b02309c..50470a4b 100644 --- a/test/ioextras/abstract_output_stream_test.rb +++ b/test/ioextras/abstract_output_stream_test.rb @@ -25,7 +25,7 @@ def setup end def teardown - $, = @save_comma_sep + $OUTPUT_FIELD_SEPARATOR = @save_comma_sep $\ = @save_output_sep end @@ -57,7 +57,7 @@ def test_print @output_stream.print('I sure hope so!') assert_equal("hello world. You ok out there?\nI sure hope so!\n", @output_stream.buffer) - $, = 'X' + $OUTPUT_FIELD_SEPARATOR = '\n' @output_stream.buffer = '' @output_stream.print('monkey', 'duck', 'zebra') assert_equal("monkeyXduckXzebra\n", @output_stream.buffer) From adfd15a1c658fd84956d6c9f925ddbf91b0b2fb5 Mon Sep 17 00:00:00 2001 From: Brian Buchalter Date: Tue, 26 Jan 2021 04:35:21 -0700 Subject: [PATCH 042/311] Prefer OUTPUT_RECORD_SEPARATOR to $\ --- test/ioextras/abstract_output_stream_test.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/ioextras/abstract_output_stream_test.rb b/test/ioextras/abstract_output_stream_test.rb index 50470a4b..1871b5f4 100644 --- a/test/ioextras/abstract_output_stream_test.rb +++ b/test/ioextras/abstract_output_stream_test.rb @@ -26,7 +26,7 @@ def setup def teardown $OUTPUT_FIELD_SEPARATOR = @save_comma_sep - $\ = @save_output_sep + $OUTPUT_RECORD_SEPARATOR = @save_output_sep end def test_write @@ -40,7 +40,7 @@ def test_write end def test_print - $\ = nil # record separator set to nil + $OUTPUT_RECORD_SEPARATOR = nil # record separator set to nil @output_stream.print('hello') assert_equal('hello', @output_stream.buffer) @@ -50,7 +50,7 @@ def test_print @output_stream.print(' You ok ', 'out ', 'there?') assert_equal('hello world. You ok out there?', @output_stream.buffer) - $\ = "\n" + $OUTPUT_RECORD_SEPARATOR = "\n" @output_stream.print assert_equal("hello world. You ok out there?\n", @output_stream.buffer) @@ -62,7 +62,7 @@ def test_print @output_stream.print('monkey', 'duck', 'zebra') assert_equal("monkeyXduckXzebra\n", @output_stream.buffer) - $\ = nil + $OUTPUT_RECORD_SEPARATOR = nil @output_stream.buffer = '' @output_stream.print(20) assert_equal('20', @output_stream.buffer) From 0585e4e36bb4e8f3f78b296a6c90dcb6ceda105d Mon Sep 17 00:00:00 2001 From: Brian Buchalter Date: Tue, 26 Jan 2021 04:36:13 -0700 Subject: [PATCH 043/311] Set OUTPUT_FIELD_SEPARATOR to nil in test As discussed in https://bugs.ruby-lang.org/issues/14240 and implemented in https://github.com/ruby/ruby/commit/6298ec2875a6f1a1e75698c96ceac94362f20bcf setting OUTPUT_FIELD_SEPERATOR to a non-nil value is now depricated. --- test/ioextras/abstract_output_stream_test.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/ioextras/abstract_output_stream_test.rb b/test/ioextras/abstract_output_stream_test.rb index 1871b5f4..e517a473 100644 --- a/test/ioextras/abstract_output_stream_test.rb +++ b/test/ioextras/abstract_output_stream_test.rb @@ -57,10 +57,10 @@ def test_print @output_stream.print('I sure hope so!') assert_equal("hello world. You ok out there?\nI sure hope so!\n", @output_stream.buffer) - $OUTPUT_FIELD_SEPARATOR = '\n' + $OUTPUT_FIELD_SEPARATOR = nil @output_stream.buffer = '' @output_stream.print('monkey', 'duck', 'zebra') - assert_equal("monkeyXduckXzebra\n", @output_stream.buffer) + assert_equal("monkeyduckzebra\n", @output_stream.buffer) $OUTPUT_RECORD_SEPARATOR = nil @output_stream.buffer = '' From 68b9ed4cfe964cbad46c31d8c5010aed07213431 Mon Sep 17 00:00:00 2001 From: Brian Buchalter Date: Tue, 26 Jan 2021 04:41:51 -0700 Subject: [PATCH 044/311] Remove OUTPUT_FIELD_SEPARATOR-related test behaviors Since modifying OUTPUT_FIELD_SEPARATOR is deprecated, there's no need for us to do this in our test. --- test/ioextras/abstract_output_stream_test.rb | 3 --- 1 file changed, 3 deletions(-) diff --git a/test/ioextras/abstract_output_stream_test.rb b/test/ioextras/abstract_output_stream_test.rb index e517a473..c6117aa7 100644 --- a/test/ioextras/abstract_output_stream_test.rb +++ b/test/ioextras/abstract_output_stream_test.rb @@ -20,12 +20,10 @@ def <<(data) def setup @output_stream = TestOutputStream.new - @save_comma_sep = $OUTPUT_FIELD_SEPARATOR @save_output_sep = $OUTPUT_RECORD_SEPARATOR end def teardown - $OUTPUT_FIELD_SEPARATOR = @save_comma_sep $OUTPUT_RECORD_SEPARATOR = @save_output_sep end @@ -57,7 +55,6 @@ def test_print @output_stream.print('I sure hope so!') assert_equal("hello world. You ok out there?\nI sure hope so!\n", @output_stream.buffer) - $OUTPUT_FIELD_SEPARATOR = nil @output_stream.buffer = '' @output_stream.print('monkey', 'duck', 'zebra') assert_equal("monkeyduckzebra\n", @output_stream.buffer) From ab9f546557fd53621907f0204a1462a88127ac1b Mon Sep 17 00:00:00 2001 From: Brian Buchalter Date: Tue, 26 Jan 2021 04:43:26 -0700 Subject: [PATCH 045/311] Use default ruby behavior for Array.join Since we are using the default behavior of OUTPUT_FIELD_SEPERATOR anyway, let's allow Ruby to maange that default for us, so if it should change in the future, we don't have to change. --- lib/zip/ioextras/abstract_output_stream.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/zip/ioextras/abstract_output_stream.rb b/lib/zip/ioextras/abstract_output_stream.rb index b94c9d49..7c0c5682 100644 --- a/lib/zip/ioextras/abstract_output_stream.rb +++ b/lib/zip/ioextras/abstract_output_stream.rb @@ -11,7 +11,7 @@ def write(data) end def print(*params) - self << params.join($OUTPUT_FIELD_SEPARATOR) << $OUTPUT_RECORD_SEPARATOR.to_s + self << params.join << $OUTPUT_RECORD_SEPARATOR.to_s end def printf(a_format_string, *params) From 72cedd7ce4993dcdc5c4c115e67ab00a627b51f9 Mon Sep 17 00:00:00 2001 From: Brian Buchalter Date: Tue, 26 Jan 2021 06:15:14 -0700 Subject: [PATCH 046/311] Remove compare_enumerables from test_helper.rb This change has several benefits: * When errors occur, the test provides useful feedback, showing you expected vs. actual. * We no longer need to open and modify the Enumerable module. * The test is more readable. --- test/central_directory_test.rb | 4 +--- test/test_helper.rb | 10 ---------- 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/test/central_directory_test.rb b/test/central_directory_test.rb index c4f7afa0..00c618a5 100644 --- a/test/central_directory_test.rb +++ b/test/central_directory_test.rb @@ -10,9 +10,7 @@ def test_read_from_stream cdir = ::Zip::CentralDirectory.read_from_stream(zip_file) assert_equal(TestZipFile::TEST_ZIP2.entry_names.size, cdir.size) - assert(cdir.entries.sort.compare_enumerables(TestZipFile::TEST_ZIP2.entry_names.sort) do |cdir_entry, test_entry_name| - cdir_entry.name == test_entry_name - end) + assert_equal(cdir.entries.map(&:name).sort, TestZipFile::TEST_ZIP2.entry_names.sort) assert_equal(TestZipFile::TEST_ZIP2.comment, cdir.comment) end end diff --git a/test/test_helper.rb b/test/test_helper.rb index 598736e6..eb3e13bc 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -165,16 +165,6 @@ def run_crc_test(compressor_class) end end -module Enumerable - def compare_enumerables(enumerable) - array = enumerable.to_a - each_with_index do |element, index| - return false unless yield(element, array[index]) - end - size == array.size - end -end - module CommonZipFileFixture include AssertEntry From 2c4de67d9f12c4bdeedae5da97c7a86a854d8143 Mon Sep 17 00:00:00 2001 From: Brian Buchalter Date: Tue, 26 Jan 2021 07:54:23 -0700 Subject: [PATCH 047/311] Simplify assertions in basic_zip_file_test We can get the same strength of assertions with less code. Also, by using assert_equal instead of assert, we get better feedback when assertion does not meet expectations. --- test/basic_zip_file_test.rb | 43 +++++++++++++++---------------------- 1 file changed, 17 insertions(+), 26 deletions(-) diff --git a/test/basic_zip_file_test.rb b/test/basic_zip_file_test.rb index 994728a3..8ff999bb 100644 --- a/test/basic_zip_file_test.rb +++ b/test/basic_zip_file_test.rb @@ -8,44 +8,35 @@ def setup end def test_entries - assert_equal(TestZipFile::TEST_ZIP2.entry_names.sort, - @zip_file.entries.entries.sort.map(&:name)) + expected_entry_names = TestZipFile::TEST_ZIP2.entry_names + actual_entry_names = @zip_file.entries.entries.map(&:name) + assert_equal(expected_entry_names.sort, actual_entry_names.sort) end def test_each - count = 0 - visited = {} - @zip_file.each do |entry| - assert(TestZipFile::TEST_ZIP2.entry_names.include?(entry.name)) - assert(!visited.include?(entry.name)) - visited[entry.name] = nil - count = count.succ - end - assert_equal(TestZipFile::TEST_ZIP2.entry_names.length, count) + expected_entry_names = TestZipFile::TEST_ZIP2.entry_names + actual_entry_names = [] + @zip_file.each { |entry| actual_entry_names << entry.name } + assert_equal(expected_entry_names.sort, actual_entry_names.sort) end def test_foreach - count = 0 - visited = {} - ::Zip::File.foreach(TestZipFile::TEST_ZIP2.zip_name) do |entry| - assert(TestZipFile::TEST_ZIP2.entry_names.include?(entry.name)) - assert(!visited.include?(entry.name)) - visited[entry.name] = nil - count = count.succ - end - assert_equal(TestZipFile::TEST_ZIP2.entry_names.length, count) + expected_entry_names = TestZipFile::TEST_ZIP2.entry_names + actual_entry_names = [] + ::Zip::File.foreach(TestZipFile::TEST_ZIP2.zip_name) { |entry| actual_entry_names << entry.name } + assert_equal(expected_entry_names.sort, actual_entry_names.sort) end def test_get_input_stream - count = 0 - visited = {} + expected_entry_names = TestZipFile::TEST_ZIP2.entry_names + actual_entry_names = [] + @zip_file.each do |entry| + actual_entry_names << entry.name assert_entry(entry.name, @zip_file.get_input_stream(entry), entry.name) - assert(!visited.include?(entry.name)) - visited[entry.name] = nil - count = count.succ end - assert_equal(TestZipFile::TEST_ZIP2.entry_names.length, count) + + assert_equal(expected_entry_names.sort, actual_entry_names.sort) end def test_get_input_stream_block From 9da6be98d827cb35808974f94311bd87e2f54618 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Tue, 16 Feb 2021 13:21:24 +0000 Subject: [PATCH 048/311] Fix the compression level tests to be relative. Made little sense to use hardcoded bytes sizes; the tests end up too brittle. --- test/file_test.rb | 130 +++++++++++++++++++--------------------------- 1 file changed, 53 insertions(+), 77 deletions(-) diff --git a/test/file_test.rb b/test/file_test.rb index 6c52adfc..f283a884 100644 --- a/test/file_test.rb +++ b/test/file_test.rb @@ -192,95 +192,71 @@ def test_cleans_up_tempfiles_after_close assert_equal(false, File.exist?(@tempfile_path)) end - def test_add_default_compression + def test_add_different_compression src_file = 'test/data/file2.txt' entry_name = 'newEntryName.rb' - assert(::File.exist?(src_file)) - zf = ::Zip::File.new(EMPTY_FILENAME, ::Zip::File::CREATE) - zf.add(entry_name, src_file) - zf.close - - zf_read = ::Zip::File.new(EMPTY_FILENAME) - entry = zf_read.entries.first - assert_equal('', zf_read.comment) - assert_equal(1, zf_read.entries.length) - assert_equal(entry_name, zf_read.entries.first.name) - assert_equal(File.size(src_file), entry.size) - assert_equal(8_764, entry.compressed_size) - AssertEntry.assert_contents(src_file, - zf_read.get_input_stream(entry_name, &:read)) - end - - def test_add_best_compression - src_file = 'test/data/file2.txt' - entry_name = 'newEntryName.rb' - assert(::File.exist?(src_file)) - zf = ::Zip::File.new(EMPTY_FILENAME, ::Zip::File::CREATE, false, { compression_level: Zlib::BEST_COMPRESSION }) - zf.add(entry_name, src_file) - zf.close - - zf_read = ::Zip::File.new(EMPTY_FILENAME) - entry = zf_read.entries.first - assert_equal(1, zf_read.entries.length) - assert_equal(File.size(src_file), entry.size) - assert_equal(8_658, entry.compressed_size) - AssertEntry.assert_contents(src_file, - zf_read.get_input_stream(entry_name, &:read)) - end + files = [ + ['test/data/fast_comp.zip', Zlib::BEST_SPEED], + ['test/data/default_comp.zip', Zlib::DEFAULT_COMPRESSION], + ['test/data/best_comp.zip', Zlib::BEST_COMPRESSION] + ] + sizes = [] + + files.each do |name, comp| + zf = ::Zip::File.new( + name, ::Zip::File::CREATE, false, { compression_level: comp } + ) + + zf.add(entry_name, src_file) + zf.close - def test_add_best_compression_as_default - ::Zip.default_compression = Zlib::BEST_COMPRESSION + zf_read = ::Zip::File.new(name) + entry = zf_read.entries.first + assert_equal(File.size(src_file), entry.size) + AssertEntry.assert_contents( + src_file, zf_read.get_input_stream(entry.name, &:read) + ) + sizes << entry.compressed_size + zf_read.close - src_file = 'test/data/file2.txt' - entry_name = 'newEntryName.rb' - assert(::File.exist?(src_file)) - zf = ::Zip::File.new(EMPTY_FILENAME, ::Zip::File::CREATE) - zf.add(entry_name, src_file) - zf.close + ::File.delete(name) + end - zf_read = ::Zip::File.new(EMPTY_FILENAME) - entry = zf_read.entries.first - assert_equal(1, zf_read.entries.length) - assert_equal(File.size(src_file), entry.size) - assert_equal(8_658, entry.compressed_size) - AssertEntry.assert_contents(src_file, - zf_read.get_input_stream(entry_name, &:read)) + assert(sizes[0] > sizes[1]) + assert(sizes[1] > sizes[2]) end - def test_add_best_speed + def test_add_different_compression_as_default src_file = 'test/data/file2.txt' entry_name = 'newEntryName.rb' - assert(::File.exist?(src_file)) - zf = ::Zip::File.new(EMPTY_FILENAME, ::Zip::File::CREATE, false, { compression_level: Zlib::BEST_SPEED }) - zf.add(entry_name, src_file) - zf.close - - zf_read = ::Zip::File.new(EMPTY_FILENAME) - entry = zf_read.entries.first - assert_equal(1, zf_read.entries.length) - assert_equal(File.size(src_file), entry.size) - assert_equal(10_938, entry.compressed_size) - AssertEntry.assert_contents(src_file, - zf_read.get_input_stream(entry_name, &:read)) - end + files = [ + ['test/data/fast_comp.zip', Zlib::BEST_SPEED], + ['test/data/default_comp.zip', Zlib::DEFAULT_COMPRESSION], + ['test/data/best_comp.zip', Zlib::BEST_COMPRESSION] + ] + sizes = [] + + files.each do |name, comp| + ::Zip.default_compression = comp + zf = ::Zip::File.new(name, ::Zip::File::CREATE) + + zf.add(entry_name, src_file) + zf.close - def test_add_best_speed_as_default - ::Zip.default_compression = Zlib::BEST_SPEED + zf_read = ::Zip::File.new(name) + entry = zf_read.entries.first + assert_equal(File.size(src_file), entry.size) + AssertEntry.assert_contents( + src_file, zf_read.get_input_stream(entry.name, &:read) + ) + sizes << entry.compressed_size + zf_read.close - src_file = 'test/data/file2.txt' - entry_name = 'newEntryName.rb' - assert(::File.exist?(src_file)) - zf = ::Zip::File.new(EMPTY_FILENAME, ::Zip::File::CREATE) - zf.add(entry_name, src_file) - zf.close + ::File.delete(name) + end - zf_read = ::Zip::File.new(EMPTY_FILENAME) - entry = zf_read.entries.first - assert_equal(1, zf_read.entries.length) - assert_equal(File.size(src_file), entry.size) - assert_equal(10_938, entry.compressed_size) - AssertEntry.assert_contents(src_file, - zf_read.get_input_stream(entry_name, &:read)) + assert(sizes[0] > sizes[1]) + assert(sizes[1] > sizes[2]) end def test_add_stored From 6b656d3277885afbecd18a67b2ed6a56dfb4d461 Mon Sep 17 00:00:00 2001 From: Taichi Ishitani Date: Sat, 6 Mar 2021 00:11:47 +0900 Subject: [PATCH 049/311] add Ruby 3.0 to CI --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index c73a251b..231e70c8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,6 +6,7 @@ rvm: - 2.5 - 2.6 - 2.7 + - 3.0 - ruby-head - truffleruby-head - truffleruby From fd510e07eb41919371af8459921d5b1199e41f5d Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sun, 16 May 2021 19:43:27 +0100 Subject: [PATCH 050/311] Add a GitHub action for linting. --- .github/workflows/lint.yml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 .github/workflows/lint.yml diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 00000000..fc6b09bd --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,15 @@ +name: Linter + +on: [push, pull_request] + +jobs: + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: ruby/setup-ruby@v1 + with: + ruby-version: 2.4 + bundler-cache: true + - name: Rubocop + run: bundle exec rubocop From 25ce623d139498a21454d8e04561c6e038c86c43 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sun, 16 May 2021 21:36:04 +0100 Subject: [PATCH 051/311] Add a GitHub action for CI tests. --- .github/workflows/tests.yml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 .github/workflows/tests.yml diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 00000000..8dea992f --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,17 @@ +name: Tests + +on: [push, pull_request] + +jobs: + test: + strategy: + matrix: + ruby: [2.4, 2.5, 2.6, 2.7, '3.0', head, jruby, truffleruby] + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{ matrix.ruby }} + bundler-cache: true + - run: bundle exec rake From e24f191222b64f405ba37c24e794f179390b49d8 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sun, 16 May 2021 21:47:49 +0100 Subject: [PATCH 052/311] Add some head versions to CI action and allow errors. --- .github/workflows/tests.yml | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 8dea992f..36238058 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -6,8 +6,17 @@ jobs: test: strategy: matrix: - ruby: [2.4, 2.5, 2.6, 2.7, '3.0', head, jruby, truffleruby] + ruby: [2.4, 2.5, 2.6, 2.7, '3.0', jruby, truffleruby] + can-fail: [false] + include: + - ruby: head + can-fail: true + - ruby: jruby-head + can-fail: true + - ruby: truffleruby-head + can-fail: true runs-on: ubuntu-latest + continue-on-error: ${{ matrix.can-fail }} steps: - uses: actions/checkout@v2 - uses: ruby/setup-ruby@v1 From 6536b964583232dc13debde49186cd38590eae8b Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Mon, 17 May 2021 12:38:46 +0100 Subject: [PATCH 053/311] Turn off fail-fast for the CI tests action. --- .github/workflows/tests.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 36238058..7760ad69 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -5,6 +5,7 @@ on: [push, pull_request] jobs: test: strategy: + fail-fast: false matrix: ruby: [2.4, 2.5, 2.6, 2.7, '3.0', jruby, truffleruby] can-fail: [false] From 65886ac875cf060bddc535faf1d741fa86cb261e Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Mon, 17 May 2021 12:49:49 +0100 Subject: [PATCH 054/311] Add GitHub actions badges to the README. --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index fe1fd79d..a2f59025 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ # rubyzip [![Gem Version](https://badge.fury.io/rb/rubyzip.svg)](http://badge.fury.io/rb/rubyzip) +[![Tests](https://github.com/rubyzip/rubyzip/actions/workflows/tests.yml/badge.svg)](https://github.com/rubyzip/rubyzip/actions/workflows/tests.yml) +[![Linter](https://github.com/rubyzip/rubyzip/actions/workflows/lint.yml/badge.svg)](https://github.com/rubyzip/rubyzip/actions/workflows/lint.yml) [![Build Status](https://secure.travis-ci.org/rubyzip/rubyzip.svg)](http://travis-ci.org/rubyzip/rubyzip) [![Code Climate](https://codeclimate.com/github/rubyzip/rubyzip.svg)](https://codeclimate.com/github/rubyzip/rubyzip) [![Coverage Status](https://img.shields.io/coveralls/rubyzip/rubyzip.svg)](https://coveralls.io/r/rubyzip/rubyzip?branch=master) From 6e9f2976d165ab33621a1f65d223172871c8ac81 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sun, 16 May 2021 16:47:12 +0100 Subject: [PATCH 055/311] Add temporary fix for JRuby to workaround Time cmp bug. Workaround jruby/jruby#6668 until fix is released. Version 9.2.18.0 is hopefully the version that will fix this, but we can adjust the version accordingly if not. --- lib/zip/dos_time.rb | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/lib/zip/dos_time.rb b/lib/zip/dos_time.rb index 326645ad..54907a21 100644 --- a/lib/zip/dos_time.rb +++ b/lib/zip/dos_time.rb @@ -1,3 +1,5 @@ +require 'rubygems' + module Zip class DOSTime < Time #:nodoc:all # MS-DOS File Date and Time format as used in Interrupt 21H Function 57H: @@ -50,6 +52,32 @@ def self.parse_binary_dos_format(bin_dos_date, bin_dos_time) local(year, month, day, hour, minute, second) end end + + if defined? JRUBY_VERSION && Gem::Version.new(JRUBY_VERSION) < '9.2.18.0' + module JRubyCMP # :nodoc: + def ==(other) + (self <=> other).zero? + end + + def <(other) + (self <=> other).negative? + end + + def <=(other) + (self <=> other) <= 0 + end + + def >(other) + (self <=> other).positive? + end + + def >=(other) + (self <=> other) >= 0 + end + end + + include JRubyCMP + end end end From 4ed35cae9435307cff5ef3558ce8ce7880f46212 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Mon, 17 May 2021 19:57:16 +0100 Subject: [PATCH 056/311] DosTime#<=> should return `nil` if other is not comparable. --- lib/zip/dos_time.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/zip/dos_time.rb b/lib/zip/dos_time.rb index 54907a21..51139dde 100644 --- a/lib/zip/dos_time.rb +++ b/lib/zip/dos_time.rb @@ -33,6 +33,8 @@ def dos_equals(other) # Dos time is only stored with two seconds accuracy. def <=>(other) + return unless other.kind_of?(Time) + (to_i / 2) <=> (other.to_i / 2) end From 5c8bb1c4a062e3869d23fe064d29c1a2e8d8dbc7 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Mon, 17 May 2021 20:21:00 +0100 Subject: [PATCH 057/311] Update CI information in the README. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a2f59025..75763bc3 100644 --- a/README.md +++ b/README.md @@ -349,7 +349,7 @@ rake Please also run `rubocop` over your changes. -Our CI is here: https://travis-ci.org/github/rubyzip/rubyzip. Please note that `rubocop` is run as part of the CI configuration and will fail a build if errors are found. +Our CI currently runs on [Travis](https://travis-ci.org/github/rubyzip/rubyzip) and [GitHub Actions](https://github.com/rubyzip/rubyzip/actions). Please note that `rubocop` is run as part of the CI configuration and will fail a build if errors are found. ## Website and Project Home From 1f5aa847389ba189112a3652ed94a8dad74931dd Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Mon, 17 May 2021 20:21:24 +0100 Subject: [PATCH 058/311] Update maintainer information in the README. --- README.md | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/README.md b/README.md index 75763bc3..f465ff3b 100644 --- a/README.md +++ b/README.md @@ -361,16 +361,11 @@ http://rdoc.info/github/rubyzip/rubyzip/master/frames See https://github.com/rubyzip/rubyzip/graphs/contributors for a comprehensive list. -### Current contributors +### Current maintainers * Robert Haines (@hainesr) * John Lees-Miller (@jdleesmiller) - -### Past contributors - -* Pavel Lobashov (@ShockwaveNN) * Oleksandr Simonov (@simonoff) -* Alan Harper (@aussiegeek) ### Original author From 2b2e0ee56853f9ced9bfee3e8f7eee1ffcae5a68 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Tue, 18 May 2021 19:23:56 +0100 Subject: [PATCH 059/311] Bump version to 3.0.0. There are breaking changes in the recent PRs that have been merged. --- lib/zip/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/zip/version.rb b/lib/zip/version.rb index 0b20c214..55d4ac29 100644 --- a/lib/zip/version.rb +++ b/lib/zip/version.rb @@ -1,3 +1,3 @@ module Zip - VERSION = '2.3.0' + VERSION = '3.0.0' end From 25795c7b0e6c9918d09ceb5420c1b6f9fdc26780 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Tue, 18 May 2021 19:24:47 +0100 Subject: [PATCH 060/311] Update Changelog with some recent merged PRs. --- Changelog.md | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index 5a1bbe12..3a88ab3e 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,10 +1,21 @@ -# X.X.X (Next) +# 3.0.0 (Next) +- Replace and deprecate `Zip::DOSTime#dos_equals`. [#464](https://github.com/rubyzip/rubyzip/pull/464) +- Fix input stream partial read error. [#462](https://github.com/rubyzip/rubyzip/pull/462) +- Fix loading extra fields. [#459](https://github.com/rubyzip/rubyzip/pull/459) - Set compression level on a per-zipfile basis. [#448](https://github.com/rubyzip/rubyzip/pull/448) - Fix input stream partial read error. [#462](https://github.com/rubyzip/rubyzip/pull/462) +- Fix zlib deflate buffer growth. [#447](https://github.com/rubyzip/rubyzip/pull/447) Tooling: +- Add GitHub Actions CI infrastructure. [#469](https://github.com/rubyzip/rubyzip/issues/469) +- Add Ruby 3.0 to CI. [#474](https://github.com/rubyzip/rubyzip/pull/474) +- Fix the compression level tests to compare relative sizes. [#473](https://github.com/rubyzip/rubyzip/pull/473) +- Simplify assertions in basic_zip_file_test. [#470](https://github.com/rubyzip/rubyzip/pull/470) +- Remove compare_enumerables from test_helper.rb. [#468](https://github.com/rubyzip/rubyzip/pull/468) +- Use correct SPDX license identifier. [#458](https://github.com/rubyzip/rubyzip/pull/458) +- Enable truffle ruby in Travis CI. [#450](https://github.com/rubyzip/rubyzip/pull/450) - Update rubocop again and run it in CI. [#444](https://github.com/rubyzip/rubyzip/pull/444) - Fix a test that was incorrect on big-endian architectures. [#445](https://github.com/rubyzip/rubyzip/pull/445) From 34237efc002d66b0a33c09998b2d4e38bb3f7507 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Tue, 18 May 2021 19:51:06 +0100 Subject: [PATCH 061/311] Ensure that `Entry#time=` sets times as `DOSTime` objects. Fixes #481. --- Changelog.md | 1 + lib/zip/entry.rb | 2 ++ test/entry_test.rb | 9 +++++++++ 3 files changed, 12 insertions(+) diff --git a/Changelog.md b/Changelog.md index 3a88ab3e..24d6e3e1 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,5 +1,6 @@ # 3.0.0 (Next) +- Ensure that `Entry#time=` sets times as `DOSTime` objects. [#481](https://github.com/rubyzip/rubyzip/issues/481) - Replace and deprecate `Zip::DOSTime#dos_equals`. [#464](https://github.com/rubyzip/rubyzip/pull/464) - Fix input stream partial read error. [#462](https://github.com/rubyzip/rubyzip/pull/462) - Fix loading extra fields. [#459](https://github.com/rubyzip/rubyzip/pull/459) diff --git a/lib/zip/entry.rb b/lib/zip/entry.rb index e6875e3e..9f658723 100644 --- a/lib/zip/entry.rb +++ b/lib/zip/entry.rb @@ -121,6 +121,8 @@ def time=(value) unless @extra.member?('UniversalTime') || @extra.member?('NTFS') @extra.create('UniversalTime') end + + value = DOSTime.from_time(value) (@extra['UniversalTime'] || @extra['NTFS']).mtime = value @time = value end diff --git a/test/entry_test.rb b/test/entry_test.rb index dda0a7ba..36390e87 100644 --- a/test/entry_test.rb +++ b/test/entry_test.rb @@ -254,4 +254,13 @@ def test_compression_method_reader ) assert_equal(Zip::Entry::STORED, entry.compression_method) end + + def test_set_time_as_dos_time + entry = ::Zip::Entry.new + assert(entry.time.kind_of?(::Zip::DOSTime)) + entry.time = Time.now + assert(entry.time.kind_of?(::Zip::DOSTime)) + entry.time = ::Zip::DOSTime.now + assert(entry.time.kind_of?(::Zip::DOSTime)) + end end From bae056efb4def85c3cc1cba65bb1a7d603cfb928 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Tue, 18 May 2021 21:12:49 +0100 Subject: [PATCH 062/311] Optimise the GitHub Actions tests workflow. --- .github/workflows/tests.yml | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 7760ad69..c67d988d 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -7,17 +7,9 @@ jobs: strategy: fail-fast: false matrix: - ruby: [2.4, 2.5, 2.6, 2.7, '3.0', jruby, truffleruby] - can-fail: [false] - include: - - ruby: head - can-fail: true - - ruby: jruby-head - can-fail: true - - ruby: truffleruby-head - can-fail: true + ruby: [2.4, 2.5, 2.6, 2.7, '3.0', head, jruby, jruby-head, truffleruby, truffleruby-head] runs-on: ubuntu-latest - continue-on-error: ${{ matrix.can-fail }} + continue-on-error: ${{ endsWith(matrix.ruby, 'head') }} steps: - uses: actions/checkout@v2 - uses: ruby/setup-ruby@v1 From df9d39730eeb042b1935a9ea5a0c41b4e718062e Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Tue, 18 May 2021 21:21:41 +0100 Subject: [PATCH 063/311] Add macos and windows tests to Actions. Just one run of each for now should be enough. Allow windows tests to fail for now as our tests are broken there at the moment. --- .github/workflows/tests.yml | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index c67d988d..fec5899d 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -7,9 +7,15 @@ jobs: strategy: fail-fast: false matrix: + os: [ubuntu] ruby: [2.4, 2.5, 2.6, 2.7, '3.0', head, jruby, jruby-head, truffleruby, truffleruby-head] - runs-on: ubuntu-latest - continue-on-error: ${{ endsWith(matrix.ruby, 'head') }} + include: + - os: macos + ruby: 2.4 + - os: windows + ruby: 2.4 + runs-on: ${{ matrix.os }}-latest + continue-on-error: ${{ endsWith(matrix.ruby, 'head') || matrix.os == 'windows' }} steps: - uses: actions/checkout@v2 - uses: ruby/setup-ruby@v1 From 8702876e552c7b8b658ee0eadf4a6d4380547a9d Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Tue, 18 May 2021 21:55:03 +0100 Subject: [PATCH 064/311] Set the default `Entry` time to the file's mtime on Windows. For some reason this was being skipped on Windows, but not Linux or MacOS. --- Changelog.md | 1 + lib/zip/entry.rb | 6 +++--- test/entry_test.rb | 6 ++++++ 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/Changelog.md b/Changelog.md index 24d6e3e1..36c4b7c6 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,5 +1,6 @@ # 3.0.0 (Next) +- Set the default `Entry` time to the file's mtime on Windows. [#465](https://github.com/rubyzip/rubyzip/issues/465) - Ensure that `Entry#time=` sets times as `DOSTime` objects. [#481](https://github.com/rubyzip/rubyzip/issues/481) - Replace and deprecate `Zip::DOSTime#dos_equals`. [#464](https://github.com/rubyzip/rubyzip/pull/464) - Fix input stream partial read error. [#462](https://github.com/rubyzip/rubyzip/pull/462) diff --git a/lib/zip/entry.rb b/lib/zip/entry.rb index 9f658723..0b13470d 100644 --- a/lib/zip/entry.rb +++ b/lib/zip/entry.rb @@ -442,13 +442,13 @@ def file_stat(path) # :nodoc: end def get_extra_attributes_from_path(path) # :nodoc: - return if Zip::RUNNING_ON_WINDOWS + stat = file_stat(path) + @time = DOSTime.from_time(stat.mtime) + return if ::Zip::RUNNING_ON_WINDOWS - stat = file_stat(path) @unix_uid = stat.uid @unix_gid = stat.gid @unix_perms = stat.mode & 0o7777 - @time = ::Zip::DOSTime.from_time(stat.mtime) end def set_unix_attributes_on_path(dest_path) diff --git a/test/entry_test.rb b/test/entry_test.rb index 36390e87..e6ac7a4b 100644 --- a/test/entry_test.rb +++ b/test/entry_test.rb @@ -263,4 +263,10 @@ def test_set_time_as_dos_time entry.time = ::Zip::DOSTime.now assert(entry.time.kind_of?(::Zip::DOSTime)) end + + def test_ensure_entry_time_set_to_file_mtime + entry = ::Zip::Entry.new + entry.gather_fileinfo_from_srcpath('test/data/mimetype') + assert_equal(entry.time, File.stat('test/data/mimetype').mtime) + end end From 43d99840441c4bb4a4fcc9966c24bc6e3d5d4315 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Tue, 18 May 2021 22:43:22 +0100 Subject: [PATCH 065/311] Install zip for the Windows CI test Action. And remove hardcoded paths for zip in the tests. --- .github/workflows/tests.yml | 15 ++++++++++++--- test/gentestfiles.rb | 14 +++++++------- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index fec5899d..a8794f8c 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -17,9 +17,18 @@ jobs: runs-on: ${{ matrix.os }}-latest continue-on-error: ${{ endsWith(matrix.ruby, 'head') || matrix.os == 'windows' }} steps: - - uses: actions/checkout@v2 - - uses: ruby/setup-ruby@v1 + - name: Checkout rubyzip code + uses: actions/checkout@v2 + + - name: Install and set up ruby + uses: ruby/setup-ruby@v1 with: ruby-version: ${{ matrix.ruby }} bundler-cache: true - - run: bundle exec rake + + - name: Install other dependencies + if: matrix.os == 'windows' + run: choco install zip + + - name: Run the tests + run: bundle exec rake diff --git a/test/gentestfiles.rb b/test/gentestfiles.rb index 503a0d00..bb1b981d 100755 --- a/test/gentestfiles.rb +++ b/test/gentestfiles.rb @@ -73,10 +73,10 @@ def initialize(zip_name, entry_names, comment = '') def self.create_test_zips raise "failed to create test zip '#{TEST_ZIP1.zip_name}'" \ - unless system("/usr/bin/zip -q #{TEST_ZIP1.zip_name} test/data/file2.txt") + unless system("zip -q #{TEST_ZIP1.zip_name} test/data/file2.txt") raise "failed to remove entry from '#{TEST_ZIP1.zip_name}'" \ unless system( - "/usr/bin/zip -q #{TEST_ZIP1.zip_name} -d test/data/file2.txt" + "zip -q #{TEST_ZIP1.zip_name} -d test/data/file2.txt" ) File.open('test/data/generated/empty.txt', 'w') {} @@ -102,31 +102,31 @@ def self.create_test_zips raise "failed to create test zip '#{TEST_ZIP2.zip_name}'" \ unless system( - "/usr/bin/zip -q #{TEST_ZIP2.zip_name} #{TEST_ZIP2.entry_names.join(' ')}" + "zip -q #{TEST_ZIP2.zip_name} #{TEST_ZIP2.entry_names.join(' ')}" ) if RUBY_PLATFORM =~ /mswin|mingw|cygwin/ raise "failed to add comment to test zip '#{TEST_ZIP2.zip_name}'" \ unless system( - "echo #{TEST_ZIP2.comment}| /usr/bin/zip -zq #{TEST_ZIP2.zip_name}\"" + "echo #{TEST_ZIP2.comment}| zip -zq #{TEST_ZIP2.zip_name}\"" ) else # without bash system interprets everything after echo as parameters to # echo including | zip -z ... raise "failed to add comment to test zip '#{TEST_ZIP2.zip_name}'" \ unless system( - "bash -c \"echo #{TEST_ZIP2.comment} | /usr/bin/zip -zq #{TEST_ZIP2.zip_name}\"" + "bash -c \"echo #{TEST_ZIP2.comment} | zip -zq #{TEST_ZIP2.zip_name}\"" ) end raise "failed to create test zip '#{TEST_ZIP3.zip_name}'" \ unless system( - "/usr/bin/zip -q #{TEST_ZIP3.zip_name} #{TEST_ZIP3.entry_names.join(' ')}" + "zip -q #{TEST_ZIP3.zip_name} #{TEST_ZIP3.entry_names.join(' ')}" ) raise "failed to create test zip '#{TEST_ZIP4.zip_name}'" \ unless system( - "/usr/bin/zip -q #{TEST_ZIP4.zip_name} #{TEST_ZIP4.entry_names.join(' ')}" + "zip -q #{TEST_ZIP4.zip_name} #{TEST_ZIP4.entry_names.join(' ')}" ) rescue StandardError # If there are any Windows developers wanting to use a command line zip.exe From 3958039497e8bdcb159e6156f7670c78ee268988 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Tue, 18 May 2021 23:12:51 +0100 Subject: [PATCH 066/311] Name the steps in the linter Action. To match those of the tests Action. --- .github/workflows/lint.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index fc6b09bd..dccec7f3 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -6,10 +6,14 @@ jobs: lint: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - uses: ruby/setup-ruby@v1 + - name: Checkout rubyzip code + uses: actions/checkout@v2 + + - name: Install and set up ruby + uses: ruby/setup-ruby@v1 with: ruby-version: 2.4 bundler-cache: true + - name: Rubocop run: bundle exec rubocop From db3ce93027bdfb6718d127a5bd50224211bf8a4a Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Wed, 19 May 2021 21:39:53 +0100 Subject: [PATCH 067/311] Add coveralls integration to the GitHub CI Action. Fixes #480. --- .github/workflows/tests.yml | 18 ++++++++++++++++++ .simplecov | 15 ++++++++++++--- rubyzip.gemspec | 3 ++- 3 files changed, 32 insertions(+), 4 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index a8794f8c..467bc0f2 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -32,3 +32,21 @@ jobs: - name: Run the tests run: bundle exec rake + + - name: Coveralls + if: matrix.os == 'ubuntu' && !endsWith(matrix.ruby, 'head') + uses: coverallsapp/github-action@master + with: + github-token: ${{ secrets.github_token }} + flag-name: ${{ matrix.ruby }} + parallel: true + + finish: + needs: test + runs-on: ubuntu-latest + steps: + - name: Coveralls Finished + uses: coverallsapp/github-action@master + with: + github-token: ${{ secrets.github_token }} + parallel-finished: true diff --git a/.simplecov b/.simplecov index 770d7dc6..fc3979d2 100644 --- a/.simplecov +++ b/.simplecov @@ -1,9 +1,18 @@ -require 'coveralls' +require 'simplecov-lcov' + +SimpleCov::Formatter::LcovFormatter.config do |c| + c.output_directory = 'coverage' + c.lcov_file_name = 'lcov.info' + c.report_with_single_file = true + c.single_report_path = 'coverage/lcov.info' +end SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter.new([ SimpleCov::Formatter::HTMLFormatter, - Coveralls::SimpleCov::Formatter + SimpleCov::Formatter::LcovFormatter ]) + SimpleCov.start do - add_filter '/test' + # enable_coverage :branch <-- Re-enable this when we move to ruby ~> 2.5. + add_filter ['/test/', '/samples/'] end diff --git a/rubyzip.gemspec b/rubyzip.gemspec index 67de78dc..da062710 100644 --- a/rubyzip.gemspec +++ b/rubyzip.gemspec @@ -21,9 +21,10 @@ Gem::Specification.new do |s| 'wiki_uri' => 'https://github.com/rubyzip/rubyzip/wiki' } s.required_ruby_version = '>= 2.4' - s.add_development_dependency 'coveralls', '~> 0.7' s.add_development_dependency 'minitest', '~> 5.4' s.add_development_dependency 'pry', '~> 0.10' s.add_development_dependency 'rake', '~> 12.3', '>= 12.3.3' s.add_development_dependency 'rubocop', '~> 0.80.1' + s.add_development_dependency 'simplecov', '~> 0.18.0' + s.add_development_dependency 'simplecov-lcov', '~> 0.8' end From f0b50d3c6c7e54bbfeea7486c3a20eb39706bd86 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Wed, 19 May 2021 22:23:54 +0100 Subject: [PATCH 068/311] Add JRUBY_OPTS=--debug to the CI environment. --- .github/workflows/tests.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 467bc0f2..bc4c0710 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -31,6 +31,8 @@ jobs: run: choco install zip - name: Run the tests + env: + JRUBY_OPTS: --debug run: bundle exec rake - name: Coveralls From af716bef324fa0935aedb80b2124ecbd91beb5c3 Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Tue, 25 May 2021 18:03:28 +0200 Subject: [PATCH 069/311] Refactor assert_forwarded so it does not need ObjectSpace._id2ref or eval --- test/test_helper.rb | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/test/test_helper.rb b/test/test_helper.rb index d4e52354..a42ecbcf 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -182,22 +182,21 @@ def setup module ExtraAssertions def assert_forwarded(object, method, ret_val, *expected_args) call_args = nil - call_args_proc = proc { |args| call_args = args } - object.instance_eval <<-END_EVAL, __FILE__, __LINE__ + 1 - alias #{method}_org #{method} - def #{method}(*args) - ObjectSpace._id2ref(#{call_args_proc.object_id}).call(args) - ObjectSpace._id2ref(#{ret_val.object_id}) - end - END_EVAL + object.singleton_class.class_exec do + alias_method :"#{method}_org", method + define_method(method) do |*args| + call_args = args + ret_val + end + end assert_equal(ret_val, yield) # Invoke test assert_equal(expected_args, call_args) ensure - object.instance_eval <<-END_EVAL, __FILE__, __LINE__ + 1 - undef #{method} - alias #{method} #{method}_org - END_EVAL + object.singleton_class.class_exec do + remove_method method + alias_method method, :"#{method}_org" + end end end From 3b3b932f2dff55445d607221118ec10adf18dfbe Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Tue, 25 May 2021 19:53:42 +0100 Subject: [PATCH 070/311] No longer need to turn on `objectspace` in JRuby. --- test/test_helper.rb | 5 ----- 1 file changed, 5 deletions(-) diff --git a/test/test_helper.rb b/test/test_helper.rb index a42ecbcf..35a5085c 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -10,11 +10,6 @@ TestFiles.create_test_files TestZipFile.create_test_zips -if defined? JRUBY_VERSION - require 'jruby' - JRuby.objectspace = true -end - ::MiniTest.after_run do FileUtils.rm_rf('test/data/generated') end From cb69bd520ff2734f2becc47e8fce21c1182ca3e4 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Tue, 25 May 2021 19:58:08 +0100 Subject: [PATCH 071/311] Update Changelog. --- Changelog.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Changelog.md b/Changelog.md index 36c4b7c6..293acfb3 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,5 +1,6 @@ # 3.0.0 (Next) +- Fix frozen string literal error. [#475](https://github.com/rubyzip/rubyzip/pull/475) - Set the default `Entry` time to the file's mtime on Windows. [#465](https://github.com/rubyzip/rubyzip/issues/465) - Ensure that `Entry#time=` sets times as `DOSTime` objects. [#481](https://github.com/rubyzip/rubyzip/issues/481) - Replace and deprecate `Zip::DOSTime#dos_equals`. [#464](https://github.com/rubyzip/rubyzip/pull/464) @@ -11,6 +12,7 @@ Tooling: +- Refactor `assert_forwarded` so it does not need `ObjectSpace._id2ref` or `eval`. [#483](https://github.com/rubyzip/rubyzip/pull/483) - Add GitHub Actions CI infrastructure. [#469](https://github.com/rubyzip/rubyzip/issues/469) - Add Ruby 3.0 to CI. [#474](https://github.com/rubyzip/rubyzip/pull/474) - Fix the compression level tests to compare relative sizes. [#473](https://github.com/rubyzip/rubyzip/pull/473) From f1e73b047eb62f4eaed4d3e6a4b18b233babc42c Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sun, 23 May 2021 17:14:30 +0100 Subject: [PATCH 072/311] Tidy up dependencies in gemspec. --- rubyzip.gemspec | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/rubyzip.gemspec b/rubyzip.gemspec index da062710..e6d4c5bf 100644 --- a/rubyzip.gemspec +++ b/rubyzip.gemspec @@ -22,8 +22,7 @@ Gem::Specification.new do |s| } s.required_ruby_version = '>= 2.4' s.add_development_dependency 'minitest', '~> 5.4' - s.add_development_dependency 'pry', '~> 0.10' - s.add_development_dependency 'rake', '~> 12.3', '>= 12.3.3' + s.add_development_dependency 'rake', '~> 12.3.3' s.add_development_dependency 'rubocop', '~> 0.80.1' s.add_development_dependency 'simplecov', '~> 0.18.0' s.add_development_dependency 'simplecov-lcov', '~> 0.8' From 3d33e4a8e0f9f70e29ecee4008a75814364fb524 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sun, 23 May 2021 17:44:36 +0100 Subject: [PATCH 073/311] Update Rubocop version. Now using as late a version as we can for Ruby 2.4. --- .rubocop.yml | 3 +- .rubocop_todo.yml | 186 +++++++++++++++++++++++++++++++++++++++------- rubyzip.gemspec | 2 +- 3 files changed, 162 insertions(+), 29 deletions(-) diff --git a/.rubocop.yml b/.rubocop.yml index ef285c5e..ba47414b 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -4,6 +4,7 @@ inherit_from: .rubocop_todo.yml # we get errors if our ruby version doesn't match. AllCops: TargetRubyVersion: 2.4 + NewCops: enable Layout/HashAlignment: EnforcedHashRocketStyle: table @@ -24,7 +25,7 @@ Lint/SuppressedException: - 'test/**/*.rb' # Allow this "useless" test, as we are testing <=> here. -Lint/UselessComparison: +Lint/BinaryOperatorWithIdenticalOperands: Exclude: - 'test/entry_test.rb' diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 0a045c53..7e98e30b 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -1,31 +1,84 @@ # This configuration was generated by # `rubocop --auto-gen-config` -# on 2020-03-14 17:50:29 +0000 using RuboCop version 0.80.0. +# on 2021-05-23 16:31:53 UTC using RuboCop version 1.12.1. # The point is for the user to remove these configuration records # one by one as the offenses are removed from the code base. # Note that changes in the inspected code, or installation of new # versions of RuboCop, may require this file to be generated again. +# Offense count: 8 +# Cop supports --auto-correct. +# Configuration parameters: EmptyLineBetweenMethodDefs, EmptyLineBetweenClassDefs, EmptyLineBetweenModuleDefs, AllowAdjacentOneLineDefs, NumberOfEmptyLines. +Layout/EmptyLineBetweenDefs: + Exclude: + - 'lib/zip/errors.rb' + +# Offense count: 3 +# Cop supports --auto-correct. +# Configuration parameters: AllowAliasSyntax, AllowedMethods. +# AllowedMethods: alias_method, public, protected, private +Layout/EmptyLinesAroundAttributeAccessor: + Exclude: + - 'lib/zip/extra_field/zip64.rb' + - 'lib/zip/filesystem.rb' + - 'samples/gtk_ruby_zip.rb' + +# Offense count: 2 +# Cop supports --auto-correct. +# Configuration parameters: IndentationWidth. +# SupportedStyles: special_inside_parentheses, consistent, align_brackets +Layout/FirstArrayElementIndentation: + EnforcedStyle: consistent + +# Offense count: 5 +# Configuration parameters: AllowComments, AllowEmptyLambdas. +Lint/EmptyBlock: + Exclude: + - 'lib/zip/file.rb' + - 'test/gentestfiles.rb' + - 'test/settings_test.rb' + +# Offense count: 3 +# Configuration parameters: AllowComments. +Lint/EmptyClass: + Exclude: + - 'lib/zip/crypto/encryption.rb' + - 'test/decompressor_test.rb' + +# Offense count: 7 +Lint/MissingSuper: + Exclude: + - 'lib/zip/extra_field.rb' + - 'lib/zip/extra_field/ntfs.rb' + - 'lib/zip/extra_field/old_unix.rb' + - 'lib/zip/extra_field/universal_time.rb' + - 'lib/zip/extra_field/unix.rb' + - 'lib/zip/extra_field/zip64.rb' + - 'lib/zip/extra_field/zip64_placeholder.rb' + # Offense count: 5 -# Configuration parameters: CountComments. +# Configuration parameters: CountComments, CountAsOne. Metrics/ClassLength: - Max: 610 + Max: 601 -# Offense count: 26 +# Offense count: 20 +# Configuration parameters: IgnoredMethods. Metrics/CyclomaticComplexity: - Max: 15 + Max: 14 -# Offense count: 44 -# Configuration parameters: CountComments, ExcludedMethods. +# Offense count: 46 +# Configuration parameters: CountComments, CountAsOne, ExcludedMethods, IgnoredMethods. Metrics/MethodLength: Max: 32 -# Offense count: 2 +# Offense count: 5 # Configuration parameters: CountKeywordArgs. Metrics/ParameterLists: - Max: 12 + Max: 11 + MaxOptionalParameters: 9 -# Offense count: 20 +# Offense count: 14 +# Configuration parameters: IgnoredMethods. Metrics/PerceivedComplexity: Max: 15 @@ -37,18 +90,27 @@ Naming/AccessorMethodName: - 'lib/zip/input_stream.rb' - 'lib/zip/streamable_stream.rb' -# Offense count: 7 +# Offense count: 4 +# Configuration parameters: EnforcedStyle, CheckMethodNames, CheckSymbols, AllowedIdentifiers. +# SupportedStyles: snake_case, normalcase, non_integer +# AllowedIdentifiers: capture3, iso8601, rfc1123_date, rfc822, rfc2822, rfc3339 +Naming/VariableNumber: + Exclude: + - 'test/file_extract_test.rb' + - 'test/file_permissions_test.rb' + +# Offense count: 4 +# Cop supports --auto-correct. # Configuration parameters: EnforcedStyle. -# SupportedStyles: inline, group -Style/AccessModifierDeclarations: +# SupportedStyles: separated, grouped +Style/AccessorGrouping: Exclude: - - 'lib/zip/central_directory.rb' - - 'lib/zip/extra_field/zip64.rb' - - 'lib/zip/filesystem.rb' + - 'lib/zip/decompressor.rb' + - 'lib/zip/entry.rb' # Offense count: 7 # Cop supports --auto-correct. -# Configuration parameters: AutoCorrect, EnforcedStyle. +# Configuration parameters: EnforcedStyle. # SupportedStyles: nested, compact Style/ClassAndModuleChildren: Exclude: @@ -60,26 +122,50 @@ Style/ClassAndModuleChildren: - 'lib/zip/extra_field/zip64.rb' - 'lib/zip/extra_field/zip64_placeholder.rb' -# Offense count: 25 +# Offense count: 2 +# Cop supports --auto-correct. +# Configuration parameters: Keywords. +# Keywords: TODO, FIXME, OPTIMIZE, HACK, REVIEW, NOTE +Style/CommentAnnotation: + Exclude: + - 'test/file_test.rb' + - 'test/zip64_full_test.rb' + +# Offense count: 1 +# Cop supports --auto-correct. +Style/Dir: + Exclude: + - 'test/file_test.rb' + +# Offense count: 2 +Style/DocumentDynamicEvalDefinition: + Exclude: + - 'test/test_helper.rb' + +# Offense count: 24 +# Configuration parameters: AllowedConstants. Style/Documentation: Enabled: false -# Offense count: 3 -# Configuration parameters: . +# Offense count: 2 +# Configuration parameters: MaxUnannotatedPlaceholdersAllowed, IgnoredMethods. # SupportedStyles: annotated, template, unannotated Style/FormatStringToken: EnforcedStyle: unannotated -# Offense count: 96 +# Offense count: 97 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle. # SupportedStyles: always, always_true, never Style/FrozenStringLiteralComment: Enabled: false -# This one has to be off until our base ruby is at least 2.5. -Style/HashTransformKeys: - Enabled: false +# Offense count: 1 +# Cop supports --auto-correct. +# Configuration parameters: AllowSplatArgument. +Style/HashConversion: + Exclude: + - 'lib/zip/entry_set.rb' # Offense count: 13 # Cop supports --auto-correct. @@ -94,7 +180,7 @@ Style/IfUnlessModifier: # Offense count: 1 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, Autocorrect. -# SupportedStyles: module_function, extend_self +# SupportedStyles: module_function, extend_self, forbidden Style/ModuleFunction: Exclude: - 'lib/zip.rb' @@ -106,9 +192,15 @@ Style/ModuleFunction: Style/MutableConstant: Enabled: false -# Offense count: 23 +# Offense count: 2 # Cop supports --auto-correct. -# Configuration parameters: AutoCorrect, EnforcedStyle, IgnoredMethods. +Style/NegatedIfElseCondition: + Exclude: + - 'samples/qtzip.rb' + +# Offense count: 24 +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle, IgnoredMethods. # SupportedStyles: predicate, comparison Style/NumericPredicate: Exclude: @@ -125,6 +217,35 @@ Style/NumericPredicate: - 'test/file_split_test.rb' - 'test/test_helper.rb' +# Offense count: 6 +# Configuration parameters: AllowedMethods. +# AllowedMethods: respond_to_missing? +Style/OptionalBooleanParameter: + Exclude: + - 'lib/zip/entry.rb' + - 'lib/zip/file.rb' + - 'lib/zip/output_stream.rb' + +# Offense count: 1 +# Cop supports --auto-correct. +Style/RedundantBegin: + Exclude: + - 'lib/zip/dos_time.rb' + +# Offense count: 1 +# Cop supports --auto-correct. +Style/RedundantFileExtensionInRequire: + Exclude: + - 'samples/qtzip.rb' + +# Offense count: 29 +# Cop supports --auto-correct. +Style/RedundantRegexpEscape: + Exclude: + - 'Guardfile' + - 'test/file_extract_test.rb' + - 'test/path_traversal_test.rb' + # Offense count: 17 # Cop supports --auto-correct. # Configuration parameters: ConvertCodeThatCanStartToReturnNil, AllowedMethods. @@ -138,3 +259,14 @@ Style/SafeNavigation: - 'test/filesystem/file_nonmutating_test.rb' - 'test/filesystem/file_stat_test.rb' - 'test/test_helper.rb' + +# Offense count: 9 +# Cop supports --auto-correct. +Style/StringConcatenation: + Exclude: + - 'lib/zip/filesystem.rb' + - 'samples/gtk_ruby_zip.rb' + - 'test/encryption_test.rb' + - 'test/file_test.rb' + - 'test/ioextras/abstract_input_stream_test.rb' + - 'test/test_helper.rb' diff --git a/rubyzip.gemspec b/rubyzip.gemspec index e6d4c5bf..f858ee80 100644 --- a/rubyzip.gemspec +++ b/rubyzip.gemspec @@ -23,7 +23,7 @@ Gem::Specification.new do |s| s.required_ruby_version = '>= 2.4' s.add_development_dependency 'minitest', '~> 5.4' s.add_development_dependency 'rake', '~> 12.3.3' - s.add_development_dependency 'rubocop', '~> 0.80.1' + s.add_development_dependency 'rubocop', '~> 1.12.0' s.add_development_dependency 'simplecov', '~> 0.18.0' s.add_development_dependency 'simplecov-lcov', '~> 0.8' end From 6f929b603f50c6125cd71bdb33cd527403bbda38 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sun, 23 May 2021 17:58:18 +0100 Subject: [PATCH 074/311] Configure Layout/EmptyLineBetweenDefs cop. --- .rubocop.yml | 5 +++++ .rubocop_todo.yml | 7 ------- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/.rubocop.yml b/.rubocop.yml index ba47414b..a51a3608 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -6,6 +6,11 @@ AllCops: TargetRubyVersion: 2.4 NewCops: enable +# Allow this in this file because adding the extra lines is pointless. +Layout/EmptyLineBetweenDefs: + Exclude: + - 'lib/zip/errors.rb' + Layout/HashAlignment: EnforcedHashRocketStyle: table EnforcedColonStyle: table diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 7e98e30b..99324285 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -6,13 +6,6 @@ # Note that changes in the inspected code, or installation of new # versions of RuboCop, may require this file to be generated again. -# Offense count: 8 -# Cop supports --auto-correct. -# Configuration parameters: EmptyLineBetweenMethodDefs, EmptyLineBetweenClassDefs, EmptyLineBetweenModuleDefs, AllowAdjacentOneLineDefs, NumberOfEmptyLines. -Layout/EmptyLineBetweenDefs: - Exclude: - - 'lib/zip/errors.rb' - # Offense count: 3 # Cop supports --auto-correct. # Configuration parameters: AllowAliasSyntax, AllowedMethods. From 0e4dc676a0c447537d233b5738da7ecddc26e12e Mon Sep 17 00:00:00 2001 From: Taichi Ishitani Date: Sat, 6 Mar 2021 09:36:09 +0900 Subject: [PATCH 075/311] fix frozen string literal error --- lib/zip/extra_field/ntfs.rb | 2 +- lib/zip/extra_field/zip64.rb | 2 +- lib/zip/file.rb | 2 +- lib/zip/filesystem.rb | 4 ++-- lib/zip/inflater.rb | 2 +- lib/zip/ioextras.rb | 4 ++-- lib/zip/ioextras/abstract_input_stream.rb | 6 +++--- lib/zip/pass_thru_decompressor.rb | 2 +- test/encryption_test.rb | 2 +- test/extra_field_test.rb | 2 +- test/file_test.rb | 2 +- test/input_stream_test.rb | 4 ++-- test/ioextras/abstract_output_stream_test.rb | 14 +++++++------- test/output_stream_test.rb | 4 ++-- test/test_helper.rb | 2 +- 15 files changed, 27 insertions(+), 27 deletions(-) diff --git a/lib/zip/extra_field/ntfs.rb b/lib/zip/extra_field/ntfs.rb index f4f11b2d..f80b5078 100644 --- a/lib/zip/extra_field/ntfs.rb +++ b/lib/zip/extra_field/ntfs.rb @@ -51,7 +51,7 @@ def pack_for_c_dir # reserved 0 and tag 1 s = [0, 1].pack('Vv') - tag1 = ''.force_encoding(Encoding::BINARY) + tag1 = (+'').force_encoding(Encoding::BINARY) if @mtime tag1 << [to_ntfs_time(@mtime)].pack('Q<') if @atime diff --git a/lib/zip/extra_field/zip64.rb b/lib/zip/extra_field/zip64.rb index 9826c6cf..875fc625 100644 --- a/lib/zip/extra_field/zip64.rb +++ b/lib/zip/extra_field/zip64.rb @@ -59,7 +59,7 @@ def pack_for_local def pack_for_c_dir # central directory entries contain only fields that didn't fit in the main entry part - packed = ''.force_encoding('BINARY') + packed = (+'').force_encoding('BINARY') packed << [@original_size].pack('Q<') if @original_size packed << [@compressed_size].pack('Q<') if @compressed_size packed << [@relative_header_offset].pack('Q<') if @relative_header_offset diff --git a/lib/zip/file.rb b/lib/zip/file.rb index c96d6615..65784565 100644 --- a/lib/zip/file.rb +++ b/lib/zip/file.rb @@ -133,7 +133,7 @@ def open(file_name, create = false, options = {}) # Same as #open. But outputs data to a buffer instead of a file def add_buffer - io = ::StringIO.new('') + io = ::StringIO.new(+'') zf = ::Zip::File.new(io, true, true) yield zf zf.write_buffer(io) diff --git a/lib/zip/filesystem.rb b/lib/zip/filesystem.rb index d9928d4a..6981b071 100644 --- a/lib/zip/filesystem.rb +++ b/lib/zip/filesystem.rb @@ -239,7 +239,7 @@ def directory?(filename) end def open(filename, mode = 'r', permissions = 0o644, &block) - mode.delete!('b') # ignore b option + mode = mode.tr('b', '') # ignore b option case mode when 'r' @mapped_zip.get_input_stream(filename, &block) @@ -619,7 +619,7 @@ def each end def expand_path(path) - expanded = path.start_with?('/') ? path : ::File.join(@pwd, path) + expanded = ::File.expand_path(path, @pwd) expanded.gsub!(/\/\.(\/|$)/, '') expanded.gsub!(/[^\/]+\/\.\.(\/|$)/, '') expanded.empty? ? '/' : expanded diff --git a/lib/zip/inflater.rb b/lib/zip/inflater.rb index 530f98aa..705ab0b5 100644 --- a/lib/zip/inflater.rb +++ b/lib/zip/inflater.rb @@ -7,7 +7,7 @@ def initialize(*args) @zlib_inflater = ::Zlib::Inflate.new(-Zlib::MAX_WBITS) end - def read(length = nil, outbuf = '') + def read(length = nil, outbuf = +'') return (length.nil? || length.zero? ? '' : nil) if eof while length.nil? || (@buffer.bytesize < length) diff --git a/lib/zip/ioextras.rb b/lib/zip/ioextras.rb index 63774d33..7a97a518 100644 --- a/lib/zip/ioextras.rb +++ b/lib/zip/ioextras.rb @@ -6,14 +6,14 @@ module IOExtras #:nodoc: class << self def copy_stream(ostream, istream) - ostream.write(istream.read(CHUNK_SIZE, '')) until istream.eof? + ostream.write(istream.read(CHUNK_SIZE, +'')) until istream.eof? end def copy_stream_n(ostream, istream, nbytes) toread = nbytes while toread > 0 && !istream.eof? tr = toread > CHUNK_SIZE ? CHUNK_SIZE : toread - ostream.write(istream.read(tr, '')) + ostream.write(istream.read(tr, +'')) toread -= tr end end diff --git a/lib/zip/ioextras/abstract_input_stream.rb b/lib/zip/ioextras/abstract_input_stream.rb index 848dcae3..bf32811a 100644 --- a/lib/zip/ioextras/abstract_input_stream.rb +++ b/lib/zip/ioextras/abstract_input_stream.rb @@ -11,13 +11,13 @@ def initialize super @lineno = 0 @pos = 0 - @output_buffer = '' + @output_buffer = +'' end attr_accessor :lineno attr_reader :pos - def read(number_of_bytes = nil, buf = '') + def read(number_of_bytes = nil, buf = +'') tbuf = if @output_buffer.bytesize > 0 if number_of_bytes && number_of_bytes <= @output_buffer.bytesize @output_buffer.slice!(0, number_of_bytes) @@ -93,7 +93,7 @@ def ungetc(byte) def flush ret_val = @output_buffer - @output_buffer = '' + @output_buffer = +'' ret_val end diff --git a/lib/zip/pass_thru_decompressor.rb b/lib/zip/pass_thru_decompressor.rb index e638540e..22db40e0 100644 --- a/lib/zip/pass_thru_decompressor.rb +++ b/lib/zip/pass_thru_decompressor.rb @@ -5,7 +5,7 @@ def initialize(*args) @read_so_far = 0 end - def read(length = nil, outbuf = '') + def read(length = nil, outbuf = +'') return (length.nil? || length.zero? ? '' : nil) if eof if length.nil? || (@read_so_far + length) > decompressed_size diff --git a/test/encryption_test.rb b/test/encryption_test.rb index cad0525a..0b59e64e 100644 --- a/test/encryption_test.rb +++ b/test/encryption_test.rb @@ -18,7 +18,7 @@ def test_encrypt password = 'swordfish' - encrypted_zip = Zip::OutputStream.write_buffer(::StringIO.new(''), Zip::TraditionalEncrypter.new(password)) do |out| + encrypted_zip = Zip::OutputStream.write_buffer(::StringIO.new(+''), Zip::TraditionalEncrypter.new(password)) do |out| out.put_next_entry(test_filename) out.write content end diff --git a/test/extra_field_test.rb b/test/extra_field_test.rb index 52140a2d..c8958fad 100644 --- a/test/extra_field_test.rb +++ b/test/extra_field_test.rb @@ -28,7 +28,7 @@ def test_bad_header_id 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" + 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) assert(extra.member?('NTFS')) t = ::Zip::DOSTime.at(1_410_496_497.405178) diff --git a/test/file_test.rb b/test/file_test.rb index f283a884..bdba5696 100644 --- a/test/file_test.rb +++ b/test/file_test.rb @@ -536,7 +536,7 @@ def test_write_buffer zf = ::Zip::File.new(TEST_ZIP.zip_name) old_name = zf.entries.first zf.rename(old_name, new_name) - io = ::StringIO.new('') + io = ::StringIO.new(+'') buffer = zf.write_buffer(io) File.open(TEST_ZIP.zip_name, 'wb') { |f| f.write buffer.string } zf_read = ::Zip::File.new(TEST_ZIP.zip_name) diff --git a/test/input_stream_test.rb b/test/input_stream_test.rb index 34042ba9..a1adf10b 100644 --- a/test/input_stream_test.rb +++ b/test/input_stream_test.rb @@ -134,7 +134,7 @@ def test_rewind assert_equal(TestZipFile::TEST_ZIP2.entry_names[0], e.name) # Do a little reading - buf = '' + buf = +'' buf << zis.read(100) assert_equal(100, zis.pos) buf << (zis.gets || '') @@ -143,7 +143,7 @@ def test_rewind zis.rewind - buf2 = '' + buf2 = +'' buf2 << zis.read(100) buf2 << (zis.gets || '') buf2 << (zis.gets || '') diff --git a/test/ioextras/abstract_output_stream_test.rb b/test/ioextras/abstract_output_stream_test.rb index 9b02309c..8c612445 100644 --- a/test/ioextras/abstract_output_stream_test.rb +++ b/test/ioextras/abstract_output_stream_test.rb @@ -8,7 +8,7 @@ class TestOutputStream attr_accessor :buffer def initialize - @buffer = '' + @buffer = +'' end def <<(data) @@ -58,12 +58,12 @@ def test_print assert_equal("hello world. You ok out there?\nI sure hope so!\n", @output_stream.buffer) $, = 'X' - @output_stream.buffer = '' + @output_stream.buffer = +'' @output_stream.print('monkey', 'duck', 'zebra') assert_equal("monkeyXduckXzebra\n", @output_stream.buffer) $\ = nil - @output_stream.buffer = '' + @output_stream.buffer = +'' @output_stream.print(20) assert_equal('20', @output_stream.buffer) end @@ -87,19 +87,19 @@ def test_puts @output_stream.puts('hello', 'world') assert_equal("\nhello\nworld\n", @output_stream.buffer) - @output_stream.buffer = '' + @output_stream.buffer = +'' @output_stream.puts("hello\n", "world\n") assert_equal("hello\nworld\n", @output_stream.buffer) - @output_stream.buffer = '' + @output_stream.buffer = +'' @output_stream.puts(%W[hello\n world\n]) assert_equal("hello\nworld\n", @output_stream.buffer) - @output_stream.buffer = '' + @output_stream.buffer = +'' @output_stream.puts(%W[hello\n world\n], 'bingo') assert_equal("hello\nworld\nbingo\n", @output_stream.buffer) - @output_stream.buffer = '' + @output_stream.buffer = +'' @output_stream.puts(16, 20, 50, 'hello') assert_equal("16\n20\n50\nhello\n", @output_stream.buffer) end diff --git a/test/output_stream_test.rb b/test/output_stream_test.rb index 73846742..c8139456 100644 --- a/test/output_stream_test.rb +++ b/test/output_stream_test.rb @@ -23,7 +23,7 @@ def test_open end def test_write_buffer - io = ::StringIO.new('') + io = ::StringIO.new(+'') buffer = ::Zip::OutputStream.write_buffer(io) do |zos| zos.comment = TEST_ZIP.comment write_test_zip(zos) @@ -33,7 +33,7 @@ def test_write_buffer end def test_write_buffer_binmode - io = ::StringIO.new('') + io = ::StringIO.new(+'') buffer = ::Zip::OutputStream.write_buffer(io) do |zos| zos.comment = TEST_ZIP.comment write_test_zip(zos) diff --git a/test/test_helper.rb b/test/test_helper.rb index 35a5085c..212a24c0 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -141,7 +141,7 @@ class TestOutputStream attr_accessor :buffer def initialize - @buffer = '' + @buffer = +'' end def <<(data) From e10badf68e476884d9805125050420d143e1be30 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sun, 23 May 2021 18:24:22 +0100 Subject: [PATCH 076/311] Fix Style/FrozenStringLiteralComment cop. --- .rubocop_todo.yml | 7 ---- .simplecov | 2 + Gemfile | 2 + Guardfile | 2 + Rakefile | 2 + bin/console | 1 + lib/zip.rb | 2 + lib/zip/central_directory.rb | 2 + lib/zip/compressor.rb | 2 + lib/zip/constants.rb | 44 ++++++++++---------- lib/zip/crypto/decrypted_io.rb | 2 + lib/zip/crypto/encryption.rb | 2 + lib/zip/crypto/null_encryption.rb | 2 + lib/zip/crypto/traditional_encryption.rb | 2 + lib/zip/decompressor.rb | 2 + lib/zip/deflater.rb | 2 + lib/zip/dos_time.rb | 2 + lib/zip/entry.rb | 2 + lib/zip/entry_set.rb | 2 + lib/zip/errors.rb | 2 + lib/zip/extra_field.rb | 2 + lib/zip/extra_field/generic.rb | 2 + lib/zip/extra_field/ntfs.rb | 2 + lib/zip/extra_field/old_unix.rb | 2 + lib/zip/extra_field/universal_time.rb | 2 + lib/zip/extra_field/unix.rb | 2 + lib/zip/extra_field/zip64.rb | 2 + lib/zip/extra_field/zip64_placeholder.rb | 2 + lib/zip/file.rb | 2 + lib/zip/filesystem.rb | 2 + lib/zip/inflater.rb | 2 + lib/zip/input_stream.rb | 2 + lib/zip/ioextras.rb | 2 + lib/zip/ioextras/abstract_input_stream.rb | 2 + lib/zip/ioextras/abstract_output_stream.rb | 2 + lib/zip/null_compressor.rb | 2 + lib/zip/null_decompressor.rb | 2 + lib/zip/null_input_stream.rb | 2 + lib/zip/output_stream.rb | 2 + lib/zip/pass_thru_compressor.rb | 2 + lib/zip/pass_thru_decompressor.rb | 2 + lib/zip/streamable_directory.rb | 2 + lib/zip/streamable_stream.rb | 2 + lib/zip/version.rb | 2 + rubyzip.gemspec | 2 + samples/example.rb | 1 + samples/example_filesystem.rb | 1 + samples/example_recursive.rb | 2 + samples/gtk_ruby_zip.rb | 1 + samples/qtzip.rb | 1 + samples/write_simple.rb | 1 + samples/zipfind.rb | 1 + test/basic_zip_file_test.rb | 2 + test/bzip2_support_test.rb | 2 + test/case_sensitivity_test.rb | 2 + test/central_directory_entry_test.rb | 2 + test/central_directory_test.rb | 2 + test/constants_test.rb | 2 + test/crypto/null_encryption_test.rb | 2 + test/crypto/traditional_encryption_test.rb | 2 + test/data/notzippedruby.rb | 1 + test/decompressor_test.rb | 2 + test/deflater_test.rb | 2 + test/encryption_test.rb | 2 + test/entry_set_test.rb | 2 + test/entry_test.rb | 2 + test/errors_test.rb | 2 + test/extra_field_test.rb | 2 + test/extra_field_ut_test.rb | 2 + test/file_extract_directory_test.rb | 2 + test/file_extract_test.rb | 2 + test/file_options_test.rb | 8 ++-- test/file_permissions_test.rb | 2 + test/file_split_test.rb | 2 + test/file_test.rb | 2 + test/filesystem/dir_iterator_test.rb | 2 + test/filesystem/directory_test.rb | 2 + test/filesystem/file_mutating_test.rb | 2 + test/filesystem/file_nonmutating_test.rb | 2 + test/filesystem/file_stat_test.rb | 2 + test/gentestfiles.rb | 1 + test/inflater_test.rb | 2 + test/input_stream_test.rb | 2 + test/ioextras/abstract_input_stream_test.rb | 2 + test/ioextras/abstract_output_stream_test.rb | 2 + test/ioextras/fake_io_test.rb | 2 + test/local_entry_test.rb | 2 + test/output_stream_test.rb | 2 + test/pass_thru_compressor_test.rb | 2 + test/pass_thru_decompressor_test.rb | 2 + test/path_traversal_test.rb | 2 + test/samples/example_recursive_test.rb | 2 + test/settings_test.rb | 2 + test/stored_support_test.rb | 2 + test/test_helper.rb | 2 + test/unicode_file_names_and_comments_test.rb | 2 + test/zip64_full_test.rb | 2 + test/zip64_support_test.rb | 2 + 98 files changed, 209 insertions(+), 31 deletions(-) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 99324285..b120cadf 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -146,13 +146,6 @@ Style/Documentation: Style/FormatStringToken: EnforcedStyle: unannotated -# Offense count: 97 -# Cop supports --auto-correct. -# Configuration parameters: EnforcedStyle. -# SupportedStyles: always, always_true, never -Style/FrozenStringLiteralComment: - Enabled: false - # Offense count: 1 # Cop supports --auto-correct. # Configuration parameters: AllowSplatArgument. diff --git a/.simplecov b/.simplecov index fc3979d2..6c400bf6 100644 --- a/.simplecov +++ b/.simplecov @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'simplecov-lcov' SimpleCov::Formatter::LcovFormatter.config do |c| diff --git a/Gemfile b/Gemfile index fa75df15..7f4f5e95 100644 --- a/Gemfile +++ b/Gemfile @@ -1,3 +1,5 @@ +# frozen_string_literal: true + source 'https://rubygems.org' gemspec diff --git a/Guardfile b/Guardfile index 1508e4c9..5365a7a5 100644 --- a/Guardfile +++ b/Guardfile @@ -1,3 +1,5 @@ +# frozen_string_literal: true + guard :minitest do # with Minitest::Unit watch(%r{^test/(.*)\/?(.*)_test\.rb$}) diff --git a/Rakefile b/Rakefile index 717c6b73..1fc36185 100644 --- a/Rakefile +++ b/Rakefile @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'bundler/gem_tasks' require 'rake/testtask' require 'rubocop/rake_task' diff --git a/bin/console b/bin/console index 6df9a590..15e7b977 100755 --- a/bin/console +++ b/bin/console @@ -1,4 +1,5 @@ #!/usr/bin/env ruby +# frozen_string_literal: true require 'bundler/setup' require 'zip' diff --git a/lib/zip.rb b/lib/zip.rb index 8cf982a5..5e142938 100644 --- a/lib/zip.rb +++ b/lib/zip.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'English' require 'delegate' require 'singleton' diff --git a/lib/zip/central_directory.rb b/lib/zip/central_directory.rb index 5b64c7f5..36a150ba 100644 --- a/lib/zip/central_directory.rb +++ b/lib/zip/central_directory.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Zip class CentralDirectory include Enumerable diff --git a/lib/zip/compressor.rb b/lib/zip/compressor.rb index 079c1cb0..8c0680e0 100644 --- a/lib/zip/compressor.rb +++ b/lib/zip/compressor.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Zip class Compressor #:nodoc:all def finish; end diff --git a/lib/zip/constants.rb b/lib/zip/constants.rb index fe89847a..c15aeae2 100644 --- a/lib/zip/constants.rb +++ b/lib/zip/constants.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Zip RUNNING_ON_WINDOWS = RbConfig::CONFIG['host_os'] =~ /mswin|mingw|cygwin/i @@ -38,27 +40,27 @@ module Zip FSTYPE_ATHEOS = 30 FSTYPES = { - FSTYPE_FAT => 'FAT'.freeze, - FSTYPE_AMIGA => 'Amiga'.freeze, - FSTYPE_VMS => 'VMS (Vax or Alpha AXP)'.freeze, - FSTYPE_UNIX => 'Unix'.freeze, - FSTYPE_VM_CMS => 'VM/CMS'.freeze, - FSTYPE_ATARI => 'Atari ST'.freeze, - FSTYPE_HPFS => 'OS/2 or NT HPFS'.freeze, - FSTYPE_MAC => 'Macintosh'.freeze, - FSTYPE_Z_SYSTEM => 'Z-System'.freeze, - FSTYPE_CPM => 'CP/M'.freeze, - FSTYPE_TOPS20 => 'TOPS-20'.freeze, - FSTYPE_NTFS => 'NTFS'.freeze, - FSTYPE_QDOS => 'SMS/QDOS'.freeze, - FSTYPE_ACORN => 'Acorn RISC OS'.freeze, - FSTYPE_VFAT => 'Win32 VFAT'.freeze, - FSTYPE_MVS => 'MVS'.freeze, - FSTYPE_BEOS => 'BeOS'.freeze, - FSTYPE_TANDEM => 'Tandem NSK'.freeze, - FSTYPE_THEOS => 'Theos'.freeze, - FSTYPE_MAC_OSX => 'Mac OS/X (Darwin)'.freeze, - FSTYPE_ATHEOS => 'AtheOS'.freeze + FSTYPE_FAT => 'FAT', + FSTYPE_AMIGA => 'Amiga', + FSTYPE_VMS => 'VMS (Vax or Alpha AXP)', + FSTYPE_UNIX => 'Unix', + FSTYPE_VM_CMS => 'VM/CMS', + FSTYPE_ATARI => 'Atari ST', + FSTYPE_HPFS => 'OS/2 or NT HPFS', + FSTYPE_MAC => 'Macintosh', + FSTYPE_Z_SYSTEM => 'Z-System', + FSTYPE_CPM => 'CP/M', + FSTYPE_TOPS20 => 'TOPS-20', + FSTYPE_NTFS => 'NTFS', + FSTYPE_QDOS => 'SMS/QDOS', + FSTYPE_ACORN => 'Acorn RISC OS', + FSTYPE_VFAT => 'Win32 VFAT', + FSTYPE_MVS => 'MVS', + FSTYPE_BEOS => 'BeOS', + FSTYPE_TANDEM => 'Tandem NSK', + FSTYPE_THEOS => 'Theos', + FSTYPE_MAC_OSX => 'Mac OS/X (Darwin)', + FSTYPE_ATHEOS => 'AtheOS' }.freeze COMPRESSION_METHOD_STORE = 0 diff --git a/lib/zip/crypto/decrypted_io.rb b/lib/zip/crypto/decrypted_io.rb index 61a377da..92ccde63 100644 --- a/lib/zip/crypto/decrypted_io.rb +++ b/lib/zip/crypto/decrypted_io.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Zip class DecryptedIo #:nodoc:all CHUNK_SIZE = 32_768 diff --git a/lib/zip/crypto/encryption.rb b/lib/zip/crypto/encryption.rb index 4351be1c..c792e38b 100644 --- a/lib/zip/crypto/encryption.rb +++ b/lib/zip/crypto/encryption.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Zip class Encrypter #:nodoc:all end diff --git a/lib/zip/crypto/null_encryption.rb b/lib/zip/crypto/null_encryption.rb index a93f707c..ae33c40c 100644 --- a/lib/zip/crypto/null_encryption.rb +++ b/lib/zip/crypto/null_encryption.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Zip module NullEncryption def header_bytesize diff --git a/lib/zip/crypto/traditional_encryption.rb b/lib/zip/crypto/traditional_encryption.rb index 270e9efd..eef3b18d 100644 --- a/lib/zip/crypto/traditional_encryption.rb +++ b/lib/zip/crypto/traditional_encryption.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Zip module TraditionalEncryption def initialize(password) diff --git a/lib/zip/decompressor.rb b/lib/zip/decompressor.rb index 2f89545c..0d29e618 100644 --- a/lib/zip/decompressor.rb +++ b/lib/zip/decompressor.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Zip class Decompressor #:nodoc:all CHUNK_SIZE = 32_768 diff --git a/lib/zip/deflater.rb b/lib/zip/deflater.rb index 9f5371c8..8f4827b4 100644 --- a/lib/zip/deflater.rb +++ b/lib/zip/deflater.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Zip class Deflater < Compressor #:nodoc:all def initialize(output_stream, level = Zip.default_compression, encrypter = NullEncrypter.new) diff --git a/lib/zip/dos_time.rb b/lib/zip/dos_time.rb index 51139dde..264ead28 100644 --- a/lib/zip/dos_time.rb +++ b/lib/zip/dos_time.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'rubygems' module Zip diff --git a/lib/zip/entry.rb b/lib/zip/entry.rb index 0b13470d..453c174b 100644 --- a/lib/zip/entry.rb +++ b/lib/zip/entry.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'pathname' module Zip class Entry diff --git a/lib/zip/entry_set.rb b/lib/zip/entry_set.rb index 9c503781..a5822453 100644 --- a/lib/zip/entry_set.rb +++ b/lib/zip/entry_set.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Zip class EntrySet #:nodoc:all include Enumerable diff --git a/lib/zip/errors.rb b/lib/zip/errors.rb index 0ff0e1e1..947f8dc4 100644 --- a/lib/zip/errors.rb +++ b/lib/zip/errors.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Zip class Error < StandardError; end class EntryExistsError < Error; end diff --git a/lib/zip/extra_field.rb b/lib/zip/extra_field.rb index aa3ef8a8..24352aae 100644 --- a/lib/zip/extra_field.rb +++ b/lib/zip/extra_field.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Zip class ExtraField < Hash ID_MAP = {} diff --git a/lib/zip/extra_field/generic.rb b/lib/zip/extra_field/generic.rb index 9237a1db..b3db791a 100644 --- a/lib/zip/extra_field/generic.rb +++ b/lib/zip/extra_field/generic.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Zip class ExtraField::Generic def self.register_map diff --git a/lib/zip/extra_field/ntfs.rb b/lib/zip/extra_field/ntfs.rb index f80b5078..cd691b1d 100644 --- a/lib/zip/extra_field/ntfs.rb +++ b/lib/zip/extra_field/ntfs.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Zip # PKWARE NTFS Extra Field (0x000a) # Only Tag 0x0001 is supported diff --git a/lib/zip/extra_field/old_unix.rb b/lib/zip/extra_field/old_unix.rb index dfd2ba56..351339fe 100644 --- a/lib/zip/extra_field/old_unix.rb +++ b/lib/zip/extra_field/old_unix.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Zip # Olf Info-ZIP Extra for UNIX uid/gid and file timestampes class ExtraField::OldUnix < ExtraField::Generic diff --git a/lib/zip/extra_field/universal_time.rb b/lib/zip/extra_field/universal_time.rb index 424c281d..63ef070e 100644 --- a/lib/zip/extra_field/universal_time.rb +++ b/lib/zip/extra_field/universal_time.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Zip # Info-ZIP Additional timestamp field class ExtraField::UniversalTime < ExtraField::Generic diff --git a/lib/zip/extra_field/unix.rb b/lib/zip/extra_field/unix.rb index d83087e4..f88f1355 100644 --- a/lib/zip/extra_field/unix.rb +++ b/lib/zip/extra_field/unix.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Zip # Info-ZIP Extra for UNIX uid/gid class ExtraField::IUnix < ExtraField::Generic diff --git a/lib/zip/extra_field/zip64.rb b/lib/zip/extra_field/zip64.rb index 875fc625..2f7c32c9 100644 --- a/lib/zip/extra_field/zip64.rb +++ b/lib/zip/extra_field/zip64.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Zip # Info-ZIP Extra for Zip64 size class ExtraField::Zip64 < ExtraField::Generic diff --git a/lib/zip/extra_field/zip64_placeholder.rb b/lib/zip/extra_field/zip64_placeholder.rb index dfaa56e8..2619a68e 100644 --- a/lib/zip/extra_field/zip64_placeholder.rb +++ b/lib/zip/extra_field/zip64_placeholder.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Zip # placeholder to reserve space for a Zip64 extra information record, for the # local file header only, that we won't know if we'll need until after diff --git a/lib/zip/file.rb b/lib/zip/file.rb index 65784565..1c579539 100644 --- a/lib/zip/file.rb +++ b/lib/zip/file.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Zip # ZipFile is modeled after java.util.zip.ZipFile from the Java SDK. # The most important methods are those inherited from diff --git a/lib/zip/filesystem.rb b/lib/zip/filesystem.rb index 6981b071..04162ca0 100644 --- a/lib/zip/filesystem.rb +++ b/lib/zip/filesystem.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'zip' module Zip diff --git a/lib/zip/inflater.rb b/lib/zip/inflater.rb index 705ab0b5..8f686f3e 100644 --- a/lib/zip/inflater.rb +++ b/lib/zip/inflater.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Zip class Inflater < Decompressor #:nodoc:all def initialize(*args) diff --git a/lib/zip/input_stream.rb b/lib/zip/input_stream.rb index f942d190..d044a294 100644 --- a/lib/zip/input_stream.rb +++ b/lib/zip/input_stream.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Zip # InputStream is the basic class for reading zip entries in a # zip file. It is possible to create a InputStream object directly, diff --git a/lib/zip/ioextras.rb b/lib/zip/ioextras.rb index 7a97a518..879a5145 100644 --- a/lib/zip/ioextras.rb +++ b/lib/zip/ioextras.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Zip module IOExtras #:nodoc: CHUNK_SIZE = 131_072 diff --git a/lib/zip/ioextras/abstract_input_stream.rb b/lib/zip/ioextras/abstract_input_stream.rb index bf32811a..11438fc7 100644 --- a/lib/zip/ioextras/abstract_input_stream.rb +++ b/lib/zip/ioextras/abstract_input_stream.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Zip module IOExtras # Implements many of the convenience methods of IO diff --git a/lib/zip/ioextras/abstract_output_stream.rb b/lib/zip/ioextras/abstract_output_stream.rb index b94c9d49..e826b54a 100644 --- a/lib/zip/ioextras/abstract_output_stream.rb +++ b/lib/zip/ioextras/abstract_output_stream.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Zip module IOExtras # Implements many of the output convenience methods of IO. diff --git a/lib/zip/null_compressor.rb b/lib/zip/null_compressor.rb index 70fd3294..41da0c61 100644 --- a/lib/zip/null_compressor.rb +++ b/lib/zip/null_compressor.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Zip class NullCompressor < Compressor #:nodoc:all include Singleton diff --git a/lib/zip/null_decompressor.rb b/lib/zip/null_decompressor.rb index 6534b161..7b2557d4 100644 --- a/lib/zip/null_decompressor.rb +++ b/lib/zip/null_decompressor.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Zip module NullDecompressor #:nodoc:all module_function diff --git a/lib/zip/null_input_stream.rb b/lib/zip/null_input_stream.rb index 2cd36616..69bc38d7 100644 --- a/lib/zip/null_input_stream.rb +++ b/lib/zip/null_input_stream.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Zip module NullInputStream #:nodoc:all include ::Zip::NullDecompressor diff --git a/lib/zip/output_stream.rb b/lib/zip/output_stream.rb index 65b7d620..6bfd9382 100644 --- a/lib/zip/output_stream.rb +++ b/lib/zip/output_stream.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Zip # ZipOutputStream is the basic class for writing zip files. It is # possible to create a ZipOutputStream object directly, passing diff --git a/lib/zip/pass_thru_compressor.rb b/lib/zip/pass_thru_compressor.rb index 2dbaa273..484d0da1 100644 --- a/lib/zip/pass_thru_compressor.rb +++ b/lib/zip/pass_thru_compressor.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Zip class PassThruCompressor < Compressor #:nodoc:all def initialize(output_stream) diff --git a/lib/zip/pass_thru_decompressor.rb b/lib/zip/pass_thru_decompressor.rb index 22db40e0..c9973c8d 100644 --- a/lib/zip/pass_thru_decompressor.rb +++ b/lib/zip/pass_thru_decompressor.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Zip class PassThruDecompressor < Decompressor #:nodoc:all def initialize(*args) diff --git a/lib/zip/streamable_directory.rb b/lib/zip/streamable_directory.rb index 3738ce2c..a3c7c93b 100644 --- a/lib/zip/streamable_directory.rb +++ b/lib/zip/streamable_directory.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Zip class StreamableDirectory < Entry def initialize(zipfile, entry, src_path = nil, permission = nil) diff --git a/lib/zip/streamable_stream.rb b/lib/zip/streamable_stream.rb index 68f3e0e8..e823e002 100644 --- a/lib/zip/streamable_stream.rb +++ b/lib/zip/streamable_stream.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Zip class StreamableStream < DelegateClass(Entry) # :nodoc:all def initialize(entry) diff --git a/lib/zip/version.rb b/lib/zip/version.rb index 55d4ac29..633899d6 100644 --- a/lib/zip/version.rb +++ b/lib/zip/version.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Zip VERSION = '3.0.0' end diff --git a/rubyzip.gemspec b/rubyzip.gemspec index f858ee80..b331648d 100644 --- a/rubyzip.gemspec +++ b/rubyzip.gemspec @@ -1,3 +1,5 @@ +# frozen_string_literal: true + lib = File.expand_path('lib', __dir__) $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) require 'zip/version' diff --git a/samples/example.rb b/samples/example.rb index 345e7e19..3e82d0a2 100755 --- a/samples/example.rb +++ b/samples/example.rb @@ -1,4 +1,5 @@ #!/usr/bin/env ruby +# frozen_string_literal: true $LOAD_PATH << '../lib' system('zip example.zip example.rb gtk_ruby_zip.rb') diff --git a/samples/example_filesystem.rb b/samples/example_filesystem.rb index 0d93ab6b..d37eaee6 100755 --- a/samples/example_filesystem.rb +++ b/samples/example_filesystem.rb @@ -1,4 +1,5 @@ #!/usr/bin/env ruby +# frozen_string_literal: true $LOAD_PATH << '../lib' diff --git a/samples/example_recursive.rb b/samples/example_recursive.rb index 56a5cc7c..175e69f4 100644 --- a/samples/example_recursive.rb +++ b/samples/example_recursive.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'zip' # This is a simple example which uses rubyzip to diff --git a/samples/gtk_ruby_zip.rb b/samples/gtk_ruby_zip.rb index a86f0a9e..ed8410a2 100755 --- a/samples/gtk_ruby_zip.rb +++ b/samples/gtk_ruby_zip.rb @@ -1,4 +1,5 @@ #!/usr/bin/env ruby +# frozen_string_literal: true $LOAD_PATH << '../lib' diff --git a/samples/qtzip.rb b/samples/qtzip.rb index 2c189ed6..715623a4 100755 --- a/samples/qtzip.rb +++ b/samples/qtzip.rb @@ -1,4 +1,5 @@ #!/usr/bin/env ruby +# frozen_string_literal: true $VERBOSE = true diff --git a/samples/write_simple.rb b/samples/write_simple.rb index 8bb31bb3..f7939200 100755 --- a/samples/write_simple.rb +++ b/samples/write_simple.rb @@ -1,4 +1,5 @@ #!/usr/bin/env ruby +# frozen_string_literal: true $LOAD_PATH << '../lib' diff --git a/samples/zipfind.rb b/samples/zipfind.rb index 8f0dbf2e..c3888389 100755 --- a/samples/zipfind.rb +++ b/samples/zipfind.rb @@ -1,4 +1,5 @@ #!/usr/bin/env ruby +# frozen_string_literal: true $VERBOSE = true diff --git a/test/basic_zip_file_test.rb b/test/basic_zip_file_test.rb index 8ff999bb..160fd208 100644 --- a/test/basic_zip_file_test.rb +++ b/test/basic_zip_file_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'test_helper' class BasicZipFileTest < MiniTest::Test diff --git a/test/bzip2_support_test.rb b/test/bzip2_support_test.rb index ab86b4e8..119d1c0d 100644 --- a/test/bzip2_support_test.rb +++ b/test/bzip2_support_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'test_helper' class Bzip2SupportTest < MiniTest::Test diff --git a/test/case_sensitivity_test.rb b/test/case_sensitivity_test.rb index fdbee8e3..0a9844b1 100644 --- a/test/case_sensitivity_test.rb +++ b/test/case_sensitivity_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'test_helper' class ZipCaseSensitivityTest < MiniTest::Test diff --git a/test/central_directory_entry_test.rb b/test/central_directory_entry_test.rb index c060a4d3..a6898c5c 100644 --- a/test/central_directory_entry_test.rb +++ b/test/central_directory_entry_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'test_helper' class ZipCentralDirectoryEntryTest < MiniTest::Test diff --git a/test/central_directory_test.rb b/test/central_directory_test.rb index 6dfd9ad9..011bdb5f 100644 --- a/test/central_directory_test.rb +++ b/test/central_directory_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'test_helper' class ZipCentralDirectoryTest < MiniTest::Test diff --git a/test/constants_test.rb b/test/constants_test.rb index 8be01715..d31419f5 100644 --- a/test/constants_test.rb +++ b/test/constants_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'test_helper' class ConstantsTest < MiniTest::Test diff --git a/test/crypto/null_encryption_test.rb b/test/crypto/null_encryption_test.rb index ca039962..e6b6ef55 100644 --- a/test/crypto/null_encryption_test.rb +++ b/test/crypto/null_encryption_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'test_helper' class NullEncrypterTest < MiniTest::Test diff --git a/test/crypto/traditional_encryption_test.rb b/test/crypto/traditional_encryption_test.rb index 51f6cbb4..c3cc9fe0 100644 --- a/test/crypto/traditional_encryption_test.rb +++ b/test/crypto/traditional_encryption_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'test_helper' class TraditionalEncrypterTest < MiniTest::Test diff --git a/test/data/notzippedruby.rb b/test/data/notzippedruby.rb index 79f9cbb9..65b52b50 100755 --- a/test/data/notzippedruby.rb +++ b/test/data/notzippedruby.rb @@ -1,4 +1,5 @@ #!/usr/bin/env ruby +# frozen_string_literal: true class NotZippedRuby def return_true diff --git a/test/decompressor_test.rb b/test/decompressor_test.rb index d7ff2e73..9109a2e4 100644 --- a/test/decompressor_test.rb +++ b/test/decompressor_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'test_helper' class DecompressorTest < MiniTest::Test TEST_COMPRESSION_METHOD = 255 diff --git a/test/deflater_test.rb b/test/deflater_test.rb index 35d7b0c6..fb782205 100644 --- a/test/deflater_test.rb +++ b/test/deflater_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'test_helper' class DeflaterTest < MiniTest::Test diff --git a/test/encryption_test.rb b/test/encryption_test.rb index 0b59e64e..110e7a3d 100644 --- a/test/encryption_test.rb +++ b/test/encryption_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'test_helper' class EncryptionTest < MiniTest::Test diff --git a/test/entry_set_test.rb b/test/entry_set_test.rb index fd038deb..4c9f2021 100644 --- a/test/entry_set_test.rb +++ b/test/entry_set_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'test_helper' class ZipEntrySetTest < MiniTest::Test diff --git a/test/entry_test.rb b/test/entry_test.rb index e6ac7a4b..7c1cf60d 100644 --- a/test/entry_test.rb +++ b/test/entry_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'test_helper' class ZipEntryTest < MiniTest::Test diff --git a/test/errors_test.rb b/test/errors_test.rb index 5e6260f8..2f1b506d 100644 --- a/test/errors_test.rb +++ b/test/errors_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'test_helper' class ErrorsTest < MiniTest::Test diff --git a/test/extra_field_test.rb b/test/extra_field_test.rb index c8958fad..7aea91e5 100644 --- a/test/extra_field_test.rb +++ b/test/extra_field_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'test_helper' class ZipExtraFieldTest < MiniTest::Test diff --git a/test/extra_field_ut_test.rb b/test/extra_field_ut_test.rb index 6b854978..74cb21f6 100644 --- a/test/extra_field_ut_test.rb +++ b/test/extra_field_ut_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'test_helper' class ZipExtraFieldUTTest < MiniTest::Test diff --git a/test/file_extract_directory_test.rb b/test/file_extract_directory_test.rb index 02a3fd0d..fc10979d 100644 --- a/test/file_extract_directory_test.rb +++ b/test/file_extract_directory_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'test_helper' class ZipFileExtractDirectoryTest < MiniTest::Test diff --git a/test/file_extract_test.rb b/test/file_extract_test.rb index d8306b35..16fba3a9 100644 --- a/test/file_extract_test.rb +++ b/test/file_extract_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'test_helper' class ZipFileExtractTest < MiniTest::Test diff --git a/test/file_options_test.rb b/test/file_options_test.rb index 61b86e85..e9c612ce 100644 --- a/test/file_options_test.rb +++ b/test/file_options_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'test_helper' class FileOptionsTest < MiniTest::Test @@ -8,9 +10,9 @@ class FileOptionsTest < MiniTest::Test EXTPATH_1 = ::File.join(Dir.tmpdir, 'extracted_1.txt').freeze EXTPATH_2 = ::File.join(Dir.tmpdir, 'extracted_2.txt').freeze EXTPATH_3 = ::File.join(Dir.tmpdir, 'extracted_3.txt').freeze - ENTRY_1 = 'entry_1.txt'.freeze - ENTRY_2 = 'entry_2.txt'.freeze - ENTRY_3 = 'entry_3.txt'.freeze + ENTRY_1 = 'entry_1.txt' + ENTRY_2 = 'entry_2.txt' + ENTRY_3 = 'entry_3.txt' def teardown ::File.unlink(ZIPPATH) if ::File.exist?(ZIPPATH) diff --git a/test/file_permissions_test.rb b/test/file_permissions_test.rb index 2d8283c9..1b31a79b 100644 --- a/test/file_permissions_test.rb +++ b/test/file_permissions_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'test_helper' class FilePermissionsTest < MiniTest::Test diff --git a/test/file_split_test.rb b/test/file_split_test.rb index 22dd1348..50fc4a4b 100644 --- a/test/file_split_test.rb +++ b/test/file_split_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'test_helper' class ZipFileSplitTest < MiniTest::Test diff --git a/test/file_test.rb b/test/file_test.rb index bdba5696..6f1e109c 100644 --- a/test/file_test.rb +++ b/test/file_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'test_helper' class ZipFileTest < MiniTest::Test diff --git a/test/filesystem/dir_iterator_test.rb b/test/filesystem/dir_iterator_test.rb index e46da426..6223b44e 100644 --- a/test/filesystem/dir_iterator_test.rb +++ b/test/filesystem/dir_iterator_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'test_helper' require 'zip/filesystem' diff --git a/test/filesystem/directory_test.rb b/test/filesystem/directory_test.rb index 8ad04d9e..57177028 100644 --- a/test/filesystem/directory_test.rb +++ b/test/filesystem/directory_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'test_helper' require 'zip/filesystem' diff --git a/test/filesystem/file_mutating_test.rb b/test/filesystem/file_mutating_test.rb index ccba6e3d..91d45afb 100644 --- a/test/filesystem/file_mutating_test.rb +++ b/test/filesystem/file_mutating_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'test_helper' require 'zip/filesystem' diff --git a/test/filesystem/file_nonmutating_test.rb b/test/filesystem/file_nonmutating_test.rb index 485298fa..bc0e41d3 100644 --- a/test/filesystem/file_nonmutating_test.rb +++ b/test/filesystem/file_nonmutating_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'test_helper' require 'zip/filesystem' diff --git a/test/filesystem/file_stat_test.rb b/test/filesystem/file_stat_test.rb index b8efe754..0f6e2b50 100644 --- a/test/filesystem/file_stat_test.rb +++ b/test/filesystem/file_stat_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'test_helper' require 'zip/filesystem' diff --git a/test/gentestfiles.rb b/test/gentestfiles.rb index bb1b981d..c46e9c22 100755 --- a/test/gentestfiles.rb +++ b/test/gentestfiles.rb @@ -1,4 +1,5 @@ #!/usr/bin/env ruby +# frozen_string_literal: true $VERBOSE = true diff --git a/test/inflater_test.rb b/test/inflater_test.rb index 7748c94f..424d47a3 100644 --- a/test/inflater_test.rb +++ b/test/inflater_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'test_helper' class InflaterTest < MiniTest::Test include DecompressorTests diff --git a/test/input_stream_test.rb b/test/input_stream_test.rb index a1adf10b..2d192abd 100644 --- a/test/input_stream_test.rb +++ b/test/input_stream_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'test_helper' class ZipInputStreamTest < MiniTest::Test diff --git a/test/ioextras/abstract_input_stream_test.rb b/test/ioextras/abstract_input_stream_test.rb index a18c4e3d..6bc87d4d 100644 --- a/test/ioextras/abstract_input_stream_test.rb +++ b/test/ioextras/abstract_input_stream_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'test_helper' require 'zip/ioextras' diff --git a/test/ioextras/abstract_output_stream_test.rb b/test/ioextras/abstract_output_stream_test.rb index 8c612445..d4a8d0a8 100644 --- a/test/ioextras/abstract_output_stream_test.rb +++ b/test/ioextras/abstract_output_stream_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'test_helper' require 'zip/ioextras' diff --git a/test/ioextras/fake_io_test.rb b/test/ioextras/fake_io_test.rb index 612f442f..07b24b5a 100644 --- a/test/ioextras/fake_io_test.rb +++ b/test/ioextras/fake_io_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'test_helper' require 'zip/ioextras' diff --git a/test/local_entry_test.rb b/test/local_entry_test.rb index 1317fb45..9d2e3ed5 100644 --- a/test/local_entry_test.rb +++ b/test/local_entry_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'test_helper' class ZipLocalEntryTest < MiniTest::Test diff --git a/test/output_stream_test.rb b/test/output_stream_test.rb index c8139456..3b46f615 100644 --- a/test/output_stream_test.rb +++ b/test/output_stream_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'test_helper' class ZipOutputStreamTest < MiniTest::Test diff --git a/test/pass_thru_compressor_test.rb b/test/pass_thru_compressor_test.rb index 334ba90c..455f800b 100644 --- a/test/pass_thru_compressor_test.rb +++ b/test/pass_thru_compressor_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'test_helper' class PassThruCompressorTest < MiniTest::Test diff --git a/test/pass_thru_decompressor_test.rb b/test/pass_thru_decompressor_test.rb index e0b66892..937c3ac6 100644 --- a/test/pass_thru_decompressor_test.rb +++ b/test/pass_thru_decompressor_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'test_helper' class PassThruDecompressorTest < MiniTest::Test include DecompressorTests diff --git a/test/path_traversal_test.rb b/test/path_traversal_test.rb index 47c7e30f..9676432b 100644 --- a/test/path_traversal_test.rb +++ b/test/path_traversal_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'test_helper' class PathTraversalTest < MiniTest::Test diff --git a/test/samples/example_recursive_test.rb b/test/samples/example_recursive_test.rb index 4fb14883..a2af3ec7 100644 --- a/test/samples/example_recursive_test.rb +++ b/test/samples/example_recursive_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'test_helper' require 'fileutils' require_relative '../../samples/example_recursive' diff --git a/test/settings_test.rb b/test/settings_test.rb index 0510a6fc..6e33573c 100644 --- a/test/settings_test.rb +++ b/test/settings_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'test_helper' class ZipSettingsTest < MiniTest::Test diff --git a/test/stored_support_test.rb b/test/stored_support_test.rb index 28836b9e..1a125916 100644 --- a/test/stored_support_test.rb +++ b/test/stored_support_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'test_helper' class StoredSupportTest < MiniTest::Test diff --git a/test/test_helper.rb b/test/test_helper.rb index 212a24c0..125ee99c 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'simplecov' require 'minitest/autorun' require 'minitest/unit' diff --git a/test/unicode_file_names_and_comments_test.rb b/test/unicode_file_names_and_comments_test.rb index 7950e289..1518171a 100644 --- a/test/unicode_file_names_and_comments_test.rb +++ b/test/unicode_file_names_and_comments_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'test_helper' class ZipUnicodeFileNamesAndComments < MiniTest::Test diff --git a/test/zip64_full_test.rb b/test/zip64_full_test.rb index ed11ed65..769709e9 100644 --- a/test/zip64_full_test.rb +++ b/test/zip64_full_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + if ENV['FULL_ZIP64_TEST'] require 'minitest/autorun' require 'minitest/unit' diff --git a/test/zip64_support_test.rb b/test/zip64_support_test.rb index 3e4154a8..0e96a9c6 100644 --- a/test/zip64_support_test.rb +++ b/test/zip64_support_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'test_helper' class Zip64SupportTest < MiniTest::Test From 606b5ffbb2ec58db61527946b468145ec828a7bd Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sun, 23 May 2021 20:18:50 +0100 Subject: [PATCH 077/311] Fix Lint/EmptyBlock cop. --- .rubocop_todo.yml | 8 -------- lib/zip/file.rb | 3 +-- test/gentestfiles.rb | 4 ++-- test/settings_test.rb | 6 ++---- 4 files changed, 5 insertions(+), 16 deletions(-) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index b120cadf..5a4db7b0 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -23,14 +23,6 @@ Layout/EmptyLinesAroundAttributeAccessor: Layout/FirstArrayElementIndentation: EnforcedStyle: consistent -# Offense count: 5 -# Configuration parameters: AllowComments, AllowEmptyLambdas. -Lint/EmptyBlock: - Exclude: - - 'lib/zip/file.rb' - - 'test/gentestfiles.rb' - - 'test/settings_test.rb' - # Offense count: 3 # Configuration parameters: AllowComments. Lint/EmptyClass: diff --git a/lib/zip/file.rb b/lib/zip/file.rb index 1c579539..2efc8cd3 100644 --- a/lib/zip/file.rb +++ b/lib/zip/file.rb @@ -242,8 +242,7 @@ def split(zip_file_name, segment_size = MAX_SEGMENT_SIZE, delete_zip_file = true return if zip_file_size <= segment_size segment_count = get_segment_count_for_split(zip_file_size, segment_size) - # Checking for correct zip structure - ::Zip::File.open(zip_file_name) {} + ::Zip::File.open(zip_file_name) {} # Check for correct zip structure. partial_zip_file_name = get_partial_zip_file_name(zip_file_name, partial_zip_file_name) szip_file_index = 0 ::File.open(zip_file_name, 'rb') do |zip_file| diff --git a/test/gentestfiles.rb b/test/gentestfiles.rb index c46e9c22..756344e3 100755 --- a/test/gentestfiles.rb +++ b/test/gentestfiles.rb @@ -80,8 +80,8 @@ def self.create_test_zips "zip -q #{TEST_ZIP1.zip_name} -d test/data/file2.txt" ) - File.open('test/data/generated/empty.txt', 'w') {} - File.open('test/data/generated/empty_chmod640.txt', 'w') {} + File.open('test/data/generated/empty.txt', 'w') {} # Empty file. + File.open('test/data/generated/empty_chmod640.txt', 'w') {} # Empty file. ::File.chmod(0o640, 'test/data/generated/empty_chmod640.txt') File.open('test/data/generated/short.txt', 'w') { |file| file << 'ABCDEF' } diff --git a/test/settings_test.rb b/test/settings_test.rb index 6e33573c..a0c6906d 100644 --- a/test/settings_test.rb +++ b/test/settings_test.rb @@ -73,8 +73,7 @@ def test_false_warn_invalid_date Zip.warn_invalid_date = false assert_output('', '') do - ::Zip::File.open(test_file) do |_zf| - end + ::Zip::File.open(test_file) {} # Do nothing with the open file. end end @@ -83,8 +82,7 @@ def test_true_warn_invalid_date Zip.warn_invalid_date = true assert_output('', /invalid date\/time in zip entry/) do - ::Zip::File.open(test_file) do |_zf| - end + ::Zip::File.open(test_file) {} # Do nothing with the open file. end end From deac4fa3139e7c254d41615197f065f793dcb712 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sun, 23 May 2021 20:47:04 +0100 Subject: [PATCH 078/311] Fix Style/CommentAnnotation cop. --- .rubocop_todo.yml | 9 --------- test/file_test.rb | 2 +- test/zip64_full_test.rb | 2 +- 3 files changed, 2 insertions(+), 11 deletions(-) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 5a4db7b0..b98ae8cf 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -107,15 +107,6 @@ Style/ClassAndModuleChildren: - 'lib/zip/extra_field/zip64.rb' - 'lib/zip/extra_field/zip64_placeholder.rb' -# Offense count: 2 -# Cop supports --auto-correct. -# Configuration parameters: Keywords. -# Keywords: TODO, FIXME, OPTIMIZE, HACK, REVIEW, NOTE -Style/CommentAnnotation: - Exclude: - - 'test/file_test.rb' - - 'test/zip64_full_test.rb' - # Offense count: 1 # Cop supports --auto-correct. Style/Dir: diff --git a/test/file_test.rb b/test/file_test.rb index 6f1e109c..a24611b6 100644 --- a/test/file_test.rb +++ b/test/file_test.rb @@ -134,7 +134,7 @@ def test_open_buffer_no_op_does_not_change_file test_zip = File.join(tmp, 'test.zip') FileUtils.cp 'test/data/rubycode.zip', test_zip - # Note: this may change the file if it is opened with r+b instead of rb. + # NOTE: this may change the file if it is opened with r+b instead of rb. # The 'extra fields' in this particular zip file get reordered. File.open(test_zip, 'rb') do |file| Zip::File.open_buffer(file) do diff --git a/test/zip64_full_test.rb b/test/zip64_full_test.rb index 769709e9..6f900bb4 100644 --- a/test/zip64_full_test.rb +++ b/test/zip64_full_test.rb @@ -43,7 +43,7 @@ def test_large_zip_file assert_equal last_text, zf.read('last_file.txt') end - # note: if this fails, be sure you have UnZip version 6.0 or newer + # NOTE: if this fails, be sure you have UnZip version 6.0 or newer # as this is the first version to support zip64 extensions # but some OSes (*cough* OSX) still bundle a 5.xx release assert system("unzip -tqq #{test_filename}"), 'third-party zip validation failed' From e2c16991e5e70e457cb5bb15533c3f8166169f63 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sun, 23 May 2021 20:51:35 +0100 Subject: [PATCH 079/311] Fix Style/Dir cop. --- .rubocop_todo.yml | 6 ------ test/file_test.rb | 2 +- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index b98ae8cf..c92dcad9 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -107,12 +107,6 @@ Style/ClassAndModuleChildren: - 'lib/zip/extra_field/zip64.rb' - 'lib/zip/extra_field/zip64_placeholder.rb' -# Offense count: 1 -# Cop supports --auto-correct. -Style/Dir: - Exclude: - - 'test/file_test.rb' - # Offense count: 2 Style/DocumentDynamicEvalDefinition: Exclude: diff --git a/test/file_test.rb b/test/file_test.rb index a24611b6..3a9e903a 100644 --- a/test/file_test.rb +++ b/test/file_test.rb @@ -687,7 +687,7 @@ def test_preserve_file_order end def test_streaming - fname = ::File.join(::File.expand_path(::File.dirname(__FILE__)), '../README.md') + fname = ::File.join(__dir__, '..', 'README.md') zname = 'test/data/generated/README.zip' Zip::File.open(zname, Zip::File::CREATE) do |zipfile| zipfile.get_output_stream(File.basename(fname)) do |f| From 8fa35de52892756fc8d8e3ed56830c2130d4d26c Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sun, 23 May 2021 20:59:27 +0100 Subject: [PATCH 080/311] Fix Style/FormatStringToken cop. --- .rubocop_todo.yml | 6 ------ test/ioextras/abstract_output_stream_test.rb | 2 +- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index c92dcad9..fac7500e 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -117,12 +117,6 @@ Style/DocumentDynamicEvalDefinition: Style/Documentation: Enabled: false -# Offense count: 2 -# Configuration parameters: MaxUnannotatedPlaceholdersAllowed, IgnoredMethods. -# SupportedStyles: annotated, template, unannotated -Style/FormatStringToken: - EnforcedStyle: unannotated - # Offense count: 1 # Cop supports --auto-correct. # Configuration parameters: AllowSplatArgument. diff --git a/test/ioextras/abstract_output_stream_test.rb b/test/ioextras/abstract_output_stream_test.rb index d4a8d0a8..80218ec7 100644 --- a/test/ioextras/abstract_output_stream_test.rb +++ b/test/ioextras/abstract_output_stream_test.rb @@ -71,7 +71,7 @@ def test_print end def test_printf - @output_stream.printf('%d %04x', 123, 123) + @output_stream.printf('%d %04x', dec: 123, hex: 123) assert_equal('123 007b', @output_stream.buffer) end From 1b3f4bb7b8708cc962b27226cf3a8a1879d89625 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sun, 23 May 2021 21:05:46 +0100 Subject: [PATCH 081/311] Fix Style/HashConversion cop. --- .rubocop_todo.yml | 7 ------- lib/zip/entry_set.rb | 2 +- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index fac7500e..e9cb7f22 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -117,13 +117,6 @@ Style/DocumentDynamicEvalDefinition: Style/Documentation: Enabled: false -# Offense count: 1 -# Cop supports --auto-correct. -# Configuration parameters: AllowSplatArgument. -Style/HashConversion: - Exclude: - - 'lib/zip/entry_set.rb' - # Offense count: 13 # Cop supports --auto-correct. Style/IfUnlessModifier: diff --git a/lib/zip/entry_set.rb b/lib/zip/entry_set.rb index a5822453..530ef7ef 100644 --- a/lib/zip/entry_set.rb +++ b/lib/zip/entry_set.rb @@ -72,7 +72,7 @@ def glob(pattern, flags = ::File::FNM_PATHNAME | ::File::FNM_DOTMATCH | ::File:: protected def sorted_entries - ::Zip.sort_entries ? Hash[@entry_set.sort] : @entry_set + ::Zip.sort_entries ? @entry_set.sort.to_h : @entry_set end private From efa23a84baf1ca52795f80d7c143404e57c02d05 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sun, 23 May 2021 21:08:53 +0100 Subject: [PATCH 082/311] Fix Style/RedundantBegin cop. --- .rubocop_todo.yml | 6 ------ lib/zip/dos_time.rb | 5 ++--- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index e9cb7f22..d1f7b409 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -176,12 +176,6 @@ Style/OptionalBooleanParameter: - 'lib/zip/file.rb' - 'lib/zip/output_stream.rb' -# Offense count: 1 -# Cop supports --auto-correct. -Style/RedundantBegin: - Exclude: - - 'lib/zip/dos_time.rb' - # Offense count: 1 # Cop supports --auto-correct. Style/RedundantFileExtensionInRequire: diff --git a/lib/zip/dos_time.rb b/lib/zip/dos_time.rb index 264ead28..9203d734 100644 --- a/lib/zip/dos_time.rb +++ b/lib/zip/dos_time.rb @@ -52,9 +52,8 @@ def self.parse_binary_dos_format(bin_dos_date, bin_dos_time) day = (0b11111 & bin_dos_date) month = (0b111100000 & bin_dos_date) >> 5 year = ((0b1111111000000000 & bin_dos_date) >> 9) + 1980 - begin - local(year, month, day, hour, minute, second) - end + + local(year, month, day, hour, minute, second) end if defined? JRUBY_VERSION && Gem::Version.new(JRUBY_VERSION) < '9.2.18.0' From 2b04cc26fa75fc0fbdb266b8957fdbe690bfaa06 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sun, 23 May 2021 21:11:07 +0100 Subject: [PATCH 083/311] Fix Style/RedundantFileExtensionInRequire cop. --- .rubocop_todo.yml | 6 ------ samples/qtzip.rb | 2 +- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index d1f7b409..ee5adfa8 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -176,12 +176,6 @@ Style/OptionalBooleanParameter: - 'lib/zip/file.rb' - 'lib/zip/output_stream.rb' -# Offense count: 1 -# Cop supports --auto-correct. -Style/RedundantFileExtensionInRequire: - Exclude: - - 'samples/qtzip.rb' - # Offense count: 29 # Cop supports --auto-correct. Style/RedundantRegexpEscape: diff --git a/samples/qtzip.rb b/samples/qtzip.rb index 715623a4..51e084cc 100755 --- a/samples/qtzip.rb +++ b/samples/qtzip.rb @@ -7,7 +7,7 @@ require 'Qt' system('rbuic -o zipdialogui.rb zipdialogui.ui') -require 'zipdialogui.rb' +require 'zipdialogui' require 'zip' a = Qt::Application.new(ARGV) From e64132f4fc682b7e8d9327ebe9462beb3b16242e Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sun, 23 May 2021 21:14:33 +0100 Subject: [PATCH 084/311] Fix Style/NegatedIfElseCondition cop. --- .rubocop_todo.yml | 6 ------ samples/qtzip.rb | 8 ++++---- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index ee5adfa8..e994fee0 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -142,12 +142,6 @@ Style/ModuleFunction: Style/MutableConstant: Enabled: false -# Offense count: 2 -# Cop supports --auto-correct. -Style/NegatedIfElseCondition: - Exclude: - - 'samples/qtzip.rb' - # Offense count: 24 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, IgnoredMethods. diff --git a/samples/qtzip.rb b/samples/qtzip.rb index 51e084cc..917255fd 100755 --- a/samples/qtzip.rb +++ b/samples/qtzip.rb @@ -66,14 +66,14 @@ def extract_files end puts "selected_items.size = #{selected_items.size}" puts "unselected_items.size = #{unselected_items.size}" - items = !selected_items.empty? ? selected_items : unselected_items + items = selected_items.empty? ? unselected_items : selected_items puts "items.size = #{items.size}" d = Qt::FileDialog.get_existing_directory(nil, self) - if !d - puts 'No directory chosen' - else + if d zipfile { |zf| items.each { |e| zf.extract(e, File.join(d, e)) } } + else + puts 'No directory chosen' end end From 57fa5013c051780e5d5d21e246e228f15053f2b3 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sun, 23 May 2021 21:28:33 +0100 Subject: [PATCH 085/311] Turn off Lint/EmptyClass cop. --- .rubocop.yml | 3 +++ .rubocop_todo.yml | 7 ------- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/.rubocop.yml b/.rubocop.yml index a51a3608..f6f132e2 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -22,6 +22,9 @@ Layout/LineLength: Exclude: - 'test/**/*.rb' +Lint/EmptyClass: + Enabled: false + # In some cases we just need to catch an exception, rather than # actually handle it. Allow the tests to make use of this shortcut. Lint/SuppressedException: diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index e994fee0..9c650fad 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -23,13 +23,6 @@ Layout/EmptyLinesAroundAttributeAccessor: Layout/FirstArrayElementIndentation: EnforcedStyle: consistent -# Offense count: 3 -# Configuration parameters: AllowComments. -Lint/EmptyClass: - Exclude: - - 'lib/zip/crypto/encryption.rb' - - 'test/decompressor_test.rb' - # Offense count: 7 Lint/MissingSuper: Exclude: From deabe0279862bb68aac6956f5bcdbcb188e2d141 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sun, 23 May 2021 21:36:27 +0100 Subject: [PATCH 086/311] Fix Layout/FirstArrayElementIndentation cop. --- .rubocop_todo.yml | 7 ------- .simplecov | 10 ++++++---- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 9c650fad..19905951 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -16,13 +16,6 @@ Layout/EmptyLinesAroundAttributeAccessor: - 'lib/zip/filesystem.rb' - 'samples/gtk_ruby_zip.rb' -# Offense count: 2 -# Cop supports --auto-correct. -# Configuration parameters: IndentationWidth. -# SupportedStyles: special_inside_parentheses, consistent, align_brackets -Layout/FirstArrayElementIndentation: - EnforcedStyle: consistent - # Offense count: 7 Lint/MissingSuper: Exclude: diff --git a/.simplecov b/.simplecov index 6c400bf6..611a9527 100644 --- a/.simplecov +++ b/.simplecov @@ -9,10 +9,12 @@ SimpleCov::Formatter::LcovFormatter.config do |c| c.single_report_path = 'coverage/lcov.info' end -SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter.new([ - SimpleCov::Formatter::HTMLFormatter, - SimpleCov::Formatter::LcovFormatter -]) +SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter.new( + [ + SimpleCov::Formatter::HTMLFormatter, + SimpleCov::Formatter::LcovFormatter + ] +) SimpleCov.start do # enable_coverage :branch <-- Re-enable this when we move to ruby ~> 2.5. From fe998a5aecda44db180dd6b138ce35d8446af9ea Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sun, 23 May 2021 21:43:11 +0100 Subject: [PATCH 087/311] Fix Layout/EmptyLinesAroundAttributeAccessor cop. --- .rubocop_todo.yml | 10 ---------- lib/zip/extra_field/zip64.rb | 4 +++- lib/zip/filesystem.rb | 1 - samples/gtk_ruby_zip.rb | 3 ++- 4 files changed, 5 insertions(+), 13 deletions(-) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 19905951..c49b22d6 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -6,16 +6,6 @@ # Note that changes in the inspected code, or installation of new # versions of RuboCop, may require this file to be generated again. -# Offense count: 3 -# Cop supports --auto-correct. -# Configuration parameters: AllowAliasSyntax, AllowedMethods. -# AllowedMethods: alias_method, public, protected, private -Layout/EmptyLinesAroundAttributeAccessor: - Exclude: - - 'lib/zip/extra_field/zip64.rb' - - 'lib/zip/filesystem.rb' - - 'samples/gtk_ruby_zip.rb' - # Offense count: 7 Lint/MissingSuper: Exclude: diff --git a/lib/zip/extra_field/zip64.rb b/lib/zip/extra_field/zip64.rb index 2f7c32c9..0e73ce0d 100644 --- a/lib/zip/extra_field/zip64.rb +++ b/lib/zip/extra_field/zip64.rb @@ -3,7 +3,9 @@ module Zip # Info-ZIP Extra for Zip64 size class ExtraField::Zip64 < ExtraField::Generic - attr_accessor :original_size, :compressed_size, :relative_header_offset, :disk_start_number + attr_accessor :compressed_size, :disk_start_number, + :original_size, :relative_header_offset + HEADER_ID = ['0100'].pack('H*') register_map diff --git a/lib/zip/filesystem.rb b/lib/zip/filesystem.rb index 04162ca0..2815069b 100644 --- a/lib/zip/filesystem.rb +++ b/lib/zip/filesystem.rb @@ -66,7 +66,6 @@ def file # similarity with the methods in File class ZipFsFile attr_writer :dir - # protected :dir class ZipFsStat class << self diff --git a/samples/gtk_ruby_zip.rb b/samples/gtk_ruby_zip.rb index ed8410a2..674e8e77 100755 --- a/samples/gtk_ruby_zip.rb +++ b/samples/gtk_ruby_zip.rb @@ -43,7 +43,8 @@ def initialize end class ButtonPanel < Gtk::HButtonBox - attr_reader :open_button, :extract_button + attr_reader :extract_button, :open_button + def initialize super set_layout(Gtk::BUTTONBOX_START) From 3131e6a4aa90b1f887d8c876aa0b3a231488d2ae Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sun, 23 May 2021 21:59:18 +0100 Subject: [PATCH 088/311] Fix/configure Naming/VariableNumber cop. --- .rubocop.yml | 5 +++++ .rubocop_todo.yml | 9 --------- test/file_extract_test.rb | 2 +- 3 files changed, 6 insertions(+), 10 deletions(-) diff --git a/.rubocop.yml b/.rubocop.yml index f6f132e2..4ef52bd1 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -59,6 +59,11 @@ Metrics/MethodLength: Exclude: - 'test/**/*.rb' +# These tests are just better with snake_case numbers. +Naming/VariableNumber: + Exclude: + - 'test/file_permissions_test.rb' + # Set a consistent way of checking types. Style/ClassCheck: EnforcedStyle: kind_of? diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index c49b22d6..8a404912 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -51,15 +51,6 @@ Naming/AccessorMethodName: - 'lib/zip/input_stream.rb' - 'lib/zip/streamable_stream.rb' -# Offense count: 4 -# Configuration parameters: EnforcedStyle, CheckMethodNames, CheckSymbols, AllowedIdentifiers. -# SupportedStyles: snake_case, normalcase, non_integer -# AllowedIdentifiers: capture3, iso8601, rfc1123_date, rfc822, rfc2822, rfc3339 -Naming/VariableNumber: - Exclude: - - 'test/file_extract_test.rb' - - 'test/file_permissions_test.rb' - # Offense count: 4 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle. diff --git a/test/file_extract_test.rb b/test/file_extract_test.rb index 16fba3a9..3df4308b 100644 --- a/test/file_extract_test.rb +++ b/test/file_extract_test.rb @@ -75,7 +75,7 @@ def test_extract_non_entry zf.close if zf end - def test_extract_non_entry_2 + def test_extract_another_non_entry out_file = 'outfile' assert_raises(Errno::ENOENT) do zf = ::Zip::File.new(TEST_ZIP.zip_name) From 55ed74c20e721a8440655407ec6be75c1d0f5831 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sun, 23 May 2021 22:03:33 +0100 Subject: [PATCH 089/311] Fix/configure Style/AccessorGrouping cop. --- .rubocop.yml | 5 +++++ .rubocop_todo.yml | 9 --------- lib/zip/decompressor.rb | 3 +-- 3 files changed, 6 insertions(+), 11 deletions(-) diff --git a/.rubocop.yml b/.rubocop.yml index 4ef52bd1..643cb498 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -64,6 +64,11 @@ Naming/VariableNumber: Exclude: - 'test/file_permissions_test.rb' +# Need to allow accessors in Entry to be separated for doc purposes. +Style/AccessorGrouping: + Exclude: + - 'lib/zip/entry.rb' + # Set a consistent way of checking types. Style/ClassCheck: EnforcedStyle: kind_of? diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 8a404912..38e57302 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -51,15 +51,6 @@ Naming/AccessorMethodName: - 'lib/zip/input_stream.rb' - 'lib/zip/streamable_stream.rb' -# Offense count: 4 -# Cop supports --auto-correct. -# Configuration parameters: EnforcedStyle. -# SupportedStyles: separated, grouped -Style/AccessorGrouping: - Exclude: - - 'lib/zip/decompressor.rb' - - 'lib/zip/entry.rb' - # Offense count: 7 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle. diff --git a/lib/zip/decompressor.rb b/lib/zip/decompressor.rb index 0d29e618..b6bb9cc8 100644 --- a/lib/zip/decompressor.rb +++ b/lib/zip/decompressor.rb @@ -16,8 +16,7 @@ def self.find_by_compression_method(compression_method) decompressor_classes[compression_method] end - attr_reader :input_stream - attr_reader :decompressed_size + attr_reader :decompressed_size, :input_stream def initialize(input_stream, decompressed_size = nil) super() From 984d86ce4baf3ab3e2c12671326a3e49d8f7cfe8 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Mon, 24 May 2021 14:24:01 +0100 Subject: [PATCH 090/311] Configure Style/ModuleFunction cop. --- .rubocop.yml | 5 +++++ .rubocop_todo.yml | 8 -------- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/.rubocop.yml b/.rubocop.yml index 643cb498..d9fa5a77 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -79,6 +79,11 @@ Style/HashEachMethods: Style/HashTransformValues: Enabled: true +# Allow non-default behaviour for Zip. +Style/ModuleFunction: + Exclude: + - 'lib/zip.rb' + # Allow this multi-line block chain as it actually reads better # than the alternatives. Style/MultilineBlockChain: diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 38e57302..6eda637a 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -85,14 +85,6 @@ Style/IfUnlessModifier: - 'lib/zip/pass_thru_decompressor.rb' - 'lib/zip/streamable_stream.rb' -# Offense count: 1 -# Cop supports --auto-correct. -# Configuration parameters: EnforcedStyle, Autocorrect. -# SupportedStyles: module_function, extend_self, forbidden -Style/ModuleFunction: - Exclude: - - 'lib/zip.rb' - # Offense count: 56 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle. From f6cebc651440c708d48ea2a05501e0f422d1d606 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Mon, 24 May 2021 20:58:54 +0100 Subject: [PATCH 091/311] Add rubocop-rake. --- .rubocop.yml | 3 +++ rubyzip.gemspec | 1 + 2 files changed, 4 insertions(+) diff --git a/.rubocop.yml b/.rubocop.yml index d9fa5a77..7c84a9fb 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -1,3 +1,6 @@ +require: + - rubocop-rake + inherit_from: .rubocop_todo.yml # Set this to the minimum supported ruby in the gemspec. Otherwise diff --git a/rubyzip.gemspec b/rubyzip.gemspec index b331648d..2469f54c 100644 --- a/rubyzip.gemspec +++ b/rubyzip.gemspec @@ -26,6 +26,7 @@ Gem::Specification.new do |s| s.add_development_dependency 'minitest', '~> 5.4' s.add_development_dependency 'rake', '~> 12.3.3' s.add_development_dependency 'rubocop', '~> 1.12.0' + s.add_development_dependency 'rubocop-rake', '~> 0.5.0' s.add_development_dependency 'simplecov', '~> 0.18.0' s.add_development_dependency 'simplecov-lcov', '~> 0.8' end From 255480f22f37527fda47460a2527da7c36772deb Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Mon, 24 May 2021 21:15:19 +0100 Subject: [PATCH 092/311] Add rubocop-performance. --- .rubocop.yml | 1 + .rubocop_todo.yml | 40 +++++++++++++++++++++++++++++++++++++--- rubyzip.gemspec | 1 + 3 files changed, 39 insertions(+), 3 deletions(-) diff --git a/.rubocop.yml b/.rubocop.yml index 7c84a9fb..7f1fc683 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -1,4 +1,5 @@ require: + - rubocop-performance - rubocop-rake inherit_from: .rubocop_todo.yml diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 6eda637a..67b40a52 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -1,6 +1,6 @@ # This configuration was generated by # `rubocop --auto-gen-config` -# on 2021-05-23 16:31:53 UTC using RuboCop version 1.12.1. +# on 2021-05-24 20:07:32 UTC using RuboCop version 1.12.1. # The point is for the user to remove these configuration records # one by one as the offenses are removed from the code base. # Note that changes in the inspected code, or installation of new @@ -51,6 +51,31 @@ Naming/AccessorMethodName: - 'lib/zip/input_stream.rb' - 'lib/zip/streamable_stream.rb' +# Offense count: 3 +# Cop supports --auto-correct. +Performance/BlockGivenWithExplicitBlock: + Exclude: + - 'lib/zip/entry.rb' + +# Offense count: 2 +Performance/FixedSize: + Exclude: + - 'test/ioextras/abstract_output_stream_test.rb' + +# Offense count: 2 +# Cop supports --auto-correct. +Performance/RegexpMatch: + Exclude: + - 'test/file_test.rb' + - 'test/gentestfiles.rb' + +# Offense count: 1 +# Cop supports --auto-correct. +# Configuration parameters: AutoCorrect. +Performance/StringInclude: + Exclude: + - 'test/file_test.rb' + # Offense count: 7 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle. @@ -85,12 +110,21 @@ Style/IfUnlessModifier: - 'lib/zip/pass_thru_decompressor.rb' - 'lib/zip/streamable_stream.rb' -# Offense count: 56 +# Offense count: 12 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle. # SupportedStyles: literals, strict Style/MutableConstant: - Enabled: false + Exclude: + - 'lib/zip/extra_field.rb' + - 'lib/zip/file.rb' + - 'lib/zip/ioextras.rb' + - 'test/case_sensitivity_test.rb' + - 'test/entry_set_test.rb' + - 'test/extra_field_ut_test.rb' + - 'test/filesystem/dir_iterator_test.rb' + - 'test/gentestfiles.rb' + - 'test/ioextras/abstract_input_stream_test.rb' # Offense count: 24 # Cop supports --auto-correct. diff --git a/rubyzip.gemspec b/rubyzip.gemspec index 2469f54c..295601a0 100644 --- a/rubyzip.gemspec +++ b/rubyzip.gemspec @@ -26,6 +26,7 @@ Gem::Specification.new do |s| s.add_development_dependency 'minitest', '~> 5.4' s.add_development_dependency 'rake', '~> 12.3.3' s.add_development_dependency 'rubocop', '~> 1.12.0' + s.add_development_dependency 'rubocop-performance', '~> 1.10.0' s.add_development_dependency 'rubocop-rake', '~> 0.5.0' s.add_development_dependency 'simplecov', '~> 0.18.0' s.add_development_dependency 'simplecov-lcov', '~> 0.8' From 530afe5d0c63e703c4a1e45010e42627b66e75d2 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Mon, 24 May 2021 21:50:20 +0100 Subject: [PATCH 093/311] Fix Performance/BlockGivenWithExplicitBlock cop. --- .rubocop_todo.yml | 6 ------ lib/zip/entry.rb | 6 +++--- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 67b40a52..44c55efb 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -51,12 +51,6 @@ Naming/AccessorMethodName: - 'lib/zip/input_stream.rb' - 'lib/zip/streamable_stream.rb' -# Offense count: 3 -# Cop supports --auto-correct. -Performance/BlockGivenWithExplicitBlock: - Exclude: - - 'lib/zip/entry.rb' - # Offense count: 2 Performance/FixedSize: Exclude: diff --git a/lib/zip/entry.rb b/lib/zip/entry.rb index 453c174b..08e10cd0 100644 --- a/lib/zip/entry.rb +++ b/lib/zip/entry.rb @@ -547,7 +547,7 @@ def <=>(other) # Warning: may behave weird with symlinks. def get_input_stream(&block) if @ftype == :directory - yield ::Zip::NullInputStream if block_given? + yield ::Zip::NullInputStream if block ::Zip::NullInputStream elsif @filepath case @ftype @@ -556,7 +556,7 @@ def get_input_stream(&block) when :symlink linkpath = ::File.readlink(@filepath) stringio = ::StringIO.new(linkpath) - yield(stringio) if block_given? + yield(stringio) if block stringio else raise "unknown @file_type #{@ftype}" @@ -565,7 +565,7 @@ def get_input_stream(&block) zis = ::Zip::InputStream.new(@zipfile, local_header_offset) zis.instance_variable_set(:@complete_entry, self) zis.get_next_entry - if block_given? + if block begin yield(zis) ensure From ed21f9cf17cca90a1200ee201204edcad47c2753 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Mon, 24 May 2021 21:55:40 +0100 Subject: [PATCH 094/311] Fix Performance/StringInclude cop. --- .rubocop_todo.yml | 7 ------- test/file_test.rb | 4 ++-- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 44c55efb..9074359d 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -63,13 +63,6 @@ Performance/RegexpMatch: - 'test/file_test.rb' - 'test/gentestfiles.rb' -# Offense count: 1 -# Cop supports --auto-correct. -# Configuration parameters: AutoCorrect. -Performance/StringInclude: - Exclude: - - 'test/file_test.rb' - # Offense count: 7 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle. diff --git a/test/file_test.rb b/test/file_test.rb index 3a9e903a..52fe356e 100644 --- a/test/file_test.rb +++ b/test/file_test.rb @@ -699,14 +699,14 @@ def test_streaming File.open(zname, 'rb') do |f| Zip::File.open_buffer(f) do |zipfile| zipfile.each do |entry| - next unless entry.name =~ /README.md/ + next unless entry.name.include?('README.md') data = zipfile.read(entry) end end end assert data - assert data =~ /Simonov/ + assert data.include?('Simonov') end def test_nonexistant_zip From 7af12ca887e6215bb044e31a95820d9c60a8bccc Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Mon, 24 May 2021 21:59:26 +0100 Subject: [PATCH 095/311] Fix Performance/FixedSize cop. --- .rubocop_todo.yml | 5 ----- test/ioextras/abstract_output_stream_test.rb | 16 +++++++++------- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 9074359d..b196cfe9 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -51,11 +51,6 @@ Naming/AccessorMethodName: - 'lib/zip/input_stream.rb' - 'lib/zip/streamable_stream.rb' -# Offense count: 2 -Performance/FixedSize: - Exclude: - - 'test/ioextras/abstract_output_stream_test.rb' - # Offense count: 2 # Cop supports --auto-correct. Performance/RegexpMatch: diff --git a/test/ioextras/abstract_output_stream_test.rb b/test/ioextras/abstract_output_stream_test.rb index 80218ec7..619107c0 100644 --- a/test/ioextras/abstract_output_stream_test.rb +++ b/test/ioextras/abstract_output_stream_test.rb @@ -32,13 +32,15 @@ def teardown end def test_write - count = @output_stream.write('a little string') - assert_equal('a little string', @output_stream.buffer) - assert_equal('a little string'.length, count) - - count = @output_stream.write('. a little more') - assert_equal('a little string. a little more', @output_stream.buffer) - assert_equal('. a little more'.length, count) + str1 = 'a little string' + count = @output_stream.write(str1) + assert_equal(str1, @output_stream.buffer) + assert_equal(str1.length, count) + + str2 = '. a little more' + count = @output_stream.write(str2) + assert_equal(str1 + str2, @output_stream.buffer) + assert_equal(str2.length, count) end def test_print From 86758175f09dca80771f19fbefd02a1e81e50b5d Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Mon, 24 May 2021 22:02:39 +0100 Subject: [PATCH 096/311] Fix Performance/RegexpMatch cop. --- .rubocop_todo.yml | 7 ------- test/gentestfiles.rb | 2 +- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index b196cfe9..61eb9caf 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -51,13 +51,6 @@ Naming/AccessorMethodName: - 'lib/zip/input_stream.rb' - 'lib/zip/streamable_stream.rb' -# Offense count: 2 -# Cop supports --auto-correct. -Performance/RegexpMatch: - Exclude: - - 'test/file_test.rb' - - 'test/gentestfiles.rb' - # Offense count: 7 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle. diff --git a/test/gentestfiles.rb b/test/gentestfiles.rb index 756344e3..66818694 100755 --- a/test/gentestfiles.rb +++ b/test/gentestfiles.rb @@ -106,7 +106,7 @@ def self.create_test_zips "zip -q #{TEST_ZIP2.zip_name} #{TEST_ZIP2.entry_names.join(' ')}" ) - if RUBY_PLATFORM =~ /mswin|mingw|cygwin/ + if RUBY_PLATFORM.match?(/mswin|mingw|cygwin/) raise "failed to add comment to test zip '#{TEST_ZIP2.zip_name}'" \ unless system( "echo #{TEST_ZIP2.comment}| zip -zq #{TEST_ZIP2.zip_name}\"" From ca516df01e6f72d6e6fb87846619e6c5cda0b0ae Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Tue, 25 May 2021 19:42:05 +0100 Subject: [PATCH 097/311] Remove stale Style/DocumentDynamicEvalDefinition cop. --- .rubocop_todo.yml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 61eb9caf..ac505127 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -65,11 +65,6 @@ Style/ClassAndModuleChildren: - 'lib/zip/extra_field/zip64.rb' - 'lib/zip/extra_field/zip64_placeholder.rb' -# Offense count: 2 -Style/DocumentDynamicEvalDefinition: - Exclude: - - 'test/test_helper.rb' - # Offense count: 24 # Configuration parameters: AllowedConstants. Style/Documentation: From ce08405c1afb874569e207cb094df1e757e3de9d Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Tue, 25 May 2021 21:50:06 +0100 Subject: [PATCH 098/311] Fix (most) Style/MutableConstant cop errors. The last one, in `ExtraField` needs a sizeable refactor to fix. --- .rubocop_todo.yml | 10 +--------- lib/zip/file.rb | 2 +- lib/zip/ioextras.rb | 2 -- test/case_sensitivity_test.rb | 6 ++++-- test/entry_set_test.rb | 2 +- test/extra_field_ut_test.rb | 2 +- test/filesystem/dir_iterator_test.rb | 2 +- test/gentestfiles.rb | 8 +++++--- test/ioextras/abstract_input_stream_test.rb | 11 +++++++---- 9 files changed, 21 insertions(+), 24 deletions(-) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index ac505127..fffe19f4 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -80,21 +80,13 @@ Style/IfUnlessModifier: - 'lib/zip/pass_thru_decompressor.rb' - 'lib/zip/streamable_stream.rb' -# Offense count: 12 +# Offense count: 1 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle. # SupportedStyles: literals, strict Style/MutableConstant: Exclude: - 'lib/zip/extra_field.rb' - - 'lib/zip/file.rb' - - 'lib/zip/ioextras.rb' - - 'test/case_sensitivity_test.rb' - - 'test/entry_set_test.rb' - - 'test/extra_field_ut_test.rb' - - 'test/filesystem/dir_iterator_test.rb' - - 'test/gentestfiles.rb' - - 'test/ioextras/abstract_input_stream_test.rb' # Offense count: 24 # Cop supports --auto-correct. diff --git a/lib/zip/file.rb b/lib/zip/file.rb index 2efc8cd3..10a3f495 100644 --- a/lib/zip/file.rb +++ b/lib/zip/file.rb @@ -51,7 +51,7 @@ class File < CentralDirectory MAX_SEGMENT_SIZE = 3_221_225_472 MIN_SEGMENT_SIZE = 65_536 DATA_BUFFER_SIZE = 8192 - IO_METHODS = [:tell, :seek, :read, :eof, :close] + IO_METHODS = [:tell, :seek, :read, :eof, :close].freeze DEFAULT_OPTIONS = { restore_ownership: false, diff --git a/lib/zip/ioextras.rb b/lib/zip/ioextras.rb index 879a5145..df208da1 100644 --- a/lib/zip/ioextras.rb +++ b/lib/zip/ioextras.rb @@ -4,8 +4,6 @@ module Zip module IOExtras #:nodoc: CHUNK_SIZE = 131_072 - RANGE_ALL = 0..-1 - class << self def copy_stream(ostream, istream) ostream.write(istream.read(CHUNK_SIZE, +'')) until istream.eof? diff --git a/test/case_sensitivity_test.rb b/test/case_sensitivity_test.rb index 0a9844b1..af4443db 100644 --- a/test/case_sensitivity_test.rb +++ b/test/case_sensitivity_test.rb @@ -5,8 +5,10 @@ class ZipCaseSensitivityTest < MiniTest::Test include CommonZipFileFixture - SRC_FILES = [['test/data/file1.txt', 'testfile.rb'], - ['test/data/file2.txt', 'testFILE.rb']] + SRC_FILES = [ + ['test/data/file1.txt', 'testfile.rb'], + ['test/data/file2.txt', 'testFILE.rb'] + ].freeze def teardown ::Zip.reset! diff --git a/test/entry_set_test.rb b/test/entry_set_test.rb index 4c9f2021..fa91b77d 100644 --- a/test/entry_set_test.rb +++ b/test/entry_set_test.rb @@ -10,7 +10,7 @@ class ZipEntrySetTest < MiniTest::Test ::Zip::Entry.new('zipfile.zip', 'name4', comment: 'comment1'), ::Zip::Entry.new('zipfile.zip', 'name5', comment: 'comment1'), ::Zip::Entry.new('zipfile.zip', 'name6', comment: 'comment1') - ] + ].freeze def setup @zip_entry_set = ::Zip::EntrySet.new(ZIP_ENTRIES) diff --git a/test/extra_field_ut_test.rb b/test/extra_field_ut_test.rb index 74cb21f6..9f16b616 100644 --- a/test/extra_field_ut_test.rb +++ b/test/extra_field_ut_test.rb @@ -11,7 +11,7 @@ class ZipExtraFieldUTTest < MiniTest::Test ["UT\x09\x00\x05PS>APS>A", 0b101, true, false, false], ["UT\x09\x00\x06PS>APS>A", 0b110, false, false, true], ["UT\x13\x00\x07PS>APS>APS>A", 0b111, false, false, false] - ] + ].freeze def test_parse PARSE_TESTS.each do |bin, flags, a, c, m| diff --git a/test/filesystem/dir_iterator_test.rb b/test/filesystem/dir_iterator_test.rb index 6223b44e..bf964c41 100644 --- a/test/filesystem/dir_iterator_test.rb +++ b/test/filesystem/dir_iterator_test.rb @@ -4,7 +4,7 @@ require 'zip/filesystem' class ZipFsDirIteratorTest < MiniTest::Test - FILENAME_ARRAY = %w[f1 f2 f3 f4 f5 f6] + FILENAME_ARRAY = %w[f1 f2 f3 f4 f5 f6].freeze def setup @dir_iter = ::Zip::FileSystem::ZipFsDirIterator.new(FILENAME_ARRAY) diff --git a/test/gentestfiles.rb b/test/gentestfiles.rb index 66818694..5bd82241 100755 --- a/test/gentestfiles.rb +++ b/test/gentestfiles.rb @@ -14,9 +14,11 @@ class TestFiles EMPTY_TEST_DIR = 'test/data/generated/emptytestdir' - ASCII_TEST_FILES = [RANDOM_ASCII_FILE1, RANDOM_ASCII_FILE2, RANDOM_ASCII_FILE3] - BINARY_TEST_FILES = [RANDOM_BINARY_FILE1, RANDOM_BINARY_FILE2] - TEST_DIRECTORIES = [EMPTY_TEST_DIR] + ASCII_TEST_FILES = [ + RANDOM_ASCII_FILE1, RANDOM_ASCII_FILE2, RANDOM_ASCII_FILE3 + ].freeze + BINARY_TEST_FILES = [RANDOM_BINARY_FILE1, RANDOM_BINARY_FILE2].freeze + TEST_DIRECTORIES = [EMPTY_TEST_DIR].freeze TEST_FILES = [ASCII_TEST_FILES, BINARY_TEST_FILES, EMPTY_TEST_DIR].flatten! class << self diff --git a/test/ioextras/abstract_input_stream_test.rb b/test/ioextras/abstract_input_stream_test.rb index 6bc87d4d..04b20990 100644 --- a/test/ioextras/abstract_input_stream_test.rb +++ b/test/ioextras/abstract_input_stream_test.rb @@ -6,10 +6,13 @@ class AbstractInputStreamTest < MiniTest::Test # AbstractInputStream subclass that provides a read method - TEST_LINES = ["Hello world#{$INPUT_RECORD_SEPARATOR}", - "this is the second line#{$INPUT_RECORD_SEPARATOR}", - 'this is the last line'] + TEST_LINES = [ + "Hello world#{$INPUT_RECORD_SEPARATOR}", + "this is the second line#{$INPUT_RECORD_SEPARATOR}", + 'this is the last line' + ].freeze TEST_STRING = TEST_LINES.join + class TestAbstractInputStream include ::Zip::IOExtras::AbstractInputStream @@ -59,7 +62,7 @@ def test_gets_multi_char_seperator 'x' * 48 + "\r\n", 'y' * 49 + "\r\n", 'rest' - ] + ].freeze def test_gets_mulit_char_seperator_split io = TestAbstractInputStream.new(LONG_LINES.join) From e15f80718dfdaf72f63e6a7ede2e3949e9baea8b Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sat, 22 May 2021 18:51:19 +0100 Subject: [PATCH 099/311] Remove stale task from the Rakefile. --- Rakefile | 7 ------- 1 file changed, 7 deletions(-) diff --git a/Rakefile b/Rakefile index 1fc36185..4326fdf9 100644 --- a/Rakefile +++ b/Rakefile @@ -14,10 +14,3 @@ Rake::TestTask.new(:test) do |test| end RuboCop::RakeTask.new - -# Rake::TestTask.new(:zip64_full_test) do |test| -# test.libs << File.join(File.dirname(__FILE__), 'lib') -# test.libs << File.join(File.dirname(__FILE__), 'test') -# test.pattern = File.join(File.dirname(__FILE__), 'test/zip64_full_test.rb') -# test.verbose = true -# end From c438defe73ad204e32f40557ee3e477cebd9c21b Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sun, 23 May 2021 10:15:14 +0100 Subject: [PATCH 100/311] Remove stale .cvsignore file. --- .gitignore | 1 + samples/.cvsignore | 4 ---- 2 files changed, 1 insertion(+), 4 deletions(-) delete mode 100644 samples/.cvsignore diff --git a/.gitignore b/.gitignore index f870b5cb..08ebec6b 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ Gemfile.lock +samples/*.zip +samples/*.zip.* +samples/zipdialogui.rb coverage pkg/ .ruby-gemset diff --git a/samples/.cvsignore b/samples/.cvsignore deleted file mode 100644 index cf125d08..00000000 --- a/samples/.cvsignore +++ /dev/null @@ -1,4 +0,0 @@ -example.zip -exampleout.zip -filesystem.zip -zipdialogui.rb From 15f3a98ff56b823260c0c6de17091cbf6fd46af4 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sun, 23 May 2021 16:17:13 +0100 Subject: [PATCH 101/311] Fix gentestfiles: create ASCII files with wider char range. --- test/gentestfiles.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/gentestfiles.rb b/test/gentestfiles.rb index 5bd82241..3a537dcd 100755 --- a/test/gentestfiles.rb +++ b/test/gentestfiles.rb @@ -42,7 +42,7 @@ def create_test_files def create_random_ascii(filename, size) File.open(filename, 'wb') do |file| - file << rand while file.tell < size + file << (0...size).map { rand(33..126).chr }.join end end From 667328cc93cd2f4b9b3b5992eac7eff1fb18a713 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sun, 23 May 2021 16:18:16 +0100 Subject: [PATCH 102/311] Fix gentestfiles: create binary files with bytes other than zero. --- test/gentestfiles.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/gentestfiles.rb b/test/gentestfiles.rb index 3a537dcd..4c6e4f09 100755 --- a/test/gentestfiles.rb +++ b/test/gentestfiles.rb @@ -48,7 +48,7 @@ def create_random_ascii(filename, size) def create_random_binary(filename, size) File.open(filename, 'wb') do |file| - file << [rand].pack('V') while file.tell < size + file << (0...size).map { rand(255) }.pack('C*') end end From 3011c4a7051523f7a4eb8aff839582330abefa8e Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sun, 23 May 2021 16:19:19 +0100 Subject: [PATCH 103/311] Fix gentestfiles: remove unused constants. --- test/gentestfiles.rb | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/gentestfiles.rb b/test/gentestfiles.rb index 4c6e4f09..5892fd6c 100755 --- a/test/gentestfiles.rb +++ b/test/gentestfiles.rb @@ -18,8 +18,6 @@ class TestFiles RANDOM_ASCII_FILE1, RANDOM_ASCII_FILE2, RANDOM_ASCII_FILE3 ].freeze BINARY_TEST_FILES = [RANDOM_BINARY_FILE1, RANDOM_BINARY_FILE2].freeze - TEST_DIRECTORIES = [EMPTY_TEST_DIR].freeze - TEST_FILES = [ASCII_TEST_FILES, BINARY_TEST_FILES, EMPTY_TEST_DIR].flatten! class << self def create_test_files From aa3a2cba986ec8ac61d9afcdc04188f167d9e1c8 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sun, 23 May 2021 16:20:10 +0100 Subject: [PATCH 104/311] Fix gentestfiles: fix some overly long lines. --- test/gentestfiles.rb | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/test/gentestfiles.rb b/test/gentestfiles.rb index 5892fd6c..e5353cae 100755 --- a/test/gentestfiles.rb +++ b/test/gentestfiles.rb @@ -152,7 +152,11 @@ def self.create_test_zips ], 'my zip comment' ) - TEST_ZIP3 = TestZipFile.new('test/data/generated/test1.zip', %w[test/data/file1.txt]) - TEST_ZIP4 = TestZipFile.new('test/data/generated/zipWithDir.zip', ['test/data/file1.txt', - TestFiles::EMPTY_TEST_DIR]) + TEST_ZIP3 = TestZipFile.new( + 'test/data/generated/test1.zip', %w[test/data/file1.txt] + ) + TEST_ZIP4 = TestZipFile.new( + 'test/data/generated/zipWithDir.zip', + ['test/data/file1.txt', TestFiles::EMPTY_TEST_DIR] + ) end From 922afbf5bb300ffc03a1ef9d7907e1c8d6f5e03b Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Tue, 25 May 2021 22:04:03 +0100 Subject: [PATCH 105/311] Fix gentestfiles: remove redundant she-bang. --- test/gentestfiles.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/test/gentestfiles.rb b/test/gentestfiles.rb index e5353cae..a95743b3 100755 --- a/test/gentestfiles.rb +++ b/test/gentestfiles.rb @@ -1,4 +1,3 @@ -#!/usr/bin/env ruby # frozen_string_literal: true $VERBOSE = true From dea45613bbcaf3476fa206c478f0a099d87e6aa3 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sun, 13 Sep 2020 18:49:07 +0100 Subject: [PATCH 106/311] Test non-block version of File#get_output_stream. --- test/file_test.rb | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/test/file_test.rb b/test/file_test.rb index 52fe356e..0b494668 100644 --- a/test/file_test.rb +++ b/test/file_test.rb @@ -73,9 +73,12 @@ def test_get_output_stream assert_equal(count + 1, zf.size) assert_equal('Putting stuff in new_entry.txt', zf.read('new_entry.txt')) - zf.get_output_stream(zf.get_entry('test/data/generated/empty.txt')) do |os| - os.write 'Putting stuff in data/generated/empty.txt' - end + # Use the non-block version of `get_output_stream` not tested elsewhere. + ostream = + zf.get_output_stream(zf.get_entry('test/data/generated/empty.txt')) + ostream.write 'Putting stuff in data/generated/empty.txt' + ostream.close + assert_equal(count + 1, zf.size) assert_equal('Putting stuff in data/generated/empty.txt', zf.read('test/data/generated/empty.txt')) From e70e1d3080efc09fa83963b0b2b08116532ee760 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Wed, 26 May 2021 13:35:16 +0100 Subject: [PATCH 107/311] Add `InputStream#size`. This will enable `InputStream` to be used with external APIs that expect to be able to query the expected size of data they will receive, such as S3. Fixes #451. --- lib/zip/input_stream.rb | 7 +++++++ test/input_stream_test.rb | 12 ++++++++++++ 2 files changed, 19 insertions(+) diff --git a/lib/zip/input_stream.rb b/lib/zip/input_stream.rb index d044a294..3dce2fcc 100644 --- a/lib/zip/input_stream.rb +++ b/lib/zip/input_stream.rb @@ -87,6 +87,13 @@ def sysread(length = nil, outbuf = '') @decompressor.read(length, outbuf) end + # Returns the size of the current entry, or `nil` if there isn't one. + def size + return if @current_entry.nil? + + @current_entry.size + end + class << self # Same as #initialize but if a block is passed the opened # stream is passed to the block and closed when the block diff --git a/test/input_stream_test.rb b/test/input_stream_test.rb index 2d192abd..3d0e6217 100644 --- a/test/input_stream_test.rb +++ b/test/input_stream_test.rb @@ -67,6 +67,18 @@ def test_open_io_like_with_block end end + def test_size_no_entry + zis = ::Zip::InputStream.open(TestZipFile::TEST_ZIP2.zip_name) + assert_nil(zis.size) + end + + def test_size_with_entry + ::Zip::InputStream.open(TestZipFile::TEST_ZIP2.zip_name) do |zis| + zis.get_next_entry + assert_equal(123_702, zis.size) + end + end + def test_incomplete_reads ::Zip::InputStream.open(TestZipFile::TEST_ZIP2.zip_name) do |zis| entry = zis.get_next_entry # longAscii.txt From 01acd0488a0b5b67547f34aef150488a6ce6da19 Mon Sep 17 00:00:00 2001 From: Ariel Zelivansky Date: Sun, 23 Sep 2018 12:20:03 +0300 Subject: [PATCH 108/311] Quick fix to prevent crash when mtime is nil --- lib/zip/entry.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/zip/entry.rb b/lib/zip/entry.rb index 08e10cd0..27e150e1 100644 --- a/lib/zip/entry.rb +++ b/lib/zip/entry.rb @@ -106,7 +106,7 @@ def extra=(field) end def time - if @extra['UniversalTime'] + if @extra['UniversalTime'] && @extra['UniversalTime'].mtime @extra['UniversalTime'].mtime elsif @extra['NTFS'] @extra['NTFS'].mtime From f54e3b7f56dfbda84704782419d1ce18c1ba8ae6 Mon Sep 17 00:00:00 2001 From: Ariel Zelivansky Date: Sun, 23 Sep 2018 13:05:53 +0300 Subject: [PATCH 109/311] Fix improvement & fix NTFS --- lib/zip/entry.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/zip/entry.rb b/lib/zip/entry.rb index 27e150e1..41a14edc 100644 --- a/lib/zip/entry.rb +++ b/lib/zip/entry.rb @@ -106,9 +106,9 @@ def extra=(field) end def time - if @extra['UniversalTime'] && @extra['UniversalTime'].mtime + if @extra['UniversalTime'] && !@extra['UniversalTime'].mtime.nil? @extra['UniversalTime'].mtime - elsif @extra['NTFS'] + elsif @extra['NTFS'] && !@extra['NTFS'].mtime.nil? @extra['NTFS'].mtime else # Standard time field in central directory has local time From ca51c7ce9bbf8f9341f6d232d337d794274c3a71 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sun, 30 May 2021 11:25:43 +0100 Subject: [PATCH 110/311] Remove Travis config as its community offering is closing. Our GitHub Actions workflows are working well now and integrated with Coveralls, so Travis is no longer needed anyway. --- .travis.yml | 44 -------------------------------------------- README.md | 1 - 2 files changed, 45 deletions(-) delete mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 231e70c8..00000000 --- a/.travis.yml +++ /dev/null @@ -1,44 +0,0 @@ -language: ruby -dist: xenial -cache: bundler -rvm: - - 2.4 - - 2.5 - - 2.6 - - 2.7 - - 3.0 - - ruby-head - - truffleruby-head - - truffleruby -matrix: - fast_finish: true - include: - - rvm: jruby-9.2 - jdk: openjdk8 - - rvm: jruby-9.2 - jdk: openjdk11 - - rvm: jruby-head - jdk: openjdk11 - - rvm: rbx-4 - - name: Rubocop - rvm: 2.4 - script: - - bundle info rubocop - - bundle exec rubocop - allow_failures: - - rvm: ruby-head - - rvm: rbx-4 - - rvm: jruby-head - - rvm: truffleruby-head - - rvm: truffleruby -before_install: - - gem --version -before_script: - - echo `whereis zip` - - echo `whereis unzip` -env: - global: - - JRUBY_OPTS="--debug" - - COVERALLS_PARALLEL=true -notifications: - webhooks: https://coveralls.io/webhook diff --git a/README.md b/README.md index f465ff3b..f5cabcd0 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,6 @@ [![Gem Version](https://badge.fury.io/rb/rubyzip.svg)](http://badge.fury.io/rb/rubyzip) [![Tests](https://github.com/rubyzip/rubyzip/actions/workflows/tests.yml/badge.svg)](https://github.com/rubyzip/rubyzip/actions/workflows/tests.yml) [![Linter](https://github.com/rubyzip/rubyzip/actions/workflows/lint.yml/badge.svg)](https://github.com/rubyzip/rubyzip/actions/workflows/lint.yml) -[![Build Status](https://secure.travis-ci.org/rubyzip/rubyzip.svg)](http://travis-ci.org/rubyzip/rubyzip) [![Code Climate](https://codeclimate.com/github/rubyzip/rubyzip.svg)](https://codeclimate.com/github/rubyzip/rubyzip) [![Coverage Status](https://img.shields.io/coveralls/rubyzip/rubyzip.svg)](https://coveralls.io/r/rubyzip/rubyzip?branch=master) From 1777a3ff53c70b43dafb2e1076cd970c0c83bc00 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Tue, 1 Jun 2021 22:38:43 +0100 Subject: [PATCH 111/311] Make sure `::Zip.force_entry_names_encoding` is reset. It was the one option left out of `::Zip.reset!` for some reason. --- lib/zip.rb | 1 + test/unicode_file_names_and_comments_test.rb | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/zip.rb b/lib/zip.rb index 5e142938..9af80b15 100644 --- a/lib/zip.rb +++ b/lib/zip.rb @@ -58,6 +58,7 @@ def reset! @write_zip64_support = false @warn_invalid_date = true @case_insensitive_match = false + @force_entry_names_encoding = nil @validate_entry_sizes = true end diff --git a/test/unicode_file_names_and_comments_test.rb b/test/unicode_file_names_and_comments_test.rb index 1518171a..ceac94fa 100644 --- a/test/unicode_file_names_and_comments_test.rb +++ b/test/unicode_file_names_and_comments_test.rb @@ -47,7 +47,6 @@ def test_unicode_file_name refute_nil(zip.find_entry(filepath)) end end - ::Zip.force_entry_names_encoding = nil ::File.unlink(FILENAME) end From 22a54853e6c1dc39579e6d18a527ab11b200b637 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Tue, 1 Jun 2021 22:33:47 +0100 Subject: [PATCH 112/311] Reinstate normalising pathname separators to `/`. But only do it after we have set filename encoding appropriately to avoid breaking multibyte characters with `\`s in them. Fixes #324. --- .rubocop_todo.yml | 2 +- lib/zip/entry.rb | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index fffe19f4..75ed58a3 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -20,7 +20,7 @@ Lint/MissingSuper: # Offense count: 5 # Configuration parameters: CountComments, CountAsOne. Metrics/ClassLength: - Max: 601 + Max: 610 # Offense count: 20 # Configuration parameters: IgnoredMethods. diff --git a/lib/zip/entry.rb b/lib/zip/entry.rb index 41a14edc..16336df6 100644 --- a/lib/zip/entry.rb +++ b/lib/zip/entry.rb @@ -296,13 +296,12 @@ def read_local_entry(io) #:nodoc:all set_time(@last_mod_date, @last_mod_time) @name = io.read(@name_length) - extra = io.read(@extra_length) - - @name.tr!('\\', '/') if ::Zip.force_entry_names_encoding @name.force_encoding(::Zip.force_entry_names_encoding) end + @name.tr!('\\', '/') # Normalise filepath separators after encoding set. + extra = io.read(@extra_length) if extra && extra.bytesize != @extra_length raise ::Zip::Error, 'Truncated local zip entry header' end @@ -424,10 +423,13 @@ def read_c_dir_entry(io) #:nodoc:all unpack_c_dir_entry(static_sized_fields_buf) check_c_dir_entry_signature set_time(@last_mod_date, @last_mod_time) + @name = io.read(@name_length) if ::Zip.force_entry_names_encoding @name.force_encoding(::Zip.force_entry_names_encoding) end + @name.tr!('\\', '/') # Normalise filepath separators after encoding set. + read_extra_field(io.read(@extra_length)) @comment = io.read(@comment_length) check_c_dir_entry_comment_size From b705085b09861999c602a2fbcf508119cfabd4ea Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sun, 30 May 2021 19:08:31 +0100 Subject: [PATCH 113/311] `Entry#name_safe?` now allows Windows drive mappings. --- lib/zip/entry.rb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/zip/entry.rb b/lib/zip/entry.rb index 16336df6..2e484284 100644 --- a/lib/zip/entry.rb +++ b/lib/zip/entry.rb @@ -164,8 +164,9 @@ def name_safe? return false unless cleanpath.relative? root = ::File::SEPARATOR - naive_expanded_path = ::File.join(root, cleanpath.to_s) - ::File.absolute_path(cleanpath.to_s, root) == naive_expanded_path + naive = ::File.join(root, cleanpath.to_s) + # Allow for Windows drive mappings at the root. + ::File.absolute_path(cleanpath.to_s, root).match?(/([A-Z]:)?#{naive}/i) end def local_entry_offset #:nodoc:all From cf22ff1b92c38dd9da1a0f023e5b1567051c6f55 Mon Sep 17 00:00:00 2001 From: Jan-Joost Spanjers Date: Sun, 6 Jun 2021 14:44:52 +0200 Subject: [PATCH 114/311] Preserve eol linefeed for text test data files This prevents converting lf to crlf on Windows. --- test/data/.gitattributes | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 test/data/.gitattributes diff --git a/test/data/.gitattributes b/test/data/.gitattributes new file mode 100644 index 00000000..8c740099 --- /dev/null +++ b/test/data/.gitattributes @@ -0,0 +1,2 @@ +file1.txt eol=lf +file2.txt eol=lf From 0051d5bb1fe2525cdf3a8beaa274aae84d98a1ae Mon Sep 17 00:00:00 2001 From: Jan-Joost Spanjers Date: Sun, 30 May 2021 08:04:56 +0200 Subject: [PATCH 115/311] Read/write test files in binay mode (for Windows compatibility) --- test/file_test.rb | 8 ++++---- test/gentestfiles.rb | 12 ++++++------ test/input_stream_test.rb | 6 +++--- test/output_stream_test.rb | 2 +- test/pass_thru_decompressor_test.rb | 2 +- test/test_helper.rb | 2 +- 6 files changed, 16 insertions(+), 16 deletions(-) diff --git a/test/file_test.rb b/test/file_test.rb index 0b494668..b34a9474 100644 --- a/test/file_test.rb +++ b/test/file_test.rb @@ -113,21 +113,21 @@ def test_get_output_stream end def test_open_buffer_with_string - string = File.read('test/data/rubycode.zip') + string = File.read('test/data/rubycode.zip', mode: 'rb') ::Zip::File.open_buffer string do |zf| assert zf.entries.map(&:name).include?('zippedruby1.rb') end end def test_open_buffer_with_stringio - string_io = StringIO.new File.read('test/data/rubycode.zip') + string_io = StringIO.new File.read('test/data/rubycode.zip', mode: 'rb') ::Zip::File.open_buffer string_io do |zf| assert zf.entries.map(&:name).include?('zippedruby1.rb') end end def test_close_buffer_with_stringio - string_io = StringIO.new File.read('test/data/rubycode.zip') + string_io = StringIO.new File.read('test/data/rubycode.zip', mode: 'rb') zf = ::Zip::File.open_buffer string_io assert_nil zf.close end @@ -178,7 +178,7 @@ def test_open_buffer_with_io_and_block end def test_open_buffer_without_block - string_io = StringIO.new File.read('test/data/rubycode.zip') + string_io = StringIO.new File.read('test/data/rubycode.zip', mode: 'rb') zf = ::Zip::File.open_buffer string_io assert zf.entries.map(&:name).include?('zippedruby1.rb') end diff --git a/test/gentestfiles.rb b/test/gentestfiles.rb index a95743b3..78709162 100755 --- a/test/gentestfiles.rb +++ b/test/gentestfiles.rb @@ -79,19 +79,19 @@ def self.create_test_zips "zip -q #{TEST_ZIP1.zip_name} -d test/data/file2.txt" ) - File.open('test/data/generated/empty.txt', 'w') {} # Empty file. - File.open('test/data/generated/empty_chmod640.txt', 'w') {} # Empty file. + File.open('test/data/generated/empty.txt', 'wb') {} # Empty file. + File.open('test/data/generated/empty_chmod640.txt', 'wb') {} # Empty file. ::File.chmod(0o640, 'test/data/generated/empty_chmod640.txt') - File.open('test/data/generated/short.txt', 'w') { |file| file << 'ABCDEF' } + File.open('test/data/generated/short.txt', 'wb') { |file| file << 'ABCDEF' } test_text = '' - File.open('test/data/file2.txt') { |file| test_text = file.read } - File.open('test/data/generated/longAscii.txt', 'w') do |file| + File.open('test/data/file2.txt', 'rb') { |file| test_text = file.read } + File.open('test/data/generated/longAscii.txt', 'wb') do |file| file << test_text while file.tell < 1E5 end binary_pattern = '' - File.open('test/data/generated/empty.zip') do |file| + File.open('test/data/generated/empty.zip', 'rb') do |file| binary_pattern = file.read end binary_pattern *= 4 diff --git a/test/input_stream_test.rb b/test/input_stream_test.rb index 3d0e6217..40d19476 100644 --- a/test/input_stream_test.rb +++ b/test/input_stream_test.rb @@ -42,13 +42,13 @@ def test_open_buffer_with_block end def test_open_string_io_without_block - string_io = ::StringIO.new(::File.read(TestZipFile::TEST_ZIP2.zip_name)) + string_io = ::StringIO.new(::File.read(TestZipFile::TEST_ZIP2.zip_name, mode: 'rb')) zis = ::Zip::InputStream.open(string_io) assert_stream_contents(zis, TestZipFile::TEST_ZIP2) end def test_open_string_io_with_block - string_io = ::StringIO.new(::File.read(TestZipFile::TEST_ZIP2.zip_name)) + string_io = ::StringIO.new(::File.read(TestZipFile::TEST_ZIP2.zip_name, mode: 'rb')) ::Zip::InputStream.open(string_io) do |zis| assert_stream_contents(zis, TestZipFile::TEST_ZIP2) assert_equal(true, zis.eof?) @@ -106,7 +106,7 @@ def test_incomplete_reads end def test_incomplete_reads_from_string_io - string_io = ::StringIO.new(::File.read(TestZipFile::TEST_ZIP2.zip_name)) + string_io = ::StringIO.new(::File.read(TestZipFile::TEST_ZIP2.zip_name, mode: 'rb')) ::Zip::InputStream.open(string_io) do |zis| entry = zis.get_next_entry # longAscii.txt assert_equal(false, zis.eof?) diff --git a/test/output_stream_test.rb b/test/output_stream_test.rb index 3b46f615..7a994b36 100644 --- a/test/output_stream_test.rb +++ b/test/output_stream_test.rb @@ -85,7 +85,7 @@ def test_put_next_entry zos << stored_text end - assert(File.read(TEST_ZIP.zip_name)[stored_text]) + assert(File.read(TEST_ZIP.zip_name, mode: 'rb')[stored_text]) ::Zip::File.open(TEST_ZIP.zip_name) do |zf| assert_equal(stored_text, zf.read(entry_name)) end diff --git a/test/pass_thru_decompressor_test.rb b/test/pass_thru_decompressor_test.rb index 937c3ac6..9c251a09 100644 --- a/test/pass_thru_decompressor_test.rb +++ b/test/pass_thru_decompressor_test.rb @@ -6,7 +6,7 @@ class PassThruDecompressorTest < MiniTest::Test def setup super - @file = File.new(TEST_FILE) + @file = File.new(TEST_FILE, 'rb') @decompressor = ::Zip::PassThruDecompressor.new(@file, File.size(TEST_FILE)) end diff --git a/test/test_helper.rb b/test/test_helper.rb index 125ee99c..b51fd5d4 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -57,7 +57,7 @@ module DecompressorTests def setup @ref_text = '' - File.open(TEST_FILE) { |f| @ref_text = f.read } + File.open(TEST_FILE, 'rb') { |f| @ref_text = f.read } @ref_lines = @ref_text.split($INPUT_RECORD_SEPARATOR) end From 5c9b8507293343ca0a44e5321416818199b80b4e Mon Sep 17 00:00:00 2001 From: Jan-Joost Spanjers Date: Thu, 3 Jun 2021 15:56:42 +0200 Subject: [PATCH 116/311] Prevent adding a newline to comments for generated test files on Windows --- test/gentestfiles.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/gentestfiles.rb b/test/gentestfiles.rb index 78709162..50820427 100755 --- a/test/gentestfiles.rb +++ b/test/gentestfiles.rb @@ -108,7 +108,7 @@ def self.create_test_zips if RUBY_PLATFORM.match?(/mswin|mingw|cygwin/) raise "failed to add comment to test zip '#{TEST_ZIP2.zip_name}'" \ unless system( - "echo #{TEST_ZIP2.comment}| zip -zq #{TEST_ZIP2.zip_name}\"" + "cmd /c \" Date: Fri, 4 Jun 2021 15:51:25 +0200 Subject: [PATCH 117/311] Do not hardcode /tmp in entry_test --- test/entry_test.rb | 43 +++++++++++++++++++++++-------------------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/test/entry_test.rb b/test/entry_test.rb index 7c1cf60d..d57962ca 100644 --- a/test/entry_test.rb +++ b/test/entry_test.rb @@ -151,32 +151,35 @@ def test_entry_name_cannot_start_with_slash end def test_store_file_without_compression - File.delete('/tmp/no_compress.zip') if File.exist?('/tmp/no_compress.zip') - files = Dir[File.join('test/data/globTest', '**', '**')] + Dir.mktmpdir do |tmp| + tmp_zip = File.join(tmp, 'no_compress.zip') - Zip.setup do |z| - z.write_zip64_support = false - end + Zip.setup do |z| + z.write_zip64_support = false + end - zipfile = Zip::File.open('/tmp/no_compress.zip', Zip::File::CREATE) - mimetype_entry = Zip::Entry.new( - zipfile, # @zipfile - 'mimetype', # @name - compression_method: Zip::Entry::STORED - ) + zipfile = Zip::File.open(tmp_zip, Zip::File::CREATE) + + mimetype_entry = Zip::Entry.new( + zipfile, # @zipfile + 'mimetype', # @name + compression_method: Zip::Entry::STORED + ) + zipfile.add(mimetype_entry, 'test/data/mimetype') - zipfile.add(mimetype_entry, 'test/data/mimetype') + files = Dir[File.join('test/data/globTest', '**', '**')] + files.each do |file| + zipfile.add(file.sub('test/data/globTest/', ''), file) + end - files.each do |file| - zipfile.add(file.sub('test/data/globTest/', ''), file) - end - zipfile.close + zipfile.close - f = File.open('/tmp/no_compress.zip', 'rb') - first_100_bytes = f.read(100) - f.close + f = File.open(tmp_zip, 'rb') + first_100_bytes = f.read(100) + f.close - assert_match(/mimetypeapplication\/epub\+zip/, first_100_bytes) + assert_match(/mimetypeapplication\/epub\+zip/, first_100_bytes) + end end def test_encrypted? From 8a5fef807407c3c945a9ef912efb630c6aa66ccc Mon Sep 17 00:00:00 2001 From: Jan-Joost Spanjers Date: Sat, 5 Jun 2021 10:38:22 +0200 Subject: [PATCH 118/311] Fix FileSystem::ZipFileNameMapper#expand_path on Windows Fixes regression introduced by 0e4dc676a0c447537d233b5738da7ecddc26e12e. --- lib/zip/filesystem.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/zip/filesystem.rb b/lib/zip/filesystem.rb index 2815069b..61072700 100644 --- a/lib/zip/filesystem.rb +++ b/lib/zip/filesystem.rb @@ -620,7 +620,7 @@ def each end def expand_path(path) - expanded = ::File.expand_path(path, @pwd) + expanded = path.start_with?('/') ? path.dup : ::File.join(@pwd, path) expanded.gsub!(/\/\.(\/|$)/, '') expanded.gsub!(/[^\/]+\/\.\.(\/|$)/, '') expanded.empty? ? '/' : expanded From cdef4a518738c249c4843c34e9c8bba1c9e99c89 Mon Sep 17 00:00:00 2001 From: Jan-Joost Spanjers Date: Fri, 4 Jun 2021 17:51:09 +0200 Subject: [PATCH 119/311] Prevent directory not empty error when running file_test on Windows Fixed error: ZipFileTest#test_open_buffer_no_op_does_not_change_file: Errno::ENOTEMPTY: Directory not empty @ dir_s_rmdir - D:/a/_temp/d20210605-6612-1yi35sp C:/hostedtoolcache/windows/Ruby/2.4.10/x64/lib/ruby/2.4.0/fileutils.rb:1335:in `rmdir' C:/hostedtoolcache/windows/Ruby/2.4.10/x64/lib/ruby/2.4.0/fileutils.rb:1335:in `block in remove_dir1' C:/hostedtoolcache/windows/Ruby/2.4.10/x64/lib/ruby/2.4.0/fileutils.rb:1349:in `platform_support' C:/hostedtoolcache/windows/Ruby/2.4.10/x64/lib/ruby/2.4.0/fileutils.rb:1334:in `remove_dir1' C:/hostedtoolcache/windows/Ruby/2.4.10/x64/lib/ruby/2.4.0/fileutils.rb:1327:in `remove' C:/hostedtoolcache/windows/Ruby/2.4.10/x64/lib/ruby/2.4.0/fileutils.rb:689:in `block in remove_entry' C:/hostedtoolcache/windows/Ruby/2.4.10/x64/lib/ruby/2.4.0/fileutils.rb:1384:in `ensure in postorder_traverse' C:/hostedtoolcache/windows/Ruby/2.4.10/x64/lib/ruby/2.4.0/fileutils.rb:1384:in `postorder_traverse' C:/hostedtoolcache/windows/Ruby/2.4.10/x64/lib/ruby/2.4.0/fileutils.rb:687:in `remove_entry' C:/hostedtoolcache/windows/Ruby/2.4.10/x64/lib/ruby/2.4.0/tmpdir.rb:101:in `mktmpdir' D:/a/rubyzip/rubyzip/test/file_test.rb:136:in `test_open_buffer_no_op_does_not_change_file' Rationale: File#dup does not behave like what you would expect from #dup on Ruby. File#dup calls dup(2), which has OS dependant behavoir. On Windows, calling File#dup seems to cause an extra reference to an open file, which prevents deleting that file later. With this commit, we leave out the call to File#dup on Windows. It is not clear to me that removing this call has no undesired consequences, but all other existing tests still succeed. --- lib/zip/output_stream.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/zip/output_stream.rb b/lib/zip/output_stream.rb index 6bfd9382..5d13ebad 100644 --- a/lib/zip/output_stream.rb +++ b/lib/zip/output_stream.rb @@ -30,7 +30,7 @@ def initialize(file_name, stream = false, encrypter = nil) super() @file_name = file_name @output_stream = if stream - iostream = @file_name.dup + iostream = Zip::RUNNING_ON_WINDOWS ? @file_name : @file_name.dup iostream.reopen(@file_name) iostream.rewind iostream From c3443c06ea47722f96ab140b8354419e7e3775f3 Mon Sep 17 00:00:00 2001 From: Jan-Joost Spanjers Date: Fri, 4 Jun 2021 14:05:59 +0200 Subject: [PATCH 120/311] Make recover file permissions test better understandable --- test/file_test.rb | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/test/file_test.rb b/test/file_test.rb index b34a9474..7ecdac0b 100644 --- a/test/file_test.rb +++ b/test/file_test.rb @@ -286,15 +286,16 @@ def test_add_stored def test_recover_permissions_after_add_files_to_archive src_zip = TEST_ZIP.zip_name - ::File.chmod(0o664, src_zip) - src_file = 'test/data/file2.txt' - entry_name = 'newEntryName.rb' - assert_equal(::File.stat(src_zip).mode, 0o100664) assert(::File.exist?(src_zip)) + + ::File.chmod(0o664, src_zip) + assert_equal(0o100664, ::File.stat(src_zip).mode) + zf = ::Zip::File.new(src_zip, ::Zip::File::CREATE) - zf.add(entry_name, src_file) + zf.add('newEntryName.rb', 'test/data/file2.txt') zf.close - assert_equal(::File.stat(src_zip).mode, 0o100664) + + assert_equal(0o100664, ::File.stat(src_zip).mode) end def test_add_existing_entry_name From 8a24bff1b2ca4fc0f7ca0125ed067fca226503ce Mon Sep 17 00:00:00 2001 From: Jan-Joost Spanjers Date: Fri, 4 Jun 2021 14:18:48 +0200 Subject: [PATCH 121/311] Disable recover file permissions test on Windows --- test/file_test.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/file_test.rb b/test/file_test.rb index 7ecdac0b..a802f105 100644 --- a/test/file_test.rb +++ b/test/file_test.rb @@ -285,6 +285,9 @@ def test_add_stored end def test_recover_permissions_after_add_files_to_archive + # Windows NT does not support granular permissions + skip if Zip::RUNNING_ON_WINDOWS + src_zip = TEST_ZIP.zip_name assert(::File.exist?(src_zip)) From 6ac2cb207d80a0900491538543c5a8589377f8cf Mon Sep 17 00:00:00 2001 From: Jan-Joost Spanjers Date: Fri, 4 Jun 2021 17:50:56 +0200 Subject: [PATCH 122/311] REVERT ME: This disables a test fixed by #486 --- test/file_options_test.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/file_options_test.rb b/test/file_options_test.rb index e9c612ce..4bffa4f1 100644 --- a/test/file_options_test.rb +++ b/test/file_options_test.rb @@ -58,6 +58,10 @@ def test_restore_times_true zip.extract(ENTRY_2, EXTPATH_2) end + # this test is disabled on Windows for now, waiting for #486. + # please remove this after merging #486. + skip if Zip::RUNNING_ON_WINDOWS + assert_time_equal(::File.mtime(TXTPATH), ::File.mtime(EXTPATH_1)) assert_time_equal(::File.mtime(TXTPATH), ::File.mtime(EXTPATH_2)) end From 64c54cc61b654b4cca700c0db5933acfed08d0b2 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sun, 6 Jun 2021 14:56:29 +0100 Subject: [PATCH 123/311] Update Changelog. --- Changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/Changelog.md b/Changelog.md index 293acfb3..14b181ce 100644 --- a/Changelog.md +++ b/Changelog.md @@ -12,6 +12,7 @@ Tooling: +- Fix Windows tests. [#489](https://github.com/rubyzip/rubyzip/pull/489) - Refactor `assert_forwarded` so it does not need `ObjectSpace._id2ref` or `eval`. [#483](https://github.com/rubyzip/rubyzip/pull/483) - Add GitHub Actions CI infrastructure. [#469](https://github.com/rubyzip/rubyzip/issues/469) - Add Ruby 3.0 to CI. [#474](https://github.com/rubyzip/rubyzip/pull/474) From 204d084fdf647f8bfcd26d4757ad01114d93279e Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Wed, 2 Jun 2021 12:23:51 +0100 Subject: [PATCH 124/311] Extract `ZipFileNameMapper` from the main filesystem file. --- lib/zip/filesystem.rb | 77 +------------------- lib/zip/filesystem/zip_file_name_mapper.rb | 81 ++++++++++++++++++++++ 2 files changed, 82 insertions(+), 76 deletions(-) create mode 100644 lib/zip/filesystem/zip_file_name_mapper.rb diff --git a/lib/zip/filesystem.rb b/lib/zip/filesystem.rb index 61072700..59bdf145 100644 --- a/lib/zip/filesystem.rb +++ b/lib/zip/filesystem.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true require 'zip' +require_relative 'filesystem/zip_file_name_mapper' module Zip # The ZipFileSystem API provides an API for accessing entries in @@ -556,82 +557,6 @@ def tell @index end end - - # All access to Zip::File from ZipFsFile and ZipFsDir goes through a - # ZipFileNameMapper, which has one responsibility: ensure - class ZipFileNameMapper # :nodoc:all - include Enumerable - - def initialize(zip_file) - @zip_file = zip_file - @pwd = '/' - end - - attr_accessor :pwd - - def find_entry(filename) - @zip_file.find_entry(expand_to_entry(filename)) - end - - def get_entry(filename) - @zip_file.get_entry(expand_to_entry(filename)) - end - - def get_input_stream(filename, &a_proc) - @zip_file.get_input_stream(expand_to_entry(filename), &a_proc) - end - - def get_output_stream(filename, permissions = nil, &a_proc) - @zip_file.get_output_stream( - expand_to_entry(filename), permissions, &a_proc - ) - end - - def glob(pattern, *flags, &block) - @zip_file.glob(expand_to_entry(pattern), *flags, &block) - end - - def read(filename) - @zip_file.read(expand_to_entry(filename)) - end - - def remove(filename) - @zip_file.remove(expand_to_entry(filename)) - end - - def rename(filename, new_name, &continue_on_exists_proc) - @zip_file.rename( - expand_to_entry(filename), - expand_to_entry(new_name), - &continue_on_exists_proc - ) - end - - def mkdir(filename, permissions = 0o755) - @zip_file.mkdir(expand_to_entry(filename), permissions) - end - - # Turns entries into strings and adds leading / - # and removes trailing slash on directories - def each - @zip_file.each do |e| - yield('/' + e.to_s.chomp('/')) - end - end - - def expand_path(path) - expanded = path.start_with?('/') ? path.dup : ::File.join(@pwd, path) - expanded.gsub!(/\/\.(\/|$)/, '') - expanded.gsub!(/[^\/]+\/\.\.(\/|$)/, '') - expanded.empty? ? '/' : expanded - end - - private - - def expand_to_entry(path) - expand_path(path)[1..-1] - end - end end class File diff --git a/lib/zip/filesystem/zip_file_name_mapper.rb b/lib/zip/filesystem/zip_file_name_mapper.rb new file mode 100644 index 00000000..66adffec --- /dev/null +++ b/lib/zip/filesystem/zip_file_name_mapper.rb @@ -0,0 +1,81 @@ +# frozen_string_literal: true + +module Zip + module FileSystem + # All access to Zip::File from ZipFsFile and ZipFsDir goes through a + # ZipFileNameMapper, which has one responsibility: ensure + class ZipFileNameMapper # :nodoc:all + include Enumerable + + def initialize(zip_file) + @zip_file = zip_file + @pwd = '/' + end + + attr_accessor :pwd + + def find_entry(filename) + @zip_file.find_entry(expand_to_entry(filename)) + end + + def get_entry(filename) + @zip_file.get_entry(expand_to_entry(filename)) + end + + def get_input_stream(filename, &a_proc) + @zip_file.get_input_stream(expand_to_entry(filename), &a_proc) + end + + def get_output_stream(filename, permissions = nil, &a_proc) + @zip_file.get_output_stream( + expand_to_entry(filename), permissions, &a_proc + ) + end + + def glob(pattern, *flags, &block) + @zip_file.glob(expand_to_entry(pattern), *flags, &block) + end + + def read(filename) + @zip_file.read(expand_to_entry(filename)) + end + + def remove(filename) + @zip_file.remove(expand_to_entry(filename)) + end + + def rename(filename, new_name, &continue_on_exists_proc) + @zip_file.rename( + expand_to_entry(filename), + expand_to_entry(new_name), + &continue_on_exists_proc + ) + end + + def mkdir(filename, permissions = 0o755) + @zip_file.mkdir(expand_to_entry(filename), permissions) + end + + # Turns entries into strings and adds leading / + # and removes trailing slash on directories + def each + @zip_file.each do |e| + yield("/#{e.to_s.chomp('/')}") + end + end + + def expand_path(path) + expanded = path.start_with?('/') ? path.dup : ::File.join(@pwd, path) + expanded.gsub!(/\/\.(\/|$)/, '') + expanded.gsub!(/[^\/]+\/\.\.(\/|$)/, '') + expanded.empty? ? '/' : expanded + end + + private + + def expand_to_entry(path) + expand_path(path)[1..-1] + end + end + end +end From 239baef8459e7d7ed96d9b8ccdc0e93c1b4c7c19 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Wed, 2 Jun 2021 17:04:45 +0100 Subject: [PATCH 125/311] Extract `DirectoryIterator` from the main filesystem file. --- lib/zip/filesystem.rb | 46 +----------------- lib/zip/filesystem/directory_iterator.rb | 48 +++++++++++++++++++ ...tor_test.rb => directory_iterator_test.rb} | 4 +- 3 files changed, 52 insertions(+), 46 deletions(-) create mode 100644 lib/zip/filesystem/directory_iterator.rb rename test/filesystem/{dir_iterator_test.rb => directory_iterator_test.rb} (91%) diff --git a/lib/zip/filesystem.rb b/lib/zip/filesystem.rb index 59bdf145..38d66a96 100644 --- a/lib/zip/filesystem.rb +++ b/lib/zip/filesystem.rb @@ -2,6 +2,7 @@ require 'zip' require_relative 'filesystem/zip_file_name_mapper' +require_relative 'filesystem/directory_iterator' module Zip # The ZipFileSystem API provides an API for accessing entries in @@ -442,7 +443,7 @@ def initialize(mapped_zip) attr_writer :file def new(directory_name) - ZipFsDirIterator.new(entries(directory_name)) + DirectoryIterator.new(entries(directory_name)) end def open(directory_name) @@ -514,49 +515,6 @@ def chroot(*_args) raise NotImplementedError, 'The chroot() function is not implemented' end end - - class ZipFsDirIterator # :nodoc:all - include Enumerable - - def initialize(filenames) - @filenames = filenames - @index = 0 - end - - def close - @filenames = nil - end - - def each(&a_proc) - raise IOError, 'closed directory' if @filenames.nil? - - @filenames.each(&a_proc) - end - - def read - raise IOError, 'closed directory' if @filenames.nil? - - @filenames[(@index += 1) - 1] - end - - def rewind - raise IOError, 'closed directory' if @filenames.nil? - - @index = 0 - end - - def seek(position) - raise IOError, 'closed directory' if @filenames.nil? - - @index = position - end - - def tell - raise IOError, 'closed directory' if @filenames.nil? - - @index - end - end end class File diff --git a/lib/zip/filesystem/directory_iterator.rb b/lib/zip/filesystem/directory_iterator.rb new file mode 100644 index 00000000..91b1fc2e --- /dev/null +++ b/lib/zip/filesystem/directory_iterator.rb @@ -0,0 +1,48 @@ +# frozen_string_literal: true + +module Zip + module FileSystem + class DirectoryIterator # :nodoc:all + include Enumerable + + def initialize(filenames) + @filenames = filenames + @index = 0 + end + + def close + @filenames = nil + end + + def each(&a_proc) + raise IOError, 'closed directory' if @filenames.nil? + + @filenames.each(&a_proc) + end + + def read + raise IOError, 'closed directory' if @filenames.nil? + + @filenames[(@index += 1) - 1] + end + + def rewind + raise IOError, 'closed directory' if @filenames.nil? + + @index = 0 + end + + def seek(position) + raise IOError, 'closed directory' if @filenames.nil? + + @index = position + end + + def tell + raise IOError, 'closed directory' if @filenames.nil? + + @index + end + end + end +end diff --git a/test/filesystem/dir_iterator_test.rb b/test/filesystem/directory_iterator_test.rb similarity index 91% rename from test/filesystem/dir_iterator_test.rb rename to test/filesystem/directory_iterator_test.rb index bf964c41..ba809dfd 100644 --- a/test/filesystem/dir_iterator_test.rb +++ b/test/filesystem/directory_iterator_test.rb @@ -3,11 +3,11 @@ require 'test_helper' require 'zip/filesystem' -class ZipFsDirIteratorTest < MiniTest::Test +class DirectoryIteratorTest < MiniTest::Test FILENAME_ARRAY = %w[f1 f2 f3 f4 f5 f6].freeze def setup - @dir_iter = ::Zip::FileSystem::ZipFsDirIterator.new(FILENAME_ARRAY) + @dir_iter = ::Zip::FileSystem::DirectoryIterator.new(FILENAME_ARRAY) end def test_close From a1c9b63e616eb83e5839f443a6539f48ca564695 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Wed, 2 Jun 2021 17:22:58 +0100 Subject: [PATCH 126/311] Extract `FileSystem::Dir` from the main filesystem file. --- .rubocop_todo.yml | 1 + lib/zip/filesystem.rb | 92 +------------------ lib/zip/filesystem/dir.rb | 86 +++++++++++++++++ lib/zip/filesystem/zip_file_name_mapper.rb | 4 +- .../{directory_test.rb => dir_test.rb} | 2 +- 5 files changed, 93 insertions(+), 92 deletions(-) create mode 100644 lib/zip/filesystem/dir.rb rename test/filesystem/{directory_test.rb => dir_test.rb} (98%) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 75ed58a3..2931b545 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -77,6 +77,7 @@ Style/IfUnlessModifier: - 'lib/zip/entry.rb' - 'lib/zip/file.rb' - 'lib/zip/filesystem.rb' + - 'lib/zip/filesystem/dir.rb' - 'lib/zip/pass_thru_decompressor.rb' - 'lib/zip/streamable_stream.rb' diff --git a/lib/zip/filesystem.rb b/lib/zip/filesystem.rb index 38d66a96..7a53eb5c 100644 --- a/lib/zip/filesystem.rb +++ b/lib/zip/filesystem.rb @@ -3,6 +3,7 @@ require 'zip' require_relative 'filesystem/zip_file_name_mapper' require_relative 'filesystem/directory_iterator' +require_relative 'filesystem/dir' module Zip # The ZipFileSystem API provides an API for accessing entries in @@ -40,13 +41,13 @@ module Zip module FileSystem def initialize # :nodoc: mapped_zip = ZipFileNameMapper.new(self) - @zip_fs_dir = ZipFsDir.new(mapped_zip) + @zip_fs_dir = Dir.new(mapped_zip) @zip_fs_file = ZipFsFile.new(mapped_zip) @zip_fs_dir.file = @zip_fs_file @zip_fs_file.dir = @zip_fs_dir end - # Returns a ZipFsDir which is much like ruby's builtin Dir (class) + # Returns a FileSystem::Dir which is much like ruby's builtin Dir (class) # object, except it works on the Zip::File on which this method is # invoked def dir @@ -428,93 +429,6 @@ def expand_path(path) @mapped_zip.expand_path(path) end end - - # Instances of this class are normally accessed via the accessor - # ZipFile::dir. An instance of ZipFsDir behaves like ruby's - # builtin Dir (class) object, except it works on ZipFile entries. - # - # The individual methods are not documented due to their - # similarity with the methods in Dir - class ZipFsDir - def initialize(mapped_zip) - @mapped_zip = mapped_zip - end - - attr_writer :file - - def new(directory_name) - DirectoryIterator.new(entries(directory_name)) - end - - def open(directory_name) - dir_iter = new(directory_name) - if block_given? - begin - yield(dir_iter) - return nil - ensure - dir_iter.close - end - end - dir_iter - end - - def pwd - @mapped_zip.pwd - end - alias getwd pwd - - def chdir(directory_name) - unless @file.stat(directory_name).directory? - raise Errno::EINVAL, "Invalid argument - #{directory_name}" - end - - @mapped_zip.pwd = @file.expand_path(directory_name) - end - - def entries(directory_name) - entries = [] - foreach(directory_name) { |e| entries << e } - entries - end - - def glob(*args, &block) - @mapped_zip.glob(*args, &block) - end - - def foreach(directory_name) - unless @file.stat(directory_name).directory? - raise Errno::ENOTDIR, directory_name - end - - path = @file.expand_path(directory_name) - path << '/' unless path.end_with?('/') - path = Regexp.escape(path) - subdir_entry_regex = Regexp.new("^#{path}([^/]+)$") - @mapped_zip.each do |filename| - match = subdir_entry_regex.match(filename) - yield(match[1]) unless match.nil? - end - end - - def delete(entry_name) - unless @file.stat(entry_name).directory? - raise Errno::EINVAL, "Invalid argument - #{entry_name}" - end - - @mapped_zip.remove(entry_name) - end - alias rmdir delete - alias unlink delete - - def mkdir(entry_name, permissions = 0o755) - @mapped_zip.mkdir(entry_name, permissions) - end - - def chroot(*_args) - raise NotImplementedError, 'The chroot() function is not implemented' - end - end end class File diff --git a/lib/zip/filesystem/dir.rb b/lib/zip/filesystem/dir.rb new file mode 100644 index 00000000..a5e1ef9e --- /dev/null +++ b/lib/zip/filesystem/dir.rb @@ -0,0 +1,86 @@ +# frozen_string_literal: true + +module Zip + module FileSystem + class Dir # :nodoc:all + def initialize(mapped_zip) + @mapped_zip = mapped_zip + end + + attr_writer :file + + def new(directory_name) + DirectoryIterator.new(entries(directory_name)) + end + + def open(directory_name) + dir_iter = new(directory_name) + if block_given? + begin + yield(dir_iter) + return nil + ensure + dir_iter.close + end + end + dir_iter + end + + def pwd + @mapped_zip.pwd + end + alias getwd pwd + + def chdir(directory_name) + unless @file.stat(directory_name).directory? + raise Errno::EINVAL, "Invalid argument - #{directory_name}" + end + + @mapped_zip.pwd = @file.expand_path(directory_name) + end + + def entries(directory_name) + entries = [] + foreach(directory_name) { |e| entries << e } + entries + end + + def glob(*args, &block) + @mapped_zip.glob(*args, &block) + end + + def foreach(directory_name) + unless @file.stat(directory_name).directory? + raise Errno::ENOTDIR, directory_name + end + + path = @file.expand_path(directory_name) + path << '/' unless path.end_with?('/') + path = Regexp.escape(path) + subdir_entry_regex = Regexp.new("^#{path}([^/]+)$") + @mapped_zip.each do |filename| + match = subdir_entry_regex.match(filename) + yield(match[1]) unless match.nil? + end + end + + def delete(entry_name) + unless @file.stat(entry_name).directory? + raise Errno::EINVAL, "Invalid argument - #{entry_name}" + end + + @mapped_zip.remove(entry_name) + end + alias rmdir delete + alias unlink delete + + def mkdir(entry_name, permissions = 0o755) + @mapped_zip.mkdir(entry_name, permissions) + end + + def chroot(*_args) + raise NotImplementedError, 'The chroot() function is not implemented' + end + end + end +end diff --git a/lib/zip/filesystem/zip_file_name_mapper.rb b/lib/zip/filesystem/zip_file_name_mapper.rb index 66adffec..fda5660d 100644 --- a/lib/zip/filesystem/zip_file_name_mapper.rb +++ b/lib/zip/filesystem/zip_file_name_mapper.rb @@ -2,8 +2,8 @@ module Zip module FileSystem - # All access to Zip::File from ZipFsFile and ZipFsDir goes through a - # ZipFileNameMapper, which has one responsibility: ensure + # All access to Zip::File from ZipFsFile and FileSystem::Dir goes through + # a ZipFileNameMapper, which has one responsibility: ensure class ZipFileNameMapper # :nodoc:all include Enumerable diff --git a/test/filesystem/directory_test.rb b/test/filesystem/dir_test.rb similarity index 98% rename from test/filesystem/directory_test.rb rename to test/filesystem/dir_test.rb index 57177028..2db69c22 100644 --- a/test/filesystem/directory_test.rb +++ b/test/filesystem/dir_test.rb @@ -3,7 +3,7 @@ require 'test_helper' require 'zip/filesystem' -class ZipFsDirectoryTest < MiniTest::Test +class DirectoryTest < MiniTest::Test TEST_ZIP = 'test/data/generated/zipWithDirs_copy.zip' GLOB_TEST_ZIP = 'test/data/globTest.zip' From d1329299c3db7deb1542e0035fa9f3283860599d Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Wed, 2 Jun 2021 19:42:18 +0100 Subject: [PATCH 127/311] Extract `FileSystem::File` from the main filesystem file. --- .rubocop_todo.yml | 6 +- lib/zip/filesystem.rb | 376 +-------------------- lib/zip/filesystem/file.rb | 374 ++++++++++++++++++++ lib/zip/filesystem/zip_file_name_mapper.rb | 4 +- test/filesystem/file_mutating_test.rb | 2 +- test/filesystem/file_nonmutating_test.rb | 2 +- test/filesystem/file_stat_test.rb | 2 +- 7 files changed, 386 insertions(+), 380 deletions(-) create mode 100644 lib/zip/filesystem/file.rb diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 2931b545..68f251b9 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -47,7 +47,7 @@ Metrics/PerceivedComplexity: Naming/AccessorMethodName: Exclude: - 'lib/zip/entry.rb' - - 'lib/zip/filesystem.rb' + - 'lib/zip/filesystem/file.rb' - 'lib/zip/input_stream.rb' - 'lib/zip/streamable_stream.rb' @@ -76,8 +76,8 @@ Style/IfUnlessModifier: Exclude: - 'lib/zip/entry.rb' - 'lib/zip/file.rb' - - 'lib/zip/filesystem.rb' - 'lib/zip/filesystem/dir.rb' + - 'lib/zip/filesystem/file.rb' - 'lib/zip/pass_thru_decompressor.rb' - 'lib/zip/streamable_stream.rb' @@ -101,7 +101,7 @@ Style/NumericPredicate: - 'lib/zip/extra_field/universal_time.rb' - 'lib/zip/extra_field/unix.rb' - 'lib/zip/file.rb' - - 'lib/zip/filesystem.rb' + - 'lib/zip/filesystem/file.rb' - 'lib/zip/input_stream.rb' - 'lib/zip/ioextras.rb' - 'lib/zip/ioextras/abstract_input_stream.rb' diff --git a/lib/zip/filesystem.rb b/lib/zip/filesystem.rb index 7a53eb5c..5fe0c994 100644 --- a/lib/zip/filesystem.rb +++ b/lib/zip/filesystem.rb @@ -4,6 +4,7 @@ require_relative 'filesystem/zip_file_name_mapper' require_relative 'filesystem/directory_iterator' require_relative 'filesystem/dir' +require_relative 'filesystem/file' module Zip # The ZipFileSystem API provides an API for accessing entries in @@ -42,7 +43,7 @@ module FileSystem def initialize # :nodoc: mapped_zip = ZipFileNameMapper.new(self) @zip_fs_dir = Dir.new(mapped_zip) - @zip_fs_file = ZipFsFile.new(mapped_zip) + @zip_fs_file = File.new(mapped_zip) @zip_fs_dir.file = @zip_fs_file @zip_fs_file.dir = @zip_fs_dir end @@ -54,381 +55,12 @@ def dir @zip_fs_dir end - # Returns a ZipFsFile which is much like ruby's builtin File (class) - # object, except it works on the Zip::File on which this method is + # Returns a FileSystem::File which is much like ruby's builtin File + # (class) object, except it works on the Zip::File on which this method is # invoked def file @zip_fs_file end - - # Instances of this class are normally accessed via the accessor - # Zip::File::file. An instance of ZipFsFile behaves like ruby's - # builtin File (class) object, except it works on Zip::File entries. - # - # The individual methods are not documented due to their - # similarity with the methods in File - class ZipFsFile - attr_writer :dir - - class ZipFsStat - class << self - def delegate_to_fs_file(*methods) - methods.each do |method| - class_eval <<-END_EVAL, __FILE__, __LINE__ + 1 - def #{method} # def file? - @zip_fs_file.#{method}(@entry_name) # @zip_fs_file.file?(@entry_name) - end # end - END_EVAL - end - end - end - - def initialize(zip_fs_file, entry_name) - @zip_fs_file = zip_fs_file - @entry_name = entry_name - end - - def kind_of?(type) - super || type == ::File::Stat - end - - delegate_to_fs_file :file?, :directory?, :pipe?, :chardev?, :symlink?, - :socket?, :blockdev?, :readable?, :readable_real?, :writable?, :ctime, - :writable_real?, :executable?, :executable_real?, :sticky?, :owned?, - :grpowned?, :setuid?, :setgid?, :zero?, :size, :size?, :mtime, :atime - - def blocks - nil - end - - def get_entry - @zip_fs_file.__send__(:get_entry, @entry_name) - end - private :get_entry - - def gid - e = get_entry - if e.extra.member? 'IUnix' - e.extra['IUnix'].gid || 0 - else - 0 - end - end - - def uid - e = get_entry - if e.extra.member? 'IUnix' - e.extra['IUnix'].uid || 0 - else - 0 - end - end - - def ino - 0 - end - - def dev - 0 - end - - def rdev - 0 - end - - def rdev_major - 0 - end - - def rdev_minor - 0 - end - - def ftype - if file? - 'file' - elsif directory? - 'directory' - else - raise StandardError, 'Unknown file type' - end - end - - def nlink - 1 - end - - def blksize - nil - end - - def mode - e = get_entry - if e.fstype == 3 - e.external_file_attributes >> 16 - else - 33_206 # 33206 is equivalent to -rw-rw-rw- - end - end - end - - def initialize(mapped_zip) - @mapped_zip = mapped_zip - end - - def get_entry(filename) - unless exists?(filename) - raise Errno::ENOENT, "No such file or directory - #{filename}" - end - - @mapped_zip.find_entry(filename) - end - private :get_entry - - def unix_mode_cmp(filename, mode) - e = get_entry(filename) - e.fstype == 3 && ((e.external_file_attributes >> 16) & mode) != 0 - rescue Errno::ENOENT - false - end - private :unix_mode_cmp - - def exists?(filename) - expand_path(filename) == '/' || !@mapped_zip.find_entry(filename).nil? - end - alias exist? exists? - - # Permissions not implemented, so if the file exists it is accessible - alias owned? exists? - alias grpowned? exists? - - def readable?(filename) - unix_mode_cmp(filename, 0o444) - end - alias readable_real? readable? - - def writable?(filename) - unix_mode_cmp(filename, 0o222) - end - alias writable_real? writable? - - def executable?(filename) - unix_mode_cmp(filename, 0o111) - end - alias executable_real? executable? - - def setuid?(filename) - unix_mode_cmp(filename, 0o4000) - end - - def setgid?(filename) - unix_mode_cmp(filename, 0o2000) - end - - def sticky?(filename) - unix_mode_cmp(filename, 0o1000) - end - - def umask(*args) - ::File.umask(*args) - end - - def truncate(_filename, _len) - raise StandardError, 'truncate not supported' - end - - def directory?(filename) - entry = @mapped_zip.find_entry(filename) - expand_path(filename) == '/' || (!entry.nil? && entry.directory?) - end - - def open(filename, mode = 'r', permissions = 0o644, &block) - mode = mode.tr('b', '') # ignore b option - case mode - when 'r' - @mapped_zip.get_input_stream(filename, &block) - when 'w' - @mapped_zip.get_output_stream(filename, permissions, &block) - else - raise StandardError, "openmode '#{mode} not supported" unless mode == 'r' - end - end - - def new(filename, mode = 'r') - self.open(filename, mode) - end - - def size(filename) - @mapped_zip.get_entry(filename).size - end - - # Returns nil for not found and nil for directories - def size?(filename) - entry = @mapped_zip.find_entry(filename) - entry.nil? || entry.directory? ? nil : entry.size - end - - def chown(owner, group, *filenames) - filenames.each do |filename| - e = get_entry(filename) - e.extra.create('IUnix') unless e.extra.member?('IUnix') - e.extra['IUnix'].uid = owner - e.extra['IUnix'].gid = group - end - filenames.size - end - - def chmod(mode, *filenames) - filenames.each do |filename| - e = get_entry(filename) - e.fstype = 3 # force convertion filesystem type to unix - e.unix_perms = mode - e.external_file_attributes = mode << 16 - e.dirty = true - end - filenames.size - end - - def zero?(filename) - sz = size(filename) - sz.nil? || sz == 0 - rescue Errno::ENOENT - false - end - - def file?(filename) - entry = @mapped_zip.find_entry(filename) - !entry.nil? && entry.file? - end - - def dirname(filename) - ::File.dirname(filename) - end - - def basename(filename) - ::File.basename(filename) - end - - def split(filename) - ::File.split(filename) - end - - def join(*fragments) - ::File.join(*fragments) - end - - def utime(modified_time, *filenames) - filenames.each do |filename| - get_entry(filename).time = modified_time - end - end - - def mtime(filename) - @mapped_zip.get_entry(filename).mtime - end - - def atime(filename) - e = get_entry(filename) - if e.extra.member? 'UniversalTime' - e.extra['UniversalTime'].atime - elsif e.extra.member? 'NTFS' - e.extra['NTFS'].atime - end - end - - def ctime(filename) - e = get_entry(filename) - if e.extra.member? 'UniversalTime' - e.extra['UniversalTime'].ctime - elsif e.extra.member? 'NTFS' - e.extra['NTFS'].ctime - end - end - - def pipe?(_filename) - false - end - - def blockdev?(_filename) - false - end - - def chardev?(_filename) - false - end - - def symlink?(_filename) - false - end - - def socket?(_filename) - false - end - - def ftype(filename) - @mapped_zip.get_entry(filename).directory? ? 'directory' : 'file' - end - - def readlink(_filename) - raise NotImplementedError, 'The readlink() function is not implemented' - end - - def symlink(_filename, _symlink_name) - raise NotImplementedError, 'The symlink() function is not implemented' - end - - def link(_filename, _symlink_name) - raise NotImplementedError, 'The link() function is not implemented' - end - - def pipe - raise NotImplementedError, 'The pipe() function is not implemented' - end - - def stat(filename) - raise Errno::ENOENT, filename unless exists?(filename) - - ZipFsStat.new(self, filename) - end - - alias lstat stat - - def readlines(filename) - self.open(filename, &:readlines) - end - - def read(filename) - @mapped_zip.read(filename) - end - - def popen(*args, &a_proc) - ::File.popen(*args, &a_proc) - end - - def foreach(filename, sep = $INPUT_RECORD_SEPARATOR, &a_proc) - self.open(filename) { |is| is.each_line(sep, &a_proc) } - end - - def delete(*args) - args.each do |filename| - if directory?(filename) - raise Errno::EISDIR, "Is a directory - \"#{filename}\"" - end - - @mapped_zip.remove(filename) - end - end - - def rename(file_to_rename, new_name) - @mapped_zip.rename(file_to_rename, new_name) { true } - end - - alias unlink delete - - def expand_path(path) - @mapped_zip.expand_path(path) - end - end end class File diff --git a/lib/zip/filesystem/file.rb b/lib/zip/filesystem/file.rb new file mode 100644 index 00000000..4c66209b --- /dev/null +++ b/lib/zip/filesystem/file.rb @@ -0,0 +1,374 @@ +# frozen_string_literal: true + +module Zip + module FileSystem + # Instances of this class are normally accessed via the accessor + # Zip::File::file. An instance of File behaves like ruby's + # builtin File (class) object, except it works on Zip::File entries. + # + # The individual methods are not documented due to their + # similarity with the methods in File + class File # :nodoc:all + attr_writer :dir + + class ZipFsStat + class << self + def delegate_to_fs_file(*methods) + methods.each do |method| + class_eval <<-END_EVAL, __FILE__, __LINE__ + 1 + def #{method} # def file? + @zip_fs_file.#{method}(@entry_name) # @zip_fs_file.file?(@entry_name) + end # end + END_EVAL + end + end + end + + def initialize(zip_fs_file, entry_name) + @zip_fs_file = zip_fs_file + @entry_name = entry_name + end + + def kind_of?(type) + super || type == ::File::Stat + end + + delegate_to_fs_file :file?, :directory?, :pipe?, :chardev?, :symlink?, + :socket?, :blockdev?, :readable?, :readable_real?, :writable?, :ctime, + :writable_real?, :executable?, :executable_real?, :sticky?, :owned?, + :grpowned?, :setuid?, :setgid?, :zero?, :size, :size?, :mtime, :atime + + def blocks + nil + end + + def get_entry + @zip_fs_file.__send__(:get_entry, @entry_name) + end + private :get_entry + + def gid + e = get_entry + if e.extra.member? 'IUnix' + e.extra['IUnix'].gid || 0 + else + 0 + end + end + + def uid + e = get_entry + if e.extra.member? 'IUnix' + e.extra['IUnix'].uid || 0 + else + 0 + end + end + + def ino + 0 + end + + def dev + 0 + end + + def rdev + 0 + end + + def rdev_major + 0 + end + + def rdev_minor + 0 + end + + def ftype + if file? + 'file' + elsif directory? + 'directory' + else + raise StandardError, 'Unknown file type' + end + end + + def nlink + 1 + end + + def blksize + nil + end + + def mode + e = get_entry + if e.fstype == 3 + e.external_file_attributes >> 16 + else + 33_206 # 33206 is equivalent to -rw-rw-rw- + end + end + end + + def initialize(mapped_zip) + @mapped_zip = mapped_zip + end + + def get_entry(filename) + unless exists?(filename) + raise Errno::ENOENT, "No such file or directory - #{filename}" + end + + @mapped_zip.find_entry(filename) + end + private :get_entry + + def unix_mode_cmp(filename, mode) + e = get_entry(filename) + e.fstype == 3 && ((e.external_file_attributes >> 16) & mode) != 0 + rescue Errno::ENOENT + false + end + private :unix_mode_cmp + + def exists?(filename) + expand_path(filename) == '/' || !@mapped_zip.find_entry(filename).nil? + end + alias exist? exists? + + # Permissions not implemented, so if the file exists it is accessible + alias owned? exists? + alias grpowned? exists? + + def readable?(filename) + unix_mode_cmp(filename, 0o444) + end + alias readable_real? readable? + + def writable?(filename) + unix_mode_cmp(filename, 0o222) + end + alias writable_real? writable? + + def executable?(filename) + unix_mode_cmp(filename, 0o111) + end + alias executable_real? executable? + + def setuid?(filename) + unix_mode_cmp(filename, 0o4000) + end + + def setgid?(filename) + unix_mode_cmp(filename, 0o2000) + end + + def sticky?(filename) + unix_mode_cmp(filename, 0o1000) + end + + def umask(*args) + ::File.umask(*args) + end + + def truncate(_filename, _len) + raise StandardError, 'truncate not supported' + end + + def directory?(filename) + entry = @mapped_zip.find_entry(filename) + expand_path(filename) == '/' || (!entry.nil? && entry.directory?) + end + + def open(filename, mode = 'r', permissions = 0o644, &block) + mode = mode.tr('b', '') # ignore b option + case mode + when 'r' + @mapped_zip.get_input_stream(filename, &block) + when 'w' + @mapped_zip.get_output_stream(filename, permissions, &block) + else + raise StandardError, "openmode '#{mode} not supported" unless mode == 'r' + end + end + + def new(filename, mode = 'r') + self.open(filename, mode) + end + + def size(filename) + @mapped_zip.get_entry(filename).size + end + + # Returns nil for not found and nil for directories + def size?(filename) + entry = @mapped_zip.find_entry(filename) + entry.nil? || entry.directory? ? nil : entry.size + end + + def chown(owner, group, *filenames) + filenames.each do |filename| + e = get_entry(filename) + e.extra.create('IUnix') unless e.extra.member?('IUnix') + e.extra['IUnix'].uid = owner + e.extra['IUnix'].gid = group + end + filenames.size + end + + def chmod(mode, *filenames) + filenames.each do |filename| + e = get_entry(filename) + e.fstype = 3 # force convertion filesystem type to unix + e.unix_perms = mode + e.external_file_attributes = mode << 16 + e.dirty = true + end + filenames.size + end + + def zero?(filename) + sz = size(filename) + sz.nil? || sz == 0 + rescue Errno::ENOENT + false + end + + def file?(filename) + entry = @mapped_zip.find_entry(filename) + !entry.nil? && entry.file? + end + + def dirname(filename) + ::File.dirname(filename) + end + + def basename(filename) + ::File.basename(filename) + end + + def split(filename) + ::File.split(filename) + end + + def join(*fragments) + ::File.join(*fragments) + end + + def utime(modified_time, *filenames) + filenames.each do |filename| + get_entry(filename).time = modified_time + end + end + + def mtime(filename) + @mapped_zip.get_entry(filename).mtime + end + + def atime(filename) + e = get_entry(filename) + if e.extra.member? 'UniversalTime' + e.extra['UniversalTime'].atime + elsif e.extra.member? 'NTFS' + e.extra['NTFS'].atime + end + end + + def ctime(filename) + e = get_entry(filename) + if e.extra.member? 'UniversalTime' + e.extra['UniversalTime'].ctime + elsif e.extra.member? 'NTFS' + e.extra['NTFS'].ctime + end + end + + def pipe?(_filename) + false + end + + def blockdev?(_filename) + false + end + + def chardev?(_filename) + false + end + + def symlink?(_filename) + false + end + + def socket?(_filename) + false + end + + def ftype(filename) + @mapped_zip.get_entry(filename).directory? ? 'directory' : 'file' + end + + def readlink(_filename) + raise NotImplementedError, 'The readlink() function is not implemented' + end + + def symlink(_filename, _symlink_name) + raise NotImplementedError, 'The symlink() function is not implemented' + end + + def link(_filename, _symlink_name) + raise NotImplementedError, 'The link() function is not implemented' + end + + def pipe + raise NotImplementedError, 'The pipe() function is not implemented' + end + + def stat(filename) + raise Errno::ENOENT, filename unless exists?(filename) + + ZipFsStat.new(self, filename) + end + + alias lstat stat + + def readlines(filename) + self.open(filename, &:readlines) + end + + def read(filename) + @mapped_zip.read(filename) + end + + def popen(*args, &a_proc) + ::File.popen(*args, &a_proc) + end + + def foreach(filename, sep = $INPUT_RECORD_SEPARATOR, &a_proc) + self.open(filename) { |is| is.each_line(sep, &a_proc) } + end + + def delete(*args) + args.each do |filename| + if directory?(filename) + raise Errno::EISDIR, "Is a directory - \"#{filename}\"" + end + + @mapped_zip.remove(filename) + end + end + + def rename(file_to_rename, new_name) + @mapped_zip.rename(file_to_rename, new_name) { true } + end + + alias unlink delete + + def expand_path(path) + @mapped_zip.expand_path(path) + end + end + end +end diff --git a/lib/zip/filesystem/zip_file_name_mapper.rb b/lib/zip/filesystem/zip_file_name_mapper.rb index fda5660d..aa699011 100644 --- a/lib/zip/filesystem/zip_file_name_mapper.rb +++ b/lib/zip/filesystem/zip_file_name_mapper.rb @@ -2,8 +2,8 @@ module Zip module FileSystem - # All access to Zip::File from ZipFsFile and FileSystem::Dir goes through - # a ZipFileNameMapper, which has one responsibility: ensure + # All access to Zip::File from FileSystem::File and FileSystem::Dir + # goes through a ZipFileNameMapper, which has one responsibility: ensure class ZipFileNameMapper # :nodoc:all include Enumerable diff --git a/test/filesystem/file_mutating_test.rb b/test/filesystem/file_mutating_test.rb index 91d45afb..6264f0f0 100644 --- a/test/filesystem/file_mutating_test.rb +++ b/test/filesystem/file_mutating_test.rb @@ -3,7 +3,7 @@ require 'test_helper' require 'zip/filesystem' -class ZipFsFileMutatingTest < MiniTest::Test +class FileMutatingTest < MiniTest::Test TEST_ZIP = 'test/data/generated/zipWithDirs_copy.zip' def setup FileUtils.cp('test/data/zipWithDirs.zip', TEST_ZIP) diff --git a/test/filesystem/file_nonmutating_test.rb b/test/filesystem/file_nonmutating_test.rb index bc0e41d3..d4007e6f 100644 --- a/test/filesystem/file_nonmutating_test.rb +++ b/test/filesystem/file_nonmutating_test.rb @@ -3,7 +3,7 @@ require 'test_helper' require 'zip/filesystem' -class ZipFsFileNonmutatingTest < MiniTest::Test +class FileNonmutatingTest < MiniTest::Test def setup @zipsha = Digest::SHA1.file('test/data/zipWithDirs.zip') @zip_file = ::Zip::File.new('test/data/zipWithDirs.zip') diff --git a/test/filesystem/file_stat_test.rb b/test/filesystem/file_stat_test.rb index 0f6e2b50..f10a5db4 100644 --- a/test/filesystem/file_stat_test.rb +++ b/test/filesystem/file_stat_test.rb @@ -3,7 +3,7 @@ require 'test_helper' require 'zip/filesystem' -class ZipFsFileStatTest < MiniTest::Test +class FileStatTest < MiniTest::Test def setup @zip_file = ::Zip::File.new('test/data/zipWithDirs.zip') end From 7b2e9c7970de6f34f23cc282766cfda734865eed Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Wed, 2 Jun 2021 21:37:20 +0100 Subject: [PATCH 128/311] Extract `FileSystem::File::Stat` from `FileSystem::File`. --- .rubocop_todo.yml | 2 +- lib/zip/filesystem/file.rb | 106 +------------------------------ lib/zip/filesystem/file_stat.rb | 109 ++++++++++++++++++++++++++++++++ 3 files changed, 113 insertions(+), 104 deletions(-) create mode 100644 lib/zip/filesystem/file_stat.rb diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 68f251b9..a7095755 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -47,7 +47,7 @@ Metrics/PerceivedComplexity: Naming/AccessorMethodName: Exclude: - 'lib/zip/entry.rb' - - 'lib/zip/filesystem/file.rb' + - 'lib/zip/filesystem/file_stat.rb' - 'lib/zip/input_stream.rb' - 'lib/zip/streamable_stream.rb' diff --git a/lib/zip/filesystem/file.rb b/lib/zip/filesystem/file.rb index 4c66209b..9b47161a 100644 --- a/lib/zip/filesystem/file.rb +++ b/lib/zip/filesystem/file.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true +require_relative 'file_stat' + module Zip module FileSystem # Instances of this class are normally accessed via the accessor @@ -11,108 +13,6 @@ module FileSystem class File # :nodoc:all attr_writer :dir - class ZipFsStat - class << self - def delegate_to_fs_file(*methods) - methods.each do |method| - class_eval <<-END_EVAL, __FILE__, __LINE__ + 1 - def #{method} # def file? - @zip_fs_file.#{method}(@entry_name) # @zip_fs_file.file?(@entry_name) - end # end - END_EVAL - end - end - end - - def initialize(zip_fs_file, entry_name) - @zip_fs_file = zip_fs_file - @entry_name = entry_name - end - - def kind_of?(type) - super || type == ::File::Stat - end - - delegate_to_fs_file :file?, :directory?, :pipe?, :chardev?, :symlink?, - :socket?, :blockdev?, :readable?, :readable_real?, :writable?, :ctime, - :writable_real?, :executable?, :executable_real?, :sticky?, :owned?, - :grpowned?, :setuid?, :setgid?, :zero?, :size, :size?, :mtime, :atime - - def blocks - nil - end - - def get_entry - @zip_fs_file.__send__(:get_entry, @entry_name) - end - private :get_entry - - def gid - e = get_entry - if e.extra.member? 'IUnix' - e.extra['IUnix'].gid || 0 - else - 0 - end - end - - def uid - e = get_entry - if e.extra.member? 'IUnix' - e.extra['IUnix'].uid || 0 - else - 0 - end - end - - def ino - 0 - end - - def dev - 0 - end - - def rdev - 0 - end - - def rdev_major - 0 - end - - def rdev_minor - 0 - end - - def ftype - if file? - 'file' - elsif directory? - 'directory' - else - raise StandardError, 'Unknown file type' - end - end - - def nlink - 1 - end - - def blksize - nil - end - - def mode - e = get_entry - if e.fstype == 3 - e.external_file_attributes >> 16 - else - 33_206 # 33206 is equivalent to -rw-rw-rw- - end - end - end - def initialize(mapped_zip) @mapped_zip = mapped_zip end @@ -329,7 +229,7 @@ def pipe def stat(filename) raise Errno::ENOENT, filename unless exists?(filename) - ZipFsStat.new(self, filename) + Stat.new(self, filename) end alias lstat stat diff --git a/lib/zip/filesystem/file_stat.rb b/lib/zip/filesystem/file_stat.rb new file mode 100644 index 00000000..da53bb26 --- /dev/null +++ b/lib/zip/filesystem/file_stat.rb @@ -0,0 +1,109 @@ +# frozen_string_literal: true + +module Zip + module FileSystem + class File # :nodoc:all + class Stat # :nodoc:all + class << self + def delegate_to_fs_file(*methods) + methods.each do |method| + class_eval <<-END_EVAL, __FILE__, __LINE__ + 1 + def #{method} # def file? + @zip_fs_file.#{method}(@entry_name) # @zip_fs_file.file?(@entry_name) + end # end + END_EVAL + end + end + end + + def initialize(zip_fs_file, entry_name) + @zip_fs_file = zip_fs_file + @entry_name = entry_name + end + + def kind_of?(type) + super || type == ::File::Stat + end + + delegate_to_fs_file :file?, :directory?, :pipe?, :chardev?, :symlink?, + :socket?, :blockdev?, :readable?, :readable_real?, :writable?, :ctime, + :writable_real?, :executable?, :executable_real?, :sticky?, :owned?, + :grpowned?, :setuid?, :setgid?, :zero?, :size, :size?, :mtime, :atime + + def blocks + nil + end + + def get_entry + @zip_fs_file.__send__(:get_entry, @entry_name) + end + private :get_entry + + def gid + e = get_entry + if e.extra.member? 'IUnix' + e.extra['IUnix'].gid || 0 + else + 0 + end + end + + def uid + e = get_entry + if e.extra.member? 'IUnix' + e.extra['IUnix'].uid || 0 + else + 0 + end + end + + def ino + 0 + end + + def dev + 0 + end + + def rdev + 0 + end + + def rdev_major + 0 + end + + def rdev_minor + 0 + end + + def ftype + if file? + 'file' + elsif directory? + 'directory' + else + raise StandardError, 'Unknown file type' + end + end + + def nlink + 1 + end + + def blksize + nil + end + + def mode + e = get_entry + if e.fstype == 3 + e.external_file_attributes >> 16 + else + 33_206 # 33206 is equivalent to -rw-rw-rw- + end + end + end + end + end +end From 99ecf3638f86d702b65b292530e2faad0fecabec Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Wed, 2 Jun 2021 22:13:23 +0100 Subject: [PATCH 129/311] Remove spurious empty line at start of module. --- lib/zip/filesystem.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/zip/filesystem.rb b/lib/zip/filesystem.rb index 5fe0c994..26895df5 100644 --- a/lib/zip/filesystem.rb +++ b/lib/zip/filesystem.rb @@ -38,7 +38,6 @@ module Zip # |zipfile| # puts zipfile.file.read("first.txt") # } - module FileSystem def initialize # :nodoc: mapped_zip = ZipFileNameMapper.new(self) From 64a162ced4eb03e9472a1b03341146d769e341a8 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Wed, 2 Jun 2021 22:14:19 +0100 Subject: [PATCH 130/311] Refactor `FileSystem::File::Stat.delegate_to_fs_file`. Now uses `class_exec` instead of `class_eval`. --- lib/zip/filesystem/file_stat.rb | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/zip/filesystem/file_stat.rb b/lib/zip/filesystem/file_stat.rb index da53bb26..6c43699d 100644 --- a/lib/zip/filesystem/file_stat.rb +++ b/lib/zip/filesystem/file_stat.rb @@ -7,11 +7,11 @@ class Stat # :nodoc:all class << self def delegate_to_fs_file(*methods) methods.each do |method| - class_eval <<-END_EVAL, __FILE__, __LINE__ + 1 - def #{method} # def file? - @zip_fs_file.#{method}(@entry_name) # @zip_fs_file.file?(@entry_name) - end # end - END_EVAL + class_exec do + define_method(method) do + @zip_fs_file.__send__(method, @entry_name) + end + end end end end From 9d8fc05c439b75adfa6e2889cde6bea04bf738be Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Wed, 2 Jun 2021 22:28:40 +0100 Subject: [PATCH 131/311] Refactor `get_entry` in `FileSystem::File(::Stat)`. Rename it to `find_entry` because that is ultimately what is called on the underlying zip file. Make `FileSystem::File#find_entry` public as it need to be called from `FileSystem::File::Stat`, so now we can avoid `__send__`. Neither class is documented anyway, so no harm done there. --- .rubocop_todo.yml | 1 - lib/zip/filesystem/file.rb | 15 +++++++-------- lib/zip/filesystem/file_stat.rb | 17 +++++++++-------- 3 files changed, 16 insertions(+), 17 deletions(-) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index a7095755..aafa7fe7 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -47,7 +47,6 @@ Metrics/PerceivedComplexity: Naming/AccessorMethodName: Exclude: - 'lib/zip/entry.rb' - - 'lib/zip/filesystem/file_stat.rb' - 'lib/zip/input_stream.rb' - 'lib/zip/streamable_stream.rb' diff --git a/lib/zip/filesystem/file.rb b/lib/zip/filesystem/file.rb index 9b47161a..76adad14 100644 --- a/lib/zip/filesystem/file.rb +++ b/lib/zip/filesystem/file.rb @@ -17,17 +17,16 @@ def initialize(mapped_zip) @mapped_zip = mapped_zip end - def get_entry(filename) + def find_entry(filename) unless exists?(filename) raise Errno::ENOENT, "No such file or directory - #{filename}" end @mapped_zip.find_entry(filename) end - private :get_entry def unix_mode_cmp(filename, mode) - e = get_entry(filename) + e = find_entry(filename) e.fstype == 3 && ((e.external_file_attributes >> 16) & mode) != 0 rescue Errno::ENOENT false @@ -111,7 +110,7 @@ def size?(filename) def chown(owner, group, *filenames) filenames.each do |filename| - e = get_entry(filename) + e = find_entry(filename) e.extra.create('IUnix') unless e.extra.member?('IUnix') e.extra['IUnix'].uid = owner e.extra['IUnix'].gid = group @@ -121,7 +120,7 @@ def chown(owner, group, *filenames) def chmod(mode, *filenames) filenames.each do |filename| - e = get_entry(filename) + e = find_entry(filename) e.fstype = 3 # force convertion filesystem type to unix e.unix_perms = mode e.external_file_attributes = mode << 16 @@ -160,7 +159,7 @@ def join(*fragments) def utime(modified_time, *filenames) filenames.each do |filename| - get_entry(filename).time = modified_time + find_entry(filename).time = modified_time end end @@ -169,7 +168,7 @@ def mtime(filename) end def atime(filename) - e = get_entry(filename) + e = find_entry(filename) if e.extra.member? 'UniversalTime' e.extra['UniversalTime'].atime elsif e.extra.member? 'NTFS' @@ -178,7 +177,7 @@ def atime(filename) end def ctime(filename) - e = get_entry(filename) + e = find_entry(filename) if e.extra.member? 'UniversalTime' e.extra['UniversalTime'].ctime elsif e.extra.member? 'NTFS' diff --git a/lib/zip/filesystem/file_stat.rb b/lib/zip/filesystem/file_stat.rb index 6c43699d..169afdbd 100644 --- a/lib/zip/filesystem/file_stat.rb +++ b/lib/zip/filesystem/file_stat.rb @@ -34,13 +34,8 @@ def blocks nil end - def get_entry - @zip_fs_file.__send__(:get_entry, @entry_name) - end - private :get_entry - def gid - e = get_entry + e = find_entry if e.extra.member? 'IUnix' e.extra['IUnix'].gid || 0 else @@ -49,7 +44,7 @@ def gid end def uid - e = get_entry + e = find_entry if e.extra.member? 'IUnix' e.extra['IUnix'].uid || 0 else @@ -96,13 +91,19 @@ def blksize end def mode - e = get_entry + e = find_entry if e.fstype == 3 e.external_file_attributes >> 16 else 33_206 # 33206 is equivalent to -rw-rw-rw- end end + + private + + def find_entry + @zip_fs_file.find_entry(@entry_name) + end end end end From 26b7f98c0836b24cb053005a8a25da664831d052 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Thu, 3 Jun 2021 07:56:31 +0100 Subject: [PATCH 132/311] Use octal for more obvious definition of file-modes. --- lib/zip/filesystem/file_stat.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/zip/filesystem/file_stat.rb b/lib/zip/filesystem/file_stat.rb index 169afdbd..22a6ac3e 100644 --- a/lib/zip/filesystem/file_stat.rb +++ b/lib/zip/filesystem/file_stat.rb @@ -95,7 +95,7 @@ def mode if e.fstype == 3 e.external_file_attributes >> 16 else - 33_206 # 33206 is equivalent to -rw-rw-rw- + 0o100_666 # Equivalent to -rw-rw-rw-. end end From a4e51f15fc0b8a3cd9331cef213940b2ba5b5dbe Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Thu, 3 Jun 2021 08:38:37 +0100 Subject: [PATCH 133/311] Use constants instead of literals for some `fstype` calls. --- lib/zip/filesystem/file.rb | 4 ++-- lib/zip/filesystem/file_stat.rb | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/zip/filesystem/file.rb b/lib/zip/filesystem/file.rb index 76adad14..7a93f7e2 100644 --- a/lib/zip/filesystem/file.rb +++ b/lib/zip/filesystem/file.rb @@ -27,7 +27,7 @@ def find_entry(filename) def unix_mode_cmp(filename, mode) e = find_entry(filename) - e.fstype == 3 && ((e.external_file_attributes >> 16) & mode) != 0 + e.fstype == FSTYPE_UNIX && ((e.external_file_attributes >> 16) & mode) != 0 rescue Errno::ENOENT false end @@ -121,7 +121,7 @@ def chown(owner, group, *filenames) def chmod(mode, *filenames) filenames.each do |filename| e = find_entry(filename) - e.fstype = 3 # force convertion filesystem type to unix + e.fstype = FSTYPE_UNIX # Force conversion filesystem type to unix. e.unix_perms = mode e.external_file_attributes = mode << 16 e.dirty = true diff --git a/lib/zip/filesystem/file_stat.rb b/lib/zip/filesystem/file_stat.rb index 22a6ac3e..6117bb7c 100644 --- a/lib/zip/filesystem/file_stat.rb +++ b/lib/zip/filesystem/file_stat.rb @@ -92,7 +92,7 @@ def blksize def mode e = find_entry - if e.fstype == 3 + if e.fstype == FSTYPE_UNIX e.external_file_attributes >> 16 else 0o100_666 # Equivalent to -rw-rw-rw-. From 098bce399afacff6bb2534cca570eb97ca8463ef Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sun, 6 Jun 2021 15:45:04 +0100 Subject: [PATCH 134/311] Update Changelog. --- Changelog.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index 14b181ce..42eaa4d8 100644 --- a/Changelog.md +++ b/Changelog.md @@ -10,8 +10,9 @@ - Fix input stream partial read error. [#462](https://github.com/rubyzip/rubyzip/pull/462) - Fix zlib deflate buffer growth. [#447](https://github.com/rubyzip/rubyzip/pull/447) -Tooling: +Tooling/internal: +- Refactor, and tidy up, the `Zip::Filesystem` classes for improved maintainability. - Fix Windows tests. [#489](https://github.com/rubyzip/rubyzip/pull/489) - Refactor `assert_forwarded` so it does not need `ObjectSpace._id2ref` or `eval`. [#483](https://github.com/rubyzip/rubyzip/pull/483) - Add GitHub Actions CI infrastructure. [#469](https://github.com/rubyzip/rubyzip/issues/469) From 684b69f330c2b833d07aa8c6cbc579cd0974069f Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Mon, 31 May 2021 09:11:39 +0100 Subject: [PATCH 135/311] Move the restore options to the top level. This will ensure consistency between `File` and `Entry`. --- lib/zip.rb | 6 ++++++ lib/zip/entry.rb | 6 +++--- lib/zip/file.rb | 8 +------- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/lib/zip.rb b/lib/zip.rb index 9af80b15..7e700449 100644 --- a/lib/zip.rb +++ b/lib/zip.rb @@ -48,6 +48,12 @@ module Zip :force_entry_names_encoding, :validate_entry_sizes + DEFAULT_RESTORE_OPTIONS = { + restore_ownership: false, + restore_permissions: false, + restore_times: false + }.freeze + def reset! @_ran_once = false @unicode_names = false diff --git a/lib/zip/entry.rb b/lib/zip/entry.rb index 2e484284..a22337fa 100644 --- a/lib/zip/entry.rb +++ b/lib/zip/entry.rb @@ -42,9 +42,9 @@ def set_default_vars_values end @follow_symlinks = false - @restore_times = false - @restore_permissions = false - @restore_ownership = false + @restore_times = DEFAULT_RESTORE_OPTIONS[:restore_times] + @restore_permissions = DEFAULT_RESTORE_OPTIONS[:restore_permissions] + @restore_ownership = DEFAULT_RESTORE_OPTIONS[:restore_ownership] # BUG: need an extra field to support uid/gid's @unix_uid = nil @unix_gid = nil diff --git a/lib/zip/file.rb b/lib/zip/file.rb index 10a3f495..e45f852c 100644 --- a/lib/zip/file.rb +++ b/lib/zip/file.rb @@ -53,12 +53,6 @@ class File < CentralDirectory DATA_BUFFER_SIZE = 8192 IO_METHODS = [:tell, :seek, :read, :eof, :close].freeze - DEFAULT_OPTIONS = { - restore_ownership: false, - restore_permissions: false, - restore_times: false - }.freeze - attr_reader :name # default -> false. @@ -77,7 +71,7 @@ class File < CentralDirectory # a new archive if it doesn't exist already. def initialize(path_or_io, create = false, buffer = false, options = {}) super() - options = DEFAULT_OPTIONS + options = DEFAULT_RESTORE_OPTIONS .merge(compression_level: ::Zip.default_compression) .merge(options) @name = path_or_io.respond_to?(:path) ? path_or_io.path : path_or_io From a6c6345084eb607b5d23da79fc19670d27ab0354 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Mon, 31 May 2021 10:41:41 +0100 Subject: [PATCH 136/311] Set restoring permissions and times as the default. --- lib/zip.rb | 4 ++-- lib/zip/file.rb | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/zip.rb b/lib/zip.rb index 7e700449..87a236a8 100644 --- a/lib/zip.rb +++ b/lib/zip.rb @@ -50,8 +50,8 @@ module Zip DEFAULT_RESTORE_OPTIONS = { restore_ownership: false, - restore_permissions: false, - restore_times: false + restore_permissions: true, + restore_times: true }.freeze def reset! diff --git a/lib/zip/file.rb b/lib/zip/file.rb index e45f852c..6286778f 100644 --- a/lib/zip/file.rb +++ b/lib/zip/file.rb @@ -58,10 +58,10 @@ class File < CentralDirectory # default -> false. attr_accessor :restore_ownership - # default -> false, but will be set to true in a future version. + # default -> true. attr_accessor :restore_permissions - # default -> false, but will be set to true in a future version. + # default -> true. attr_accessor :restore_times # Returns the zip files comment, if it has one From 3260e4e6668a179e4db282bb9a92abc7f3b30258 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Mon, 31 May 2021 17:16:55 +0100 Subject: [PATCH 137/311] Add some tests to ensure the default behaviour sticks. --- test/file_options_test.rb | 66 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 65 insertions(+), 1 deletion(-) diff --git a/test/file_options_test.rb b/test/file_options_test.rb index 4bffa4f1..bf1803f3 100644 --- a/test/file_options_test.rb +++ b/test/file_options_test.rb @@ -23,7 +23,7 @@ def teardown ::File.unlink(TXTPATH_755) if ::File.exist?(TXTPATH_755) end - def test_restore_permissions + def test_restore_permissions_true # Copy and set up files with different permissions. ::FileUtils.cp(TXTPATH, TXTPATH_600) ::File.chmod(0o600, TXTPATH_600) @@ -47,6 +47,55 @@ def test_restore_permissions assert_equal(::File.stat(TXTPATH_755).mode, ::File.stat(EXTPATH_3).mode) end + def test_restore_permissions_false + # Copy and set up files with different permissions. + ::FileUtils.cp(TXTPATH, TXTPATH_600) + ::File.chmod(0o600, TXTPATH_600) + ::FileUtils.cp(TXTPATH, TXTPATH_755) + ::File.chmod(0o755, TXTPATH_755) + + ::Zip::File.open(ZIPPATH, true) do |zip| + zip.add(ENTRY_1, TXTPATH) + zip.add(ENTRY_2, TXTPATH_600) + zip.add(ENTRY_3, TXTPATH_755) + end + + ::Zip::File.open(ZIPPATH, false, restore_permissions: false) do |zip| + zip.extract(ENTRY_1, EXTPATH_1) + zip.extract(ENTRY_2, EXTPATH_2) + zip.extract(ENTRY_3, EXTPATH_3) + end + + default_perms = 0o100_666 - ::File.umask + assert_equal(default_perms, ::File.stat(EXTPATH_1).mode) + assert_equal(default_perms, ::File.stat(EXTPATH_2).mode) + assert_equal(default_perms, ::File.stat(EXTPATH_3).mode) + end + + def test_restore_permissions_as_default + # Copy and set up files with different permissions. + ::FileUtils.cp(TXTPATH, TXTPATH_600) + ::File.chmod(0o600, TXTPATH_600) + ::FileUtils.cp(TXTPATH, TXTPATH_755) + ::File.chmod(0o755, TXTPATH_755) + + ::Zip::File.open(ZIPPATH, true) do |zip| + zip.add(ENTRY_1, TXTPATH) + zip.add(ENTRY_2, TXTPATH_600) + zip.add(ENTRY_3, TXTPATH_755) + end + + ::Zip::File.open(ZIPPATH) do |zip| + zip.extract(ENTRY_1, EXTPATH_1) + zip.extract(ENTRY_2, EXTPATH_2) + zip.extract(ENTRY_3, EXTPATH_3) + end + + assert_equal(::File.stat(TXTPATH).mode, ::File.stat(EXTPATH_1).mode) + assert_equal(::File.stat(TXTPATH_600).mode, ::File.stat(EXTPATH_2).mode) + assert_equal(::File.stat(TXTPATH_755).mode, ::File.stat(EXTPATH_3).mode) + end + def test_restore_times_true ::Zip::File.open(ZIPPATH, true) do |zip| zip.add(ENTRY_1, TXTPATH) @@ -81,6 +130,21 @@ def test_restore_times_false assert_time_equal(::Time.now, ::File.mtime(EXTPATH_2)) end + def test_restore_times_true_as_default + ::Zip::File.open(ZIPPATH, true) do |zip| + zip.add(ENTRY_1, TXTPATH) + zip.add_stored(ENTRY_2, TXTPATH) + end + + ::Zip::File.open(ZIPPATH) do |zip| + zip.extract(ENTRY_1, EXTPATH_1) + zip.extract(ENTRY_2, EXTPATH_2) + end + + assert_time_equal(::File.mtime(TXTPATH), ::File.mtime(EXTPATH_1)) + assert_time_equal(::File.mtime(TXTPATH), ::File.mtime(EXTPATH_2)) + end + def test_get_find_consistency testzip = ::File.expand_path(::File.join('data', 'globTest.zip'), __dir__) file_f = ::File.expand_path('f_test.txt', Dir.tmpdir) From 2410f2889e69e23db23a2038772b7e0aaf99228e Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Mon, 31 May 2021 17:44:25 +0100 Subject: [PATCH 138/311] Restore file timestamps on all platforms. Was only being done on Unix-type filesystems for some reason. Moved code so that it is run for all files, whatever the underlying platform. --- lib/zip/entry.rb | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/zip/entry.rb b/lib/zip/entry.rb index a22337fa..2327cd07 100644 --- a/lib/zip/entry.rb +++ b/lib/zip/entry.rb @@ -462,11 +462,6 @@ def set_unix_attributes_on_path(dest_path) unix_perms_mask = 0o7777 if @restore_ownership ::FileUtils.chmod(@unix_perms & unix_perms_mask, dest_path) if @restore_permissions && @unix_perms ::FileUtils.chown(@unix_uid, @unix_gid, dest_path) if @restore_ownership && @unix_uid && @unix_gid && ::Process.egid == 0 - - # Restore the timestamp on a file. This will either have come from the - # original source file that was copied into the archive, or from the - # creation date of the archive if there was no original source file. - ::FileUtils.touch(dest_path, mtime: time) if @restore_times end def set_extra_attributes_on_path(dest_path) # :nodoc: @@ -476,6 +471,11 @@ def set_extra_attributes_on_path(dest_path) # :nodoc: when ::Zip::FSTYPE_UNIX set_unix_attributes_on_path(dest_path) end + + # Restore the timestamp on a file. This will either have come from the + # original source file that was copied into the archive, or from the + # creation date of the archive if there was no original source file. + ::FileUtils.touch(dest_path, mtime: time) if @restore_times end def pack_c_dir_entry From 369056ff3071d09a8418f86d86c3037ec7e240b8 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Mon, 31 May 2021 18:15:39 +0100 Subject: [PATCH 139/311] Update Changelog. --- Changelog.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Changelog.md b/Changelog.md index 42eaa4d8..3e54456e 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,5 +1,7 @@ # 3.0.0 (Next) +- Fix restore options consistency. [#486](https://github.com/rubyzip/rubyzip/pull/486) +- View and/or preserve original date created, date modified? (Windows). [#336](https://github.com/rubyzip/rubyzip/issues/336) - Fix frozen string literal error. [#475](https://github.com/rubyzip/rubyzip/pull/475) - Set the default `Entry` time to the file's mtime on Windows. [#465](https://github.com/rubyzip/rubyzip/issues/465) - Ensure that `Entry#time=` sets times as `DOSTime` objects. [#481](https://github.com/rubyzip/rubyzip/issues/481) From 52bcfc72f9d234363cbffba9b7ea4fa5333b3478 Mon Sep 17 00:00:00 2001 From: Jan-Joost Spanjers Date: Sun, 6 Jun 2021 15:51:48 +0200 Subject: [PATCH 140/311] Revert "REVERT ME: This disables a test fixed by #486" This reverts commit 6ac2cb207d80a0900491538543c5a8589377f8cf. --- test/file_options_test.rb | 4 ---- 1 file changed, 4 deletions(-) diff --git a/test/file_options_test.rb b/test/file_options_test.rb index bf1803f3..f82fa9ca 100644 --- a/test/file_options_test.rb +++ b/test/file_options_test.rb @@ -107,10 +107,6 @@ def test_restore_times_true zip.extract(ENTRY_2, EXTPATH_2) end - # this test is disabled on Windows for now, waiting for #486. - # please remove this after merging #486. - skip if Zip::RUNNING_ON_WINDOWS - assert_time_equal(::File.mtime(TXTPATH), ::File.mtime(EXTPATH_1)) assert_time_equal(::File.mtime(TXTPATH), ::File.mtime(EXTPATH_2)) end From 4a01537f32ffed29a53d34d84d6c444be1488c4e Mon Sep 17 00:00:00 2001 From: Jan-Joost Spanjers Date: Sun, 6 Jun 2021 15:59:25 +0200 Subject: [PATCH 141/311] Fix restore permissons test on Windows --- test/file_options_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/file_options_test.rb b/test/file_options_test.rb index f82fa9ca..da9a402c 100644 --- a/test/file_options_test.rb +++ b/test/file_options_test.rb @@ -66,7 +66,7 @@ def test_restore_permissions_false zip.extract(ENTRY_3, EXTPATH_3) end - default_perms = 0o100_666 - ::File.umask + default_perms = (Zip::RUNNING_ON_WINDOWS ? 0o100_644 : 0o100_666) - ::File.umask assert_equal(default_perms, ::File.stat(EXTPATH_1).mode) assert_equal(default_perms, ::File.stat(EXTPATH_2).mode) assert_equal(default_perms, ::File.stat(EXTPATH_3).mode) From 4fe6bc89837fd50cc53e89d98db1e10aa6d57ede Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sun, 6 Jun 2021 18:11:41 +0100 Subject: [PATCH 142/311] Update README to remove link to Travis. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f5cabcd0..5b20f664 100644 --- a/README.md +++ b/README.md @@ -348,7 +348,7 @@ rake Please also run `rubocop` over your changes. -Our CI currently runs on [Travis](https://travis-ci.org/github/rubyzip/rubyzip) and [GitHub Actions](https://github.com/rubyzip/rubyzip/actions). Please note that `rubocop` is run as part of the CI configuration and will fail a build if errors are found. +Our CI runs on [GitHub Actions](https://github.com/rubyzip/rubyzip/actions). Please note that `rubocop` is run as part of the CI configuration and will fail a build if errors are found. ## Website and Project Home From 51c6c10e7a9448fee340445c05b30bbd4cff95e9 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sun, 6 Jun 2021 20:06:59 +0100 Subject: [PATCH 143/311] Add a compatibility table to the README. Fixes #455. --- README.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/README.md b/README.md index 5b20f664..52ba31f3 100644 --- a/README.md +++ b/README.md @@ -332,6 +332,21 @@ You can set multiple settings at the same time by using a block: end ``` +## Compatibility + +Rubyzip is known to run on a number of platforms and under a number of different Ruby versions. Please see the table below for what we think the current situation is. Note: an empty cell means "unknown", not "does not work". + +| OS | 2.4 | 2.5 | 2.6 | 2.7 | 3.0 | Head | JRuby 9.2.17.0 | JRuby Head | Truffleruby 21.1.0 | Truffleruby Head | +|----|-----|-----|-----|-----|-----|------|----------------|------------|--------------------|------------------| +|Ubuntu 20.04| CI | CI | CI | CI | CI | ci | CI | ci | CI | ci | +|Mac OS 10.15.7| CI | x | x | x | x | | x | | x | | +|Windows 10| | | | x | | | | | | | +|Windows Server 2019| CI | | | | | | | | | | + +Key: `CI` - tested in CI, should work; `ci` - tested in CI, might fail; `x` - known working; `o` - known failing. + +Please [raise a PR](https://github.com/rubyzip/rubyzip/pulls) if you know Rubyzip works on a platform/Ruby combination not listed here, or [raise an issue](https://github.com/rubyzip/rubyzip/issues) if you see a failure where we think it should work. + ## Developing Install the dependencies: From cd9a3fcad1a46bcaf29a30033a11ad6dff72211a Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Thu, 10 Jun 2021 17:29:00 +0100 Subject: [PATCH 144/311] Move all the `read_zip_*` methods out of `Entry`. They were only ever used in `CentralDirectory` anyway. --- lib/zip/central_directory.rb | 46 +++++++++++++++++++++++------------- lib/zip/entry.rb | 12 ---------- 2 files changed, 30 insertions(+), 28 deletions(-) diff --git a/lib/zip/central_directory.rb b/lib/zip/central_directory.rb index 36a150ba..d6f5b0c5 100644 --- a/lib/zip/central_directory.rb +++ b/lib/zip/central_directory.rb @@ -88,28 +88,28 @@ def write_64_eocd_locator(io, zip64_eocd_offset) def read_64_e_o_c_d(buf) #:nodoc: buf = get_64_e_o_c_d(buf) - @size_of_zip64_e_o_c_d = Entry.read_zip_64_long(buf) - @version_made_by = Entry.read_zip_short(buf) - @version_needed_for_extract = Entry.read_zip_short(buf) - @number_of_this_disk = Entry.read_zip_long(buf) - @number_of_disk_with_start_of_cdir = Entry.read_zip_long(buf) - @total_number_of_entries_in_cdir_on_this_disk = Entry.read_zip_64_long(buf) - @size = Entry.read_zip_64_long(buf) - @size_in_bytes = Entry.read_zip_64_long(buf) - @cdir_offset = Entry.read_zip_64_long(buf) + @size_of_zip64_e_o_c_d = read_long64(buf) + @version_made_by = read_short(buf) + @version_needed_for_extract = read_short(buf) + @number_of_this_disk = read_long(buf) + @number_of_disk_with_start_of_cdir = read_long(buf) + @total_number_of_entries_in_cdir_on_this_disk = read_long64(buf) + @size = read_long64(buf) + @size_in_bytes = read_long64(buf) + @cdir_offset = read_long64(buf) @zip_64_extensible = buf.slice!(0, buf.bytesize) raise Error, 'Zip consistency problem while reading eocd structure' unless buf.empty? end def read_e_o_c_d(buf) #:nodoc: buf = get_e_o_c_d(buf) - @number_of_this_disk = Entry.read_zip_short(buf) - @number_of_disk_with_start_of_cdir = Entry.read_zip_short(buf) - @total_number_of_entries_in_cdir_on_this_disk = Entry.read_zip_short(buf) - @size = Entry.read_zip_short(buf) - @size_in_bytes = Entry.read_zip_long(buf) - @cdir_offset = Entry.read_zip_long(buf) - comment_length = Entry.read_zip_short(buf) + @number_of_this_disk = read_short(buf) + @number_of_disk_with_start_of_cdir = read_short(buf) + @total_number_of_entries_in_cdir_on_this_disk = read_short(buf) + @size = read_short(buf) + @size_in_bytes = read_long(buf) + @cdir_offset = read_long(buf) + comment_length = read_short(buf) @comment = if comment_length.to_i <= 0 buf.slice!(0, buf.size) else @@ -233,6 +233,20 @@ def ==(other) #:nodoc: @entry_set.entries.sort == other.entries.sort && comment == other.comment end + + private + + def read_short(io) # :nodoc: + io.read(2).unpack1('v') + end + + def read_long(io) # :nodoc: + io.read(4).unpack1('V') + end + + def read_long64(io) # :nodoc: + io.read(8).unpack1('Q<') + end end end diff --git a/lib/zip/entry.rb b/lib/zip/entry.rb index 2327cd07..cdff9baa 100644 --- a/lib/zip/entry.rb +++ b/lib/zip/entry.rb @@ -230,18 +230,6 @@ def to_s end class << self - def read_zip_short(io) # :nodoc: - io.read(2).unpack1('v') - end - - def read_zip_long(io) # :nodoc: - io.read(4).unpack1('V') - end - - def read_zip_64_long(io) # :nodoc: - io.read(8).unpack1('Q<') - end - def read_c_dir_entry(io) #:nodoc:all path = if io.respond_to?(:path) io.path From 7e254dc581af8f27ffcbe6b18a52662daf13b7a6 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Thu, 10 Jun 2021 22:44:51 +0100 Subject: [PATCH 145/311] Refactor unpacking the eocd record. The old version used some really obfuscated code to perform what is an essentially fairly simple job. --- lib/zip/central_directory.rb | 50 +++++++++++++++--------------------- 1 file changed, 21 insertions(+), 29 deletions(-) diff --git a/lib/zip/central_directory.rb b/lib/zip/central_directory.rb index d6f5b0c5..51f14370 100644 --- a/lib/zip/central_directory.rb +++ b/lib/zip/central_directory.rb @@ -101,21 +101,26 @@ def read_64_e_o_c_d(buf) #:nodoc: raise Error, 'Zip consistency problem while reading eocd structure' unless buf.empty? end - def read_e_o_c_d(buf) #:nodoc: - buf = get_e_o_c_d(buf) - @number_of_this_disk = read_short(buf) - @number_of_disk_with_start_of_cdir = read_short(buf) - @total_number_of_entries_in_cdir_on_this_disk = read_short(buf) - @size = read_short(buf) - @size_in_bytes = read_long(buf) - @cdir_offset = read_long(buf) - comment_length = read_short(buf) - @comment = if comment_length.to_i <= 0 - buf.slice!(0, buf.size) - else - buf.read(comment_length) - end - raise Error, 'Zip consistency problem while reading eocd structure' unless buf.empty? + def unpack_e_o_c_d(buffer) #:nodoc: + index = buffer.rindex([END_OF_CDS].pack('V')) + raise Error, 'Zip end of central directory signature not found' unless index + + buf = buffer.slice(index, buffer.size) + + _, # END_OF_CDS signature. We know we have this at this point. + @number_of_this_disk, + @number_of_disk_with_start_of_cdir, + @total_number_of_entries_in_cdir_on_this_disk, + @size, + @size_in_bytes, + @cdir_offset, + comment_length = buf.unpack('VvvvvVVv') + + @comment = if comment_length.positive? + buf.slice(STATIC_EOCD_SIZE, comment_length) + else + '' + end end def read_central_directory_entries(io) #:nodoc: @@ -162,24 +167,11 @@ def read_from_stream(io) #:nodoc: if zip64_file?(buf) read_64_e_o_c_d(buf) else - read_e_o_c_d(buf) + unpack_e_o_c_d(buf) end read_central_directory_entries(io) end - def get_e_o_c_d(buf) #:nodoc: - sig_index = buf.rindex([END_OF_CDS].pack('V')) - raise Error, 'Zip end of central directory signature not found' unless sig_index - - buf = buf.slice!((sig_index + 4)..(buf.bytesize)) - - def buf.read(count) - slice!(0, count) - end - - buf - end - def zip64_file?(buf) buf.rindex([ZIP64_END_OF_CDS].pack('V')) && buf.rindex([ZIP64_EOCD_LOCATOR].pack('V')) end From dc27c99eb146afafb972526887b78b47de2bdabc Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Fri, 11 Jun 2021 13:50:09 +0100 Subject: [PATCH 146/311] Refactor unpacking the Zip64 eocd record. --- lib/zip/central_directory.rb | 62 +++++++++++++++++++----------------- 1 file changed, 32 insertions(+), 30 deletions(-) diff --git a/lib/zip/central_directory.rb b/lib/zip/central_directory.rb index 51f14370..314b246f 100644 --- a/lib/zip/central_directory.rb +++ b/lib/zip/central_directory.rb @@ -9,6 +9,7 @@ class CentralDirectory ZIP64_EOCD_LOCATOR = 0x07064b50 MAX_END_OF_CDS_SIZE = 65_536 + 18 STATIC_EOCD_SIZE = 22 + ZIP64_STATIC_EOCD_SIZE = 56 attr_reader :comment @@ -86,19 +87,36 @@ def write_64_eocd_locator(io, zip64_eocd_offset) private :write_64_eocd_locator - def read_64_e_o_c_d(buf) #:nodoc: - buf = get_64_e_o_c_d(buf) - @size_of_zip64_e_o_c_d = read_long64(buf) - @version_made_by = read_short(buf) - @version_needed_for_extract = read_short(buf) - @number_of_this_disk = read_long(buf) - @number_of_disk_with_start_of_cdir = read_long(buf) - @total_number_of_entries_in_cdir_on_this_disk = read_long64(buf) - @size = read_long64(buf) - @size_in_bytes = read_long64(buf) - @cdir_offset = read_long64(buf) - @zip_64_extensible = buf.slice!(0, buf.bytesize) - raise Error, 'Zip consistency problem while reading eocd structure' unless buf.empty? + def unpack_64_e_o_c_d(buffer) #:nodoc: + index = buffer.rindex([ZIP64_END_OF_CDS].pack('V')) + raise Error, 'Zip64 end of central directory signature not found' unless index + + l_index = buffer.rindex([ZIP64_EOCD_LOCATOR].pack('V')) + raise Error, 'Zip64 end of central directory signature locator not found' unless l_index + + buf = buffer.slice(index..l_index) + + _, # ZIP64_END_OF_CDS signature. We know we have this at this point. + @size_of_zip64_e_o_c_d, + @version_made_by, + @version_needed_for_extract, + @number_of_this_disk, + @number_of_disk_with_start_of_cdir, + @total_number_of_entries_in_cdir_on_this_disk, + @size, + @size_in_bytes, + @cdir_offset = buf.unpack('VQ Date: Fri, 11 Jun 2021 13:51:40 +0100 Subject: [PATCH 147/311] Remove the now redundant `read_zip_*` methods. We're unpacking headers in chunks now, using `unpack`. --- lib/zip/central_directory.rb | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/lib/zip/central_directory.rb b/lib/zip/central_directory.rb index 314b246f..f6e6cd9c 100644 --- a/lib/zip/central_directory.rb +++ b/lib/zip/central_directory.rb @@ -227,20 +227,6 @@ def ==(other) #:nodoc: @entry_set.entries.sort == other.entries.sort && comment == other.comment end - - private - - def read_short(io) # :nodoc: - io.read(2).unpack1('v') - end - - def read_long(io) # :nodoc: - io.read(4).unpack1('V') - end - - def read_long64(io) # :nodoc: - io.read(8).unpack1('Q<') - end end end From 3fbb48de31dade45a1cb7c303aa31034f7d5f1e4 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Fri, 11 Jun 2021 17:03:43 +0100 Subject: [PATCH 148/311] Refactor the full 64bit tests. --- test/zip64_full_test.rb | 82 +++++++++++++++++++++-------------------- 1 file changed, 43 insertions(+), 39 deletions(-) diff --git a/test/zip64_full_test.rb b/test/zip64_full_test.rb index 6f900bb4..f1429a01 100644 --- a/test/zip64_full_test.rb +++ b/test/zip64_full_test.rb @@ -1,53 +1,57 @@ # frozen_string_literal: true -if ENV['FULL_ZIP64_TEST'] - require 'minitest/autorun' - require 'minitest/unit' - require 'fileutils' - require 'zip' - - # test zip64 support for real, by actually exceeding the 32-bit size/offset limits - # this test does not, of course, run with the normal unit tests! ;) - - class Zip64FullTest < MiniTest::Test - def teardown - ::Zip.reset! - end +require 'test_helper' - def prepare_test_file(test_filename) - ::File.delete(test_filename) if ::File.exist?(test_filename) - test_filename - end +# Test zip64 support for real by actually exceeding the 32-bit +# size/offset limits. This test does not, of course, run with the +# normal unit tests! ;) +class Zip64FullTest < MiniTest::Test + HUGE_ZIP = 'huge.zip' + + def teardown + ::Zip.reset! + ::FileUtils.rm_f HUGE_ZIP + end - def test_large_zip_file - ::Zip.write_zip64_support = true - first_text = 'starting out small' - last_text = 'this tests files starting after 4GB in the archive' - test_filename = prepare_test_file('huge.zip') - ::Zip::OutputStream.open(test_filename) do |io| - io.put_next_entry('first_file.txt') + def test_large_zip_file + skip unless ENV['FULL_ZIP64_TEST'] && !Zip::RUNNING_ON_WINDOWS + + ::Zip.write_zip64_support = true + first_text = 'starting out small' + last_text = 'this tests files starting after 4GB in the archive' + + ::Zip::File.open(HUGE_ZIP, ::Zip::File::CREATE) do |zf| + zf.get_output_stream('first_file.txt') do |io| io.write(first_text) + end - # write just over 4GB (stored, so the zip file exceeds 4GB) - buf = 'blah' * 16_384 - io.put_next_entry('huge_file', nil, nil, ::Zip::Entry::STORED) + # Write just over 4GB (stored, so the zip file exceeds 4GB). + buf = 'blah' * 16_384 + zf.get_output_stream( + 'huge_file', nil, nil, nil, nil, nil, ::Zip::Entry::STORED + ) do |io| 65_537.times { io.write(buf) } - - io.put_next_entry('last_file.txt') - io.write(last_text) end - ::Zip::File.open(test_filename) do |zf| - assert_equal %w[first_file.txt huge_file last_file.txt], zf.entries.map(&:name) - assert_equal first_text, zf.read('first_file.txt') - assert_equal last_text, zf.read('last_file.txt') + zf.get_output_stream('last_file.txt') do |io| + io.write(last_text) end + end - # NOTE: if this fails, be sure you have UnZip version 6.0 or newer - # as this is the first version to support zip64 extensions - # but some OSes (*cough* OSX) still bundle a 5.xx release - assert system("unzip -tqq #{test_filename}"), 'third-party zip validation failed' + ::Zip::File.open(HUGE_ZIP) do |zf| + assert_equal( + %w[first_file.txt huge_file last_file.txt], zf.entries.map(&:name) + ) + assert_equal(first_text, zf.read('first_file.txt')) + assert_equal(last_text, zf.read('last_file.txt')) end - end + # NOTE: if this fails, be sure you have UnZip version 6.0 or newer + # as this is the first version to support zip64 extensions + # but some OSes (*cough* OSX) still bundle a 5.xx release + assert( + system("unzip -tqq #{HUGE_ZIP}"), + 'third-party zip validation failed' + ) + end end From be1c5b7c03b52b872bc9494f6e0de698c09dffa7 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Fri, 11 Jun 2021 17:14:49 +0100 Subject: [PATCH 149/311] Turn on FULL_ZIP64_TEST in CI. --- .github/workflows/tests.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index bc4c0710..8b2abc0e 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -33,6 +33,7 @@ jobs: - name: Run the tests env: JRUBY_OPTS: --debug + FULL_ZIP64_TEST: 1 run: bundle exec rake - name: Coveralls From 7df623fb0ec3f1781305d6e0028f28132e5d1417 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Fri, 11 Jun 2021 23:23:34 +0100 Subject: [PATCH 150/311] Read EOCD record for Zip64 files. Means we actually read in the file-level comment now! Fixes #492. --- lib/zip/central_directory.rb | 26 +++++++++++++++----------- test/zip64_full_test.rb | 4 ++++ 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/lib/zip/central_directory.rb b/lib/zip/central_directory.rb index f6e6cd9c..d54e5670 100644 --- a/lib/zip/central_directory.rb +++ b/lib/zip/central_directory.rb @@ -126,14 +126,21 @@ def unpack_e_o_c_d(buffer) #:nodoc: buf = buffer.slice(index, buffer.size) _, # END_OF_CDS signature. We know we have this at this point. - @number_of_this_disk, - @number_of_disk_with_start_of_cdir, - @total_number_of_entries_in_cdir_on_this_disk, - @size, - @size_in_bytes, - @cdir_offset, + num_disk, + num_disk_cdir, + num_cdir_disk, + num_entries, + size_in_bytes, + cdir_offset, comment_length = buf.unpack('VvvvvVVv') + @number_of_this_disk = num_disk unless num_disk == 0xFFFF + @number_of_disk_with_start_of_cdir = num_disk_cdir unless num_disk_cdir == 0xFFFF + @total_number_of_entries_in_cdir_on_this_disk = num_cdir_disk unless num_cdir_disk == 0xFFFF + @size = num_entries unless num_entries == 0xFFFF + @size_in_bytes = size_in_bytes unless size_in_bytes == 0xFFFFFFFF + @cdir_offset = cdir_offset unless cdir_offset == 0xFFFFFFFF + @comment = if comment_length.positive? buf.slice(STATIC_EOCD_SIZE, comment_length) else @@ -182,11 +189,8 @@ def read_local_extra_field(io) def read_from_stream(io) #:nodoc: buf = start_buf(io) - if zip64_file?(buf) - unpack_64_e_o_c_d(buf) - else - unpack_e_o_c_d(buf) - end + unpack_64_e_o_c_d(buf) if zip64_file?(buf) + unpack_e_o_c_d(buf) read_central_directory_entries(io) end diff --git a/test/zip64_full_test.rb b/test/zip64_full_test.rb index f1429a01..5d174e3b 100644 --- a/test/zip64_full_test.rb +++ b/test/zip64_full_test.rb @@ -19,8 +19,11 @@ def test_large_zip_file ::Zip.write_zip64_support = true first_text = 'starting out small' last_text = 'this tests files starting after 4GB in the archive' + comment_text = 'this is a file comment in a zip64 archive' ::Zip::File.open(HUGE_ZIP, ::Zip::File::CREATE) do |zf| + zf.comment = comment_text + zf.get_output_stream('first_file.txt') do |io| io.write(first_text) end @@ -44,6 +47,7 @@ def test_large_zip_file ) assert_equal(first_text, zf.read('first_file.txt')) assert_equal(last_text, zf.read('last_file.txt')) + assert_equal(comment_text, zf.comment) end # NOTE: if this fails, be sure you have UnZip version 6.0 or newer From bd2f15e4bb1211c58bcf29f919fa81ef044e14a7 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sat, 12 Jun 2021 10:33:16 +0100 Subject: [PATCH 151/311] Extract the `Zip::File::split` code into its own module. This code is rarely used and may not even be correct according to the standard. Also this de-clutters the `File` class. --- .rubocop_todo.yml | 3 ++ lib/zip/file.rb | 85 ++-------------------------------------- lib/zip/file_split.rb | 86 +++++++++++++++++++++++++++++++++++++++++ test/file_split_test.rb | 2 +- 4 files changed, 94 insertions(+), 82 deletions(-) create mode 100644 lib/zip/file_split.rb diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index aafa7fe7..4e443b4d 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -75,6 +75,7 @@ Style/IfUnlessModifier: Exclude: - 'lib/zip/entry.rb' - 'lib/zip/file.rb' + - 'lib/zip/file_split.rb' - 'lib/zip/filesystem/dir.rb' - 'lib/zip/filesystem/file.rb' - 'lib/zip/pass_thru_decompressor.rb' @@ -100,6 +101,7 @@ Style/NumericPredicate: - 'lib/zip/extra_field/universal_time.rb' - 'lib/zip/extra_field/unix.rb' - 'lib/zip/file.rb' + - 'lib/zip/file_split.rb' - 'lib/zip/filesystem/file.rb' - 'lib/zip/input_stream.rb' - 'lib/zip/ioextras.rb' @@ -114,6 +116,7 @@ Style/OptionalBooleanParameter: Exclude: - 'lib/zip/entry.rb' - 'lib/zip/file.rb' + - 'lib/zip/file_split.rb' - 'lib/zip/output_stream.rb' # Offense count: 29 diff --git a/lib/zip/file.rb b/lib/zip/file.rb index 6286778f..6665698a 100644 --- a/lib/zip/file.rb +++ b/lib/zip/file.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true +require_relative 'file_split' + module Zip # ZipFile is modeled after java.util.zip.ZipFile from the Java SDK. # The most important methods are those inherited from @@ -45,12 +47,9 @@ module Zip # interface for accessing the filesystem, ie. the File and Dir classes. class File < CentralDirectory + extend FileSplit + CREATE = true - SPLIT_SIGNATURE = 0x08074b50 - ZIP64_EOCD_SIGNATURE = 0x06064b50 - MAX_SEGMENT_SIZE = 3_221_225_472 - MIN_SEGMENT_SIZE = 65_536 - DATA_BUFFER_SIZE = 8192 IO_METHODS = [:tell, :seek, :read, :eof, :close].freeze attr_reader :name @@ -172,82 +171,6 @@ def foreach(zip_file_name, &block) zip_file.each(&block) end end - - def get_segment_size_for_split(segment_size) - if MIN_SEGMENT_SIZE > segment_size - MIN_SEGMENT_SIZE - elsif MAX_SEGMENT_SIZE < segment_size - MAX_SEGMENT_SIZE - else - segment_size - end - end - - def get_partial_zip_file_name(zip_file_name, partial_zip_file_name) - unless partial_zip_file_name.nil? - partial_zip_file_name = zip_file_name.sub(/#{::File.basename(zip_file_name)}\z/, - partial_zip_file_name + ::File.extname(zip_file_name)) - end - partial_zip_file_name ||= zip_file_name - partial_zip_file_name - end - - def get_segment_count_for_split(zip_file_size, segment_size) - (zip_file_size / segment_size).to_i + (zip_file_size % segment_size == 0 ? 0 : 1) - end - - def put_split_signature(szip_file, segment_size) - signature_packed = [SPLIT_SIGNATURE].pack('V') - szip_file << signature_packed - segment_size - signature_packed.size - end - - # - # TODO: Make the code more understandable - # - def save_splited_part(zip_file, partial_zip_file_name, zip_file_size, szip_file_index, segment_size, segment_count) - ssegment_size = zip_file_size - zip_file.pos - ssegment_size = segment_size if ssegment_size > segment_size - szip_file_name = "#{partial_zip_file_name}.#{format('%03d', szip_file_index)}" - ::File.open(szip_file_name, 'wb') do |szip_file| - if szip_file_index == 1 - ssegment_size = put_split_signature(szip_file, segment_size) - end - chunk_bytes = 0 - until ssegment_size == chunk_bytes || zip_file.eof? - segment_bytes_left = ssegment_size - chunk_bytes - buffer_size = segment_bytes_left < DATA_BUFFER_SIZE ? segment_bytes_left : DATA_BUFFER_SIZE - chunk = zip_file.read(buffer_size) - chunk_bytes += buffer_size - szip_file << chunk - # Info for track splitting - yield segment_count, szip_file_index, chunk_bytes, ssegment_size if block_given? - end - end - end - - # Splits an archive into parts with segment size - def split(zip_file_name, segment_size = MAX_SEGMENT_SIZE, delete_zip_file = true, partial_zip_file_name = nil) - raise Error, "File #{zip_file_name} not found" unless ::File.exist?(zip_file_name) - raise Errno::ENOENT, zip_file_name unless ::File.readable?(zip_file_name) - - zip_file_size = ::File.size(zip_file_name) - segment_size = get_segment_size_for_split(segment_size) - return if zip_file_size <= segment_size - - segment_count = get_segment_count_for_split(zip_file_size, segment_size) - ::Zip::File.open(zip_file_name) {} # Check for correct zip structure. - partial_zip_file_name = get_partial_zip_file_name(zip_file_name, partial_zip_file_name) - szip_file_index = 0 - ::File.open(zip_file_name, 'rb') do |zip_file| - until zip_file.eof? - szip_file_index += 1 - save_splited_part(zip_file, partial_zip_file_name, zip_file_size, szip_file_index, segment_size, segment_count) - end - end - ::File.delete(zip_file_name) if delete_zip_file - szip_file_index - end end # Returns an input stream to the specified entry. If a block is passed diff --git a/lib/zip/file_split.rb b/lib/zip/file_split.rb new file mode 100644 index 00000000..5af3514f --- /dev/null +++ b/lib/zip/file_split.rb @@ -0,0 +1,86 @@ +# frozen_string_literal: true + +module Zip + module FileSplit #:nodoc: + SPLIT_SIGNATURE = 0x08074b50 + MAX_SEGMENT_SIZE = 3_221_225_472 + MIN_SEGMENT_SIZE = 65_536 + DATA_BUFFER_SIZE = 8192 + + def get_segment_size_for_split(segment_size) + if MIN_SEGMENT_SIZE > segment_size + MIN_SEGMENT_SIZE + elsif MAX_SEGMENT_SIZE < segment_size + MAX_SEGMENT_SIZE + else + segment_size + end + end + + def get_partial_zip_file_name(zip_file_name, partial_zip_file_name) + unless partial_zip_file_name.nil? + partial_zip_file_name = zip_file_name.sub(/#{::File.basename(zip_file_name)}\z/, + partial_zip_file_name + ::File.extname(zip_file_name)) + end + partial_zip_file_name ||= zip_file_name + partial_zip_file_name + end + + def get_segment_count_for_split(zip_file_size, segment_size) + (zip_file_size / segment_size).to_i + (zip_file_size % segment_size == 0 ? 0 : 1) + end + + def put_split_signature(szip_file, segment_size) + signature_packed = [SPLIT_SIGNATURE].pack('V') + szip_file << signature_packed + segment_size - signature_packed.size + end + + # + # TODO: Make the code more understandable + # + def save_splited_part(zip_file, partial_zip_file_name, zip_file_size, szip_file_index, segment_size, segment_count) + ssegment_size = zip_file_size - zip_file.pos + ssegment_size = segment_size if ssegment_size > segment_size + szip_file_name = "#{partial_zip_file_name}.#{format('%03d', szip_file_index)}" + ::File.open(szip_file_name, 'wb') do |szip_file| + if szip_file_index == 1 + ssegment_size = put_split_signature(szip_file, segment_size) + end + chunk_bytes = 0 + until ssegment_size == chunk_bytes || zip_file.eof? + segment_bytes_left = ssegment_size - chunk_bytes + buffer_size = segment_bytes_left < DATA_BUFFER_SIZE ? segment_bytes_left : DATA_BUFFER_SIZE + chunk = zip_file.read(buffer_size) + chunk_bytes += buffer_size + szip_file << chunk + # Info for track splitting + yield segment_count, szip_file_index, chunk_bytes, ssegment_size if block_given? + end + end + end + + # Splits an archive into parts with segment size + def split(zip_file_name, segment_size = MAX_SEGMENT_SIZE, delete_zip_file = true, partial_zip_file_name = nil) + raise Error, "File #{zip_file_name} not found" unless ::File.exist?(zip_file_name) + raise Errno::ENOENT, zip_file_name unless ::File.readable?(zip_file_name) + + zip_file_size = ::File.size(zip_file_name) + segment_size = get_segment_size_for_split(segment_size) + return if zip_file_size <= segment_size + + segment_count = get_segment_count_for_split(zip_file_size, segment_size) + ::Zip::File.open(zip_file_name) {} # Check for correct zip structure. + partial_zip_file_name = get_partial_zip_file_name(zip_file_name, partial_zip_file_name) + szip_file_index = 0 + ::File.open(zip_file_name, 'rb') do |zip_file| + until zip_file.eof? + szip_file_index += 1 + save_splited_part(zip_file, partial_zip_file_name, zip_file_size, szip_file_index, segment_size, segment_count) + end + end + ::File.delete(zip_file_name) if delete_zip_file + szip_file_index + end + end +end diff --git a/test/file_split_test.rb b/test/file_split_test.rb index 50fc4a4b..fbe7bff1 100644 --- a/test/file_split_test.rb +++ b/test/file_split_test.rb @@ -33,7 +33,7 @@ def test_split Dir["#{TEST_ZIP.zip_name}.*"].sort.each_with_index do |zip_file_name, index| File.open(zip_file_name, 'rb') do |zip_file| - zip_file.read([::Zip::File::SPLIT_SIGNATURE].pack('V').size) if index == 0 + zip_file.read([::Zip::FileSplit::SPLIT_SIGNATURE].pack('V').size) if index == 0 File.open(UNSPLITTED_FILENAME, 'ab') do |file| file << zip_file.read end From 80382135e58061ff270d14b0d6491eb1a71320f7 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sat, 12 Jun 2021 11:13:45 +0100 Subject: [PATCH 152/311] Tidy up some of the file split code. --- .rubocop_todo.yml | 2 -- lib/zip/file_split.rb | 9 ++++++--- test/file_split_test.rb | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 4e443b4d..e043313c 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -101,12 +101,10 @@ Style/NumericPredicate: - 'lib/zip/extra_field/universal_time.rb' - 'lib/zip/extra_field/unix.rb' - 'lib/zip/file.rb' - - 'lib/zip/file_split.rb' - 'lib/zip/filesystem/file.rb' - 'lib/zip/input_stream.rb' - 'lib/zip/ioextras.rb' - 'lib/zip/ioextras/abstract_input_stream.rb' - - 'test/file_split_test.rb' - 'test/test_helper.rb' # Offense count: 6 diff --git a/lib/zip/file_split.rb b/lib/zip/file_split.rb index 5af3514f..68ac0248 100644 --- a/lib/zip/file_split.rb +++ b/lib/zip/file_split.rb @@ -19,15 +19,18 @@ def get_segment_size_for_split(segment_size) def get_partial_zip_file_name(zip_file_name, partial_zip_file_name) unless partial_zip_file_name.nil? - partial_zip_file_name = zip_file_name.sub(/#{::File.basename(zip_file_name)}\z/, - partial_zip_file_name + ::File.extname(zip_file_name)) + partial_zip_file_name = zip_file_name.sub( + /#{::File.basename(zip_file_name)}\z/, + partial_zip_file_name + ::File.extname(zip_file_name) + ) end partial_zip_file_name ||= zip_file_name partial_zip_file_name end def get_segment_count_for_split(zip_file_size, segment_size) - (zip_file_size / segment_size).to_i + (zip_file_size % segment_size == 0 ? 0 : 1) + (zip_file_size / segment_size).to_i + + ((zip_file_size % segment_size).zero? ? 0 : 1) end def put_split_signature(szip_file, segment_size) diff --git a/test/file_split_test.rb b/test/file_split_test.rb index fbe7bff1..92861837 100644 --- a/test/file_split_test.rb +++ b/test/file_split_test.rb @@ -33,7 +33,7 @@ def test_split Dir["#{TEST_ZIP.zip_name}.*"].sort.each_with_index do |zip_file_name, index| File.open(zip_file_name, 'rb') do |zip_file| - zip_file.read([::Zip::FileSplit::SPLIT_SIGNATURE].pack('V').size) if index == 0 + zip_file.read([::Zip::FileSplit::SPLIT_SIGNATURE].pack('V').size) if index.zero? File.open(UNSPLITTED_FILENAME, 'ab') do |file| file << zip_file.read end From 21ba82c67ce6aa7e6ccfb53e78616c8f00919b56 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sat, 12 Jun 2021 16:27:45 +0100 Subject: [PATCH 153/311] Move the split signature to the constants file. --- lib/zip/constants.rb | 2 ++ lib/zip/file_split.rb | 9 ++++----- test/file_split_test.rb | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/lib/zip/constants.rb b/lib/zip/constants.rb index c15aeae2..d0c5927e 100644 --- a/lib/zip/constants.rb +++ b/lib/zip/constants.rb @@ -13,6 +13,8 @@ module Zip VERSION_NEEDED_TO_EXTRACT = 20 VERSION_NEEDED_TO_EXTRACT_ZIP64 = 45 + SPLIT_FILE_SIGNATURE = 0x08074b50 + FILE_TYPE_FILE = 0o10 FILE_TYPE_DIR = 0o04 FILE_TYPE_SYMLINK = 0o12 diff --git a/lib/zip/file_split.rb b/lib/zip/file_split.rb index 68ac0248..841aab0b 100644 --- a/lib/zip/file_split.rb +++ b/lib/zip/file_split.rb @@ -2,10 +2,9 @@ module Zip module FileSplit #:nodoc: - SPLIT_SIGNATURE = 0x08074b50 - MAX_SEGMENT_SIZE = 3_221_225_472 - MIN_SEGMENT_SIZE = 65_536 - DATA_BUFFER_SIZE = 8192 + MAX_SEGMENT_SIZE = 3_221_225_472 + MIN_SEGMENT_SIZE = 65_536 + DATA_BUFFER_SIZE = 8192 def get_segment_size_for_split(segment_size) if MIN_SEGMENT_SIZE > segment_size @@ -34,7 +33,7 @@ def get_segment_count_for_split(zip_file_size, segment_size) end def put_split_signature(szip_file, segment_size) - signature_packed = [SPLIT_SIGNATURE].pack('V') + signature_packed = [SPLIT_FILE_SIGNATURE].pack('V') szip_file << signature_packed segment_size - signature_packed.size end diff --git a/test/file_split_test.rb b/test/file_split_test.rb index 92861837..517723dc 100644 --- a/test/file_split_test.rb +++ b/test/file_split_test.rb @@ -33,7 +33,7 @@ def test_split Dir["#{TEST_ZIP.zip_name}.*"].sort.each_with_index do |zip_file_name, index| File.open(zip_file_name, 'rb') do |zip_file| - zip_file.read([::Zip::FileSplit::SPLIT_SIGNATURE].pack('V').size) if index.zero? + zip_file.read([::Zip::SPLIT_FILE_SIGNATURE].pack('V').size) if index.zero? File.open(UNSPLITTED_FILENAME, 'ab') do |file| file << zip_file.read end From 750c47461048cd098ed93ad1adca9ba08812dd73 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sat, 12 Jun 2021 16:31:50 +0100 Subject: [PATCH 154/311] Update Changelog. --- Changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/Changelog.md b/Changelog.md index 3e54456e..4036881d 100644 --- a/Changelog.md +++ b/Changelog.md @@ -14,6 +14,7 @@ Tooling/internal: +- Extract the file splitting code out into its own module. - Refactor, and tidy up, the `Zip::Filesystem` classes for improved maintainability. - Fix Windows tests. [#489](https://github.com/rubyzip/rubyzip/pull/489) - Refactor `assert_forwarded` so it does not need `ObjectSpace._id2ref` or `eval`. [#483](https://github.com/rubyzip/rubyzip/pull/483) From bf3ae2ad7614a4661507018691acf48ef9c74e2b Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Fri, 18 Jun 2021 11:11:11 +0100 Subject: [PATCH 155/311] Improve some entry header tests. Use `StringIO` instead of the custom `IOizeString` code in the test_helper. Also test both versions (class and instance) of the `Entry` APIs. --- test/central_directory_entry_test.rb | 25 +++++++++++++++++-------- test/local_entry_test.rb | 27 +++++++++++++++++---------- 2 files changed, 34 insertions(+), 18 deletions(-) diff --git a/test/central_directory_entry_test.rb b/test/central_directory_entry_test.rb index a6898c5c..e8af639c 100644 --- a/test/central_directory_entry_test.rb +++ b/test/central_directory_entry_test.rb @@ -59,13 +59,22 @@ def test_read_from_stream end end - def test_read_entry_from_truncated_zip_file - fragment = '' - File.open('test/data/testDirectory.bin') { |f| fragment = f.read(12) } # cdir entry header is at least 46 bytes - fragment.extend(IOizeString) - entry = ::Zip::Entry.new - entry.read_c_dir_entry(fragment) - raise 'ZipError expected' - rescue ::Zip::Error + def test_read_entry_from_truncated_zip_file_raises_error + File.open('test/data/testDirectory.bin') do |f| + # cdir entry header is at least 46 bytes, so just read a bit. + fragment = f.read(12) + assert_raises(::Zip::Error) do + entry = ::Zip::Entry.new + entry.read_c_dir_entry(StringIO.new(fragment)) + end + end + end + + def test_read_entry_from_truncated_zip_file_returns_nil + File.open('test/data/testDirectory.bin') do |f| + # cdir entry header is at least 46 bytes, so just read a bit. + fragment = f.read(12) + assert_nil(::Zip::Entry.read_c_dir_entry(StringIO.new(fragment))) + end end end diff --git a/test/local_entry_test.rb b/test/local_entry_test.rb index 9d2e3ed5..212c91af 100644 --- a/test/local_entry_test.rb +++ b/test/local_entry_test.rb @@ -42,16 +42,23 @@ def test_read_local_entry_from_non_zip_file end end - def test_read_local_entry_from_truncated_zip_file - fragment = '' - # local header is at least 30 bytes - ::File.open(TestZipFile::TEST_ZIP2.zip_name) { |f| fragment = f.read(12) } - - fragment.extend(IOizeString).reset - entry = ::Zip::Entry.new - entry.read_local_entry(fragment) - raise 'ZipError expected' - rescue ::Zip::Error + def test_read_local_entry_from_truncated_zip_file_raises_error + ::File.open(TestZipFile::TEST_ZIP2.zip_name) do |f| + # Local header is at least 30 bytes, so don't read it all here. + fragment = f.read(12) + assert_raises(::Zip::Error) do + entry = ::Zip::Entry.new + entry.read_local_entry(StringIO.new(fragment)) + end + end + end + + def test_read_local_entry_from_truncated_zip_file_returns_nil + ::File.open(TestZipFile::TEST_ZIP2.zip_name) do |f| + # Local header is at least 30 bytes, so don't read it all here. + fragment = f.read(12) + assert_nil(::Zip::Entry.read_local_entry(StringIO.new(fragment))) + end end def test_write_entry From afe1892208dcfefeb6c1c61d221d37e6aee7d314 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Fri, 18 Jun 2021 11:44:58 +0100 Subject: [PATCH 156/311] Fix a mis-firing CentralDirectory test. `test_read_from_truncated_zip_file` was not testing what it thought it was. It was testing whether we caught an out-of-bounds cdir offset, not whether we caught a corrupted cdir entry. This commit embraces the actual behaviour and tests that we catch an out-of-bounds error for both standard `IO`s and `StringIO`s. --- lib/zip/central_directory.rb | 7 ++++++- test/central_directory_test.rb | 25 ++++++++++++++++--------- 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/lib/zip/central_directory.rb b/lib/zip/central_directory.rb index d54e5670..e8c20cf2 100644 --- a/lib/zip/central_directory.rb +++ b/lib/zip/central_directory.rb @@ -149,11 +149,16 @@ def unpack_e_o_c_d(buffer) #:nodoc: end def read_central_directory_entries(io) #:nodoc: + # `StringIO` doesn't raise `EINVAL` if you seek beyond the current end, + # so we need to catch that *and* query `io#eof?` here. + eof = false begin io.seek(@cdir_offset, IO::SEEK_SET) rescue Errno::EINVAL - raise Error, 'Zip consistency problem while reading central directory entry' + eof = true end + raise Error, 'Zip consistency problem while reading central directory entry' if eof || io.eof? + @entry_set = EntrySet.new @size.times do entry = Entry.read_c_dir_entry(io) diff --git a/test/central_directory_test.rb b/test/central_directory_test.rb index 011bdb5f..afe2156e 100644 --- a/test/central_directory_test.rb +++ b/test/central_directory_test.rb @@ -26,15 +26,22 @@ def test_read_from_invalid_stream rescue ::Zip::Error end - def test_read_from_truncated_zip_file - fragment = '' - File.open('test/data/testDirectory.bin', 'rb') { |f| fragment = f.read } - fragment.slice!(12) # removed part of first cdir entry. eocd structure still complete - fragment.extend(IOizeString) - entry = ::Zip::CentralDirectory.new - entry.read_from_stream(fragment) - raise 'ZipError expected' - rescue ::Zip::Error + def test_read_eocd_with_wrong_cdir_offset_from_file + ::File.open('test/data/testDirectory.bin', 'rb') do |f| + assert_raises(::Zip::Error) do + cdir = ::Zip::CentralDirectory.new + cdir.read_from_stream(f) + end + end + end + + def test_read_eocd_with_wrong_cdir_offset_from_buffer + ::File.open('test/data/testDirectory.bin', 'rb') do |f| + assert_raises(::Zip::Error) do + cdir = ::Zip::CentralDirectory.new + cdir.read_from_stream(StringIO.new(f.read)) + end + end end def test_write_to_stream From 75386f8db6b85d5ace97fec3c9640a6499880c89 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Fri, 18 Jun 2021 11:50:07 +0100 Subject: [PATCH 157/311] Remove now redundant `IOizeString` module. --- test/test_helper.rb | 34 ---------------------------------- 1 file changed, 34 deletions(-) diff --git a/test/test_helper.rb b/test/test_helper.rb index b51fd5d4..2896dd11 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -16,40 +16,6 @@ FileUtils.rm_rf('test/data/generated') end -module IOizeString - attr_reader :tell - - def read(count = nil) - @tell ||= 0 - count ||= size - ret_val = slice(@tell, count) - @tell += count - ret_val - end - - def seek(index, offset) - @tell ||= 0 - case offset - when IO::SEEK_END - pos = size + index - when IO::SEEK_SET - pos = index - when IO::SEEK_CUR - pos = @tell + index - else - raise 'Error in test method IOizeString::seek' - end - - raise Errno::EINVAL if pos < 0 || pos >= size - - @tell = pos - end - - def reset - @tell = 0 - end -end - module DecompressorTests # expects @ref_text, @ref_lines and @decompressor From 71f2c90b20e15bd27195ec4528b8e406fd0e2a26 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Fri, 18 Jun 2021 12:07:10 +0100 Subject: [PATCH 158/311] Test that a corrupted cdir entry is caught. --- lib/zip/entry.rb | 2 +- test/central_directory_entry_test.rb | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/lib/zip/entry.rb b/lib/zip/entry.rb index cdff9baa..c317f468 100644 --- a/lib/zip/entry.rb +++ b/lib/zip/entry.rb @@ -381,7 +381,7 @@ def set_ftype_from_c_dir_entry end def check_c_dir_entry_static_header_length(buf) - return if buf.bytesize == ::Zip::CDIR_ENTRY_STATIC_HEADER_LENGTH + return unless buf.nil? || buf.bytesize != ::Zip::CDIR_ENTRY_STATIC_HEADER_LENGTH raise Error, 'Premature end of file. Not enough data for zip cdir entry header' end diff --git a/test/central_directory_entry_test.rb b/test/central_directory_entry_test.rb index e8af639c..76d8b305 100644 --- a/test/central_directory_entry_test.rb +++ b/test/central_directory_entry_test.rb @@ -77,4 +77,25 @@ def test_read_entry_from_truncated_zip_file_returns_nil assert_nil(::Zip::Entry.read_c_dir_entry(StringIO.new(fragment))) end end + + def test_read_corrupted_entry_raises_error + fragment = File.binread('test/data/testDirectory.bin') + fragment.slice!(12) + io = StringIO.new(fragment) + assert_raises(::Zip::Error) do + entry = ::Zip::Entry.new + entry.read_c_dir_entry(io) + # First entry will be read but break later entries. + entry.read_c_dir_entry(io) + end + end + + def test_read_corrupted_entry_returns_nil + fragment = File.binread('test/data/testDirectory.bin') + fragment.slice!(12) + io = StringIO.new(fragment) + refute_nil(::Zip::Entry.read_c_dir_entry(io)) + # First entry will be read but break later entries. + assert_nil(::Zip::Entry.read_c_dir_entry(io)) + end end From f66a15a85d09aa4216a51b6e58550c21d13ec4db Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Fri, 18 Jun 2021 16:00:57 +0100 Subject: [PATCH 159/311] Update rubocop config. --- .rubocop_todo.yml | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index e043313c..fd3243f3 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -1,6 +1,6 @@ # This configuration was generated by # `rubocop --auto-gen-config` -# on 2021-05-24 20:07:32 UTC using RuboCop version 1.12.1. +# on 2021-06-18 14:28:03 UTC using RuboCop version 1.12.1. # The point is for the user to remove these configuration records # one by one as the offenses are removed from the code base. # Note that changes in the inspected code, or installation of new @@ -20,14 +20,14 @@ Lint/MissingSuper: # Offense count: 5 # Configuration parameters: CountComments, CountAsOne. Metrics/ClassLength: - Max: 610 + Max: 600 -# Offense count: 20 +# Offense count: 21 # Configuration parameters: IgnoredMethods. Metrics/CyclomaticComplexity: Max: 14 -# Offense count: 46 +# Offense count: 47 # Configuration parameters: CountComments, CountAsOne, ExcludedMethods, IgnoredMethods. Metrics/MethodLength: Max: 32 @@ -43,7 +43,7 @@ Metrics/ParameterLists: Metrics/PerceivedComplexity: Max: 15 -# Offense count: 8 +# Offense count: 7 Naming/AccessorMethodName: Exclude: - 'lib/zip/entry.rb' @@ -64,7 +64,7 @@ Style/ClassAndModuleChildren: - 'lib/zip/extra_field/zip64.rb' - 'lib/zip/extra_field/zip64_placeholder.rb' -# Offense count: 24 +# Offense count: 22 # Configuration parameters: AllowedConstants. Style/Documentation: Enabled: false @@ -74,7 +74,6 @@ Style/Documentation: Style/IfUnlessModifier: Exclude: - 'lib/zip/entry.rb' - - 'lib/zip/file.rb' - 'lib/zip/file_split.rb' - 'lib/zip/filesystem/dir.rb' - 'lib/zip/filesystem/file.rb' @@ -89,13 +88,12 @@ Style/MutableConstant: Exclude: - 'lib/zip/extra_field.rb' -# Offense count: 24 +# Offense count: 21 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, IgnoredMethods. # SupportedStyles: predicate, comparison Style/NumericPredicate: Exclude: - - 'spec/**/*' - 'lib/zip/entry.rb' - 'lib/zip/extra_field/old_unix.rb' - 'lib/zip/extra_field/universal_time.rb' @@ -105,7 +103,6 @@ Style/NumericPredicate: - 'lib/zip/input_stream.rb' - 'lib/zip/ioextras.rb' - 'lib/zip/ioextras/abstract_input_stream.rb' - - 'test/test_helper.rb' # Offense count: 6 # Configuration parameters: AllowedMethods. @@ -139,11 +136,10 @@ Style/SafeNavigation: - 'test/filesystem/file_stat_test.rb' - 'test/test_helper.rb' -# Offense count: 9 +# Offense count: 8 # Cop supports --auto-correct. Style/StringConcatenation: Exclude: - - 'lib/zip/filesystem.rb' - 'samples/gtk_ruby_zip.rb' - 'test/encryption_test.rb' - 'test/file_test.rb' From f1e8c2fc9d5a45e81d0e25c8eac119217bac488e Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Fri, 18 Jun 2021 16:10:57 +0100 Subject: [PATCH 160/311] Fix Style/StringConcatenation cop. --- .rubocop_todo.yml | 10 ---------- samples/gtk_ruby_zip.rb | 2 +- test/encryption_test.rb | 2 +- test/file_test.rb | 2 +- test/ioextras/abstract_input_stream_test.rb | 6 +++--- test/test_helper.rb | 4 ++-- 6 files changed, 8 insertions(+), 18 deletions(-) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index fd3243f3..d34c8bd1 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -135,13 +135,3 @@ Style/SafeNavigation: - 'test/filesystem/file_nonmutating_test.rb' - 'test/filesystem/file_stat_test.rb' - 'test/test_helper.rb' - -# Offense count: 8 -# Cop supports --auto-correct. -Style/StringConcatenation: - Exclude: - - 'samples/gtk_ruby_zip.rb' - - 'test/encryption_test.rb' - - 'test/file_test.rb' - - 'test/ioextras/abstract_input_stream_test.rb' - - 'test/test_helper.rb' diff --git a/samples/gtk_ruby_zip.rb b/samples/gtk_ruby_zip.rb index 674e8e77..eeac2a0a 100755 --- a/samples/gtk_ruby_zip.rb +++ b/samples/gtk_ruby_zip.rb @@ -74,7 +74,7 @@ def open_zip(filename) @zipfile.each do |entry| @clist.append([entry.name, entry.size.to_s, - (100.0 * entry.compressedSize / entry.size).to_s + '%']) + "#{100.0 * entry.compressedSize / entry.size}%"]) end end end diff --git a/test/encryption_test.rb b/test/encryption_test.rb index 110e7a3d..c1bc2038 100644 --- a/test/encryption_test.rb +++ b/test/encryption_test.rb @@ -33,7 +33,7 @@ def test_encrypt end assert_raises(Zip::DecompressionError) do - Zip::InputStream.open(encrypted_zip, 0, Zip::TraditionalDecrypter.new(password + 'wrong')) do |zis| + Zip::InputStream.open(encrypted_zip, 0, Zip::TraditionalDecrypter.new("#{password}wrong")) do |zis| zis.get_next_entry assert_equal content, zis.read end diff --git a/test/file_test.rb b/test/file_test.rb index a802f105..ec18aa2a 100644 --- a/test/file_test.rb +++ b/test/file_test.rb @@ -332,7 +332,7 @@ def test_add_directory ::Zip::File.open(TEST_ZIP.zip_name) do |zf| dir_entry = zf.entries.detect do |e| - e.name == TestFiles::EMPTY_TEST_DIR + '/' + e.name == "#{TestFiles::EMPTY_TEST_DIR}/" end assert(dir_entry.directory?) diff --git a/test/ioextras/abstract_input_stream_test.rb b/test/ioextras/abstract_input_stream_test.rb index 04b20990..2ef849c8 100644 --- a/test/ioextras/abstract_input_stream_test.rb +++ b/test/ioextras/abstract_input_stream_test.rb @@ -59,8 +59,8 @@ def test_gets_multi_char_seperator end LONG_LINES = [ - 'x' * 48 + "\r\n", - 'y' * 49 + "\r\n", + "#{'x' * 48}\r\n", + "#{'y' * 49}\r\n", 'rest' ].freeze @@ -74,7 +74,7 @@ def test_gets_mulit_char_seperator_split def test_gets_with_sep_and_index io = TestAbstractInputStream.new(LONG_LINES.join) assert_equal('x', io.gets("\r\n", 1)) - assert_equal('x' * 47 + "\r", io.gets("\r\n", 48)) + assert_equal("#{'x' * 47}\r", io.gets("\r\n", 48)) assert_equal("\n", io.gets(nil, 1)) assert_equal('yy', io.gets(nil, 2)) end diff --git a/test/test_helper.rb b/test/test_helper.rb index 2896dd11..c3ab2bb7 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -56,7 +56,7 @@ def assert_entry_contents_for_stream(filename, zis, entry_name) actual = zis.read if expected != actual if (expected && actual) && (expected.length > 400 || actual.length > 400) - entry_filename = entry_name + '.zipEntry' + entry_filename = "#{entry_name}.zipEntry" File.open(entry_filename, 'wb') { |entryfile| entryfile << actual } raise("File '#{filename}' is different from '#{entry_filename}'") else @@ -72,7 +72,7 @@ def self.assert_contents(filename, string) return unless contents != string if contents.length > 400 || string.length > 400 - string_file = filename + '.other' + string_file = "#{filename}.other" File.open(string_file, 'wb') { |f| f << string } raise("File '#{filename}' is different from contents of string stored in '#{string_file}'") else From a2a14c2cd2d23628bb81a8f09384d3e51f040e8b Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Fri, 18 Jun 2021 16:31:23 +0100 Subject: [PATCH 161/311] Fix Style/RedundantRegexpEscape cop. --- .rubocop_todo.yml | 8 -------- Guardfile | 2 +- test/file_extract_test.rb | 2 +- test/path_traversal_test.rb | 26 +++++++++++++------------- 4 files changed, 15 insertions(+), 23 deletions(-) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index d34c8bd1..b86c4133 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -114,14 +114,6 @@ Style/OptionalBooleanParameter: - 'lib/zip/file_split.rb' - 'lib/zip/output_stream.rb' -# Offense count: 29 -# Cop supports --auto-correct. -Style/RedundantRegexpEscape: - Exclude: - - 'Guardfile' - - 'test/file_extract_test.rb' - - 'test/path_traversal_test.rb' - # Offense count: 17 # Cop supports --auto-correct. # Configuration parameters: ConvertCodeThatCanStartToReturnNil, AllowedMethods. diff --git a/Guardfile b/Guardfile index 5365a7a5..bf0c0f89 100644 --- a/Guardfile +++ b/Guardfile @@ -2,7 +2,7 @@ guard :minitest do # with Minitest::Unit - watch(%r{^test/(.*)\/?(.*)_test\.rb$}) + watch(%r{^test/(.*)/?(.*)_test\.rb$}) watch(%r{^lib/zip/(.*/)?([^/]+)\.rb$}) { |m| "test/#{m[1]}#{m[2]}_test.rb" } watch(%r{^test/test_helper\.rb$}) { 'test' } end diff --git a/test/file_extract_test.rb b/test/file_extract_test.rb index 3df4308b..9d0b7da4 100644 --- a/test/file_extract_test.rb +++ b/test/file_extract_test.rb @@ -129,7 +129,7 @@ def test_extract_incorrect_size assert_equal fake_size, a_entry.size ::Zip.validate_entry_sizes = false - assert_output('', /.+\'a\'.+1B.+/) do + assert_output('', /.+'a'.+1B.+/) do a_entry.extract end assert_equal true_size, File.size(file_name) diff --git a/test/path_traversal_test.rb b/test/path_traversal_test.rb index 9676432b..4b500191 100644 --- a/test/path_traversal_test.rb +++ b/test/path_traversal_test.rb @@ -39,7 +39,7 @@ def in_tmpdir end def test_leading_slash - entries = { '/tmp/moo' => /WARNING: skipped \'\/tmp\/moo\'/ } + entries = { '/tmp/moo' => /WARNING: skipped '\/tmp\/moo'/ } in_tmpdir do extract_paths(['jwilk', 'absolute1.zip'], entries) refute File.exist?('/tmp/moo') @@ -47,7 +47,7 @@ def test_leading_slash end def test_multiple_leading_slashes - entries = { '//tmp/moo' => /WARNING: skipped \'\/\/tmp\/moo\'/ } + entries = { '//tmp/moo' => /WARNING: skipped '\/\/tmp\/moo'/ } in_tmpdir do extract_paths(['jwilk', 'absolute2.zip'], entries) refute File.exist?('/tmp/moo') @@ -55,7 +55,7 @@ def test_multiple_leading_slashes end def test_leading_dot_dot - entries = { '../moo' => /WARNING: skipped \'\.\.\/moo\'/ } + entries = { '../moo' => /WARNING: skipped '\.\.\/moo'/ } in_tmpdir do extract_paths(['jwilk', 'relative0.zip'], entries) refute File.exist?('../moo') @@ -65,7 +65,7 @@ def test_leading_dot_dot def test_non_leading_dot_dot_with_existing_folder entries = { 'tmp/' => '', - 'tmp/../../moo' => /WARNING: skipped \'tmp\/\.\.\/\.\.\/moo\'/ + 'tmp/../../moo' => /WARNING: skipped 'tmp\/\.\.\/\.\.\/moo'/ } in_tmpdir do extract_paths('relative1.zip', entries) @@ -75,7 +75,7 @@ def test_non_leading_dot_dot_with_existing_folder end def test_non_leading_dot_dot_without_existing_folder - entries = { 'tmp/../../moo' => /WARNING: skipped \'tmp\/\.\.\/\.\.\/moo\'/ } + entries = { 'tmp/../../moo' => /WARNING: skipped 'tmp\/\.\.\/\.\.\/moo'/ } in_tmpdir do extract_paths(['jwilk', 'relative2.zip'], entries) refute File.exist?('../moo') @@ -94,7 +94,7 @@ def test_file_symlink def test_directory_symlink # Can't create tmp/moo, because the tmp symlink is skipped. entries = { - 'tmp' => /WARNING: skipped symlink \'tmp\'/, + 'tmp' => /WARNING: skipped symlink 'tmp'/, 'tmp/moo' => :error } in_tmpdir do @@ -106,8 +106,8 @@ def test_directory_symlink def test_two_directory_symlinks_a # Can't create par/moo because the symlinks are skipped. entries = { - 'cur' => /WARNING: skipped symlink \'cur\'/, - 'par' => /WARNING: skipped symlink \'par\'/, + 'cur' => /WARNING: skipped symlink 'cur'/, + 'par' => /WARNING: skipped symlink 'par'/, 'par/moo' => :error } in_tmpdir do @@ -121,8 +121,8 @@ def test_two_directory_symlinks_a def test_two_directory_symlinks_b # Can't create par/moo, because the symlinks are skipped. entries = { - 'cur' => /WARNING: skipped symlink \'cur\'/, - 'cur/par' => /WARNING: skipped symlink \'cur\/par\'/, + 'cur' => /WARNING: skipped symlink 'cur'/, + 'cur/par' => /WARNING: skipped symlink 'cur\/par'/, 'par/moo' => :error } in_tmpdir do @@ -134,8 +134,8 @@ def test_two_directory_symlinks_b def test_entry_name_with_absolute_path_does_not_extract entries = { - '/tmp/' => /WARNING: skipped \'\/tmp\/\'/, - '/tmp/file.txt' => /WARNING: skipped \'\/tmp\/file.txt\'/ + '/tmp/' => /WARNING: skipped '\/tmp\/'/, + '/tmp/file.txt' => /WARNING: skipped '\/tmp\/file.txt'/ } in_tmpdir do extract_paths(['tuzovakaoff', 'absolutepath.zip'], entries) @@ -158,7 +158,7 @@ def test_entry_name_with_absolute_path_extract_when_given_different_path def test_entry_name_with_relative_symlink # Doesn't create the symlink path, so can't create path/file.txt. entries = { - 'path' => /WARNING: skipped symlink \'path\'/, + 'path' => /WARNING: skipped symlink 'path'/, 'path/file.txt' => :error } in_tmpdir do From 1183607ea12349962eef4a88c91ab471048500a9 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Wed, 23 Jun 2021 22:24:44 +0100 Subject: [PATCH 162/311] Flush buffered `OutputStream` on close. Fixes #265. --- lib/zip/output_stream.rb | 1 + test/output_stream_test.rb | 14 ++++++++++++++ 2 files changed, 15 insertions(+) diff --git a/lib/zip/output_stream.rb b/lib/zip/output_stream.rb index 5d13ebad..3a034ed5 100644 --- a/lib/zip/output_stream.rb +++ b/lib/zip/output_stream.rb @@ -86,6 +86,7 @@ def close_buffer update_local_headers write_central_directory @closed = true + @output_stream.flush @output_stream end diff --git a/test/output_stream_test.rb b/test/output_stream_test.rb index 7a994b36..ecd48488 100644 --- a/test/output_stream_test.rb +++ b/test/output_stream_test.rb @@ -58,6 +58,20 @@ def test_write_buffer_with_temp_file assert_test_zip_contents(TEST_ZIP) end + def test_write_buffer_with_temp_file2 + tmp_file = ::File.join(Dir.tmpdir, 'zos.zip') + ::File.open(tmp_file, 'wb') do |f| + ::Zip::OutputStream.write_buffer(f) do |zos| + zos.comment = TEST_ZIP.comment + write_test_zip(zos) + end + end + + ::Zip::File.open(tmp_file) # Should open without error. + ensure + ::File.unlink(tmp_file) + end + def test_writing_to_closed_stream assert_i_o_error_in_closed_stream { |zos| zos << 'hello world' } assert_i_o_error_in_closed_stream { |zos| zos.puts 'hello world' } From ac053bd787b69d90ff8ed23fd800c0063c15a7d4 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Fri, 25 Jun 2021 16:37:40 +0100 Subject: [PATCH 163/311] Improve documentation and error messages for `InputStream`. Closes #196. --- README.md | 22 +++++++++++++++++----- lib/zip/inflater.rb | 5 +++-- 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 52ba31f3..d70e2881 100644 --- a/README.md +++ b/README.md @@ -174,15 +174,27 @@ Zip::File.open('foo.zip') do |zip_file| end ``` -#### Notice about ::Zip::InputStream +### Notes on `Zip::InputStream` -`::Zip::InputStream` usable for fast reading zip file content because it not read Central directory. +`Zip::InputStream` can be used for faster reading of zip file content because it does not read the Central directory up front. -But there is one exception when it is not working - General Purpose Flag Bit 3. +There is one exception where it can not work however, and this is if the file does not contain enough information in the local entry headers to extract an entry. This is indicated in an entry by the General Purpose Flag bit 3 being set. -> If bit 3 (0x08) of the general-purpose flags field is set, then the CRC-32 and file sizes are not known when the header is written. The fields in the local header are filled with zero, and the CRC-32 and size are appended in a 12-byte structure (optionally preceded by a 4-byte signature) immediately after the compressed data +> If bit 3 (0x08) of the general-purpose flags field is set, then the CRC-32 and file sizes are not known when the header is written. The fields in the local header are filled with zero, and the CRC-32 and size are appended in a 12-byte structure (optionally preceded by a 4-byte signature) immediately after the compressed data. -If `::Zip::InputStream` finds such entry in the zip archive it will raise an exception. +If `Zip::InputStream` finds such an entry in the zip archive it will raise an exception (`Zip::GPFBit3Error`). + +`Zip::InputStream` is not designed to be used for random access in a zip file. When performing any operations on an entry that you are accessing via `Zip::InputStream.get_next_entry` then you should complete any such operations before the next call to `get_next_entry`. + +```ruby +zip_stream = Zip::InputStream.new(File.open('file.zip')) + +while entry = zip_stream.get_next_entry + # All required operations on `entry` go here. +end +``` + +Any attempt to move about in a zip file opened with `Zip::InputStream` could result in the incorrect entry being accessed and/or Zlib buffer errors. If you need random access in a zip file, use `Zip::File`. ### Password Protection (Experimental) diff --git a/lib/zip/inflater.rb b/lib/zip/inflater.rb index 8f686f3e..c702da75 100644 --- a/lib/zip/inflater.rb +++ b/lib/zip/inflater.rb @@ -39,8 +39,9 @@ def produce_input retried += 1 retry end - rescue Zlib::Error - raise(::Zip::DecompressionError, 'zlib error while inflating') + rescue Zlib::Error => e + raise ::Zip::DecompressionError, + "Zlib error ('#{e.message}') while inflating" end def input_finished? From c29297c0b8a6a7f3e2441206b295cd82117cdfc9 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Fri, 25 Jun 2021 17:40:46 +0100 Subject: [PATCH 164/311] Add a test to ensure `InputStream` raises `GPFBit3Error`. --- test/input_stream_test.rb | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/test/input_stream_test.rb b/test/input_stream_test.rb index 40d19476..8a74d149 100644 --- a/test/input_stream_test.rb +++ b/test/input_stream_test.rb @@ -67,6 +67,14 @@ def test_open_io_like_with_block end end + def test_open_file_with_gp3bit_set + ::Zip::InputStream.open('test/data/gpbit3stored.zip') do |zis| + assert_raises(::Zip::GPFBit3Error) do + zis.get_next_entry + end + end + end + def test_size_no_entry zis = ::Zip::InputStream.open(TestZipFile::TEST_ZIP2.zip_name) assert_nil(zis.size) From 78565db40c60ed4e95e6cc37bf470a4194947a71 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Fri, 25 Jun 2021 17:43:53 +0100 Subject: [PATCH 165/311] Simplify `InputStream.open_entry`. Also ensure `@complete_entry` is initialized! --- lib/zip/input_stream.rb | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/lib/zip/input_stream.rb b/lib/zip/input_stream.rb index 3dce2fcc..19be8a40 100644 --- a/lib/zip/input_stream.rb +++ b/lib/zip/input_stream.rb @@ -53,10 +53,11 @@ class InputStream # @param offset [Integer] offset in the IO/StringIO def initialize(context, offset = 0, decrypter = nil) super() - @archive_io = get_io(context, offset) - @decompressor = ::Zip::NullDecompressor - @decrypter = decrypter || ::Zip::NullDecrypter.new + @archive_io = get_io(context, offset) + @decompressor = ::Zip::NullDecompressor + @decrypter = decrypter || ::Zip::NullDecrypter.new @current_entry = nil + @complete_entry = nil end def close @@ -131,17 +132,18 @@ def get_io(io_or_file, offset = 0) def open_entry @current_entry = ::Zip::Entry.read_local_entry(@archive_io) - if @current_entry && @current_entry.encrypted? && @decrypter.kind_of?(NullEncrypter) - raise Error, 'password required to decode zip file' - end + return if @current_entry.nil? - if @current_entry && @current_entry.incomplete? && @current_entry.crc == 0 \ + raise Error, 'A password is required to decode this zip file' if @current_entry.encrypted? && @decrypter.kind_of?(NullEncrypter) + + if @current_entry.incomplete? && @current_entry.crc == 0 \ && @current_entry.compressed_size == 0 \ && @current_entry.size == 0 && !@complete_entry raise GPFBit3Error, 'General purpose flag Bit 3 is set so not possible to get proper info from local header.' \ 'Please use ::Zip::File instead of ::Zip::InputStream' end + @decrypted_io = get_decrypted_io @decompressor = get_decompressor flush From 84b3e8c644e553d6baa7546acc9223080e2aca00 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Fri, 25 Jun 2021 17:51:54 +0100 Subject: [PATCH 166/311] Ensure `InputStream` raises `GPFBit3Error` for OSX Archive files. Fixes #493. --- lib/zip/input_stream.rb | 10 +++++----- test/data/osx-archive.zip | Bin 0 -> 1424 bytes test/input_stream_test.rb | 8 ++++++++ 3 files changed, 13 insertions(+), 5 deletions(-) create mode 100644 test/data/osx-archive.zip diff --git a/lib/zip/input_stream.rb b/lib/zip/input_stream.rb index 19be8a40..d7985492 100644 --- a/lib/zip/input_stream.rb +++ b/lib/zip/input_stream.rb @@ -136,12 +136,12 @@ def open_entry raise Error, 'A password is required to decode this zip file' if @current_entry.encrypted? && @decrypter.kind_of?(NullEncrypter) - if @current_entry.incomplete? && @current_entry.crc == 0 \ - && @current_entry.compressed_size == 0 \ - && @current_entry.size == 0 && !@complete_entry + if @current_entry.incomplete? && @current_entry.compressed_size == 0 \ + && !@complete_entry raise GPFBit3Error, - 'General purpose flag Bit 3 is set so not possible to get proper info from local header.' \ - 'Please use ::Zip::File instead of ::Zip::InputStream' + 'It is not possible to get complete info from the local ' \ + 'header to extract this entry (GP flags bit 3 is set).' \ + 'Please use `Zip::File` instead of `Zip::InputStream`.' end @decrypted_io = get_decrypted_io diff --git a/test/data/osx-archive.zip b/test/data/osx-archive.zip new file mode 100644 index 0000000000000000000000000000000000000000..147a743f2ebc3c80ffa8ffdd2a69d18452a9f6ed GIT binary patch literal 1424 zcmWIWW@Zs#-~hq&3qcG}FoltUft5jl!BDTHq9ingmx2Akvy%xR{0WFlE4UdLS-t_) z0JSnO)b^jwI%2@%`u$(m>`3*$?LoUFG?sKXF7#d+D0gLJk38qU-?#iUW4B!O@^Eio zef!W0eqJMGr+4v-zpt!bw7|-ypM9sGzK`F^)j`|iZoZ;-*BmgkSE~sJX73Mv1ErEA6)4G|kjF3xC5KT@`NJ{wN>l5~YKdd95foXySv$}vZ z{scw75j^rA10x-TKVwFIYoM<;v!TG==iz(4-TBI1A*k)d?Nl8h(7$-5T4M?Oq&0{C zRj1pp%kn$3yGuN4-Qm^YT5|b5M|Xa{xwF+}X-ZV&lQi3e z|BA{M#S0cbRGU@KXVLjcJ80)}#S~-B;=?J2vJRY!o$%Hstj6eFh*#47%>jbLzY@V|0u$%w2`knFzrL!XcM%kTT z;HI}nrr&JmYi?sF!L{!yYlV_8C&Y=)R9>gqvhuP>z}j`&w|u|y=o-_t{Jx~kr+5uR zd4ekE?Oc2L{)PXM{D0Tme`5v(X4Ye?*S~-P2?{dofr%w)V210UOd5<#BFu=A0XY+Z z5(f+{X#}w_Q#+P&BETEfNaU0V$~`c!q){IjoTTO@WYa;Z898cD9sCz)I%XVTN!#c~ g;&!kXGm3*TO~(?$0p6@^ATP53VG__u5-cDd0PvXm!2kdN literal 0 HcmV?d00001 diff --git a/test/input_stream_test.rb b/test/input_stream_test.rb index 8a74d149..4f8f5d29 100644 --- a/test/input_stream_test.rb +++ b/test/input_stream_test.rb @@ -75,6 +75,14 @@ def test_open_file_with_gp3bit_set end end + def test_open_file_with_gp3bit_set_created_by_osx_archive + ::Zip::InputStream.open('test/data/osx-archive.zip') do |zis| + assert_raises(::Zip::GPFBit3Error) do + zis.get_next_entry + end + end + end + def test_size_no_entry zis = ::Zip::InputStream.open(TestZipFile::TEST_ZIP2.zip_name) assert_nil(zis.size) From de6ec15610ce47e044284ff86da8e5f38b276e2e Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Thu, 24 Jun 2021 17:59:54 +0100 Subject: [PATCH 167/311] Update Changelog. --- Changelog.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Changelog.md b/Changelog.md index 4036881d..09968a8e 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,5 +1,10 @@ # 3.0.0 (Next) +- Ensure `InputStream` raises `GPFBit3Error` for OSX Archive files. [#493](https://github.com/rubyzip/rubyzip/issues/493) +- Improve documentation and error messages for `InputStream`. [#196](https://github.com/rubyzip/rubyzip/issues/196) +- Fix zip file-level comment is not read from zip64 files. [#492](https://github.com/rubyzip/rubyzip/issues/492) +- Fix `Zip::OutputStream.write_buffer` doesn't work with Tempfiles. [#265](https://github.com/rubyzip/rubyzip/issues/265) +- Reinstate normalising pathname separators to /. [#487](https://github.com/rubyzip/rubyzip/pull/487) - Fix restore options consistency. [#486](https://github.com/rubyzip/rubyzip/pull/486) - View and/or preserve original date created, date modified? (Windows). [#336](https://github.com/rubyzip/rubyzip/issues/336) - Fix frozen string literal error. [#475](https://github.com/rubyzip/rubyzip/pull/475) @@ -14,6 +19,7 @@ Tooling/internal: +- Configure Coveralls to not report a failure on minor decreases of test coverage. [#491](https://github.com/rubyzip/rubyzip/issues/491) - Extract the file splitting code out into its own module. - Refactor, and tidy up, the `Zip::Filesystem` classes for improved maintainability. - Fix Windows tests. [#489](https://github.com/rubyzip/rubyzip/pull/489) From 193507b15ada37bc74df3d3fb6a6e064be635bdf Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Fri, 25 Jun 2021 22:31:34 +0100 Subject: [PATCH 168/311] Adjust Layout/LineLength cop to 100 characters. We'll get the line length down in stages... --- .rubocop.yml | 2 +- lib/zip/central_directory.rb | 3 ++- lib/zip/entry.rb | 29 ++++++++++++++++------- lib/zip/extra_field/zip64.rb | 7 ++++-- lib/zip/file.rb | 3 ++- lib/zip/file_split.rb | 17 +++++++++---- lib/zip/input_stream.rb | 12 +++++++--- lib/zip/ioextras/abstract_input_stream.rb | 5 +++- lib/zip/output_stream.rb | 6 ++++- samples/example.rb | 10 +++++--- 10 files changed, 68 insertions(+), 26 deletions(-) diff --git a/.rubocop.yml b/.rubocop.yml index 7f1fc683..d3db8664 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -22,7 +22,7 @@ Layout/HashAlignment: # Set a workable line length, given the current state of the code, # and turn off for the tests. Layout/LineLength: - Max: 135 + Max: 100 Exclude: - 'test/**/*.rb' diff --git a/lib/zip/central_directory.rb b/lib/zip/central_directory.rb index e8c20cf2..51d72b37 100644 --- a/lib/zip/central_directory.rb +++ b/lib/zip/central_directory.rb @@ -30,7 +30,8 @@ def write_to_stream(io) #:nodoc: 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 = cdir_offset > 0xFFFFFFFF || cdir_size > 0xFFFFFFFF \ + || @entry_set.size > 0xFFFF need_zip64_eocd ||= @entry_set.any? { |entry| entry.extra['Zip64'] } if need_zip64_eocd write_64_e_o_c_d(io, cdir_offset, cdir_size) diff --git a/lib/zip/entry.rb b/lib/zip/entry.rb index c317f468..5b95d787 100644 --- a/lib/zip/entry.rb +++ b/lib/zip/entry.rb @@ -195,7 +195,10 @@ def verify_local_header_size! return if @local_header_size.nil? new_size = calculate_local_header_size - raise Error, "local header size changed (#{@local_header_size} -> #{new_size})" if @local_header_size != new_size + return unless @local_header_size != new_size + + raise Error, + "Local header size changed (#{@local_header_size} -> #{new_size})" end def cdir_header_size #:nodoc:all @@ -363,8 +366,9 @@ def set_ftype_from_c_dir_entry when ::Zip::FILE_TYPE_SYMLINK :symlink else - # best case guess for whether it is a file or not - # Otherwise this would be set to unknown and that entry would never be able to extracted + # Best case guess for whether it is a file or not. + # Otherwise this would be set to unknown and that + # entry would never be able to be extracted. if name_is_directory? :directory else @@ -444,13 +448,18 @@ def get_extra_attributes_from_path(path) # :nodoc: @unix_perms = stat.mode & 0o7777 end + # rubocop:disable Style/GuardClause def set_unix_attributes_on_path(dest_path) - # ignore setuid/setgid bits by default. honor if @restore_ownership - unix_perms_mask = 0o1777 - unix_perms_mask = 0o7777 if @restore_ownership - ::FileUtils.chmod(@unix_perms & unix_perms_mask, dest_path) if @restore_permissions && @unix_perms - ::FileUtils.chown(@unix_uid, @unix_gid, dest_path) if @restore_ownership && @unix_uid && @unix_gid && ::Process.egid == 0 + # Ignore setuid/setgid bits by default. Honour if @restore_ownership. + unix_perms_mask = (@restore_ownership ? 0o7777 : 0o1777) + if @restore_permissions && @unix_perms + ::FileUtils.chmod(@unix_perms & unix_perms_mask, dest_path) + end + if @restore_ownership && @unix_uid && @unix_gid && ::Process.egid == 0 + ::FileUtils.chown(@unix_uid, @unix_gid, dest_path) + end end + # rubocop:enable Style/GuardClause def set_extra_attributes_on_path(dest_path) # :nodoc: return unless file? || directory? @@ -693,7 +702,9 @@ def parse_zip64_extra(for_local_header) #:nodoc:all if for_local_header @size, @compressed_size = @extra['Zip64'].parse(@size, @compressed_size) else - @size, @compressed_size, @local_header_offset = @extra['Zip64'].parse(@size, @compressed_size, @local_header_offset) + @size, @compressed_size, @local_header_offset = @extra['Zip64'].parse( + @size, @compressed_size, @local_header_offset + ) end end diff --git a/lib/zip/extra_field/zip64.rb b/lib/zip/extra_field/zip64.rb index 0e73ce0d..ba7b5110 100644 --- a/lib/zip/extra_field/zip64.rb +++ b/lib/zip/extra_field/zip64.rb @@ -40,7 +40,9 @@ def merge(binstr) def parse(original_size, compressed_size, relative_header_offset = nil, disk_start_number = nil) @original_size = extract(8, 'Q<') if original_size == 0xFFFFFFFF @compressed_size = extract(8, 'Q<') if compressed_size == 0xFFFFFFFF - @relative_header_offset = extract(8, 'Q<') if relative_header_offset && relative_header_offset == 0xFFFFFFFF + if relative_header_offset && relative_header_offset == 0xFFFFFFFF + @relative_header_offset = extract(8, 'Q<') + end @disk_start_number = extract(4, 'V') if disk_start_number && disk_start_number == 0xFFFF @content = nil [@original_size || original_size, @@ -55,7 +57,8 @@ def extract(size, format) private :extract def pack_for_local - # local header entries must contain original size and compressed size; other fields do not apply + # Local header entries must contain original size and compressed size; + # other fields do not apply. return '' unless @original_size && @compressed_size [@original_size, @compressed_size].pack('Q segment_size szip_file_name = "#{partial_zip_file_name}.#{format('%03d', szip_file_index)}" @@ -52,7 +55,7 @@ def save_splited_part(zip_file, partial_zip_file_name, zip_file_size, szip_file_ chunk_bytes = 0 until ssegment_size == chunk_bytes || zip_file.eof? segment_bytes_left = ssegment_size - chunk_bytes - buffer_size = segment_bytes_left < DATA_BUFFER_SIZE ? segment_bytes_left : DATA_BUFFER_SIZE + buffer_size = [segment_bytes_left, DATA_BUFFER_SIZE].min chunk = zip_file.read(buffer_size) chunk_bytes += buffer_size szip_file << chunk @@ -63,7 +66,10 @@ def save_splited_part(zip_file, partial_zip_file_name, zip_file_size, szip_file_ end # Splits an archive into parts with segment size - def split(zip_file_name, segment_size = MAX_SEGMENT_SIZE, delete_zip_file = true, partial_zip_file_name = nil) + def split( + zip_file_name, segment_size = MAX_SEGMENT_SIZE, + delete_zip_file = true, partial_zip_file_name = nil + ) raise Error, "File #{zip_file_name} not found" unless ::File.exist?(zip_file_name) raise Errno::ENOENT, zip_file_name unless ::File.readable?(zip_file_name) @@ -78,7 +84,10 @@ def split(zip_file_name, segment_size = MAX_SEGMENT_SIZE, delete_zip_file = true ::File.open(zip_file_name, 'rb') do |zip_file| until zip_file.eof? szip_file_index += 1 - save_splited_part(zip_file, partial_zip_file_name, zip_file_size, szip_file_index, segment_size, segment_count) + save_splited_part( + zip_file, partial_zip_file_name, zip_file_size, + szip_file_index, segment_size, segment_count + ) end end ::File.delete(zip_file_name) if delete_zip_file diff --git a/lib/zip/input_stream.rb b/lib/zip/input_stream.rb index d7985492..1eb4080f 100644 --- a/lib/zip/input_stream.rb +++ b/lib/zip/input_stream.rb @@ -134,7 +134,10 @@ def open_entry @current_entry = ::Zip::Entry.read_local_entry(@archive_io) return if @current_entry.nil? - raise Error, 'A password is required to decode this zip file' if @current_entry.encrypted? && @decrypter.kind_of?(NullEncrypter) + if @current_entry.encrypted? && @decrypter.kind_of?(NullEncrypter) + raise Error, + 'A password is required to decode this zip file' + end if @current_entry.incomplete? && @current_entry.compressed_size == 0 \ && !@complete_entry @@ -161,13 +164,16 @@ def get_decompressor return ::Zip::NullDecompressor if @current_entry.nil? decompressed_size = - if @current_entry.incomplete? && @current_entry.crc == 0 && @current_entry.size == 0 && @complete_entry + if @current_entry.incomplete? && @current_entry.crc == 0 \ + && @current_entry.size == 0 && @complete_entry @complete_entry.size else @current_entry.size end - decompressor_class = ::Zip::Decompressor.find_by_compression_method(@current_entry.compression_method) + decompressor_class = ::Zip::Decompressor.find_by_compression_method( + @current_entry.compression_method + ) if decompressor_class.nil? raise ::Zip::CompressionMethodError, "Unsupported compression method #{@current_entry.compression_method}" diff --git a/lib/zip/ioextras/abstract_input_stream.rb b/lib/zip/ioextras/abstract_input_stream.rb index 11438fc7..d0ea6825 100644 --- a/lib/zip/ioextras/abstract_input_stream.rb +++ b/lib/zip/ioextras/abstract_input_stream.rb @@ -84,7 +84,10 @@ def gets(a_sep_string = $INPUT_RECORD_SEPARATOR, number_of_bytes = nil) @output_buffer << produce_input over_limit = (number_of_bytes && @output_buffer.bytesize >= number_of_bytes) end - sep_index = [match_index + a_sep_string.bytesize, number_of_bytes || @output_buffer.bytesize].min + sep_index = [ + match_index + a_sep_string.bytesize, + number_of_bytes || @output_buffer.bytesize + ].min @pos += sep_index @output_buffer.slice!(0...sep_index) end diff --git a/lib/zip/output_stream.rb b/lib/zip/output_stream.rb index 3a034ed5..a648a3d0 100644 --- a/lib/zip/output_stream.rb +++ b/lib/zip/output_stream.rb @@ -142,7 +142,11 @@ def finalize_current_entry @current_entry.calculate_local_header_size @current_entry.size = @compressor.size @current_entry.crc = @compressor.crc - @output_stream << @encrypter.data_descriptor(@current_entry.crc, @current_entry.compressed_size, @current_entry.size) + @output_stream << @encrypter.data_descriptor( + @current_entry.crc, + @current_entry.compressed_size, + @current_entry.size + ) @current_entry.gp_flags |= @encrypter.gp_flags @current_entry = nil @compressor = ::Zip::NullCompressor.instance diff --git a/samples/example.rb b/samples/example.rb index 3e82d0a2..713729fb 100755 --- a/samples/example.rb +++ b/samples/example.rb @@ -21,7 +21,8 @@ zf = Zip::File.new('example.zip') zf.each_with_index do |entry, index| - puts "entry #{index} is #{entry.name}, size = #{entry.size}, compressed size = #{entry.compressed_size}" + puts "entry #{index} is #{entry.name}, size = #{entry.size}, " \ + "compressed size = #{entry.compressed_size}" # use zf.get_input_stream(entry) to get a ZipInputStream for the entry # entry can be the ZipEntry object or any object which has a to_s method that # returns the name of the entry. @@ -71,8 +72,11 @@ puts "Zip file splitted in #{part_zips_count} parts" # Track splitting an archive -Zip::File.split('large_zip_file.zip', 1_048_576, true, 'part_zip_file') do |part_count, part_index, chunk_bytes, segment_bytes| - puts "#{part_index} of #{part_count} part splitting: #{(chunk_bytes.to_f / segment_bytes * 100).to_i}%" +Zip::File.split( + 'large_zip_file.zip', 1_048_576, true, 'part_zip_file' +) do |part_count, part_index, chunk_bytes, segment_bytes| + puts "#{part_index} of #{part_count} part splitting: " \ + "#{(chunk_bytes.to_f / segment_bytes * 100).to_i}%" end # For other examples, look at zip.rb and ziptest.rb From e000552deb661bf915b49b7b13c0c41029884e2c Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sat, 26 Jun 2021 11:26:06 +0100 Subject: [PATCH 169/311] Raise an error on reading a split archive with `InputStream`. Fixes #349. --- .rubocop_todo.yml | 2 +- Changelog.md | 1 + lib/zip/entry.rb | 11 +++++++++-- lib/zip/errors.rb | 1 + test/data/invalid-split.zip | Bin 0 -> 180 bytes test/input_stream_test.rb | 8 ++++++++ 6 files changed, 20 insertions(+), 3 deletions(-) create mode 100644 test/data/invalid-split.zip diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index b86c4133..84dcc795 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -20,7 +20,7 @@ Lint/MissingSuper: # Offense count: 5 # Configuration parameters: CountComments, CountAsOne. Metrics/ClassLength: - Max: 600 + Max: 610 # Offense count: 21 # Configuration parameters: IgnoredMethods. diff --git a/Changelog.md b/Changelog.md index 09968a8e..26143099 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,5 +1,6 @@ # 3.0.0 (Next) +- Raise an error on reading a split archive with `InputStream`. [#349](https://github.com/rubyzip/rubyzip/issues/349) - Ensure `InputStream` raises `GPFBit3Error` for OSX Archive files. [#493](https://github.com/rubyzip/rubyzip/issues/493) - Improve documentation and error messages for `InputStream`. [#196](https://github.com/rubyzip/rubyzip/issues/196) - Fix zip file-level comment is not read from zip64 files. [#492](https://github.com/rubyzip/rubyzip/issues/492) diff --git a/lib/zip/entry.rb b/lib/zip/entry.rb index 5b95d787..8b0c9bc4 100644 --- a/lib/zip/entry.rb +++ b/lib/zip/entry.rb @@ -250,6 +250,8 @@ def read_local_entry(io) entry = new(io) entry.read_local_entry(io) entry + rescue SplitArchiveError + raise rescue Error nil end @@ -281,8 +283,13 @@ def read_local_entry(io) #:nodoc:all unpack_local_entry(static_sized_fields_buf) - unless @header_signature == ::Zip::LOCAL_ENTRY_SIGNATURE - raise ::Zip::Error, "Zip local header magic not found at location '#{local_header_offset}'" + unless @header_signature == LOCAL_ENTRY_SIGNATURE + if @header_signature == SPLIT_FILE_SIGNATURE + raise SplitArchiveError, + 'Rubyzip cannot extract from split archives at this time' + end + + raise Error, "Zip local header magic not found at location '#{local_header_offset}'" end set_time(@last_mod_date, @last_mod_time) diff --git a/lib/zip/errors.rb b/lib/zip/errors.rb index 947f8dc4..ca15e2be 100644 --- a/lib/zip/errors.rb +++ b/lib/zip/errors.rb @@ -10,6 +10,7 @@ class EntrySizeError < Error; end class InternalError < Error; end class GPFBit3Error < Error; end class DecompressionError < Error; end + class SplitArchiveError < Error; end # Backwards compatibility with v1 (delete in v2) ZipError = Error diff --git a/test/data/invalid-split.zip b/test/data/invalid-split.zip new file mode 100644 index 0000000000000000000000000000000000000000..e6323d9ee771b0413ff0e596481cfca19b4319e9 GIT binary patch literal 180 zcmWIWX6Fd-W@Zs#U|`^2FzsvizH0O94i}Ke4;GPOC`m0Y(JQGa2@T<7U}g)|jM4z& z(h6<{MwYLP3=CkM+Mc?4ff|j1F$@9Tj7)OOxJ;7(n#sTb)Wxu*kp)O1%w~m{jb?6u SH!B-N2P1 Date: Sat, 26 Jun 2021 17:37:09 +0100 Subject: [PATCH 170/311] Remove the `ZipXError` v1 legacy classes. --- Changelog.md | 1 + lib/zip/errors.rb | 8 -------- test/errors_test.rb | 35 ----------------------------------- 3 files changed, 1 insertion(+), 43 deletions(-) delete mode 100644 test/errors_test.rb diff --git a/Changelog.md b/Changelog.md index 26143099..123a9f03 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,5 +1,6 @@ # 3.0.0 (Next) +- Remove the `ZipXError` v1 legacy classes. - Raise an error on reading a split archive with `InputStream`. [#349](https://github.com/rubyzip/rubyzip/issues/349) - Ensure `InputStream` raises `GPFBit3Error` for OSX Archive files. [#493](https://github.com/rubyzip/rubyzip/issues/493) - Improve documentation and error messages for `InputStream`. [#196](https://github.com/rubyzip/rubyzip/issues/196) diff --git a/lib/zip/errors.rb b/lib/zip/errors.rb index ca15e2be..66ae5356 100644 --- a/lib/zip/errors.rb +++ b/lib/zip/errors.rb @@ -11,12 +11,4 @@ class InternalError < Error; end class GPFBit3Error < Error; end class DecompressionError < Error; end class SplitArchiveError < Error; end - - # Backwards compatibility with v1 (delete in v2) - ZipError = Error - ZipEntryExistsError = EntryExistsError - ZipDestinationFileExistsError = DestinationFileExistsError - ZipCompressionMethodError = CompressionMethodError - ZipEntryNameError = EntryNameError - ZipInternalError = InternalError end diff --git a/test/errors_test.rb b/test/errors_test.rb deleted file mode 100644 index 2f1b506d..00000000 --- a/test/errors_test.rb +++ /dev/null @@ -1,35 +0,0 @@ -# frozen_string_literal: true - -require 'test_helper' - -class ErrorsTest < MiniTest::Test - def test_rescue_legacy_zip_error - raise ::Zip::Error - rescue ::Zip::ZipError - end - - def test_rescue_legacy_zip_entry_exists_error - raise ::Zip::EntryExistsError - rescue ::Zip::ZipEntryExistsError - end - - def test_rescue_legacy_zip_destination_file_exists_error - raise ::Zip::DestinationFileExistsError - rescue ::Zip::ZipDestinationFileExistsError - end - - def test_rescue_legacy_zip_compression_method_error - raise ::Zip::CompressionMethodError - rescue ::Zip::ZipCompressionMethodError - end - - def test_rescue_legacy_zip_entry_name_error - raise ::Zip::EntryNameError - rescue ::Zip::ZipEntryNameError - end - - def test_rescue_legacy_zip_internal_error - raise ::Zip::InternalError - rescue ::Zip::ZipInternalError - end -end From a301d68eebd2f96afb2696e0ff79448176f282bc Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sat, 26 Jun 2021 19:17:38 +0100 Subject: [PATCH 171/311] Raise an error if entry names exceed 65,535 characters. Fixes #247. --- .rubocop_todo.yml | 2 +- Changelog.md | 1 + lib/zip/entry.rb | 12 ++++++++++-- test/entry_test.rb | 9 +++++++++ 4 files changed, 21 insertions(+), 3 deletions(-) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 84dcc795..addf8759 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -20,7 +20,7 @@ Lint/MissingSuper: # Offense count: 5 # Configuration parameters: CountComments, CountAsOne. Metrics/ClassLength: - Max: 610 + Max: 620 # Offense count: 21 # Configuration parameters: IgnoredMethods. diff --git a/Changelog.md b/Changelog.md index 123a9f03..98bf6b5d 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,5 +1,6 @@ # 3.0.0 (Next) +- Raise an error if entry names exceed 65,535 characters. [#247](https://github.com/rubyzip/rubyzip/issues/247) - Remove the `ZipXError` v1 legacy classes. - Raise an error on reading a split archive with `InputStream`. [#349](https://github.com/rubyzip/rubyzip/issues/349) - Ensure `InputStream` raises `GPFBit3Error` for OSX Archive files. [#493](https://github.com/rubyzip/rubyzip/issues/493) diff --git a/lib/zip/entry.rb b/lib/zip/entry.rb index 8b0c9bc4..749563d9 100644 --- a/lib/zip/entry.rb +++ b/lib/zip/entry.rb @@ -55,9 +55,17 @@ def set_default_vars_values end def check_name(name) - return unless name.start_with?('/') + error = + if name.start_with?('/') + "Illegal entry name '#{name}'. Names must not start with '/'" + elsif name.length > 65_535 + 'Illegal entry name. Names must have fewer than 65,536 characters' + else + '' + end + return if error.empty? - raise ::Zip::EntryNameError, "Illegal ZipEntry name '#{name}', name must not start with /" + raise EntryNameError, error end def initialize( diff --git a/test/entry_test.rb b/test/entry_test.rb index d57962ca..17273c54 100644 --- a/test/entry_test.rb +++ b/test/entry_test.rb @@ -150,6 +150,15 @@ def test_entry_name_cannot_start_with_slash assert_raises(::Zip::EntryNameError) { ::Zip::Entry.new('zf.zip', '/hej/der') } end + def test_entry_name_cannot_be_too_long + name = 'a' * 65_535 + ::Zip::Entry.new('', name) # Should not raise anything. + + assert_raises(::Zip::EntryNameError) do + ::Zip::Entry.new('', "a#{name}") + end + end + def test_store_file_without_compression Dir.mktmpdir do |tmp| tmp_zip = File.join(tmp, 'no_compress.zip') From 8699e356d4e08550de9fc5873f8f4ddd63069014 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sat, 26 Jun 2021 20:04:17 +0100 Subject: [PATCH 172/311] Improve documentation for `File.glob`. Closes #338. --- lib/zip/file.rb | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/zip/file.rb b/lib/zip/file.rb index 8b3a348e..a8d61642 100644 --- a/lib/zip/file.rb +++ b/lib/zip/file.rb @@ -330,7 +330,11 @@ def find_entry(entry_name) selected_entry end - # Searches for entries given a glob + # Search for entries given a glob pattern. You can also supply flags + # in the second argument, which are equivalent to those used by + # `::Dir.glob` and `::File.fnmatch`. Default flags are + # `::File::FNM_PATHNAME | ::File::FNM_DOTMATCH | ::File::FNM_EXTGLOB`, + # which will be overridden if you set your own flags. def glob(*args, &block) @entry_set.glob(*args, &block) end From f76cef90f7205210a7c4e6fc9d42e03e9c08f664 Mon Sep 17 00:00:00 2001 From: "Yuya.Nishida" Date: Sun, 27 Jun 2021 05:19:15 +0900 Subject: [PATCH 173/311] Remove newer duplicated line --- Changelog.md | 1 - 1 file changed, 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index 98bf6b5d..011d560a 100644 --- a/Changelog.md +++ b/Changelog.md @@ -14,7 +14,6 @@ - Set the default `Entry` time to the file's mtime on Windows. [#465](https://github.com/rubyzip/rubyzip/issues/465) - Ensure that `Entry#time=` sets times as `DOSTime` objects. [#481](https://github.com/rubyzip/rubyzip/issues/481) - Replace and deprecate `Zip::DOSTime#dos_equals`. [#464](https://github.com/rubyzip/rubyzip/pull/464) -- Fix input stream partial read error. [#462](https://github.com/rubyzip/rubyzip/pull/462) - Fix loading extra fields. [#459](https://github.com/rubyzip/rubyzip/pull/459) - Set compression level on a per-zipfile basis. [#448](https://github.com/rubyzip/rubyzip/pull/448) - Fix input stream partial read error. [#462](https://github.com/rubyzip/rubyzip/pull/462) From e7f0aba5ffee55f017bbd6f0612465d4767e05ab Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Tue, 8 Jun 2021 17:13:20 +0100 Subject: [PATCH 174/311] Fix Style/OptionalBooleanParameter cop in `Entry`. Just an internal API so safe, and makes things a lot neater. --- .rubocop_todo.yml | 1 - lib/zip/entry.rb | 2 +- lib/zip/output_stream.rb | 2 +- test/local_entry_test.rb | 2 +- 4 files changed, 3 insertions(+), 4 deletions(-) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index addf8759..ae36271b 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -109,7 +109,6 @@ Style/NumericPredicate: # AllowedMethods: respond_to_missing? Style/OptionalBooleanParameter: Exclude: - - 'lib/zip/entry.rb' - 'lib/zip/file.rb' - 'lib/zip/file_split.rb' - 'lib/zip/output_stream.rb' diff --git a/lib/zip/entry.rb b/lib/zip/entry.rb index 749563d9..11ddb851 100644 --- a/lib/zip/entry.rb +++ b/lib/zip/entry.rb @@ -333,7 +333,7 @@ def pack_local_entry @extra ? @extra.local_size : 0].pack('VvvvvvVVVvv') end - def write_local_entry(io, rewrite = false) #:nodoc:all + def write_local_entry(io, rewrite: false) #:nodoc:all prep_zip64_extra(true) verify_local_header_size! if rewrite @local_header_offset = io.tell diff --git a/lib/zip/output_stream.rb b/lib/zip/output_stream.rb index a648a3d0..2150dfb4 100644 --- a/lib/zip/output_stream.rb +++ b/lib/zip/output_stream.rb @@ -177,7 +177,7 @@ def update_local_headers pos = @output_stream.pos @entry_set.each do |entry| @output_stream.pos = entry.local_header_offset - entry.write_local_entry(@output_stream, true) + entry.write_local_entry(@output_stream, rewrite: true) end @output_stream.pos = pos end diff --git a/test/local_entry_test.rb b/test/local_entry_test.rb index 212c91af..15c367a7 100644 --- a/test/local_entry_test.rb +++ b/test/local_entry_test.rb @@ -126,7 +126,7 @@ def test_rewrite_local_header64 buf2 = StringIO.new entry.size = 0x123456789ABCDEF0 entry.compressed_size = 0x0123456789ABCDEF - entry.write_local_entry(buf2, true) + entry.write_local_entry(buf2, rewrite: true) refute_nil(entry.extra['Zip64']) refute_equal(buf1.size, 0) assert_equal(buf1.size, buf2.size) # it can't grow, or we'd clobber file data From 7ae90be63e8f2a52ad09fdbdcea6447dd3035a8f Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Tue, 8 Jun 2021 17:25:22 +0100 Subject: [PATCH 175/311] Fix Style/OptionalBooleanParameter in `OutputStream`. --- .rubocop_todo.yml | 1 - lib/zip/output_stream.rb | 6 +++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index ae36271b..732f26d0 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -111,7 +111,6 @@ Style/OptionalBooleanParameter: Exclude: - 'lib/zip/file.rb' - 'lib/zip/file_split.rb' - - 'lib/zip/output_stream.rb' # Offense count: 17 # Cop supports --auto-correct. diff --git a/lib/zip/output_stream.rb b/lib/zip/output_stream.rb index 2150dfb4..ebf35b67 100644 --- a/lib/zip/output_stream.rb +++ b/lib/zip/output_stream.rb @@ -26,7 +26,7 @@ class OutputStream # Opens the indicated zip file. If a file with that name already # exists it will be overwritten. - def initialize(file_name, stream = false, encrypter = nil) + def initialize(file_name, stream: false, encrypter: nil) super() @file_name = file_name @output_stream = if stream @@ -52,7 +52,7 @@ class << self def open(file_name, encrypter = nil) return new(file_name) unless block_given? - zos = new(file_name, false, encrypter) + zos = new(file_name, stream: false, encrypter: encrypter) yield zos ensure zos.close if zos @@ -61,7 +61,7 @@ def open(file_name, encrypter = nil) # Same as #open but writes to a filestream instead def write_buffer(io = ::StringIO.new(''), encrypter = nil) io.binmode if io.respond_to?(:binmode) - zos = new(io, true, encrypter) + zos = new(io, stream: true, encrypter: encrypter) yield zos zos.close_buffer end From 659db85bffa7f4a5a18b9923d09c095062a19183 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Tue, 8 Jun 2021 17:32:29 +0100 Subject: [PATCH 176/311] `open` and `write_buffer` in `OutputStream` use named params. --- README.md | 4 +++- lib/zip/output_stream.rb | 4 ++-- test/encryption_test.rb | 5 ++++- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index d70e2881..5af5454b 100644 --- a/README.md +++ b/README.md @@ -201,7 +201,9 @@ Any attempt to move about in a zip file opened with `Zip::InputStream` could res Rubyzip supports reading/writing zip files with traditional zip encryption (a.k.a. "ZipCrypto"). AES encryption is not yet supported. It can be used with buffer streams, e.g.: ```ruby -Zip::OutputStream.write_buffer(::StringIO.new(''), Zip::TraditionalEncrypter.new('password')) do |out| +Zip::OutputStream.write_buffer( + ::StringIO.new, encrypter: Zip::TraditionalEncrypter.new('password') +) do |out| out.put_next_entry("my_file.txt") out.write my_data end.string diff --git a/lib/zip/output_stream.rb b/lib/zip/output_stream.rb index ebf35b67..29408bdf 100644 --- a/lib/zip/output_stream.rb +++ b/lib/zip/output_stream.rb @@ -49,7 +49,7 @@ def initialize(file_name, stream: false, encrypter: nil) # stream is passed to the block and closed when the block # returns. class << self - def open(file_name, encrypter = nil) + def open(file_name, encrypter: nil) return new(file_name) unless block_given? zos = new(file_name, stream: false, encrypter: encrypter) @@ -59,7 +59,7 @@ def open(file_name, encrypter = nil) end # Same as #open but writes to a filestream instead - def write_buffer(io = ::StringIO.new(''), encrypter = nil) + def write_buffer(io = ::StringIO.new(''), encrypter: nil) io.binmode if io.respond_to?(:binmode) zos = new(io, stream: true, encrypter: encrypter) yield zos diff --git a/test/encryption_test.rb b/test/encryption_test.rb index c1bc2038..4289df12 100644 --- a/test/encryption_test.rb +++ b/test/encryption_test.rb @@ -20,7 +20,10 @@ def test_encrypt password = 'swordfish' - encrypted_zip = Zip::OutputStream.write_buffer(::StringIO.new(+''), Zip::TraditionalEncrypter.new(password)) do |out| + encrypted_zip = Zip::OutputStream.write_buffer( + ::StringIO.new(+''), + encrypter: Zip::TraditionalEncrypter.new(password) + ) do |out| out.put_next_entry(test_filename) out.write content end From e1e1cab39c3613055e87606a5e059636c3e6dd95 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Tue, 8 Jun 2021 18:09:22 +0100 Subject: [PATCH 177/311] Fix some non-writable `StringIO`s. --- lib/zip/file.rb | 4 ++-- lib/zip/output_stream.rb | 2 +- test/encryption_test.rb | 2 +- test/file_test.rb | 3 +-- test/output_stream_test.rb | 15 +++++++++++---- 5 files changed, 16 insertions(+), 10 deletions(-) diff --git a/lib/zip/file.rb b/lib/zip/file.rb index a8d61642..256cf656 100644 --- a/lib/zip/file.rb +++ b/lib/zip/file.rb @@ -128,7 +128,7 @@ def open(file_name, create = false, options = {}) # Same as #open. But outputs data to a buffer instead of a file def add_buffer - io = ::StringIO.new(+'') + io = ::StringIO.new zf = ::Zip::File.new(io, true, true) yield zf zf.write_buffer(io) @@ -297,7 +297,7 @@ def commit end # Write buffer write changes to buffer and return - def write_buffer(io = ::StringIO.new('')) + def write_buffer(io = ::StringIO.new) ::Zip::OutputStream.write_buffer(io) do |zos| @entry_set.each { |e| e.write_to_zip_output_stream(zos) } zos.comment = comment diff --git a/lib/zip/output_stream.rb b/lib/zip/output_stream.rb index 29408bdf..f2e7eb5e 100644 --- a/lib/zip/output_stream.rb +++ b/lib/zip/output_stream.rb @@ -59,7 +59,7 @@ def open(file_name, encrypter: nil) end # Same as #open but writes to a filestream instead - def write_buffer(io = ::StringIO.new(''), encrypter: nil) + def write_buffer(io = ::StringIO.new, encrypter: nil) io.binmode if io.respond_to?(:binmode) zos = new(io, stream: true, encrypter: encrypter) yield zos diff --git a/test/encryption_test.rb b/test/encryption_test.rb index 4289df12..150aea04 100644 --- a/test/encryption_test.rb +++ b/test/encryption_test.rb @@ -21,7 +21,7 @@ def test_encrypt password = 'swordfish' encrypted_zip = Zip::OutputStream.write_buffer( - ::StringIO.new(+''), + ::StringIO.new, encrypter: Zip::TraditionalEncrypter.new(password) ) do |out| out.put_next_entry(test_filename) diff --git a/test/file_test.rb b/test/file_test.rb index ec18aa2a..25b31b22 100644 --- a/test/file_test.rb +++ b/test/file_test.rb @@ -545,8 +545,7 @@ def test_write_buffer zf = ::Zip::File.new(TEST_ZIP.zip_name) old_name = zf.entries.first zf.rename(old_name, new_name) - io = ::StringIO.new(+'') - buffer = zf.write_buffer(io) + buffer = zf.write_buffer(::StringIO.new) File.open(TEST_ZIP.zip_name, 'wb') { |f| f.write buffer.string } zf_read = ::Zip::File.new(TEST_ZIP.zip_name) refute_nil(zf_read.entries.detect { |e| e.name == new_name }) diff --git a/test/output_stream_test.rb b/test/output_stream_test.rb index ecd48488..b87f64fc 100644 --- a/test/output_stream_test.rb +++ b/test/output_stream_test.rb @@ -25,8 +25,7 @@ def test_open end def test_write_buffer - io = ::StringIO.new(+'') - buffer = ::Zip::OutputStream.write_buffer(io) do |zos| + buffer = ::Zip::OutputStream.write_buffer(::StringIO.new) do |zos| zos.comment = TEST_ZIP.comment write_test_zip(zos) end @@ -35,8 +34,7 @@ def test_write_buffer end def test_write_buffer_binmode - io = ::StringIO.new(+'') - buffer = ::Zip::OutputStream.write_buffer(io) do |zos| + buffer = ::Zip::OutputStream.write_buffer(::StringIO.new) do |zos| zos.comment = TEST_ZIP.comment write_test_zip(zos) end @@ -72,6 +70,15 @@ def test_write_buffer_with_temp_file2 ::File.unlink(tmp_file) end + def test_write_buffer_with_default_io + buffer = ::Zip::OutputStream.write_buffer do |zos| + zos.comment = TEST_ZIP.comment + write_test_zip(zos) + end + File.open(TEST_ZIP.zip_name, 'wb') { |f| f.write buffer.string } + assert_test_zip_contents(TEST_ZIP) + end + def test_writing_to_closed_stream assert_i_o_error_in_closed_stream { |zos| zos << 'hello world' } assert_i_o_error_in_closed_stream { |zos| zos.puts 'hello world' } From f033ae760dd1d3ab9eef0da7b9c7dc2548f92dfd Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Tue, 8 Jun 2021 21:24:52 +0100 Subject: [PATCH 178/311] Use named parameters for `File::new`. This is a breaking change, but now is the time to do this as we've already done the same for `Entry::new`. --- .rubocop_todo.yml | 3 +- README.md | 6 ++-- lib/zip/file.rb | 21 ++++++------ lib/zip/filesystem.rb | 2 +- samples/example_filesystem.rb | 2 +- samples/example_recursive.rb | 2 +- test/case_sensitivity_test.rb | 6 ++-- test/entry_test.rb | 2 +- test/file_extract_test.rb | 2 +- test/file_options_test.rb | 20 +++++------ test/file_permissions_test.rb | 2 +- test/file_test.rb | 36 ++++++-------------- test/unicode_file_names_and_comments_test.rb | 2 +- test/zip64_full_test.rb | 2 +- 14 files changed, 45 insertions(+), 63 deletions(-) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 732f26d0..a08030fa 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -104,12 +104,11 @@ Style/NumericPredicate: - 'lib/zip/ioextras.rb' - 'lib/zip/ioextras/abstract_input_stream.rb' -# Offense count: 6 +# Offense count: 1 # Configuration parameters: AllowedMethods. # AllowedMethods: respond_to_missing? Style/OptionalBooleanParameter: Exclude: - - 'lib/zip/file.rb' - 'lib/zip/file_split.rb' # Offense count: 17 diff --git a/README.md b/README.md index 5af5454b..4b66ec20 100644 --- a/README.md +++ b/README.md @@ -50,7 +50,7 @@ input_filenames = ['image.jpg', 'description.txt', 'stats.csv'] zipfile_name = "/Users/me/Desktop/archive.zip" -Zip::File.open(zipfile_name, Zip::File::CREATE) do |zipfile| +Zip::File.open(zipfile_name, create: true) do |zipfile| input_filenames.each do |filename| # Two arguments: # - The name of the file as it will appear in the archive @@ -89,7 +89,7 @@ class ZipFileGenerator def write entries = Dir.entries(@input_dir) - %w[. ..] - ::Zip::File.open(@output_file, ::Zip::File::CREATE) do |zipfile| + ::Zip::File.open(@output_file, create: true) do |zipfile| write_entries entries, '', zipfile end end @@ -318,7 +318,7 @@ Where X is an integer between 0 and 9, inclusive. If this option is set to 0 (`Z This can also be set for each archive as an option to `Zip::File`: ```ruby -Zip::File.open('foo.zip', Zip::File::CREATE, {compression_level: 9}) do |zip| +Zip::File.open('foo.zip', create:true, compression_level: 9) do |zip| zip.add ... end ``` diff --git a/lib/zip/file.rb b/lib/zip/file.rb index 256cf656..5623b4ea 100644 --- a/lib/zip/file.rb +++ b/lib/zip/file.rb @@ -25,7 +25,7 @@ module Zip # # require 'zip' # - # Zip::File.open("my.zip", Zip::File::CREATE) { + # Zip::File.open("my.zip", create: true) { # |zipfile| # zipfile.get_output_stream("first.txt") { |f| f.puts "Hello from ZipFile" } # zipfile.mkdir("a_dir") @@ -37,7 +37,7 @@ module Zip # # require 'zip' # - # Zip::File.open("my.zip", Zip::File::CREATE) { + # Zip::File.open("my.zip", create: true) { # |zipfile| # puts zipfile.read("first.txt") # zipfile.remove("first.txt") @@ -49,8 +49,7 @@ module Zip class File < CentralDirectory extend FileSplit - CREATE = true - IO_METHODS = [:tell, :seek, :read, :eof, :close].freeze + IO_METHODS = [:tell, :seek, :read, :eof, :close].freeze attr_reader :name @@ -66,9 +65,9 @@ class File < CentralDirectory # Returns the zip files comment, if it has one attr_accessor :comment - # Opens a zip archive. Pass true as the second parameter to create + # Opens a zip archive. Pass create: true to create # a new archive if it doesn't exist already. - def initialize(path_or_io, create = false, buffer = false, options = {}) + def initialize(path_or_io, create: false, buffer: false, **options) super() options = DEFAULT_RESTORE_OPTIONS .merge(compression_level: ::Zip.default_compression) @@ -115,8 +114,8 @@ class << self # Similar to ::new. If a block is passed the Zip::File object is passed # to the block and is automatically closed afterwards, just as with # ruby's builtin File::open method. - def open(file_name, create = false, options = {}) - zf = ::Zip::File.new(file_name, create, false, options) + def open(file_name, create: false, **options) + zf = ::Zip::File.new(file_name, create: create, **options) return zf unless block_given? begin @@ -129,7 +128,7 @@ def open(file_name, create = false, options = {}) # Same as #open. But outputs data to a buffer instead of a file def add_buffer io = ::StringIO.new - zf = ::Zip::File.new(io, true, true) + zf = ::Zip::File.new(io, create: true, buffer: true) yield zf zf.write_buffer(io) end @@ -138,7 +137,7 @@ def add_buffer # stream, and outputs data to a buffer. # (This can be used to extract data from a # downloaded zip archive without first saving it to disk.) - def open_buffer(io, options = {}) + def open_buffer(io, **options) unless IO_METHODS.map { |method| io.respond_to?(method) }.all? || io.kind_of?(String) raise 'Zip::File.open_buffer expects a String or IO-like argument' \ "(responds to #{IO_METHODS.join(', ')}). Found: #{io.class}" @@ -149,7 +148,7 @@ def open_buffer(io, options = {}) # https://github.com/rubyzip/rubyzip/issues/119 io.binmode if io.respond_to?(:binmode) - zf = ::Zip::File.new(io, true, true, options) + zf = ::Zip::File.new(io, create: true, buffer: true, **options) return zf unless block_given? yield zf diff --git a/lib/zip/filesystem.rb b/lib/zip/filesystem.rb index 26895df5..e223b3c0 100644 --- a/lib/zip/filesystem.rb +++ b/lib/zip/filesystem.rb @@ -21,7 +21,7 @@ module Zip # # require 'zip/filesystem' # - # Zip::File.open("my.zip", Zip::File::CREATE) { + # Zip::File.open("my.zip", create: true) { # |zipfile| # zipfile.file.open("first.txt", "w") { |f| f.puts "Hello world" } # zipfile.dir.mkdir("mydir") diff --git a/samples/example_filesystem.rb b/samples/example_filesystem.rb index d37eaee6..6909848f 100755 --- a/samples/example_filesystem.rb +++ b/samples/example_filesystem.rb @@ -9,7 +9,7 @@ File.delete(EXAMPLE_ZIP) if File.exist?(EXAMPLE_ZIP) -Zip::File.open(EXAMPLE_ZIP, Zip::File::CREATE) do |zf| +Zip::File.open(EXAMPLE_ZIP, create: true) do |zf| zf.file.open('file1.txt', 'w') { |os| os.write 'first file1.txt' } zf.dir.mkdir('dir1') zf.dir.chdir('dir1') diff --git a/samples/example_recursive.rb b/samples/example_recursive.rb index 175e69f4..04b6f339 100644 --- a/samples/example_recursive.rb +++ b/samples/example_recursive.rb @@ -23,7 +23,7 @@ def initialize(input_dir, output_file) def write entries = Dir.entries(@input_dir) - %w[. ..] - ::Zip::File.open(@output_file, ::Zip::File::CREATE) do |zipfile| + ::Zip::File.open(@output_file, create: true) do |zipfile| write_entries entries, '', zipfile end end diff --git a/test/case_sensitivity_test.rb b/test/case_sensitivity_test.rb index af4443db..81ad2072 100644 --- a/test/case_sensitivity_test.rb +++ b/test/case_sensitivity_test.rb @@ -19,7 +19,7 @@ def test_add_case_sensitive ::Zip.case_insensitive_match = false SRC_FILES.each { |fn, _en| assert(::File.exist?(fn)) } - zf = ::Zip::File.new(EMPTY_FILENAME, ::Zip::File::CREATE) + zf = ::Zip::File.new(EMPTY_FILENAME, create: true) SRC_FILES.each { |fn, en| zf.add(en, fn) } zf.close @@ -38,7 +38,7 @@ def test_add_case_insensitive ::Zip.case_insensitive_match = true SRC_FILES.each { |fn, _en| assert(::File.exist?(fn)) } - zf = ::Zip::File.new(EMPTY_FILENAME, ::Zip::File::CREATE) + zf = ::Zip::File.new(EMPTY_FILENAME, create: true) assert_raises Zip::EntryExistsError do SRC_FILES.each { |fn, en| zf.add(en, fn) } @@ -50,7 +50,7 @@ def test_add_case_sensitive_read_case_insensitive ::Zip.case_insensitive_match = false SRC_FILES.each { |fn, _en| assert(::File.exist?(fn)) } - zf = ::Zip::File.new(EMPTY_FILENAME, ::Zip::File::CREATE) + zf = ::Zip::File.new(EMPTY_FILENAME, create: true) SRC_FILES.each { |fn, en| zf.add(en, fn) } zf.close diff --git a/test/entry_test.rb b/test/entry_test.rb index 17273c54..13d42963 100644 --- a/test/entry_test.rb +++ b/test/entry_test.rb @@ -167,7 +167,7 @@ def test_store_file_without_compression z.write_zip64_support = false end - zipfile = Zip::File.open(tmp_zip, Zip::File::CREATE) + zipfile = Zip::File.open(tmp_zip, create: true) mimetype_entry = Zip::Entry.new( zipfile, # @zipfile diff --git a/test/file_extract_test.rb b/test/file_extract_test.rb index 9d0b7da4..e5243cab 100644 --- a/test/file_extract_test.rb +++ b/test/file_extract_test.rb @@ -99,7 +99,7 @@ def test_extract_incorrect_size true_size = 500_000 fake_size = 1 - ::Zip::File.open(real_zip, ::Zip::File::CREATE) do |zf| + ::Zip::File.open(real_zip, create: true) do |zf| zf.get_output_stream(file_name) do |os| os.write 'a' * true_size end diff --git a/test/file_options_test.rb b/test/file_options_test.rb index da9a402c..c1a568a6 100644 --- a/test/file_options_test.rb +++ b/test/file_options_test.rb @@ -30,13 +30,13 @@ def test_restore_permissions_true ::FileUtils.cp(TXTPATH, TXTPATH_755) ::File.chmod(0o755, TXTPATH_755) - ::Zip::File.open(ZIPPATH, true) do |zip| + ::Zip::File.open(ZIPPATH, create: true) do |zip| zip.add(ENTRY_1, TXTPATH) zip.add(ENTRY_2, TXTPATH_600) zip.add(ENTRY_3, TXTPATH_755) end - ::Zip::File.open(ZIPPATH, false, restore_permissions: true) do |zip| + ::Zip::File.open(ZIPPATH, restore_permissions: true) do |zip| zip.extract(ENTRY_1, EXTPATH_1) zip.extract(ENTRY_2, EXTPATH_2) zip.extract(ENTRY_3, EXTPATH_3) @@ -54,13 +54,13 @@ def test_restore_permissions_false ::FileUtils.cp(TXTPATH, TXTPATH_755) ::File.chmod(0o755, TXTPATH_755) - ::Zip::File.open(ZIPPATH, true) do |zip| + ::Zip::File.open(ZIPPATH, create: true) do |zip| zip.add(ENTRY_1, TXTPATH) zip.add(ENTRY_2, TXTPATH_600) zip.add(ENTRY_3, TXTPATH_755) end - ::Zip::File.open(ZIPPATH, false, restore_permissions: false) do |zip| + ::Zip::File.open(ZIPPATH, restore_permissions: false) do |zip| zip.extract(ENTRY_1, EXTPATH_1) zip.extract(ENTRY_2, EXTPATH_2) zip.extract(ENTRY_3, EXTPATH_3) @@ -79,7 +79,7 @@ def test_restore_permissions_as_default ::FileUtils.cp(TXTPATH, TXTPATH_755) ::File.chmod(0o755, TXTPATH_755) - ::Zip::File.open(ZIPPATH, true) do |zip| + ::Zip::File.open(ZIPPATH, create: true) do |zip| zip.add(ENTRY_1, TXTPATH) zip.add(ENTRY_2, TXTPATH_600) zip.add(ENTRY_3, TXTPATH_755) @@ -97,12 +97,12 @@ def test_restore_permissions_as_default end def test_restore_times_true - ::Zip::File.open(ZIPPATH, true) do |zip| + ::Zip::File.open(ZIPPATH, create: true) do |zip| zip.add(ENTRY_1, TXTPATH) zip.add_stored(ENTRY_2, TXTPATH) end - ::Zip::File.open(ZIPPATH, false, restore_times: true) do |zip| + ::Zip::File.open(ZIPPATH, restore_times: true) do |zip| zip.extract(ENTRY_1, EXTPATH_1) zip.extract(ENTRY_2, EXTPATH_2) end @@ -112,12 +112,12 @@ def test_restore_times_true end def test_restore_times_false - ::Zip::File.open(ZIPPATH, true) do |zip| + ::Zip::File.open(ZIPPATH, create: true) do |zip| zip.add(ENTRY_1, TXTPATH) zip.add_stored(ENTRY_2, TXTPATH) end - ::Zip::File.open(ZIPPATH, false, restore_times: false) do |zip| + ::Zip::File.open(ZIPPATH, restore_times: false) do |zip| zip.extract(ENTRY_1, EXTPATH_1) zip.extract(ENTRY_2, EXTPATH_2) end @@ -127,7 +127,7 @@ def test_restore_times_false end def test_restore_times_true_as_default - ::Zip::File.open(ZIPPATH, true) do |zip| + ::Zip::File.open(ZIPPATH, create: true) do |zip| zip.add(ENTRY_1, TXTPATH) zip.add_stored(ENTRY_2, TXTPATH) end diff --git a/test/file_permissions_test.rb b/test/file_permissions_test.rb index 1b31a79b..e9dabf3f 100644 --- a/test/file_permissions_test.rb +++ b/test/file_permissions_test.rb @@ -48,7 +48,7 @@ def assert_matching_permissions(expected_file, actual_file) end def create_files - ::Zip::File.open(ZIPNAME, ::Zip::File::CREATE) do |zip| + ::Zip::File.open(ZIPNAME, create: true) do |zip| zip.comment = 'test' end diff --git a/test/file_test.rb b/test/file_test.rb index 25b31b22..9ac2f698 100644 --- a/test/file_test.rb +++ b/test/file_test.rb @@ -32,21 +32,7 @@ def test_create_from_scratch_to_buffer def test_create_from_scratch comment = 'a short comment' - zf = ::Zip::File.new(EMPTY_FILENAME, ::Zip::File::CREATE) - zf.get_output_stream('myFile') { |os| os.write 'myFile contains just this' } - zf.mkdir('dir1') - zf.comment = comment - zf.close - - zf_read = ::Zip::File.new(EMPTY_FILENAME) - assert_equal(comment, zf_read.comment) - assert_equal(2, zf_read.entries.length) - end - - def test_create_from_scratch_with_old_create_parameter - comment = 'a short comment' - - zf = ::Zip::File.new(EMPTY_FILENAME, 1) + zf = ::Zip::File.new(EMPTY_FILENAME, create: true) zf.get_output_stream('myFile') { |os| os.write 'myFile contains just this' } zf.mkdir('dir1') zf.comment = comment @@ -184,7 +170,7 @@ def test_open_buffer_without_block end def test_cleans_up_tempfiles_after_close - zf = ::Zip::File.new(EMPTY_FILENAME, ::Zip::File::CREATE) + zf = ::Zip::File.new(EMPTY_FILENAME, create: true) zf.get_output_stream('myFile') do |os| @tempfile_path = os.path os.write 'myFile contains just this' @@ -208,9 +194,7 @@ def test_add_different_compression sizes = [] files.each do |name, comp| - zf = ::Zip::File.new( - name, ::Zip::File::CREATE, false, { compression_level: comp } - ) + zf = ::Zip::File.new(name, create: true, compression_level: comp) zf.add(entry_name, src_file) zf.close @@ -243,7 +227,7 @@ def test_add_different_compression_as_default files.each do |name, comp| ::Zip.default_compression = comp - zf = ::Zip::File.new(name, ::Zip::File::CREATE) + zf = ::Zip::File.new(name, create: true) zf.add(entry_name, src_file) zf.close @@ -268,7 +252,7 @@ def test_add_stored src_file = 'test/data/file2.txt' entry_name = 'newEntryName.rb' assert(::File.exist?(src_file)) - zf = ::Zip::File.new(EMPTY_FILENAME, ::Zip::File::CREATE) + zf = ::Zip::File.new(EMPTY_FILENAME, create: true) zf.add_stored(entry_name, src_file) zf.close @@ -294,7 +278,7 @@ def test_recover_permissions_after_add_files_to_archive ::File.chmod(0o664, src_zip) assert_equal(0o100664, ::File.stat(src_zip).mode) - zf = ::Zip::File.new(src_zip, ::Zip::File::CREATE) + zf = ::Zip::File.new(src_zip, create: true) zf.add('newEntryName.rb', 'test/data/file2.txt') zf.close @@ -385,7 +369,7 @@ def test_rename_with_each ::File.unlink(zf_name) if ::File.exist?(zf_name) arr = [] arr_renamed = [] - ::Zip::File.open(zf_name, ::Zip::File::CREATE) do |zf| + ::Zip::File.open(zf_name, create: true) do |zf| zf.mkdir('test') arr << 'test/' arr_renamed << 'Ztest/' @@ -398,7 +382,7 @@ def test_rename_with_each zf = ::Zip::File.open(zf_name) assert_equal(zf.entries.map(&:name), arr) zf.close - Zip::File.open(zf_name, 'wb') do |z| + Zip::File.open(zf_name) do |z| z.each do |f| z.rename(f, "Z#{f.name}") end @@ -522,7 +506,7 @@ def test_commit def test_double_commit(filename = 'test/data/generated/double_commit_test.zip') ::FileUtils.touch('test/data/generated/test_double_commit1.txt') ::FileUtils.touch('test/data/generated/test_double_commit2.txt') - zf = ::Zip::File.open(filename, ::Zip::File::CREATE) + zf = ::Zip::File.open(filename, create: true) zf.add('test1.txt', 'test/data/generated/test_double_commit1.txt') zf.commit zf.add('test2.txt', 'test/data/generated/test_double_commit2.txt') @@ -695,7 +679,7 @@ def test_preserve_file_order def test_streaming fname = ::File.join(__dir__, '..', 'README.md') zname = 'test/data/generated/README.zip' - Zip::File.open(zname, Zip::File::CREATE) do |zipfile| + Zip::File.open(zname, create: true) do |zipfile| zipfile.get_output_stream(File.basename(fname)) do |f| f.puts File.read(fname) end diff --git a/test/unicode_file_names_and_comments_test.rb b/test/unicode_file_names_and_comments_test.rb index ceac94fa..bc843321 100644 --- a/test/unicode_file_names_and_comments_test.rb +++ b/test/unicode_file_names_and_comments_test.rb @@ -53,7 +53,7 @@ def test_unicode_file_name def test_unicode_comment str = '渠道升级' - ::Zip::File.open(FILENAME, Zip::File::CREATE) do |z| + ::Zip::File.open(FILENAME, create: true) do |z| z.comment = str end diff --git a/test/zip64_full_test.rb b/test/zip64_full_test.rb index 5d174e3b..0837f733 100644 --- a/test/zip64_full_test.rb +++ b/test/zip64_full_test.rb @@ -21,7 +21,7 @@ def test_large_zip_file last_text = 'this tests files starting after 4GB in the archive' comment_text = 'this is a file comment in a zip64 archive' - ::Zip::File.open(HUGE_ZIP, ::Zip::File::CREATE) do |zf| + ::Zip::File.open(HUGE_ZIP, create: true) do |zf| zf.comment = comment_text zf.get_output_stream('first_file.txt') do |io| From debc9fda91e767cdaa40dea9321dab13211c5df1 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sun, 13 Jun 2021 10:18:15 +0100 Subject: [PATCH 179/311] Use named parameters for `File::split`. --- .rubocop_todo.yml | 7 ------- lib/zip/file_split.rb | 6 +++--- test/file_split_test.rb | 4 +++- 3 files changed, 6 insertions(+), 11 deletions(-) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index a08030fa..1afa96b1 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -104,13 +104,6 @@ Style/NumericPredicate: - 'lib/zip/ioextras.rb' - 'lib/zip/ioextras/abstract_input_stream.rb' -# Offense count: 1 -# Configuration parameters: AllowedMethods. -# AllowedMethods: respond_to_missing? -Style/OptionalBooleanParameter: - Exclude: - - 'lib/zip/file_split.rb' - # Offense count: 17 # Cop supports --auto-correct. # Configuration parameters: ConvertCodeThatCanStartToReturnNil, AllowedMethods. diff --git a/lib/zip/file_split.rb b/lib/zip/file_split.rb index 1b6a456c..606d2e6b 100644 --- a/lib/zip/file_split.rb +++ b/lib/zip/file_split.rb @@ -67,8 +67,8 @@ def save_splited_part( # Splits an archive into parts with segment size def split( - zip_file_name, segment_size = MAX_SEGMENT_SIZE, - delete_zip_file = true, partial_zip_file_name = nil + zip_file_name, segment_size: MAX_SEGMENT_SIZE, + delete_original: true, partial_zip_file_name: nil ) raise Error, "File #{zip_file_name} not found" unless ::File.exist?(zip_file_name) raise Errno::ENOENT, zip_file_name unless ::File.readable?(zip_file_name) @@ -90,7 +90,7 @@ def split( ) end end - ::File.delete(zip_file_name) if delete_zip_file + ::File.delete(zip_file_name) if delete_original szip_file_index end end diff --git a/test/file_split_test.rb b/test/file_split_test.rb index 517723dc..0e54cc99 100644 --- a/test/file_split_test.rb +++ b/test/file_split_test.rb @@ -27,7 +27,9 @@ def test_split_method_respond end def test_split - result = ::Zip::File.split(TEST_ZIP.zip_name, 65_536, false) + result = ::Zip::File.split( + TEST_ZIP.zip_name, segment_size: 65_536, delete_original: false + ) return if result.nil? From f75eb615789490df85e2b35cb2c7fc9b07718cd9 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Mon, 14 Jun 2021 07:43:34 +0100 Subject: [PATCH 180/311] Use named parameters for `File#get_output_stream`. --- lib/zip/file.rb | 10 +++---- lib/zip/filesystem/zip_file_name_mapper.rb | 2 +- test/file_test.rb | 32 ++++++++++++++-------- test/zip64_full_test.rb | 2 +- 4 files changed, 27 insertions(+), 19 deletions(-) diff --git a/lib/zip/file.rb b/lib/zip/file.rb index 5623b4ea..576639b5 100644 --- a/lib/zip/file.rb +++ b/lib/zip/file.rb @@ -185,10 +185,10 @@ def get_input_stream(entry, &a_proc) # specified. If a block is passed the stream object is passed to the block and # the stream is automatically closed afterwards just as with ruby's builtin # File.open method. - def get_output_stream(entry, permission_int = nil, comment = nil, - extra = nil, compressed_size = nil, crc = nil, - compression_method = nil, compression_level = nil, - size = nil, time = nil, &a_proc) + def get_output_stream(entry, permissions: nil, comment: nil, + extra: nil, compressed_size: nil, crc: nil, + compression_method: nil, compression_level: nil, + size: nil, time: nil, &a_proc) new_entry = if entry.kind_of?(Entry) @@ -205,7 +205,7 @@ def get_output_stream(entry, permission_int = nil, comment = nil, raise ArgumentError, "cannot open stream to directory entry - '#{new_entry}'" end - new_entry.unix_perms = permission_int + new_entry.unix_perms = permissions zip_streamable_entry = StreamableStream.new(new_entry) @entry_set << zip_streamable_entry zip_streamable_entry.get_output_stream(&a_proc) diff --git a/lib/zip/filesystem/zip_file_name_mapper.rb b/lib/zip/filesystem/zip_file_name_mapper.rb index aa699011..6b9c7f51 100644 --- a/lib/zip/filesystem/zip_file_name_mapper.rb +++ b/lib/zip/filesystem/zip_file_name_mapper.rb @@ -28,7 +28,7 @@ def get_input_stream(filename, &a_proc) def get_output_stream(filename, permissions = nil, &a_proc) @zip_file.get_output_stream( - expand_to_entry(filename), permissions, &a_proc + expand_to_entry(filename), permissions: permissions, &a_proc ) end diff --git a/test/file_test.rb b/test/file_test.rb index 9ac2f698..c183c26d 100644 --- a/test/file_test.rb +++ b/test/file_test.rb @@ -68,22 +68,30 @@ def test_get_output_stream assert_equal(count + 1, zf.size) assert_equal('Putting stuff in data/generated/empty.txt', zf.read('test/data/generated/empty.txt')) - custom_entry_args = [ - TEST_COMMENT, TEST_EXTRA, TEST_COMPRESSED_SIZE, TEST_CRC, - ::Zip::Entry::STORED, ::Zlib::BEST_SPEED, TEST_SIZE, TEST_TIME - ] - zf.get_output_stream('entry_with_custom_args.txt', nil, *custom_entry_args) do |os| + custom_entry_args = { + comment: TEST_COMMENT, compressed_size: TEST_COMPRESSED_SIZE, + crc: TEST_CRC, compression_method: ::Zip::COMPRESSION_METHOD_STORE, + compression_level: ::Zlib::BEST_SPEED, size: TEST_SIZE, time: TEST_TIME + } + zf.get_output_stream( + 'entry_with_custom_args.txt', **custom_entry_args + ) do |os| os.write 'Some data' end + assert_equal(count + 2, zf.size) entry = zf.get_entry('entry_with_custom_args.txt') - assert_equal(custom_entry_args[0], entry.comment) - assert_equal(custom_entry_args[2], entry.compressed_size) - assert_equal(custom_entry_args[3], entry.crc) - assert_equal(custom_entry_args[4], entry.compression_method) - assert_equal(custom_entry_args[5], entry.compression_level) - assert_equal(custom_entry_args[6], entry.size) - assert_equal(custom_entry_args[7], entry.time) + assert_equal(custom_entry_args[:comment], entry.comment) + assert_equal(custom_entry_args[:compressed_size], entry.compressed_size) + assert_equal(custom_entry_args[:crc], entry.crc) + assert_equal( + custom_entry_args[:compression_method], entry.compression_method + ) + assert_equal( + custom_entry_args[:compression_level], entry.compression_level + ) + assert_equal(custom_entry_args[:size], entry.size) + assert_equal(custom_entry_args[:time], entry.time) zf.get_output_stream('entry.bin') do |os| os.write(::File.open('test/data/generated/5entry.zip', 'rb').read) diff --git a/test/zip64_full_test.rb b/test/zip64_full_test.rb index 0837f733..7a4e0911 100644 --- a/test/zip64_full_test.rb +++ b/test/zip64_full_test.rb @@ -31,7 +31,7 @@ def test_large_zip_file # Write just over 4GB (stored, so the zip file exceeds 4GB). buf = 'blah' * 16_384 zf.get_output_stream( - 'huge_file', nil, nil, nil, nil, nil, ::Zip::Entry::STORED + 'huge_file', compression_method: ::Zip::COMPRESSION_METHOD_STORE ) do |io| 65_537.times { io.write(buf) } end From aa646ef827f2e819c075a31f83659ac6b09be6b1 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Fri, 18 Jun 2021 14:34:41 +0100 Subject: [PATCH 181/311] Use named params for `InputStream`. --- lib/zip/entry.rb | 2 +- lib/zip/input_stream.rb | 10 +++++----- test/encryption_test.rb | 14 +++++++++++--- test/stored_support_test.rb | 4 +++- 4 files changed, 20 insertions(+), 10 deletions(-) diff --git a/lib/zip/entry.rb b/lib/zip/entry.rb index 11ddb851..83ced75e 100644 --- a/lib/zip/entry.rb +++ b/lib/zip/entry.rb @@ -577,7 +577,7 @@ def get_input_stream(&block) raise "unknown @file_type #{@ftype}" end else - zis = ::Zip::InputStream.new(@zipfile, local_header_offset) + zis = ::Zip::InputStream.new(@zipfile, offset: local_header_offset) zis.instance_variable_set(:@complete_entry, self) zis.get_next_entry if block diff --git a/lib/zip/input_stream.rb b/lib/zip/input_stream.rb index 1eb4080f..00b1d99b 100644 --- a/lib/zip/input_stream.rb +++ b/lib/zip/input_stream.rb @@ -51,7 +51,7 @@ class InputStream # # @param context [String||IO||StringIO] file path or IO/StringIO object # @param offset [Integer] offset in the IO/StringIO - def initialize(context, offset = 0, decrypter = nil) + def initialize(context, offset: 0, decrypter: nil) super() @archive_io = get_io(context, offset) @decompressor = ::Zip::NullDecompressor @@ -99,8 +99,8 @@ class << self # Same as #initialize but if a block is passed the opened # stream is passed to the block and closed when the block # returns. - def open(filename_or_io, offset = 0, decrypter = nil) - zio = new(filename_or_io, offset, decrypter) + def open(filename_or_io, offset: 0, decrypter: nil) + zio = new(filename_or_io, offset: offset, decrypter: decrypter) return zio unless block_given? begin @@ -110,9 +110,9 @@ def open(filename_or_io, offset = 0, decrypter = nil) end end - def open_buffer(filename_or_io, offset = 0) + def open_buffer(filename_or_io, offset: 0) warn 'open_buffer is deprecated!!! Use open instead!' - ::Zip::InputStream.open(filename_or_io, offset) + ::Zip::InputStream.open(filename_or_io, offset: offset) end end diff --git a/test/encryption_test.rb b/test/encryption_test.rb index 150aea04..74fe2ecf 100644 --- a/test/encryption_test.rb +++ b/test/encryption_test.rb @@ -28,7 +28,9 @@ def test_encrypt out.write content end - Zip::InputStream.open(encrypted_zip, 0, Zip::TraditionalDecrypter.new(password)) do |zis| + Zip::InputStream.open( + encrypted_zip, decrypter: Zip::TraditionalDecrypter.new(password) + ) do |zis| entry = zis.get_next_entry assert_equal test_filename, entry.name assert_equal 1327, entry.size @@ -36,7 +38,10 @@ def test_encrypt end assert_raises(Zip::DecompressionError) do - Zip::InputStream.open(encrypted_zip, 0, Zip::TraditionalDecrypter.new("#{password}wrong")) do |zis| + Zip::InputStream.open( + encrypted_zip, + decrypter: Zip::TraditionalDecrypter.new("#{password}wrong") + ) do |zis| zis.get_next_entry assert_equal content, zis.read end @@ -44,7 +49,10 @@ def test_encrypt end def test_decrypt - Zip::InputStream.open(ENCRYPT_ZIP_TEST_FILE, 0, Zip::TraditionalDecrypter.new('password')) do |zis| + Zip::InputStream.open( + ENCRYPT_ZIP_TEST_FILE, + decrypter: Zip::TraditionalDecrypter.new('password') + ) do |zis| entry = zis.get_next_entry assert_equal 'file1.txt', entry.name assert_equal 1327, entry.size diff --git a/test/stored_support_test.rb b/test/stored_support_test.rb index 1a125916..16f1bed2 100644 --- a/test/stored_support_test.rb +++ b/test/stored_support_test.rb @@ -22,7 +22,9 @@ def test_read end def test_encrypted_read - Zip::InputStream.open(ENCRYPTED_STORED_ZIP_TEST_FILE, 0, Zip::TraditionalDecrypter.new('password')) do |zis| + Zip::InputStream.open( + ENCRYPTED_STORED_ZIP_TEST_FILE, decrypter: Zip::TraditionalDecrypter.new('password') + ) do |zis| entry = zis.get_next_entry assert_equal 'file1.txt', entry.name assert_equal 1_327, entry.size From 9fc12bf97b029c9219d6390651f6312c592c9dda Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sun, 20 Jun 2021 10:28:48 +0100 Subject: [PATCH 182/311] Add notes to the README and Changelog about the new API. --- Changelog.md | 1 + README.md | 12 +++++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index 011d560a..ecabf38c 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,5 +1,6 @@ # 3.0.0 (Next) +- Use named parameters for optional arguments in the public API. - Raise an error if entry names exceed 65,535 characters. [#247](https://github.com/rubyzip/rubyzip/issues/247) - Remove the `ZipXError` v1 legacy classes. - Raise an error on reading a split archive with `InputStream`. [#349](https://github.com/rubyzip/rubyzip/issues/349) diff --git a/README.md b/README.md index 4b66ec20..9fa7d5bb 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,17 @@ Rubyzip is a ruby library for reading and writing zip files. -## Important note +## Important notes + +### Version 3.0 + +The public API of some classes has been modernized to use named parameters for optional arguments. Please check your usage of the following Rubyzip classes: +* `File` +* `Entry` +* `InputStream` +* `OutputStream` + +### Older versions (pre 2.0) The Rubyzip interface has changed!!! No need to do `require "zip/zip"` and `Zip` prefix in class names removed. From 1fb74bd82f35b84c0fd4b7fbabbc2b97695fc973 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sun, 27 Jun 2021 11:04:53 +0100 Subject: [PATCH 183/311] Don't mess with the library path in the gemspec. --- rubyzip.gemspec | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/rubyzip.gemspec b/rubyzip.gemspec index 295601a0..c0793e4a 100644 --- a/rubyzip.gemspec +++ b/rubyzip.gemspec @@ -1,8 +1,6 @@ # frozen_string_literal: true -lib = File.expand_path('lib', __dir__) -$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) -require 'zip/version' +require_relative 'lib/zip/version' Gem::Specification.new do |s| s.name = 'rubyzip' From 81d95ad0a3b5973beaccab9f443537a5a814266d Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sun, 27 Jun 2021 11:18:24 +0100 Subject: [PATCH 184/311] Minor gemspec formatting changes for space/readability. --- .rubocop.yml | 3 ++- rubyzip.gemspec | 25 ++++++++++++++----------- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/.rubocop.yml b/.rubocop.yml index d3db8664..c6d2a51d 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -48,9 +48,10 @@ Metrics/AbcSize: Exclude: - 'test/**/*.rb' -# Turn block length metrics off for the tests. +# Turn block length metrics off for the tests and gemspec. Metrics/BlockLength: Exclude: + - 'rubyzip.gemspec' - 'test/**/*.rb' # Turn class length metrics off for the tests. diff --git a/rubyzip.gemspec b/rubyzip.gemspec index c0793e4a..5f2da555 100644 --- a/rubyzip.gemspec +++ b/rubyzip.gemspec @@ -3,24 +3,27 @@ require_relative 'lib/zip/version' Gem::Specification.new do |s| - s.name = 'rubyzip' - s.version = ::Zip::VERSION - s.authors = ['Alexander Simonov'] - s.email = ['alex@simonov.me'] - s.homepage = 'http://github.com/rubyzip/rubyzip' - s.platform = Gem::Platform::RUBY - s.summary = 'rubyzip is a ruby module for reading and writing zip files' - s.files = Dir.glob('{samples,lib}/**/*.rb') + %w[README.md TODO Rakefile] - s.require_paths = ['lib'] - s.license = 'BSD-2-Clause' - s.metadata = { + s.name = 'rubyzip' + s.version = ::Zip::VERSION + s.authors = ['Alexander Simonov'] + s.email = ['alex@simonov.me'] + s.homepage = 'http://github.com/rubyzip/rubyzip' + s.platform = Gem::Platform::RUBY + s.summary = 'rubyzip is a ruby module for reading and writing zip files' + s.files = Dir.glob('{samples,lib}/**/*.rb') + %w[README.md TODO Rakefile] + s.require_paths = ['lib'] + s.license = 'BSD-2-Clause' + + s.metadata = { 'bug_tracker_uri' => 'https://github.com/rubyzip/rubyzip/issues', 'changelog_uri' => "https://github.com/rubyzip/rubyzip/blob/v#{s.version}/Changelog.md", 'documentation_uri' => "https://www.rubydoc.info/gems/rubyzip/#{s.version}", 'source_code_uri' => "https://github.com/rubyzip/rubyzip/tree/v#{s.version}", 'wiki_uri' => 'https://github.com/rubyzip/rubyzip/wiki' } + s.required_ruby_version = '>= 2.4' + s.add_development_dependency 'minitest', '~> 5.4' s.add_development_dependency 'rake', '~> 12.3.3' s.add_development_dependency 'rubocop', '~> 1.12.0' From f005ca28645d838a01dd6993db4b04d93b60fd90 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sun, 27 Jun 2021 11:30:09 +0100 Subject: [PATCH 185/311] Update the list of files packaged in the gemspec. --- rubyzip.gemspec | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/rubyzip.gemspec b/rubyzip.gemspec index 5f2da555..15a50350 100644 --- a/rubyzip.gemspec +++ b/rubyzip.gemspec @@ -10,7 +10,8 @@ Gem::Specification.new do |s| s.homepage = 'http://github.com/rubyzip/rubyzip' s.platform = Gem::Platform::RUBY s.summary = 'rubyzip is a ruby module for reading and writing zip files' - s.files = Dir.glob('{samples,lib}/**/*.rb') + %w[README.md TODO Rakefile] + s.files = Dir.glob('{samples,lib}/**/*.rb') + + %w[README.md Changelog.md Rakefile rubyzip.gemspec] s.require_paths = ['lib'] s.license = 'BSD-2-Clause' From bb237aaa085d1b734d2e9e0720fb077a7b7be7f5 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sun, 27 Jun 2021 11:47:31 +0100 Subject: [PATCH 186/311] Update authors in gemspec to reflect current maintainers. --- rubyzip.gemspec | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/rubyzip.gemspec b/rubyzip.gemspec index 15a50350..1a913c03 100644 --- a/rubyzip.gemspec +++ b/rubyzip.gemspec @@ -5,8 +5,10 @@ require_relative 'lib/zip/version' Gem::Specification.new do |s| s.name = 'rubyzip' s.version = ::Zip::VERSION - s.authors = ['Alexander Simonov'] - s.email = ['alex@simonov.me'] + s.authors = ['Robert Haines', 'John Lees-Miller', 'Alexander Simonov'] + s.email = [ + 'hainesr@gmail.com', 'jdleesmiller@gmail.com', 'alex@simonov.me' + ] s.homepage = 'http://github.com/rubyzip/rubyzip' s.platform = Gem::Platform::RUBY s.summary = 'rubyzip is a ruby module for reading and writing zip files' From 50dddca0be55fc40c84470a2d7cba9ab81e33f6b Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sun, 27 Jun 2021 15:30:06 +0100 Subject: [PATCH 187/311] Update encrypted fixtures to remove data descriptors. --- test/data/zipWithEncryption.zip | Bin 612 -> 9596 bytes .../zipWithStoredCompressionAndEncryption.zip | Bin 42931 -> 42899 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/test/data/zipWithEncryption.zip b/test/data/zipWithEncryption.zip index e102b875b651a4cf13aa623003b155f50ede1084..08215fcb5ad86ee4d74b2bdef5d3cea557dad25f 100644 GIT binary patch literal 9596 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 g-{r`_{zL9R8JPb|4if5LcftOtoPYWV`Mz(Tt z_HRm8cs;%5R$jJt@O}-GAi;ot&v&*yiCF!%JH zH%)uhQ}^BomPnV9_@g_AXG6@KNghJZ`G>g_H`$$Rn6%G&`NjsZLuxvu4}X3+zNL1e z^W+W1(~2)vD^2w)sh_Bs|LE|c*-;Anrfn#iWhA!Cr)7$Cwx9DK;i&%;{1?nWch_An z@X7At;H6Hvucl-iDlM4sdB*I;Q>q0geArXTvwZrc?ugQ#a_?}f9=rKle69MT2a{UZP$Kpco9*sq{@4B$@7KldNyr0{Li>< zL;U(U+3xD(C8dG?FV^-Yb@+yB7?<7st@UE%e7%mJ{<=p4-^j3LEt&8{@YVD<^ZYgW z9d1q@55I}}eQwm|7vK?RGTzF--*v>2XOm6!=SN-sA)R}R*H~&v3MN{V_E=qD=M&%0;!y5{SJu$5ktm*&<4c(ZdLM}B}eBa;a;?t}qM8w|j7!my+f X#DXW20B=?{kN_hPngeNZCJ+w*rLGJO diff --git a/test/data/zipWithStoredCompressionAndEncryption.zip b/test/data/zipWithStoredCompressionAndEncryption.zip index 2fd545e977663a1caddcfd851accb3c6baeb5252..f2d9c1632ce16f094523e202e1120c77b8ef5a23 100644 GIT binary patch literal 42899 zcmV)BK*PUKO9KQ7009610G*h8PrU7cr#l4z051gq00{sb0A^`yWic*vcyv`%2>=61 z4B%amqWoQTcnbgl1n2_*00ig*0065t1T9z^6mU>-kx^>r-dSuBQj@)*o%=Ye@tNw7 z7JjS;tt=c6m>m)1ykdqO$6I97jFNod);?&kc@iqYlXUgC$bRK5@=yPm{kEWbv+1EK z904HYt^4*c!{$Fi!3}<^x(VyCEs=?YIs;$m_p&6LXdOk0;SQDCjp$R4OaIWapq4&* zwS&PV9Yd3sQiK0gk38cnA=8$owiyY~+`E3Jj|BK`< zwgwoLn1D3eoxCPMX?Ae7&lBg6=2%0Ao(Z7F!p5!*$Czh&?@{dse<9EL)Md5pRPW^X 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 literal 42931 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�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 z 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 From 8071290ce67967dc40ffc4fc6ea965f78710fc68 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sun, 27 Jun 2021 15:56:39 +0100 Subject: [PATCH 188/311] Update and tidy up encryption tests. --- test/encryption_test.rb | 9 +++++++-- test/stored_support_test.rb | 3 ++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/test/encryption_test.rb b/test/encryption_test.rb index 74fe2ecf..bed3fe68 100644 --- a/test/encryption_test.rb +++ b/test/encryption_test.rb @@ -5,6 +5,7 @@ class EncryptionTest < MiniTest::Test ENCRYPT_ZIP_TEST_FILE = 'test/data/zipWithEncryption.zip' INPUT_FILE1 = 'test/data/file1.txt' + INPUT_FILE2 = 'test/data/file2.txt' def setup Zip.default_compression = ::Zlib::DEFAULT_COMPRESSION @@ -33,7 +34,7 @@ def test_encrypt ) do |zis| entry = zis.get_next_entry assert_equal test_filename, entry.name - assert_equal 1327, entry.size + assert_equal 1_327, entry.size assert_equal content, zis.read end @@ -55,8 +56,12 @@ def test_decrypt ) do |zis| entry = zis.get_next_entry assert_equal 'file1.txt', entry.name - assert_equal 1327, entry.size + assert_equal 1_327, entry.size assert_equal ::File.open(INPUT_FILE1, 'r').read, zis.read + entry = zis.get_next_entry + assert_equal 'file2.txt', entry.name + assert_equal 41_234, entry.size + assert_equal ::File.open(INPUT_FILE2, 'r').read, zis.read end end end diff --git a/test/stored_support_test.rb b/test/stored_support_test.rb index 16f1bed2..4e0cdad6 100644 --- a/test/stored_support_test.rb +++ b/test/stored_support_test.rb @@ -4,7 +4,8 @@ class StoredSupportTest < MiniTest::Test STORED_ZIP_TEST_FILE = 'test/data/zipWithStoredCompression.zip' - ENCRYPTED_STORED_ZIP_TEST_FILE = 'test/data/zipWithStoredCompressionAndEncryption.zip' + ENCRYPTED_STORED_ZIP_TEST_FILE = + 'test/data/zipWithStoredCompressionAndEncryption.zip' INPUT_FILE1 = 'test/data/file1.txt' INPUT_FILE2 = 'test/data/file2.txt' From 19e5f4a8ce726b1498eb0f34f778f33708216145 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sun, 27 Jun 2021 21:43:03 +0100 Subject: [PATCH 189/311] Detect and raise GPFBit3Error in `InputStream.get_next_entry`. We were previously trying to work out where the next entry would be, even with GP bit 3 set, but the logic was flaky and cannot really be correct given the data available. It's not expected behaviour, so raise the error instead. This means that we get rid of the incorrect `Entry.data_descriptor_size` which was doing more harm than good. --- lib/zip/entry.rb | 6 +----- lib/zip/input_stream.rb | 12 +++++++++++- test/data/gpbit3stored.zip | Bin 132 -> 9604 bytes test/file_test.rb | 6 +++++- 4 files changed, 17 insertions(+), 7 deletions(-) diff --git a/lib/zip/entry.rb b/lib/zip/entry.rb index 83ced75e..e87f3362 100644 --- a/lib/zip/entry.rb +++ b/lib/zip/entry.rb @@ -215,7 +215,7 @@ def cdir_header_size #:nodoc:all end def next_header_offset #:nodoc:all - local_entry_offset + compressed_size + data_descriptor_size + local_entry_offset + compressed_size end # Extracts entry to file dest_path (defaults to @name). @@ -723,10 +723,6 @@ def parse_zip64_extra(for_local_header) #:nodoc:all end end - def data_descriptor_size - (@gp_flags & 0x0008) > 0 ? 16 : 0 - end - # For DEFLATED compression *only*: set the general purpose flags 1 and 2 to # indicate compression level. This seems to be mainly cosmetic but they are # generally set by other tools - including in docx files. It is these flags diff --git a/lib/zip/input_stream.rb b/lib/zip/input_stream.rb index 00b1d99b..5a9c289f 100644 --- a/lib/zip/input_stream.rb +++ b/lib/zip/input_stream.rb @@ -69,7 +69,17 @@ def close # the first entry in the archive. Returns nil when there are # no more entries. def get_next_entry - @archive_io.seek(@current_entry.next_header_offset, IO::SEEK_SET) if @current_entry + unless @current_entry.nil? + if @current_entry.incomplete? + raise GPFBit3Error, + 'It is not possible to get complete info from the local ' \ + 'header to extract this entry (GP flags bit 3 is set). ' \ + 'Please use `Zip::File` instead of `Zip::InputStream`.' + end + + @archive_io.seek(@current_entry.next_header_offset, IO::SEEK_SET) + end + open_entry end diff --git a/test/data/gpbit3stored.zip b/test/data/gpbit3stored.zip index 3c73eeb3bc4564638443ea75dcc6fcf46cc856bd..036424af85f5732d0efe68c6a08ac562d66af1c7 100644 GIT binary patch literal 9604 zcmai)V{j#2m+s@_#I|kQww>W$kgW>#--`tr3=9B92xe(xZ^6dw?(MFn2>^raQ!Fuz zR$eyt@J0XwhdO}-1N+Y{LUYa$#EKcP^%2X!g#zM!36{$k&S?@aR)VTXkFptME7SF` z$0YFBT%Os1LPW?eXdO8wyb|ITedsh0krvT@^=(X8R>9lai?avQ?r^|AoZwymHpuGG zFVYFqx_)_;c0$%Mv;!?l&)htg_Tq=OWa<%;IU}ll|P&IfY7=()Murhy8ZYwsrsDf9`@Qb?dvgUtN-_i;^$ir zuuM`Oi0f4B6|#;i&;#HUp#YYWMmD?CJe+C#J|4Iv$b00+5Gpz^0{YHA;^kqTM;y1~ ztIJ63qHz}5x8_MTL#_yo!mFCzKH2vWNJ_-;Smm`t&;lg6IQ z&WXkL85N^ntO?^W^;5`Qn{*DBk_@I_t(ucLg}|TU4Iy>-C!~+XOBueJyF7D+HT;r# zb-Hn%Ej0r5sUNQ#wVw3E;X@BA@*za&|J5ewMgLLVFe5+3qEosG8%ER)N{1dDiK%-ro-t8DwHIZ?D58YJ^zk#3M~}QJ@CCZ zJgQBI-W)Qm*S%oIJ_)-ViR1JUgfAZ9d=pI_I%!dV-TnP}8kg26yh@|jP?#Yv{?!GC zAx~`jmmIz^>TRL?=FOxOU3!0&yzpS=rd^#6F>TlzKOAt&o?~fP%AWsfBabX?llii5 zk7~fu(8yTy=?`~#faCf$v)||Jgc9`lo~{k&vlB{vYC0%qp&>%CEcZ@^Xf5v#Q3Ia6 zV2=+QjQB3tkYdwr-^c}5Ouz~U_6Q%PnAzeO%=i*K4@@yD;*;@R zvPzgUV$LF=8SRcxciK9=bhgVnm5DZ;r&n4U!`i-Kh7+DXmL5WLL_Xz=^U|!Ms z2_&+vQ?i|1ayHoOeSLC1*9~4s$DAsQ%FGVIfyX$Uo}h06=2Tq9^Him2y*{EBcs72 z^$HtPQsH+p4Z~MN@chYZ>14DB*Esu`P)$RRYxYUVVK`a#tnbfGB+}FOu6$#V4V_SemN`L%zGe!RQ95ggz)6_FR#mpbgUOdkaS`T61^vT^8g$qOeTE1d-=ycPwbtlncGg+lZVt zE&l#o-#rCK7YO^5-LVqRo)5>SMOIEUdX8dIK?c`Ov~aqv3wk&+UJ*&39fAFYcaI5@ zJDA}j>zi3{EN2oynns2#|Jb@YctEkv?ya8Rs1897%g~KC$s?5P6S0OVqsAhae#^md z5D14l�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 literal 132 zcmWIWW@Zs#;9y{22(4-M0a9?l4rHa}=j)YJlmIEN0B?4V6{$vbEI?rp4)A7V5@AMY fMV13;g@G-NAQsf10B=?{5SI}MO@Xu}h{FH?7~2w0 diff --git a/test/file_test.rb b/test/file_test.rb index c183c26d..6daf97d6 100644 --- a/test/file_test.rb +++ b/test/file_test.rb @@ -45,7 +45,11 @@ def test_create_from_scratch def test_get_input_stream_stored_with_gpflag_bit3 ::Zip::File.open('test/data/gpbit3stored.zip') do |zf| - assert_equal("foo\n", zf.read('foo.txt')) + zis = zf.get_input_stream('file1.txt') + assert_raises(::Zip::GPFBit3Error) do + zis.get_next_entry + end + zf.get_input_stream('file2.txt') end end From 66527ae10d86762b803964e3e6928f4ec7f7d962 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sun, 27 Jun 2021 21:55:15 +0100 Subject: [PATCH 190/311] Fix minor typo in `GPFBit3Error` message in `InputStream`. --- lib/zip/input_stream.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/zip/input_stream.rb b/lib/zip/input_stream.rb index 5a9c289f..66ffdd3e 100644 --- a/lib/zip/input_stream.rb +++ b/lib/zip/input_stream.rb @@ -153,7 +153,7 @@ def open_entry && !@complete_entry raise GPFBit3Error, 'It is not possible to get complete info from the local ' \ - 'header to extract this entry (GP flags bit 3 is set).' \ + 'header to extract this entry (GP flags bit 3 is set). ' \ 'Please use `Zip::File` instead of `Zip::InputStream`.' end From 322955c6b4cdd0f0dad336a5df4ddbd340372cfa Mon Sep 17 00:00:00 2001 From: Yo Yehudi Date: Sun, 27 Jun 2021 16:05:17 +0100 Subject: [PATCH 191/311] add research notice so that contributors will be aware metrics are being gathered on this repo over the next year. If there's a better place to put this please let me know. :) --- README.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/README.md b/README.md index 9fa7d5bb..f95a825e 100644 --- a/README.md +++ b/README.md @@ -413,3 +413,14 @@ See https://github.com/rubyzip/rubyzip/graphs/contributors for a comprehensive l Rubyzip is distributed under the same license as ruby. See http://www.ruby-lang.org/en/LICENSE.txt + +## Research notice +Please note that this repository is participating in a study into sustainability + of open source projects. Data will be gathered about this repository for + approximately the next 12 months, starting from June 2021. + +Data collected will include number of contributors, number of PRs, time taken to + close/merge these PRs, and issues closed. + +For more information, please visit +[our informational page](https://sustainable-open-science-and-software.github.io/) or download our [participant information sheet](https://sustainable-open-science-and-software.github.io/assets/PIS_sustainable_software.pdf). From 54b7762c8f481e03b733ff053750cddc7ccf13ac Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Wed, 30 Jun 2021 23:18:59 +0100 Subject: [PATCH 192/311] Don't silently alter zip files opened with `Zip::sort_entries`. Fixes #329. --- Changelog.md | 1 + README.md | 6 +++--- lib/zip/entry_set.rb | 6 ++---- test/entry_set_test.rb | 14 ++++++++++++++ 4 files changed, 20 insertions(+), 7 deletions(-) diff --git a/Changelog.md b/Changelog.md index ecabf38c..03971378 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,5 +1,6 @@ # 3.0.0 (Next) +- Don't silently alter zip files opened with `Zip::sort_entries`. [#329](https://github.com/rubyzip/rubyzip/issues/329) - Use named parameters for optional arguments in the public API. - Raise an error if entry names exceed 65,535 characters. [#247](https://github.com/rubyzip/rubyzip/issues/247) - Remove the `ZipXError` v1 legacy classes. diff --git a/README.md b/README.md index f95a825e..e5ddf6f2 100644 --- a/README.md +++ b/README.md @@ -132,9 +132,9 @@ class ZipFileGenerator end ``` -### Save zip archive entries in sorted by name state +### Save zip archive entries sorted by name -To save zip archives in sorted order like below, you need to set `::Zip.sort_entries` to `true` +To save zip archives with their entries sorted by name (see below), set `::Zip.sort_entries` to `true` ``` Vegetable/ @@ -148,7 +148,7 @@ fruit/mango fruit/orange ``` -After this, entries in the zip archive will be saved in ordered state. +Opening an existing zip file with this option set will not change the order of the entries automatically. Altering the zip file - adding an entry, renaming an entry, adding or changing the archive comment, etc - will cause the ordering to be applied when closing the file. ### Default permissions of zip archives diff --git a/lib/zip/entry_set.rb b/lib/zip/entry_set.rb index 530ef7ef..a771dc7f 100644 --- a/lib/zip/entry_set.rb +++ b/lib/zip/entry_set.rb @@ -35,10 +35,8 @@ def delete(entry) entry if @entry_set.delete(to_key(entry)) end - def each - @entry_set = sorted_entries.dup.each do |_, value| - yield(value) - end + def each(&block) + entries.each(&block) end def entries diff --git a/test/entry_set_test.rb b/test/entry_set_test.rb index fa91b77d..42e93681 100644 --- a/test/entry_set_test.rb +++ b/test/entry_set_test.rb @@ -62,9 +62,15 @@ def test_each count = 0 @zip_entry_set.each do |entry| assert(ZIP_ENTRIES.include?(entry)) + refute(entry.dirty) + entry.dirty = true # Check that entries can be changed in this block. count += 1 end + assert_equal(ZIP_ENTRIES.size, count) + @zip_entry_set.each do |entry| + assert(entry.dirty) + end end def test_entries @@ -101,6 +107,14 @@ def test_entries_sorted_in_each arr << entry end assert_equal(ZIP_ENTRIES.sort, arr) + + # Ensure `each` above hasn't permanently altered the ordering. + ::Zip.sort_entries = false + arr = [] + @zip_entry_set.each do |entry| + arr << entry + end + assert_equal(ZIP_ENTRIES, arr) end def test_compound From c3b1e5d69370ccba494c68387da78e2e3411bd21 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sat, 3 Jul 2021 13:45:22 +0100 Subject: [PATCH 193/311] Pick changes from v2.3.1. --- Changelog.md | 4 ++++ lib/zip.rb | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/Changelog.md b/Changelog.md index 03971378..f1dd25f8 100644 --- a/Changelog.md +++ b/Changelog.md @@ -38,6 +38,10 @@ Tooling/internal: - Update rubocop again and run it in CI. [#444](https://github.com/rubyzip/rubyzip/pull/444) - Fix a test that was incorrect on big-endian architectures. [#445](https://github.com/rubyzip/rubyzip/pull/445) +# 2.3.1 (2021-07-03) + +- A "dummy" release to warn about breaking changes coming in version 3.0. + # 2.3.0 (2020-03-14) - Fix frozen string literal error [#431](https://github.com/rubyzip/rubyzip/pull/431) diff --git a/lib/zip.rb b/lib/zip.rb index 87a236a8..98d5c8f4 100644 --- a/lib/zip.rb +++ b/lib/zip.rb @@ -36,6 +36,8 @@ require 'zip/errors' module Zip + BANNER = [].freeze + extend self attr_accessor :unicode_names, :on_exists_proc, @@ -55,6 +57,8 @@ module Zip }.freeze def reset! + warn BANNER.join("\n") unless BANNER.empty? + @_ran_once = false @unicode_names = false @on_exists_proc = false From bc6523ec43e911d7931aafbda2a81dae68617a90 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Mon, 5 Jul 2021 22:22:01 +0100 Subject: [PATCH 194/311] Unpick changes from v2.3.1. --- Changelog.md | 4 ++++ lib/zip.rb | 4 ---- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Changelog.md b/Changelog.md index f1dd25f8..84b45492 100644 --- a/Changelog.md +++ b/Changelog.md @@ -38,6 +38,10 @@ Tooling/internal: - Update rubocop again and run it in CI. [#444](https://github.com/rubyzip/rubyzip/pull/444) - Fix a test that was incorrect on big-endian architectures. [#445](https://github.com/rubyzip/rubyzip/pull/445) +# 2.3.2 (2021-07-05) + +- A "dummy" release to warn about breaking changes coming in version 3.0. This updated version uses the Gem `post_install_message` instead of printing to `STDERR`. + # 2.3.1 (2021-07-03) - A "dummy" release to warn about breaking changes coming in version 3.0. diff --git a/lib/zip.rb b/lib/zip.rb index 98d5c8f4..87a236a8 100644 --- a/lib/zip.rb +++ b/lib/zip.rb @@ -36,8 +36,6 @@ require 'zip/errors' module Zip - BANNER = [].freeze - extend self attr_accessor :unicode_names, :on_exists_proc, @@ -57,8 +55,6 @@ module Zip }.freeze def reset! - warn BANNER.join("\n") unless BANNER.empty? - @_ran_once = false @unicode_names = false @on_exists_proc = false From f7cd692e15d80addc7abeb7bfde643e1649a1390 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Fri, 19 Nov 2021 19:35:36 +0000 Subject: [PATCH 195/311] Fix reading zip files with max length file comment. If a zip file has a comment that is 65,535 characters long - which is a valid length and the maximum allowable length - the initial read of the archive fails to find the End of Central Directory Record and therefore cannot read the rest of the file. This commit fixes this by making sure that we look far enough back into the file from the end to find the EoCDR. Test added to catch regressions. Fixes #508. --- lib/zip/central_directory.rb | 2 +- test/data/max_length_file_comment.zip | Bin 0 -> 71396 bytes test/file_test.rb | 5 +++++ 3 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 test/data/max_length_file_comment.zip diff --git a/lib/zip/central_directory.rb b/lib/zip/central_directory.rb index 51d72b37..29fb2d0d 100644 --- a/lib/zip/central_directory.rb +++ b/lib/zip/central_directory.rb @@ -7,9 +7,9 @@ class CentralDirectory END_OF_CDS = 0x06054b50 ZIP64_END_OF_CDS = 0x06064b50 ZIP64_EOCD_LOCATOR = 0x07064b50 - MAX_END_OF_CDS_SIZE = 65_536 + 18 STATIC_EOCD_SIZE = 22 ZIP64_STATIC_EOCD_SIZE = 56 + MAX_END_OF_CDS_SIZE = 65_535 + STATIC_EOCD_SIZE attr_reader :comment diff --git a/test/data/max_length_file_comment.zip b/test/data/max_length_file_comment.zip new file mode 100644 index 0000000000000000000000000000000000000000..dc39d04ad831580547a1f53a3413994398e6150f GIT binary patch literal 71396 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 literal 0 HcmV?d00001 diff --git a/test/file_test.rb b/test/file_test.rb index 6daf97d6..6d8bc95b 100644 --- a/test/file_test.rb +++ b/test/file_test.rb @@ -181,6 +181,11 @@ def test_open_buffer_without_block assert zf.entries.map(&:name).include?('zippedruby1.rb') end + def test_open_file_with_max_length_comment + # Should not raise any errors. + Zip::File.open('test/data/max_length_file_comment.zip') + end + def test_cleans_up_tempfiles_after_close zf = ::Zip::File.new(EMPTY_FILENAME, create: true) zf.get_output_stream('myFile') do |os| From 765cb316f15a542a8eb7393fc49a5e9d0801e55d Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Tue, 16 Nov 2021 21:42:11 +0000 Subject: [PATCH 196/311] Fix reading unknown extra fields. When loading extra fields from both the central directory and local headers, unknown fields were not merged correctly. They were being appended, which means that we end up with the two versions stuck together - in some cases duplicating the field completely. This broke all kinds of things (like calculating the size of a local header) in subtle ways. This commit fixes this by implementing a new `Unknown` extra field type, and making sure that when reading local and central extra fields they are stored and preserved correctly. We cannot assume the unknown fields use the same data in the local and central headers. Fixes #505. --- .rubocop_todo.yml | 1 + lib/zip/central_directory.rb | 2 +- lib/zip/entry.rb | 8 ++--- lib/zip/extra_field.rb | 31 ++++++++----------- lib/zip/extra_field/unknown.rb | 33 +++++++++++++++++++++ test/extra_field_test.rb | 34 +++++++++++++++++---- test/extra_field_unknown_test.rb | 51 ++++++++++++++++++++++++++++++++ test/local_entry_test.rb | 12 ++++++-- 8 files changed, 139 insertions(+), 33 deletions(-) create mode 100644 lib/zip/extra_field/unknown.rb create mode 100644 test/extra_field_unknown_test.rb diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 1afa96b1..b5466f40 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -61,6 +61,7 @@ Style/ClassAndModuleChildren: - 'lib/zip/extra_field/old_unix.rb' - 'lib/zip/extra_field/universal_time.rb' - 'lib/zip/extra_field/unix.rb' + - 'lib/zip/extra_field/unknown.rb' - 'lib/zip/extra_field/zip64.rb' - 'lib/zip/extra_field/zip64_placeholder.rb' diff --git a/lib/zip/central_directory.rb b/lib/zip/central_directory.rb index 29fb2d0d..4979ea6d 100644 --- a/lib/zip/central_directory.rb +++ b/lib/zip/central_directory.rb @@ -174,7 +174,7 @@ def read_central_directory_entries(io) #:nodoc: unless offset.nil? io_save = io.tell io.seek(offset, IO::SEEK_SET) - entry.read_extra_field(read_local_extra_field(io)) + entry.read_extra_field(read_local_extra_field(io), local: true) io.seek(io_save, IO::SEEK_SET) end diff --git a/lib/zip/entry.rb b/lib/zip/entry.rb index e87f3362..d1241e56 100644 --- a/lib/zip/entry.rb +++ b/lib/zip/entry.rb @@ -313,7 +313,7 @@ def read_local_entry(io) #:nodoc:all raise ::Zip::Error, 'Truncated local zip entry header' end - read_extra_field(extra) + read_extra_field(extra, local: true) parse_zip64_extra(true) @local_header_size = calculate_local_header_size end @@ -417,11 +417,11 @@ def check_c_dir_entry_comment_size raise ::Zip::Error, 'Truncated cdir zip entry header' end - def read_extra_field(buf) + def read_extra_field(buf, local: false) if @extra.kind_of?(::Zip::ExtraField) - @extra.merge(buf) if buf + @extra.merge(buf, local: local) if buf else - @extra = ::Zip::ExtraField.new(buf) + @extra = ::Zip::ExtraField.new(buf, local: local) end end diff --git a/lib/zip/extra_field.rb b/lib/zip/extra_field.rb index 24352aae..658675b4 100644 --- a/lib/zip/extra_field.rb +++ b/lib/zip/extra_field.rb @@ -4,8 +4,8 @@ module Zip class ExtraField < Hash ID_MAP = {} - def initialize(binstr = nil) - merge(binstr) if binstr + def initialize(binstr = nil, local: false) + merge(binstr, local: local) if binstr end def extra_field_type_exist(binstr, id, len, index) @@ -18,25 +18,18 @@ def extra_field_type_exist(binstr, id, len, index) end end - def extra_field_type_unknown(binstr, len, index) - create_unknown_item unless self['Unknown'] + def extra_field_type_unknown(binstr, len, index, local) + self['Unknown'] ||= Unknown.new + if !len || len + 4 > binstr[index..-1].bytesize - self['Unknown'] << binstr[index..-1] + self['Unknown'].merge(binstr[index..-1], local: local) return end - self['Unknown'] << binstr[index, len + 4] - end - def create_unknown_item - s = +'' - class << s - alias_method :to_c_dir_bin, :to_s - alias_method :to_local_bin, :to_s - end - self['Unknown'] = s + self['Unknown'].merge(binstr[index, len + 4], local: local) end - def merge(binstr) + def merge(binstr, local: false) return if binstr.empty? i = 0 @@ -46,8 +39,7 @@ def merge(binstr) if id && ID_MAP.member?(id) extra_field_type_exist(binstr, id, len, i) elsif id - create_unknown_item unless self['Unknown'] - break unless extra_field_type_unknown(binstr, len, i) + break unless extra_field_type_unknown(binstr, len, i, local) end i += len + 4 end @@ -61,8 +53,8 @@ def create(name) self[name] = field_class.new end - # place Unknown last, so "extra" data that is missing the proper signature/size - # does not prevent known fields from being read back in + # Place Unknown last, so "extra" data that is missing the proper + # signature/size does not prevent known fields from being read back in. def ordered_values result = [] each { |k, v| k == 'Unknown' ? result.push(v) : result.unshift(v) } @@ -92,6 +84,7 @@ def local_size end end +require 'zip/extra_field/unknown' require 'zip/extra_field/generic' require 'zip/extra_field/universal_time' require 'zip/extra_field/old_unix' diff --git a/lib/zip/extra_field/unknown.rb b/lib/zip/extra_field/unknown.rb new file mode 100644 index 00000000..84e87f34 --- /dev/null +++ b/lib/zip/extra_field/unknown.rb @@ -0,0 +1,33 @@ +# frozen_string_literal: true + +module Zip + # A class to hold unknown extra fields so that they are preserved. + class ExtraField::Unknown + def initialize + @local_bin = +'' + @cdir_bin = +'' + end + + def merge(binstr, local: false) + return if binstr.empty? + + if local + @local_bin << binstr + else + @cdir_bin << binstr + end + end + + def to_local_bin + @local_bin + end + + def to_c_dir_bin + @cdir_bin + end + + def ==(other) + @local_bin == other.to_local_bin && @cdir_bin == other.to_c_dir_bin + end + end +end diff --git a/test/extra_field_test.rb b/test/extra_field_test.rb index 7aea91e5..c91a7bc6 100644 --- a/test/extra_field_test.rb +++ b/test/extra_field_test.rb @@ -6,17 +6,29 @@ class ZipExtraFieldTest < MiniTest::Test def test_new extra_pure = ::Zip::ExtraField.new('') extra_withstr = ::Zip::ExtraField.new('foo') + extra_withstr_local = ::Zip::ExtraField.new('foo', local: true) + assert_instance_of(::Zip::ExtraField, extra_pure) assert_instance_of(::Zip::ExtraField, extra_withstr) + assert_instance_of(::Zip::ExtraField, extra_withstr_local) + + assert_equal('foo', extra_withstr['Unknown'].to_c_dir_bin) + assert_equal('foo', extra_withstr_local['Unknown'].to_local_bin) end def test_unknownfield extra = ::Zip::ExtraField.new('foo') - assert_equal(extra['Unknown'], 'foo') + assert_equal('foo', extra['Unknown'].to_c_dir_bin) + extra.merge('a') - assert_equal(extra['Unknown'], 'fooa') + assert_equal('fooa', extra['Unknown'].to_c_dir_bin) + extra.merge('barbaz') - assert_equal(extra.to_s, 'fooabarbaz') + assert_equal('fooabarbaz', extra['Unknown'].to_c_dir_bin) + + extra.merge('bar', local: true) + assert_equal('bar', extra['Unknown'].to_local_bin) + assert_equal('fooabarbaz', extra['Unknown'].to_c_dir_bin) end def test_bad_header_id @@ -66,9 +78,9 @@ def test_to_s extra = ::Zip::ExtraField.new(str) assert_instance_of(String, extra.to_s) - s = extra.to_s - extra.merge('foo') - assert_equal(s.length + 3, extra.to_s.length) + extra_len = extra.to_s.length + extra.merge('foo', local: true) + assert_equal(extra_len + 3, extra.to_s.length) end def test_equality @@ -99,4 +111,14 @@ def test_read_local_extra_field end end end + + def test_load_unknown_extra_field + ::Zip::File.open('test/data/osx-archive.zip') do |zf| + zf.each do |entry| + # Check that there is only one occurance of the 'ux' extra field. + assert_equal(0, entry.extra['Unknown'].to_c_dir_bin.rindex('ux')) + assert_equal(0, entry.extra['Unknown'].to_local_bin.rindex('ux')) + end + end + end end diff --git a/test/extra_field_unknown_test.rb b/test/extra_field_unknown_test.rb new file mode 100644 index 00000000..4d24d928 --- /dev/null +++ b/test/extra_field_unknown_test.rb @@ -0,0 +1,51 @@ +# frozen_string_literal: true + +require 'test_helper' + +class ZipExtraFieldUnknownTest < MiniTest::Test + def test_new + extra = ::Zip::ExtraField::Unknown.new + assert_empty(extra.to_c_dir_bin) + assert_empty(extra.to_local_bin) + end + + def test_merge_cdir_then_local + extra = ::Zip::ExtraField::Unknown.new + field = "ux\v\x00\x01\x04\xF6\x01\x00\x00\x04\x14\x00\x00\x00" + + extra.merge(field) + assert_empty(extra.to_local_bin) + assert_equal(field, extra.to_c_dir_bin) + + extra.merge(field, local: true) + assert_equal(field, extra.to_local_bin) + assert_equal(field, extra.to_c_dir_bin) + end + + def test_merge_local_only + extra = ::Zip::ExtraField::Unknown.new + field = "ux\v\x00\x01\x04\xF6\x01\x00\x00\x04\x14\x00\x00\x00" + + extra.merge(field, local: true) + assert_equal(field, extra.to_local_bin) + assert_empty(extra.to_c_dir_bin) + end + + def test_equality + extra1 = ::Zip::ExtraField::Unknown.new + extra2 = ::Zip::ExtraField::Unknown.new + assert_equal(extra1, extra2) + + extra1.merge("ux\v\x00\x01\x04\xF6\x01\x00\x00\x04\x14\x00\x00\x00") + refute_equal(extra1, extra2) + + extra2.merge("ux\v\x00\x01\x04\xF6\x01\x00\x00\x04\x14\x00\x00\x00") + assert_equal(extra1, extra2) + + extra1.merge('foo', local: true) + refute_equal(extra1, extra2) + + extra2.merge('foo', local: true) + assert_equal(extra1, extra2) + end +end diff --git a/test/local_entry_test.rb b/test/local_entry_test.rb index 15c367a7..3cf21e62 100644 --- a/test/local_entry_test.rb +++ b/test/local_entry_test.rb @@ -83,6 +83,7 @@ def test_write_entry_with_zip64 'file.zip', 'entry_name', comment: 'my little comment', size: 400, extra: 'thisIsSomeExtraInformation', compressed_size: 100, crc: 987_654 ) + entry.extra.merge('thisIsSomeExtraInformation', local: true) write_to_file(LEH_FILE, CEH_FILE, entry) local_entry, central_entry = read_from_file(LEH_FILE, CEH_FILE) @@ -153,18 +154,23 @@ def test_read64_local_offset private - def compare_local_entry_headers(entry1, entry2) + def compare_common_entry_headers(entry1, entry2) assert_equal(entry1.compressed_size, entry2.compressed_size) assert_equal(entry1.crc, entry2.crc) - assert_equal(entry1.extra, entry2.extra) assert_equal(entry1.compression_method, entry2.compression_method) assert_equal(entry1.name, entry2.name) assert_equal(entry1.size, entry2.size) assert_equal(entry1.local_header_offset, entry2.local_header_offset) end + def compare_local_entry_headers(entry1, entry2) + compare_common_entry_headers(entry1, entry2) + assert_equal(entry1.extra.to_local_bin, entry2.extra.to_local_bin) + end + def compare_c_dir_entry_headers(entry1, entry2) - compare_local_entry_headers(entry1, entry2) + compare_common_entry_headers(entry1, entry2) + assert_equal(entry1.extra.to_c_dir_bin, entry2.extra.to_c_dir_bin) assert_equal(entry1.comment, entry2.comment) end From 6a516fb0b1045b655ad63c4acaf29c7603708fff Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sat, 20 Nov 2021 10:36:32 +0000 Subject: [PATCH 197/311] Factor out reading EOCD records. This allows for reading the EOCD records without then automatically reading all of the entry data as well, so that we can do other things faster, like provide the number of entries in an archive. --- lib/zip/central_directory.rb | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/zip/central_directory.rb b/lib/zip/central_directory.rb index 4979ea6d..d7d0cf25 100644 --- a/lib/zip/central_directory.rb +++ b/lib/zip/central_directory.rb @@ -194,10 +194,14 @@ def read_local_extra_field(io) end def read_from_stream(io) #:nodoc: + read_eocds(io) + read_central_directory_entries(io) + end + + def read_eocds(io) #:nodoc: buf = start_buf(io) unpack_64_e_o_c_d(buf) if zip64_file?(buf) unpack_e_o_c_d(buf) - read_central_directory_entries(io) end def zip64_file?(buf) From 3db1eff1e32a6bc7014ad95801f1dfe595040938 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sat, 20 Nov 2021 10:50:55 +0000 Subject: [PATCH 198/311] Add `CentralDirectory#count_entries`. This method gets the number of entries from a zip archive without loading all of the individual entries. --- lib/zip/central_directory.rb | 9 +++++++++ test/central_directory_test.rb | 18 ++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/lib/zip/central_directory.rb b/lib/zip/central_directory.rb index d7d0cf25..e45fc030 100644 --- a/lib/zip/central_directory.rb +++ b/lib/zip/central_directory.rb @@ -228,6 +228,15 @@ def size @entry_set.size end + # Reads the End of Central Directory Record (and the Zip64 equivalent if + # needs be) and returns the number of entries in the archive. This is a + # convenience method that avoids reading in all of the entry data to get a + # very quick entry count. + def count_entries(io) + read_eocds(io) + @size + end + def self.read_from_stream(io) #:nodoc: cdir = new cdir.read_from_stream(io) diff --git a/test/central_directory_test.rb b/test/central_directory_test.rb index afe2156e..97b5c482 100644 --- a/test/central_directory_test.rb +++ b/test/central_directory_test.rb @@ -44,6 +44,24 @@ def test_read_eocd_with_wrong_cdir_offset_from_buffer end end + def test_count_entries + [ + ['test/data/osx-archive.zip', 4], + ['test/data/zip64-sample.zip', 2], + ['test/data/max_length_file_comment.zip', 1] + ].each do |filename, num_entries| + cdir = ::Zip::CentralDirectory.new + + ::File.open(filename, 'rb') do |f| + assert_equal(num_entries, cdir.count_entries(f)) + + f.seek(0) + s = StringIO.new(f.read) + assert_equal(num_entries, cdir.count_entries(s)) + end + end + end + def test_write_to_stream entries = [ ::Zip::Entry.new( From 22e47641e61aac923b1e7bcb3fb653808000b006 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sat, 20 Nov 2021 10:53:00 +0000 Subject: [PATCH 199/311] Add `File::count_entries`. This method provides a short cut to finding out how many entries are in an archive by reading this number directly from the central directory, and not iterating through the entire set of entries. --- lib/zip/file.rb | 14 ++++++++++++++ test/file_test.rb | 18 ++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/lib/zip/file.rb b/lib/zip/file.rb index 576639b5..dfe9758b 100644 --- a/lib/zip/file.rb +++ b/lib/zip/file.rb @@ -171,6 +171,20 @@ def foreach(zip_file_name, &block) zip_file.each(&block) end end + + # Count the entries in a zip archive without reading the whole set of + # entry data into memory. + def count_entries(path_or_io) + cdir = ::Zip::CentralDirectory.new + + if path_or_io.kind_of?(String) + ::File.open(path_or_io, 'rb') do |f| + cdir.count_entries(f) + end + else + cdir.count_entries(path_or_io) + end + end end # Returns an input stream to the specified entry. If a block is passed diff --git a/test/file_test.rb b/test/file_test.rb index 6d8bc95b..13e98705 100644 --- a/test/file_test.rb +++ b/test/file_test.rb @@ -186,6 +186,24 @@ def test_open_file_with_max_length_comment Zip::File.open('test/data/max_length_file_comment.zip') end + def test_count_entries + [ + ['test/data/osx-archive.zip', 4], + ['test/data/zip64-sample.zip', 2], + ['test/data/max_length_file_comment.zip', 1] + ].each do |filename, num_entries| + assert_equal(num_entries, ::Zip::File.count_entries(filename)) + + ::File.open(filename, 'rb') do |f| + assert_equal(num_entries, ::Zip::File.count_entries(f)) + + f.seek(0) + s = StringIO.new(f.read) + assert_equal(num_entries, ::Zip::File.count_entries(s)) + end + end + end + def test_cleans_up_tempfiles_after_close zf = ::Zip::File.new(EMPTY_FILENAME, create: true) zf.get_output_stream('myFile') do |os| From f5e19db2736ab3daab29e09f3638386b989c1b48 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sat, 20 Nov 2021 20:02:47 +0000 Subject: [PATCH 200/311] Add a 100,000 file zip to test `count_entries`. --- test/central_directory_test.rb | 3 ++- test/data/100000-files.zip | Bin 0 -> 9377888 bytes test/file_test.rb | 3 ++- 3 files changed, 4 insertions(+), 2 deletions(-) create mode 100644 test/data/100000-files.zip diff --git a/test/central_directory_test.rb b/test/central_directory_test.rb index 97b5c482..88258caa 100644 --- a/test/central_directory_test.rb +++ b/test/central_directory_test.rb @@ -48,7 +48,8 @@ def test_count_entries [ ['test/data/osx-archive.zip', 4], ['test/data/zip64-sample.zip', 2], - ['test/data/max_length_file_comment.zip', 1] + ['test/data/max_length_file_comment.zip', 1], + ['test/data/100000-files.zip', 100_000] ].each do |filename, num_entries| cdir = ::Zip::CentralDirectory.new diff --git a/test/data/100000-files.zip b/test/data/100000-files.zip new file mode 100644 index 0000000000000000000000000000000000000000..ee3751ba3c69d08bc28e316c349fa4019db8c8bf GIT binary patch literal 9377888 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 B Date: Tue, 30 Nov 2021 22:22:37 +0000 Subject: [PATCH 201/311] Ensure `File.open_buffer` doesn't rewrite unchanged data. --- .rubocop_todo.yml | 2 +- lib/zip/file.rb | 3 +++ test/file_test.rb | 26 ++++++++++++++++++++++++-- 3 files changed, 28 insertions(+), 3 deletions(-) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index b5466f40..4406c97d 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -30,7 +30,7 @@ Metrics/CyclomaticComplexity: # Offense count: 47 # Configuration parameters: CountComments, CountAsOne, ExcludedMethods, IgnoredMethods. Metrics/MethodLength: - Max: 32 + Max: 34 # Offense count: 5 # Configuration parameters: CountKeywordArgs. diff --git a/lib/zip/file.rb b/lib/zip/file.rb index dfe9758b..ddb5e736 100644 --- a/lib/zip/file.rb +++ b/lib/zip/file.rb @@ -90,6 +90,7 @@ def initialize(path_or_io, create: false, buffer: false, **options) end elsif buffer && path_or_io.size > 0 # This zip is probably a non-empty StringIO. + @create = false read_from_stream(path_or_io) elsif @create # This zip is completely new/empty and is to be created. @@ -311,6 +312,8 @@ def commit # Write buffer write changes to buffer and return def write_buffer(io = ::StringIO.new) + return unless commit_required? + ::Zip::OutputStream.write_buffer(io) do |zos| @entry_set.each { |e| e.write_to_zip_output_stream(zos) } zos.comment = comment diff --git a/test/file_test.rb b/test/file_test.rb index cf3619fc..d08778aa 100644 --- a/test/file_test.rb +++ b/test/file_test.rb @@ -111,17 +111,27 @@ def test_get_output_stream end def test_open_buffer_with_string - string = File.read('test/data/rubycode.zip', mode: 'rb') + data = File.read('test/data/rubycode.zip', mode: 'rb') + string = data.dup + ::Zip::File.open_buffer string do |zf| assert zf.entries.map(&:name).include?('zippedruby1.rb') end + + # Ensure the buffer hasn't changed. + assert_equal(data, string) end def test_open_buffer_with_stringio - string_io = StringIO.new File.read('test/data/rubycode.zip', mode: 'rb') + data = File.read('test/data/rubycode.zip', mode: 'rb') + string_io = StringIO.new(data.dup) + ::Zip::File.open_buffer string_io do |zf| assert zf.entries.map(&:name).include?('zippedruby1.rb') end + + # Ensure the buffer hasn't changed. + assert_equal(data, string_io.string) end def test_close_buffer_with_stringio @@ -181,6 +191,18 @@ def test_open_buffer_without_block assert zf.entries.map(&:name).include?('zippedruby1.rb') end + def test_open_buffer_without_block_write_buffer_does_nothing + data = File.read('test/data/rubycode.zip', mode: 'rb') + string_io = StringIO.new(data.dup) + + zf = ::Zip::File.open_buffer(string_io) + assert zf.entries.map(&:name).include?('zippedruby1.rb') + + # Ensure the buffer isn't changed. + zf.write_buffer(string_io) + assert_equal(data, string_io.string) + end + def test_open_file_with_max_length_comment # Should not raise any errors. Zip::File.open('test/data/max_length_file_comment.zip') From 1c33f2dd90f0fb97b2e63d4a124912b98b7c06e4 Mon Sep 17 00:00:00 2001 From: Taichi Ishitani Date: Wed, 29 Dec 2021 22:36:13 +0900 Subject: [PATCH 202/311] add Ruby 3.1 to CI --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 8b2abc0e..097b8aa8 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -8,7 +8,7 @@ jobs: fail-fast: false matrix: os: [ubuntu] - ruby: [2.4, 2.5, 2.6, 2.7, '3.0', head, jruby, jruby-head, truffleruby, truffleruby-head] + ruby: [2.4, 2.5, 2.6, 2.7, '3.0', '3.1', head, jruby, jruby-head, truffleruby, truffleruby-head] include: - os: macos ruby: 2.4 From 8f743d7f6853c12087ba702dd4064f2ff642f80b Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sun, 2 Jan 2022 13:09:06 +0000 Subject: [PATCH 203/311] Make ruby versions list in the CI consistent. Ruby version `3.0` must be quoted otherwise it's interpreted as `3`. Might as well make the rest in the list consistent. --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 097b8aa8..338de224 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -8,7 +8,7 @@ jobs: fail-fast: false matrix: os: [ubuntu] - ruby: [2.4, 2.5, 2.6, 2.7, '3.0', '3.1', head, jruby, jruby-head, truffleruby, truffleruby-head] + ruby: ['2.4', '2.5', '2.6', '2.7', '3.0', '3.1', head, jruby, jruby-head, truffleruby, truffleruby-head] include: - os: macos ruby: 2.4 From e04c9cdbd8ddcca239c4628380dee7dd36de873b Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sun, 2 Jan 2022 13:57:09 +0000 Subject: [PATCH 204/311] Update compatibility matrix in the README. --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index e5ddf6f2..f012e55d 100644 --- a/README.md +++ b/README.md @@ -360,12 +360,12 @@ You can set multiple settings at the same time by using a block: Rubyzip is known to run on a number of platforms and under a number of different Ruby versions. Please see the table below for what we think the current situation is. Note: an empty cell means "unknown", not "does not work". -| OS | 2.4 | 2.5 | 2.6 | 2.7 | 3.0 | Head | JRuby 9.2.17.0 | JRuby Head | Truffleruby 21.1.0 | Truffleruby Head | -|----|-----|-----|-----|-----|-----|------|----------------|------------|--------------------|------------------| -|Ubuntu 20.04| CI | CI | CI | CI | CI | ci | CI | ci | CI | ci | -|Mac OS 10.15.7| CI | x | x | x | x | | x | | x | | -|Windows 10| | | | x | | | | | | | -|Windows Server 2019| CI | | | | | | | | | | +| OS | 2.4 | 2.5 | 2.6 | 2.7 | 3.0 | 3.1 | Head | JRuby 9.2.17.0 | JRuby Head | Truffleruby 21.1.0 | Truffleruby Head | +|----|-----|-----|-----|-----|-----|-----|------|----------------|------------|--------------------|------------------| +|Ubuntu 20.04| CI | CI | CI | CI | CI | CI | ci | CI | ci | CI | ci | +|Mac OS 10.15.7| CI | x | x | x | x | x | | x | | x | | +|Windows 10| | | | x | | | | | | | | +|Windows Server 2019| CI | | | | | | | | | | | Key: `CI` - tested in CI, should work; `ci` - tested in CI, might fail; `x` - known working; `o` - known failing. From 099d379c82a3a6144e94a17021d8b39aed0977bc Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Mon, 10 Jan 2022 17:35:49 +0000 Subject: [PATCH 205/311] Add an extra test for YJIT in ruby 3.1. I tried adding this to the matrix, but I couldn't work out how to do this *and* keep a vanilla 3.1 test in the mix as well. It seems you can't add different `env`s with `include`, but maybe I missed something. --- .github/workflows/tests.yml | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 338de224..b613695c 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -44,6 +44,25 @@ jobs: flag-name: ${{ matrix.ruby }} parallel: true + test-yjit: + runs-on: ubuntu-latest + continue-on-error: true + steps: + - name: Checkout rubyzip code + uses: actions/checkout@v2 + + - name: Install and set up ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: '3.1' + bundler-cache: true + + - name: Run the tests + env: + RUBYOPT: --enable-yjit -v + FULL_ZIP64_TEST: 1 + run: bundle exec rake + finish: needs: test runs-on: ubuntu-latest From 9bed9d053972f8afde49600dbc1921da8ebe38ea Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Tue, 11 Jan 2022 13:02:00 +0000 Subject: [PATCH 206/311] Expand the YJIT tests into a mini matrix. Test 3.1 and head on Ubuntu and MacOS. --- .github/workflows/tests.yml | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index b613695c..b4180a14 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -45,7 +45,12 @@ jobs: parallel: true test-yjit: - runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + os: [ubuntu, macos] + ruby: ['3.1', head] + runs-on: ${{ matrix.os }}-latest continue-on-error: true steps: - name: Checkout rubyzip code @@ -54,7 +59,7 @@ jobs: - name: Install and set up ruby uses: ruby/setup-ruby@v1 with: - ruby-version: '3.1' + ruby-version: ${{ matrix.ruby }} bundler-cache: true - name: Run the tests From 3fbc5e62f9f9d2274d85526cb9794370d695af52 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Tue, 11 Jan 2022 13:15:48 +0000 Subject: [PATCH 207/311] Add the YJIT tests to the README matrix. --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index f012e55d..12165d5e 100644 --- a/README.md +++ b/README.md @@ -360,12 +360,12 @@ You can set multiple settings at the same time by using a block: Rubyzip is known to run on a number of platforms and under a number of different Ruby versions. Please see the table below for what we think the current situation is. Note: an empty cell means "unknown", not "does not work". -| OS | 2.4 | 2.5 | 2.6 | 2.7 | 3.0 | 3.1 | Head | JRuby 9.2.17.0 | JRuby Head | Truffleruby 21.1.0 | Truffleruby Head | -|----|-----|-----|-----|-----|-----|-----|------|----------------|------------|--------------------|------------------| -|Ubuntu 20.04| CI | CI | CI | CI | CI | CI | ci | CI | ci | CI | ci | -|Mac OS 10.15.7| CI | x | x | x | x | x | | x | | x | | -|Windows 10| | | | x | | | | | | | | -|Windows Server 2019| CI | | | | | | | | | | | +| OS | 2.4 | 2.5 | 2.6 | 2.7 | 3.0 | 3.1 | 3.1 +YJIT | Head | Head +YJIT | JRuby 9.2.17.0 | JRuby Head | Truffleruby 21.1.0 | Truffleruby Head | +|----|-----|-----|-----|-----|-----|-----|----------|------|-----------|----------------|------------|--------------------|------------------| +|Ubuntu 20.04| CI | CI | CI | CI | CI | CI | ci | ci | ci | CI | ci | CI | ci | +|Mac OS 10.15.7| CI | x | x | x | x | x | ci | | ci | x | | x | | +|Windows 10| | | | x | | | | | | | | | | +|Windows Server 2019| CI | | | | | | | | | | | | | Key: `CI` - tested in CI, should work; `ci` - tested in CI, might fail; `x` - known working; `o` - known failing. From 20952ef38f42b14b0324e3d24665c4fabb76f7f9 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Tue, 11 Jan 2022 13:19:31 +0000 Subject: [PATCH 208/311] Update version numbers in the README test matrix. --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 12165d5e..9e93cb42 100644 --- a/README.md +++ b/README.md @@ -360,10 +360,10 @@ You can set multiple settings at the same time by using a block: Rubyzip is known to run on a number of platforms and under a number of different Ruby versions. Please see the table below for what we think the current situation is. Note: an empty cell means "unknown", not "does not work". -| OS | 2.4 | 2.5 | 2.6 | 2.7 | 3.0 | 3.1 | 3.1 +YJIT | Head | Head +YJIT | JRuby 9.2.17.0 | JRuby Head | Truffleruby 21.1.0 | Truffleruby Head | +| OS | 2.4 | 2.5 | 2.6 | 2.7 | 3.0 | 3.1 | 3.1 +YJIT | Head | Head +YJIT | JRuby 9.3.2.0 | JRuby Head | Truffleruby 21.3.0 | Truffleruby Head | |----|-----|-----|-----|-----|-----|-----|----------|------|-----------|----------------|------------|--------------------|------------------| -|Ubuntu 20.04| CI | CI | CI | CI | CI | CI | ci | ci | ci | CI | ci | CI | ci | -|Mac OS 10.15.7| CI | x | x | x | x | x | ci | | ci | x | | x | | +|Ubuntu 20.04.3| CI | CI | CI | CI | CI | CI | ci | ci | ci | CI | ci | CI | ci | +|Mac OS 11.6.2| CI | x | x | x | x | x | ci | | ci | x | | x | | |Windows 10| | | | x | | | | | | | | | | |Windows Server 2019| CI | | | | | | | | | | | | | From 90728d71095cb03b1b4b2e2d5eb4ce0edd223d2d Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Tue, 11 Jan 2022 13:24:35 +0000 Subject: [PATCH 209/311] Add `-v` switch to ruby for all tests. --- .github/workflows/tests.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index b4180a14..d9e6147d 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -32,6 +32,7 @@ jobs: - name: Run the tests env: + RUBYOPT: -v JRUBY_OPTS: --debug FULL_ZIP64_TEST: 1 run: bundle exec rake From cf258bbb719f1c8dca4ad73a2eabb130f4c88864 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Tue, 11 Jan 2022 22:01:49 +0000 Subject: [PATCH 210/311] Move to ruby 2.5 as the earliest supported version. 2.4 is nearly two years beyond EOL now. Closes #484. --- .github/workflows/lint.yml | 2 +- .github/workflows/tests.yml | 6 +++--- .rubocop.yml | 2 +- .simplecov | 2 +- lib/zip/file.rb | 12 +++++------- rubyzip.gemspec | 2 +- test/filesystem/file_nonmutating_test.rb | 2 +- 7 files changed, 13 insertions(+), 15 deletions(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index dccec7f3..66c262fb 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -12,7 +12,7 @@ jobs: - name: Install and set up ruby uses: ruby/setup-ruby@v1 with: - ruby-version: 2.4 + ruby-version: '2.5' bundler-cache: true - name: Rubocop diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index d9e6147d..49fa1a55 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -8,12 +8,12 @@ jobs: fail-fast: false matrix: os: [ubuntu] - ruby: ['2.4', '2.5', '2.6', '2.7', '3.0', '3.1', head, jruby, jruby-head, truffleruby, truffleruby-head] + ruby: ['2.5', '2.6', '2.7', '3.0', '3.1', head, jruby, jruby-head, truffleruby, truffleruby-head] include: - os: macos - ruby: 2.4 + ruby: '2.5' - os: windows - ruby: 2.4 + ruby: '2.5' runs-on: ${{ matrix.os }}-latest continue-on-error: ${{ endsWith(matrix.ruby, 'head') || matrix.os == 'windows' }} steps: diff --git a/.rubocop.yml b/.rubocop.yml index c6d2a51d..1997fe39 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -7,7 +7,7 @@ inherit_from: .rubocop_todo.yml # Set this to the minimum supported ruby in the gemspec. Otherwise # we get errors if our ruby version doesn't match. AllCops: - TargetRubyVersion: 2.4 + TargetRubyVersion: 2.5 NewCops: enable # Allow this in this file because adding the extra lines is pointless. diff --git a/.simplecov b/.simplecov index 611a9527..91415dfd 100644 --- a/.simplecov +++ b/.simplecov @@ -17,6 +17,6 @@ SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter.new( ) SimpleCov.start do - # enable_coverage :branch <-- Re-enable this when we move to ruby ~> 2.5. + enable_coverage :branch add_filter ['/test/', '/samples/'] end diff --git a/lib/zip/file.rb b/lib/zip/file.rb index ddb5e736..661609b1 100644 --- a/lib/zip/file.rb +++ b/lib/zip/file.rb @@ -406,14 +406,12 @@ def check_file(path) def on_success_replace dirname, basename = ::File.split(name) ::Dir::Tmpname.create(basename, dirname) do |tmp_filename| - begin - if yield tmp_filename - ::File.rename(tmp_filename, name) - ::File.chmod(@file_permissions, name) unless @create - end - ensure - ::File.unlink(tmp_filename) if ::File.exist?(tmp_filename) + if yield tmp_filename + ::File.rename(tmp_filename, name) + ::File.chmod(@file_permissions, name) unless @create end + ensure + ::File.unlink(tmp_filename) if ::File.exist?(tmp_filename) end end end diff --git a/rubyzip.gemspec b/rubyzip.gemspec index 1a913c03..5287d0cd 100644 --- a/rubyzip.gemspec +++ b/rubyzip.gemspec @@ -25,7 +25,7 @@ Gem::Specification.new do |s| 'wiki_uri' => 'https://github.com/rubyzip/rubyzip/wiki' } - s.required_ruby_version = '>= 2.4' + s.required_ruby_version = '>= 2.5' s.add_development_dependency 'minitest', '~> 5.4' s.add_development_dependency 'rake', '~> 12.3.3' diff --git a/test/filesystem/file_nonmutating_test.rb b/test/filesystem/file_nonmutating_test.rb index d4007e6f..0e93b641 100644 --- a/test/filesystem/file_nonmutating_test.rb +++ b/test/filesystem/file_nonmutating_test.rb @@ -445,7 +445,7 @@ def test_glob '*/foo/**/*.txt' => ['globTest/foo/bar/baz/foo.txt'] }.each do |spec, expected_results| results = zf.glob(spec) - assert(results.all? { |entry| entry.kind_of? ::Zip::Entry }) + assert(results.all?(::Zip::Entry)) result_strings = results.map(&:to_s) missing_matches = expected_results - result_strings From 7f7c4ca194ab96caf966163b6d14f6e67eb1d386 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Tue, 11 Jan 2022 22:18:18 +0000 Subject: [PATCH 211/311] Update README after the move to Ruby >= 2.5. --- README.md | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 9e93cb42..4e9d8885 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,11 @@ gem 'zip-zip' # will load compatibility for old rubyzip API. ## Requirements -- Ruby 2.4 or greater (for rubyzip 2.0; use 1.x for older rubies) +Version 3.x requires at least Ruby 2.5. + +Version 2.x requires at least Ruby 2.4, and is know to work on Ruby 3.1. + +It is not recommended to use any versions of Rubyzip earlier than 2.3 due to security issues. ## Installation @@ -360,12 +364,12 @@ You can set multiple settings at the same time by using a block: Rubyzip is known to run on a number of platforms and under a number of different Ruby versions. Please see the table below for what we think the current situation is. Note: an empty cell means "unknown", not "does not work". -| OS | 2.4 | 2.5 | 2.6 | 2.7 | 3.0 | 3.1 | 3.1 +YJIT | Head | Head +YJIT | JRuby 9.3.2.0 | JRuby Head | Truffleruby 21.3.0 | Truffleruby Head | -|----|-----|-----|-----|-----|-----|-----|----------|------|-----------|----------------|------------|--------------------|------------------| -|Ubuntu 20.04.3| CI | CI | CI | CI | CI | CI | ci | ci | ci | CI | ci | CI | ci | -|Mac OS 11.6.2| CI | x | x | x | x | x | ci | | ci | x | | x | | -|Windows 10| | | | x | | | | | | | | | | -|Windows Server 2019| CI | | | | | | | | | | | | | +| OS/Ruby | 2.5 | 2.6 | 2.7 | 3.0 | 3.1 | 3.1 +YJIT | Head | Head +YJIT | JRuby 9.3.2.0 | JRuby Head | Truffleruby 21.3.0 | Truffleruby Head | +|---------|-----|-----|-----|-----|-----|----------|------|-----------|----------------|------------|--------------------|------------------| +|Ubuntu 20.04.3| CI | CI | CI | CI | CI | ci | ci | ci | CI | ci | CI | ci | +|Mac OS 11.6.2| CI | x | x | x | x | ci | | ci | x | | x | | +|Windows 10| | | x | | | | | | | | | | +|Windows Server 2019| CI | | | | | | | | | | | | Key: `CI` - tested in CI, should work; `ci` - tested in CI, might fail; `x` - known working; `o` - known failing. From b1b82bbd9eb0fbd651744dd0916f650c4420634b Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Tue, 11 Jan 2022 22:21:20 +0000 Subject: [PATCH 212/311] Tidy up updating notes in README. --- README.md | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/README.md b/README.md index 4e9d8885..d2c98da9 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ Rubyzip is a ruby library for reading and writing zip files. ## Important notes -### Version 3.0 +### Updating to version 3.0 The public API of some classes has been modernized to use named parameters for optional arguments. Please check your usage of the following Rubyzip classes: * `File` @@ -18,17 +18,6 @@ The public API of some classes has been modernized to use named parameters for o * `InputStream` * `OutputStream` -### Older versions (pre 2.0) - -The Rubyzip interface has changed!!! No need to do `require "zip/zip"` and `Zip` prefix in class names removed. - -If you have issues with any third-party gems that require an old version of rubyzip, you can use this workaround: - -```ruby -gem 'rubyzip', '>= 1.0.0' # will load new rubyzip version -gem 'zip-zip' # will load compatibility for old rubyzip API. -``` - ## Requirements Version 3.x requires at least Ruby 2.5. From f8b9d07022e23b2aca08b10fce4b90f92987a99f Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Tue, 11 Jan 2022 20:16:53 +0000 Subject: [PATCH 213/311] Round out EOCD data size constants in CDir. --- lib/zip/central_directory.rb | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/zip/central_directory.rb b/lib/zip/central_directory.rb index e45fc030..676f863b 100644 --- a/lib/zip/central_directory.rb +++ b/lib/zip/central_directory.rb @@ -7,9 +7,12 @@ class CentralDirectory END_OF_CDS = 0x06054b50 ZIP64_END_OF_CDS = 0x06064b50 ZIP64_EOCD_LOCATOR = 0x07064b50 + STATIC_EOCD_SIZE = 22 ZIP64_STATIC_EOCD_SIZE = 56 - MAX_END_OF_CDS_SIZE = 65_535 + STATIC_EOCD_SIZE + ZIP64_EOCD_LOC_SIZE = 20 + MAX_FILE_COMMENT_SIZE = 1 << 16 + MAX_END_OF_CDS_SIZE = MAX_FILE_COMMENT_SIZE + STATIC_EOCD_SIZE attr_reader :comment From 34731b18850b553f8aa42cb891941394a7cb0b77 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sat, 15 Jan 2022 13:10:54 +0000 Subject: [PATCH 214/311] `Zip::File` no longer subclasses `Zip::CentralDirectory`. It has bothered me for years that the central directory is exposed in this way. A zip file should *have* a central directory, but it should not *be* one. This commit starts us down the path of properly separating the two. --- lib/zip/central_directory.rb | 6 ++-- lib/zip/file.rb | 60 ++++++++++++++++++------------------ 2 files changed, 33 insertions(+), 33 deletions(-) diff --git a/lib/zip/central_directory.rb b/lib/zip/central_directory.rb index 676f863b..3d3cd1a3 100644 --- a/lib/zip/central_directory.rb +++ b/lib/zip/central_directory.rb @@ -2,8 +2,6 @@ module Zip class CentralDirectory - include Enumerable - END_OF_CDS = 0x06054b50 ZIP64_END_OF_CDS = 0x06064b50 ZIP64_EOCD_LOCATOR = 0x07064b50 @@ -14,7 +12,9 @@ class CentralDirectory MAX_FILE_COMMENT_SIZE = 1 << 16 MAX_END_OF_CDS_SIZE = MAX_FILE_COMMENT_SIZE + STATIC_EOCD_SIZE - attr_reader :comment + attr_accessor :comment + + attr_reader :entry_set # Returns an Enumerable containing the entries. def entries diff --git a/lib/zip/file.rb b/lib/zip/file.rb index 661609b1..109cd86a 100644 --- a/lib/zip/file.rb +++ b/lib/zip/file.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true +require 'forwardable' + require_relative 'file_split' module Zip @@ -45,8 +47,8 @@ module Zip # # ZipFileSystem offers an alternative API that emulates ruby's # interface for accessing the filesystem, ie. the File and Dir classes. - - class File < CentralDirectory + class File + extend Forwardable extend FileSplit IO_METHODS = [:tell, :seek, :read, :eof, :close].freeze @@ -62,8 +64,7 @@ class File < CentralDirectory # default -> true. attr_accessor :restore_times - # Returns the zip files comment, if it has one - attr_accessor :comment + def_delegators :@cdir, :comment, :comment=, :each, :entries, :size # Opens a zip archive. Pass create: true to create # a new archive if it doesn't exist already. @@ -73,8 +74,8 @@ def initialize(path_or_io, create: false, buffer: false, **options) .merge(compression_level: ::Zip.default_compression) .merge(options) @name = path_or_io.respond_to?(:path) ? path_or_io.path : path_or_io - @comment = '' @create = create ? true : false # allow any truthy value to mean true + @cdir = ::Zip::CentralDirectory.new if ::File.size?(@name.to_s) # There is a file, which exists, that is associated with this zip. @@ -82,29 +83,28 @@ def initialize(path_or_io, create: false, buffer: false, **options) @file_permissions = ::File.stat(@name).mode if buffer - read_from_stream(path_or_io) + @cdir.read_from_stream(path_or_io) else ::File.open(@name, 'rb') do |f| - read_from_stream(f) + @cdir.read_from_stream(f) end end elsif buffer && path_or_io.size > 0 # This zip is probably a non-empty StringIO. @create = false - read_from_stream(path_or_io) - elsif @create - # This zip is completely new/empty and is to be created. - @entry_set = EntrySet.new - elsif ::File.zero?(@name) - # A file exists, but it is empty. + @cdir.read_from_stream(path_or_io) + elsif !@create && ::File.zero?(@name) + # A file exists, but it is empty, and we've said we're + # NOT creating a new zip. raise Error, "File #{@name} has zero size. Did you mean to pass the create flag?" - else - # Everything is wrong. + elsif !@create + # If we get here, and we're not creating a new zip, then + # everything is wrong. raise Error, "File #{@name} not found" end - @stored_entries = @entry_set.dup - @stored_comment = @comment + @stored_entries = @cdir.entry_set.dup + @stored_comment = @cdir.comment @restore_ownership = options[:restore_ownership] @restore_permissions = options[:restore_permissions] @restore_times = options[:restore_times] @@ -222,7 +222,7 @@ def get_output_stream(entry, permissions: nil, comment: nil, end new_entry.unix_perms = permissions zip_streamable_entry = StreamableStream.new(new_entry) - @entry_set << zip_streamable_entry + @cdir.entry_set << zip_streamable_entry zip_streamable_entry.get_output_stream(&a_proc) end @@ -250,7 +250,7 @@ def add(entry, src_path, &continue_on_exists_proc) end new_entry.gather_fileinfo_from_srcpath(src_path) new_entry.dirty = true - @entry_set << new_entry + @cdir.entry_set << new_entry end # Convenience method for adding the contents of a file to the archive @@ -264,16 +264,16 @@ def add_stored(entry, src_path, &continue_on_exists_proc) # Removes the specified entry. def remove(entry) - @entry_set.delete(get_entry(entry)) + @cdir.entry_set.delete(get_entry(entry)) end # Renames the specified entry. def rename(entry, new_name, &continue_on_exists_proc) found_entry = get_entry(entry) check_entry_exists(new_name, continue_on_exists_proc, 'rename') - @entry_set.delete(found_entry) + @cdir.entry_set.delete(found_entry) found_entry.name = new_name - @entry_set << found_entry + @cdir.entry_set << found_entry end # Replaces the specified entry with the contents of src_path (from @@ -298,7 +298,7 @@ def commit on_success_replace do |tmp_file| ::Zip::OutputStream.open(tmp_file) do |zos| - @entry_set.each do |e| + @cdir.each do |e| e.write_to_zip_output_stream(zos) e.dirty = false e.clean_up @@ -315,7 +315,7 @@ def write_buffer(io = ::StringIO.new) return unless commit_required? ::Zip::OutputStream.write_buffer(io) do |zos| - @entry_set.each { |e| e.write_to_zip_output_stream(zos) } + @cdir.each { |e| e.write_to_zip_output_stream(zos) } zos.comment = comment end end @@ -328,16 +328,16 @@ def close # Returns true if any changes has been made to this archive since # the previous commit def commit_required? - @entry_set.each do |e| + @cdir.each do |e| return true if e.dirty end - @comment != @stored_comment || @entry_set != @stored_entries || @create + comment != @stored_comment || @cdir.entry_set != @stored_entries || @create end # Searches for entry with the specified name. Returns nil if # no entry is found. See also get_entry def find_entry(entry_name) - selected_entry = @entry_set.find_entry(entry_name) + selected_entry = @cdir.entry_set.find_entry(entry_name) return if selected_entry.nil? selected_entry.restore_ownership = @restore_ownership @@ -352,7 +352,7 @@ def find_entry(entry_name) # `::File::FNM_PATHNAME | ::File::FNM_DOTMATCH | ::File::FNM_EXTGLOB`, # which will be overridden if you set your own flags. def glob(*args, &block) - @entry_set.glob(*args, &block) + @cdir.entry_set.glob(*args, &block) end # Searches for an entry just as find_entry, but throws Errno::ENOENT @@ -370,7 +370,7 @@ def mkdir(entry_name, permission = 0o755) entry_name = entry_name.dup.to_s entry_name << '/' unless entry_name.end_with?('/') - @entry_set << ::Zip::StreamableDirectory.new(@name, entry_name, nil, permission) + @cdir.entry_set << ::Zip::StreamableDirectory.new(@name, entry_name, nil, permission) end private @@ -389,7 +389,7 @@ def directory?(new_entry, src_path) def check_entry_exists(entry_name, continue_on_exists_proc, proc_name) continue_on_exists_proc ||= proc { Zip.continue_on_exists_proc } - return unless @entry_set.include?(entry_name) + return unless @cdir.entry_set.include?(entry_name) if continue_on_exists_proc.call remove get_entry(entry_name) From 1d6bfb7e6912e7725324843edc83327e0f9df82c Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sat, 15 Jan 2022 22:11:23 +0000 Subject: [PATCH 215/311] Expose the `EntrySet` more cleanly through `CentralDirectory`. There is now no direct access to the set of entries in a central directory. This makes the interface cleaner because we now, for example, add/delete things directly to/from the central directory, rather than to/from the entry set contained within the central directory. --- lib/zip/central_directory.rb | 24 +++++++----------------- lib/zip/file.rb | 31 +++++++++++-------------------- 2 files changed, 18 insertions(+), 37 deletions(-) diff --git a/lib/zip/central_directory.rb b/lib/zip/central_directory.rb index 3d3cd1a3..4ea444d4 100644 --- a/lib/zip/central_directory.rb +++ b/lib/zip/central_directory.rb @@ -1,7 +1,11 @@ # frozen_string_literal: true +require 'forwardable' + module Zip class CentralDirectory + extend Forwardable + END_OF_CDS = 0x06054b50 ZIP64_END_OF_CDS = 0x06064b50 ZIP64_EOCD_LOCATOR = 0x07064b50 @@ -14,12 +18,9 @@ class CentralDirectory attr_accessor :comment - attr_reader :entry_set - - # Returns an Enumerable containing the entries. - def entries - @entry_set.entries - end + def_delegators :@entry_set, + :<<, :delete, :each, :entries, :find_entry, :glob, + :include?, :size def initialize(entries = EntrySet.new, comment = '') #:nodoc: super() @@ -220,17 +221,6 @@ def start_buf(io) io.read end - # For iterating over the entries. - def each(&a_proc) - @entry_set.each(&a_proc) - end - - # Returns the number of entries in the central directory (and - # consequently in the zip archive). - def size - @entry_set.size - end - # Reads the End of Central Directory Record (and the Zip64 equivalent if # needs be) and returns the number of entries in the archive. This is a # convenience method that avoids reading in all of the entry data to get a diff --git a/lib/zip/file.rb b/lib/zip/file.rb index 109cd86a..1aa34575 100644 --- a/lib/zip/file.rb +++ b/lib/zip/file.rb @@ -64,7 +64,7 @@ class File # default -> true. attr_accessor :restore_times - def_delegators :@cdir, :comment, :comment=, :each, :entries, :size + def_delegators :@cdir, :comment, :comment=, :each, :entries, :glob, :size # Opens a zip archive. Pass create: true to create # a new archive if it doesn't exist already. @@ -103,7 +103,7 @@ def initialize(path_or_io, create: false, buffer: false, **options) raise Error, "File #{@name} not found" end - @stored_entries = @cdir.entry_set.dup + @stored_entries = @cdir.entries.map(&:dup) @stored_comment = @cdir.comment @restore_ownership = options[:restore_ownership] @restore_permissions = options[:restore_permissions] @@ -222,7 +222,7 @@ def get_output_stream(entry, permissions: nil, comment: nil, end new_entry.unix_perms = permissions zip_streamable_entry = StreamableStream.new(new_entry) - @cdir.entry_set << zip_streamable_entry + @cdir << zip_streamable_entry zip_streamable_entry.get_output_stream(&a_proc) end @@ -250,7 +250,7 @@ def add(entry, src_path, &continue_on_exists_proc) end new_entry.gather_fileinfo_from_srcpath(src_path) new_entry.dirty = true - @cdir.entry_set << new_entry + @cdir << new_entry end # Convenience method for adding the contents of a file to the archive @@ -264,16 +264,16 @@ def add_stored(entry, src_path, &continue_on_exists_proc) # Removes the specified entry. def remove(entry) - @cdir.entry_set.delete(get_entry(entry)) + @cdir.delete(get_entry(entry)) end # Renames the specified entry. def rename(entry, new_name, &continue_on_exists_proc) found_entry = get_entry(entry) check_entry_exists(new_name, continue_on_exists_proc, 'rename') - @cdir.entry_set.delete(found_entry) + @cdir.delete(found_entry) found_entry.name = new_name - @cdir.entry_set << found_entry + @cdir << found_entry end # Replaces the specified entry with the contents of src_path (from @@ -331,13 +331,13 @@ def commit_required? @cdir.each do |e| return true if e.dirty end - comment != @stored_comment || @cdir.entry_set != @stored_entries || @create + comment != @stored_comment || entries != @stored_entries || @create end # Searches for entry with the specified name. Returns nil if # no entry is found. See also get_entry def find_entry(entry_name) - selected_entry = @cdir.entry_set.find_entry(entry_name) + selected_entry = @cdir.find_entry(entry_name) return if selected_entry.nil? selected_entry.restore_ownership = @restore_ownership @@ -346,15 +346,6 @@ def find_entry(entry_name) selected_entry end - # Search for entries given a glob pattern. You can also supply flags - # in the second argument, which are equivalent to those used by - # `::Dir.glob` and `::File.fnmatch`. Default flags are - # `::File::FNM_PATHNAME | ::File::FNM_DOTMATCH | ::File::FNM_EXTGLOB`, - # which will be overridden if you set your own flags. - def glob(*args, &block) - @cdir.entry_set.glob(*args, &block) - end - # Searches for an entry just as find_entry, but throws Errno::ENOENT # if no entry is found. def get_entry(entry) @@ -370,7 +361,7 @@ def mkdir(entry_name, permission = 0o755) entry_name = entry_name.dup.to_s entry_name << '/' unless entry_name.end_with?('/') - @cdir.entry_set << ::Zip::StreamableDirectory.new(@name, entry_name, nil, permission) + @cdir << ::Zip::StreamableDirectory.new(@name, entry_name, nil, permission) end private @@ -389,7 +380,7 @@ def directory?(new_entry, src_path) def check_entry_exists(entry_name, continue_on_exists_proc, proc_name) continue_on_exists_proc ||= proc { Zip.continue_on_exists_proc } - return unless @cdir.entry_set.include?(entry_name) + return unless @cdir.include?(entry_name) if continue_on_exists_proc.call remove get_entry(entry_name) From 9c3f8254c7f7f992c94cdea4d732ef0f9e5f68c3 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Mon, 17 Jan 2022 17:45:19 +0000 Subject: [PATCH 216/311] Fix reading zip64 files with max length file comment. If a zip file has a comment that is 65,535 characters long - which is a valid length and the maximum allowable length - the initial read of the archive fails to find the Zip64 End of Central Directory Locator and therefore cannot read the rest of the file. This commit fixes this by making sure that we look far enough back into the file from the end to find this locator, and then use the information in it to find the Zip64 End of Central Directory Record. Test added to catch regressions. Fixes #509. --- lib/zip/central_directory.rb | 86 ++++++++++++-------- test/data/zip64_max_length_file_comment.zip | Bin 0 -> 66351 bytes test/file_test.rb | 5 ++ 3 files changed, 57 insertions(+), 34 deletions(-) create mode 100644 test/data/zip64_max_length_file_comment.zip diff --git a/lib/zip/central_directory.rb b/lib/zip/central_directory.rb index 4ea444d4..f5dd25a5 100644 --- a/lib/zip/central_directory.rb +++ b/lib/zip/central_directory.rb @@ -6,15 +6,16 @@ module Zip class CentralDirectory extend Forwardable - END_OF_CDS = 0x06054b50 - ZIP64_END_OF_CDS = 0x06064b50 - ZIP64_EOCD_LOCATOR = 0x07064b50 + END_OF_CD_SIG = 0x06054b50 + ZIP64_END_OF_CD_SIG = 0x06064b50 + ZIP64_EOCD_LOCATOR_SIG = 0x07064b50 STATIC_EOCD_SIZE = 22 ZIP64_STATIC_EOCD_SIZE = 56 ZIP64_EOCD_LOC_SIZE = 20 - MAX_FILE_COMMENT_SIZE = 1 << 16 - MAX_END_OF_CDS_SIZE = MAX_FILE_COMMENT_SIZE + STATIC_EOCD_SIZE + MAX_FILE_COMMENT_SIZE = (1 << 16) - 1 + MAX_END_OF_CD_SIZE = + MAX_FILE_COMMENT_SIZE + STATIC_EOCD_SIZE + ZIP64_EOCD_LOC_SIZE attr_accessor :comment @@ -47,7 +48,7 @@ def write_to_stream(io) #:nodoc: def write_e_o_c_d(io, offset, cdir_size) #:nodoc: tmp = [ - END_OF_CDS, + END_OF_CD_SIG, 0, # @numberOfThisDisk 0, # @numberOfDiskWithStartOfCDir @entry_set ? [@entry_set.size, 0xFFFF].min : 0, @@ -64,7 +65,7 @@ def write_e_o_c_d(io, offset, cdir_size) #:nodoc: def write_64_e_o_c_d(io, offset, cdir_size) #:nodoc: tmp = [ - ZIP64_END_OF_CDS, + ZIP64_END_OF_CD_SIG, 44, # size of zip64 end of central directory record (excludes signature and field itself) VERSION_MADE_BY, VERSION_NEEDED_TO_EXTRACT_ZIP64, @@ -82,7 +83,7 @@ def write_64_e_o_c_d(io, offset, cdir_size) #:nodoc: def write_64_eocd_locator(io, zip64_eocd_offset) tmp = [ - ZIP64_EOCD_LOCATOR, + ZIP64_EOCD_LOCATOR_SIG, 0, # number of disk containing the start of zip64 eocd record zip64_eocd_offset, # offset of the start of zip64 eocd record in its disk 1 # total number of disks @@ -93,15 +94,7 @@ def write_64_eocd_locator(io, zip64_eocd_offset) private :write_64_eocd_locator def unpack_64_e_o_c_d(buffer) #:nodoc: - index = buffer.rindex([ZIP64_END_OF_CDS].pack('V')) - raise Error, 'Zip64 end of central directory signature not found' unless index - - l_index = buffer.rindex([ZIP64_EOCD_LOCATOR].pack('V')) - raise Error, 'Zip64 end of central directory signature locator not found' unless l_index - - buf = buffer.slice(index..l_index) - - _, # ZIP64_END_OF_CDS signature. We know we have this at this point. + _, # ZIP64_END_OF_CD_SIG. We know we have this at this point. @size_of_zip64_e_o_c_d, @version_made_by, @version_needed_for_extract, @@ -110,7 +103,7 @@ def unpack_64_e_o_c_d(buffer) #:nodoc: @total_number_of_entries_in_cdir_on_this_disk, @size, @size_in_bytes, - @cdir_offset = buf.unpack('VQG4NvJreqwDDahC`4jpZV^!uX^q^8WfvOw8|(ySVa** ziTE0$MyAGXyc!)Y8eOzDI@|yWGT%Y5wwlcue-OtOwRbHOdX6H2SO{x3G3oq>x4B6CDC6zkbJv*;r0*0JnsV$JZG3iuFh{y zHs`6t3n^Swmt9?YDY{Q$*Q<~nk)!9AYR|-4)4lm?@2Y-J{c!Kg&!>48UAbIVZZTHK zW5Xkz=Vp0~VeKWwx}vnNJkp9!>hez4j6U$)`8bEl9y49nedx=ve;*iO9ADVayWjYY zy`ZBzs-e8~ioNsUQSa^~iy`j94-uz|TrW3C1*MjRubbUdoFfJ{X@5QFG(DYMdu-_X z{+THwn{(>hR!tq5<1Bo`#v zNuJwru{U>m+iH?8KCS9=xbvewG{?=>l=;~)O;Y9BqI*LApKR3kgJ+aDx9;vC3-gh! zTeW6Uh9zhD;*4H+{{+x}?2UIj{$8>I*n?-=b~fZD`pni?d=jLqh)=%E`S(30_4>?vlNHy_`WeJat z|66z0kb7hOX{p$iE=M@@4bSrCUiapEPE&ZgkMnJX9#F+eF$?Qe;ZMgT zU(NS;|Er}?n)8%e8cn3~B47Bw%0CbC529z>9$3c*625a Date: Mon, 17 Jan 2022 18:03:04 +0000 Subject: [PATCH 217/311] Round out the max comment size tests. Just sanity check the comment size and the number of entries once the file has been initialized. --- test/file_test.rb | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/test/file_test.rb b/test/file_test.rb index 8715c4ed..b93c4e0d 100644 --- a/test/file_test.rb +++ b/test/file_test.rb @@ -205,12 +205,18 @@ def test_open_buffer_without_block_write_buffer_does_nothing def test_open_file_with_max_length_comment # Should not raise any errors. - Zip::File.open('test/data/max_length_file_comment.zip') + Zip::File.open('test/data/max_length_file_comment.zip') do |zf| + assert_equal(1, zf.size) + assert_equal(65_535, zf.comment.length) + end end def test_open_zip64_file_with_max_length_comment # Should not raise any errors. - Zip::File.open('test/data/zip64_max_length_file_comment.zip') + Zip::File.open('test/data/zip64_max_length_file_comment.zip') do |zf| + assert_equal(2, zf.size) + assert_equal(65_535, zf.comment.length) + end end def test_count_entries From bdbd573290be5d7ff103fccb3d51291f84b63167 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Mon, 17 Jan 2022 18:10:17 +0000 Subject: [PATCH 218/311] Remove unnecessary static method from `CentralDirectory`. `CentralDirectory` shouldn't be in the public API for rubyzip and there's nothing that `CentralDirectory::read_from_stream` did that couldn't be done by just initializing an object first. Keeping it around risked things getting out of date as we streamline and fix other things. --- lib/zip/central_directory.rb | 8 -------- test/central_directory_test.rb | 3 ++- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/lib/zip/central_directory.rb b/lib/zip/central_directory.rb index f5dd25a5..55953315 100644 --- a/lib/zip/central_directory.rb +++ b/lib/zip/central_directory.rb @@ -248,14 +248,6 @@ def count_entries(io) @size end - def self.read_from_stream(io) #:nodoc: - cdir = new - cdir.read_from_stream(io) - cdir - rescue Error - nil - end - def ==(other) #:nodoc: return false unless other.kind_of?(CentralDirectory) diff --git a/test/central_directory_test.rb b/test/central_directory_test.rb index 88258caa..6197eb3b 100644 --- a/test/central_directory_test.rb +++ b/test/central_directory_test.rb @@ -9,7 +9,8 @@ def teardown def test_read_from_stream ::File.open(TestZipFile::TEST_ZIP2.zip_name, 'rb') do |zip_file| - cdir = ::Zip::CentralDirectory.read_from_stream(zip_file) + cdir = ::Zip::CentralDirectory.new + cdir.read_from_stream(zip_file) assert_equal(TestZipFile::TEST_ZIP2.entry_names.size, cdir.size) assert_equal(cdir.entries.map(&:name).sort, TestZipFile::TEST_ZIP2.entry_names.sort) From 60f8fffbc21d2abf57e80f3e939bba3370716d85 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Mon, 17 Jan 2022 22:04:45 +0000 Subject: [PATCH 219/311] Reorder methods in `CentralDirectory` with private at the end. --- lib/zip/central_directory.rb | 48 +++++++++++++++++------------------- 1 file changed, 22 insertions(+), 26 deletions(-) diff --git a/lib/zip/central_directory.rb b/lib/zip/central_directory.rb index 55953315..171d0243 100644 --- a/lib/zip/central_directory.rb +++ b/lib/zip/central_directory.rb @@ -29,6 +29,11 @@ def initialize(entries = EntrySet.new, comment = '') #:nodoc: @comment = comment end + def read_from_stream(io) + read_eocds(io) + read_central_directory_entries(io) + end + def write_to_stream(io) #:nodoc: cdir_offset = io.tell @entry_set.each { |entry| entry.write_c_dir_entry(io) } @@ -46,6 +51,23 @@ def write_to_stream(io) #:nodoc: write_e_o_c_d(io, cdir_offset, cdir_size) end + # Reads the End of Central Directory Record (and the Zip64 equivalent if + # needs be) and returns the number of entries in the archive. This is a + # convenience method that avoids reading in all of the entry data to get a + # very quick entry count. + def count_entries(io) + read_eocds(io) + @size + end + + def ==(other) #:nodoc: + return false unless other.kind_of?(CentralDirectory) + + @entry_set.entries.sort == other.entries.sort && comment == other.comment + end + + private + def write_e_o_c_d(io, offset, cdir_size) #:nodoc: tmp = [ END_OF_CD_SIG, @@ -61,8 +83,6 @@ def write_e_o_c_d(io, offset, cdir_size) #:nodoc: io << @comment end - private :write_e_o_c_d - def write_64_e_o_c_d(io, offset, cdir_size) #:nodoc: tmp = [ ZIP64_END_OF_CD_SIG, @@ -79,8 +99,6 @@ def write_64_e_o_c_d(io, offset, cdir_size) #:nodoc: io << tmp.pack('VQ Date: Mon, 17 Jan 2022 22:32:56 +0000 Subject: [PATCH 220/311] `OutputStream`: use a `CentralDirectory` object internally. Now `CentralDirectory` is a bit cleaner it actually makes sense to use it here instead of an `EntrySet` and comment separately. --- lib/zip/output_stream.rb | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/lib/zip/output_stream.rb b/lib/zip/output_stream.rb index f2e7eb5e..58d2772c 100644 --- a/lib/zip/output_stream.rb +++ b/lib/zip/output_stream.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true +require 'forwardable' + module Zip # ZipOutputStream is the basic class for writing zip files. It is # possible to create a ZipOutputStream object directly, passing @@ -20,9 +22,10 @@ module Zip # class. class OutputStream + extend Forwardable include ::Zip::IOExtras::AbstractOutputStream - attr_accessor :comment + def_delegators :@cdir, :comment, :comment= # Opens the indicated zip file. If a file with that name already # exists it will be overwritten. @@ -37,12 +40,11 @@ def initialize(file_name, stream: false, encrypter: nil) else ::File.new(@file_name, 'wb') end - @entry_set = ::Zip::EntrySet.new + @cdir = ::Zip::CentralDirectory.new @compressor = ::Zip::NullCompressor.instance @encrypter = encrypter || ::Zip::NullEncrypter.new @closed = false @current_entry = nil - @comment = nil end # Same as #initialize but if a block is passed the opened @@ -73,7 +75,7 @@ def close finalize_current_entry update_local_headers - write_central_directory + @cdir.write_to_stream(@output_stream) @output_stream.close @closed = true end @@ -84,7 +86,7 @@ def close_buffer finalize_current_entry update_local_headers - write_central_directory + @cdir.write_to_stream(@output_stream) @closed = true @output_stream.flush @output_stream @@ -118,7 +120,7 @@ def copy_raw_entry(entry) raise Error, 'entry is not a ZipEntry' unless entry.kind_of?(Entry) finalize_current_entry - @entry_set << entry + @cdir << entry src_pos = entry.local_header_offset entry.write_local_entry(@output_stream) @compressor = NullCompressor.instance @@ -154,7 +156,7 @@ def finalize_current_entry def init_next_entry(entry) finalize_current_entry - @entry_set << entry + @cdir << entry entry.write_local_entry(@output_stream) @encrypter.reset! @output_stream << @encrypter.header(entry.mtime) @@ -175,18 +177,13 @@ def get_compressor(entry) def update_local_headers pos = @output_stream.pos - @entry_set.each do |entry| + @cdir.each do |entry| @output_stream.pos = entry.local_header_offset entry.write_local_entry(@output_stream, rewrite: true) end @output_stream.pos = pos end - def write_central_directory - cdir = CentralDirectory.new(@entry_set, @comment) - cdir.write_to_stream(@output_stream) - end - protected def finish From 4cf801c5f334d989000aeabfed53edd1263551a0 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Tue, 18 Jan 2022 15:12:53 +0000 Subject: [PATCH 221/311] Tidy up `EntrySet` accessors. `entry_order` is no longer a member, so remove it. `entry_set` should not be public, but needs to be protected for use in `==`. --- lib/zip/entry_set.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/zip/entry_set.rb b/lib/zip/entry_set.rb index a771dc7f..11db9518 100644 --- a/lib/zip/entry_set.rb +++ b/lib/zip/entry_set.rb @@ -3,7 +3,9 @@ module Zip class EntrySet #:nodoc:all include Enumerable - attr_accessor :entry_set, :entry_order + + attr_reader :entry_set + protected :entry_set def initialize(an_enumerable = []) super() From 044759f50263f9263461fffc65ab2c364301c9ad Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sun, 7 Nov 2021 22:15:18 +0000 Subject: [PATCH 222/311] Fix `OutputStream#put_next_entry` to preserve `StreamableStream`s. When passing an `Entry` type to `File#get_output_stream` the entry is used to create a `StreamableStream`, which preserves all the info in the entry, such as timestamp, etc. But then in `put_next_entry` all that is lost due to the test for `kind_of?(Entry)` which a `StreamableStream` is not. See #503 for details. This change tests for `StreamableStream`s in `put_next_entry` and uses them directly. Some set-up within `Entry` needed to be made more robust to cope with this, but otherwise it's a low impact change, which does fix the problem. The reason this case was being missed before is that the tests weren't testing `get_output_stream` with an `Entry` object, so I have also added that test too. Fixes #503. --- lib/zip/entry.rb | 23 +++++++++++++++-------- lib/zip/output_stream.rb | 18 +++++++++--------- test/entry_test.rb | 5 ++++- test/file_test.rb | 23 ++++++++++++++++++++++- 4 files changed, 50 insertions(+), 19 deletions(-) diff --git a/lib/zip/entry.rb b/lib/zip/entry.rb index d1241e56..9764d91d 100644 --- a/lib/zip/entry.rb +++ b/lib/zip/entry.rb @@ -83,14 +83,21 @@ def initialize( @ftype = name_is_directory? ? :directory : :file @zipfile = zipfile - @comment = comment - @compression_method = compression_method - @compression_level = compression_level - - @compressed_size = compressed_size - @crc = crc - @size = size - @time = time + @comment = comment || '' + @compression_method = compression_method || DEFLATED + @compression_level = compression_level || ::Zip.default_compression + + @compressed_size = compressed_size || 0 + @crc = crc || 0 + @size = size || 0 + @time = case time + when ::Zip::DOSTime + time + when Time + ::Zip::DOSTime.from_time(time) + else + ::Zip::DOSTime.now + end @extra = extra.kind_of?(ExtraField) ? extra : ExtraField.new(extra.to_s) diff --git a/lib/zip/output_stream.rb b/lib/zip/output_stream.rb index 58d2772c..a2290c5d 100644 --- a/lib/zip/output_stream.rb +++ b/lib/zip/output_stream.rb @@ -100,15 +100,15 @@ def put_next_entry( ) raise Error, 'zip stream is closed' if @closed - new_entry = if entry_name.kind_of?(Entry) - entry_name - else - Entry.new( - @file_name, entry_name.to_s, comment: comment, - extra: extra, compression_method: compression_method, - compression_level: level - ) - end + new_entry = + if entry_name.kind_of?(Entry) || entry_name.kind_of?(StreamableStream) + entry_name + else + Entry.new( + @file_name, entry_name.to_s, comment: comment, extra: extra, + compression_method: compression_method, compression_level: level + ) + end init_next_entry(new_entry) @current_entry = new_entry diff --git a/test/entry_test.rb b/test/entry_test.rb index 13d42963..f0e423f4 100644 --- a/test/entry_test.rb +++ b/test/entry_test.rb @@ -26,7 +26,10 @@ def test_constructor_and_getters assert_equal(TEST_COMPRESSIONMETHOD, entry.compression_method) assert_equal(TEST_NAME, entry.name) assert_equal(TEST_SIZE, entry.size) - assert_equal(TEST_TIME, entry.time) + + # Reverse times when testing because we need to use DOSTime#== for the + # comparison, not Time#==. + assert_equal(entry.time, TEST_TIME) end def test_is_directory_and_is_file diff --git a/test/file_test.rb b/test/file_test.rb index b93c4e0d..6476e798 100644 --- a/test/file_test.rb +++ b/test/file_test.rb @@ -95,7 +95,10 @@ def test_get_output_stream custom_entry_args[:compression_level], entry.compression_level ) assert_equal(custom_entry_args[:size], entry.size) - assert_equal(custom_entry_args[:time], entry.time) + + # Reverse times when testing because we need to use DOSTime#== for the + # comparison, not Time#==. + assert_equal(entry.time, custom_entry_args[:time]) zf.get_output_stream('entry.bin') do |os| os.write(::File.open('test/data/generated/5entry.zip', 'rb').read) @@ -110,6 +113,24 @@ def test_get_output_stream end end + def test_get_output_stream_with_entry + Dir.mktmpdir do |tmp| + test_zip = File.join(tmp, 'test.zip') + time = Time.new(1999, 12, 31) + + ::Zip::File.open(test_zip, create: true) do |zip| + entry = ::Zip::Entry.new(zip.name, 'entry.txt', time: time) + zip.get_output_stream(entry) { |out| out.puts 'CONTENT!' } + end + + ::Zip::File.open(test_zip) do |zip| + # Reverse times when testing because we need to use DOSTime#== for the + # comparison, not Time#==. + assert_equal(zip.get_entry('entry.txt').time, time) + end + end + end + def test_open_buffer_with_string data = File.read('test/data/rubycode.zip', mode: 'rb') string = data.dup From e2e0e23763f81a5074b438e5819196e5e6c25ac1 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sat, 22 Jan 2022 07:34:00 +0000 Subject: [PATCH 223/311] Remove `File::add_buffer` from the API. Its functionality is now replicated in `File::open_buffer` but in a more secure way. --- lib/zip/file.rb | 12 ++---------- test/file_test.rb | 2 +- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/lib/zip/file.rb b/lib/zip/file.rb index 1aa34575..d080d6d5 100644 --- a/lib/zip/file.rb +++ b/lib/zip/file.rb @@ -126,19 +126,11 @@ def open(file_name, create: false, **options) end end - # Same as #open. But outputs data to a buffer instead of a file - def add_buffer - io = ::StringIO.new - zf = ::Zip::File.new(io, create: true, buffer: true) - yield zf - zf.write_buffer(io) - end - # Like #open, but reads zip archive contents from a String or open IO # stream, and outputs data to a buffer. # (This can be used to extract data from a # downloaded zip archive without first saving it to disk.) - def open_buffer(io, **options) + def open_buffer(io = ::StringIO.new, create: false, **options) unless IO_METHODS.map { |method| io.respond_to?(method) }.all? || io.kind_of?(String) raise 'Zip::File.open_buffer expects a String or IO-like argument' \ "(responds to #{IO_METHODS.join(', ')}). Found: #{io.class}" @@ -149,7 +141,7 @@ def open_buffer(io, **options) # https://github.com/rubyzip/rubyzip/issues/119 io.binmode if io.respond_to?(:binmode) - zf = ::Zip::File.new(io, create: true, buffer: true, **options) + zf = ::Zip::File.new(io, create: create, buffer: true, **options) return zf unless block_given? yield zf diff --git a/test/file_test.rb b/test/file_test.rb index 6476e798..c8be3dbf 100644 --- a/test/file_test.rb +++ b/test/file_test.rb @@ -16,7 +16,7 @@ def teardown def test_create_from_scratch_to_buffer comment = 'a short comment' - buffer = ::Zip::File.add_buffer do |zf| + buffer = ::Zip::File.open_buffer(create: true) do |zf| zf.get_output_stream('myFile') { |os| os.write 'myFile contains just this' } zf.mkdir('dir1') zf.comment = comment From 31e66885280054cf2e6199c9a5a27449cb4fbf75 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sat, 22 Jan 2022 07:38:18 +0000 Subject: [PATCH 224/311] Remove unused private method `File#directory?`. This was a fairly horrible method anyway, for a number of reasons. It looked like a method that tested whether a name was a 'directory' name or not, and it did, but it also had some side effects where it would convert it *to* a directory name in some cases as well. Thankfully, nothing was using it any more, and as it was private we can lose it safely. Gone. --- lib/zip/file.rb | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/lib/zip/file.rb b/lib/zip/file.rb index d080d6d5..cae4f973 100644 --- a/lib/zip/file.rb +++ b/lib/zip/file.rb @@ -358,18 +358,6 @@ def mkdir(entry_name, permission = 0o755) private - def directory?(new_entry, src_path) - path_is_directory = ::File.directory?(src_path) - if new_entry.directory? && !path_is_directory - raise ArgumentError, - "entry name '#{new_entry}' indicates directory entry, but " \ - "'#{src_path}' is not a directory" - elsif !new_entry.directory? && path_is_directory - new_entry.name += '/' - end - new_entry.directory? && path_is_directory - end - def check_entry_exists(entry_name, continue_on_exists_proc, proc_name) continue_on_exists_proc ||= proc { Zip.continue_on_exists_proc } return unless @cdir.include?(entry_name) From 05a17390697f183da4e22fc73e9b2a11451ef79f Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sat, 22 Jan 2022 08:39:43 +0000 Subject: [PATCH 225/311] Properly test `File#mkdir`. --- test/file_test.rb | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/test/file_test.rb b/test/file_test.rb index c8be3dbf..5c04d1cb 100644 --- a/test/file_test.rb +++ b/test/file_test.rb @@ -413,6 +413,44 @@ def test_add_directory end end + def test_mkdir + buffer = ::Zip::File.open_buffer(create: true) do |zf| + # Add a directory with no slash. + zf.mkdir('dir') + + # Add it again. + assert_raises(Errno::EEXIST) do + zf.mkdir('dir') + end + + # Add it with a slash. + assert_raises(Errno::EEXIST) do + zf.mkdir('dir/') + end + + # Add a directory with a slash. + zf.mkdir('folder/') + + # Add it again. + assert_raises(Errno::EEXIST) do + zf.mkdir('folder/') + end + + # Add it without a slash. + assert_raises(Errno::EEXIST) do + zf.mkdir('folder') + end + end + + ::Zip::File.open_buffer(buffer) do |zf| + assert(zf.find_entry('dir/').directory?) + assert(zf.find_entry('dir').directory?) + + assert(zf.find_entry('folder/').directory?) + assert(zf.find_entry('folder').directory?) + end + end + def test_remove entry, *remaining = TEST_ZIP.entry_names From d2789dd0e353259817cf19ab8aabd40ebb2e2b29 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sun, 6 Feb 2022 15:03:01 +0000 Subject: [PATCH 226/311] Add a note to the README about 2.3 compatibility. Closes #520. --- README.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index d2c98da9..368e9ad0 100644 --- a/README.md +++ b/README.md @@ -351,7 +351,15 @@ You can set multiple settings at the same time by using a block: ## Compatibility -Rubyzip is known to run on a number of platforms and under a number of different Ruby versions. Please see the table below for what we think the current situation is. Note: an empty cell means "unknown", not "does not work". +Rubyzip is known to run on a number of platforms and under a number of different Ruby versions. + +### Version 2.3.x + +Rubyzip 2.3 is known to work on MRI 2.4 to 3.1 on Linux and Mac, and JRuby and Truffleruby on Linux. There are known issues with Windows which have been fixed on the development branch. Please [let us know](https://github.com/rubyzip/rubyzip/pulls) if you know Rubyzip 2.3 works on a platform/Ruby combination not listed here, or [raise an issue](https://github.com/rubyzip/rubyzip/issues) if you see a failure where we think it should work. + +### Next (version 3.0.0) + +Please see the table below for what we think the current situation is. Note: an empty cell means "unknown", not "does not work". | OS/Ruby | 2.5 | 2.6 | 2.7 | 3.0 | 3.1 | 3.1 +YJIT | Head | Head +YJIT | JRuby 9.3.2.0 | JRuby Head | Truffleruby 21.3.0 | Truffleruby Head | |---------|-----|-----|-----|-----|-----|----------|------|-----------|----------------|------------|--------------------|------------------| From 243a66496a7e928eaab7f12b9dbcb4f1d6f75fdf Mon Sep 17 00:00:00 2001 From: naoto hamada Date: Wed, 6 Apr 2022 09:20:27 +0900 Subject: [PATCH 227/311] Fix indent --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 368e9ad0..4229d111 100644 --- a/README.md +++ b/README.md @@ -226,7 +226,7 @@ buffer = Zip::OutputStream.write_buffer do |out| unless [DOCUMENT_FILE_PATH, RELS_FILE_PATH].include?(e.name) out.put_next_entry(e.name) out.write e.get_input_stream.read - end + end end out.put_next_entry(DOCUMENT_FILE_PATH) From e07f01950792484e92a11edb17c46d2edc776984 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sat, 23 Apr 2022 13:52:50 +0100 Subject: [PATCH 228/311] Improve the description of `InputStream#get_next_entry`. Documentation now refects the fact that the stream is positioned at the start of the entry data. --- lib/zip/input_stream.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/zip/input_stream.rb b/lib/zip/input_stream.rb index 66ffdd3e..e292ff3d 100644 --- a/lib/zip/input_stream.rb +++ b/lib/zip/input_stream.rb @@ -64,10 +64,10 @@ def close @archive_io.close end - # Returns a Entry object. It is necessary to call this - # method on a newly created InputStream before reading from - # the first entry in the archive. Returns nil when there are - # no more entries. + # Returns an Entry object and positions the stream at the beginning of + # the entry data. It is necessary to call this method on a newly created + # InputStream before reading from the first entry in the archive. + # Returns nil when there are no more entries. def get_next_entry unless @current_entry.nil? if @current_entry.incomplete? From ffa90a37cbb85844754bc7256c994b554703aeba Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sat, 23 Apr 2022 13:54:09 +0100 Subject: [PATCH 229/311] README: improve the crypto documentation. Clean it up and provide a decryption example. --- README.md | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 4229d111..12d18720 100644 --- a/README.md +++ b/README.md @@ -204,15 +204,23 @@ Any attempt to move about in a zip file opened with `Zip::InputStream` could res Rubyzip supports reading/writing zip files with traditional zip encryption (a.k.a. "ZipCrypto"). AES encryption is not yet supported. It can be used with buffer streams, e.g.: ```ruby -Zip::OutputStream.write_buffer( - ::StringIO.new, encrypter: Zip::TraditionalEncrypter.new('password') -) do |out| - out.put_next_entry("my_file.txt") - out.write my_data -end.string +# Writing. +enc = Zip::TraditionalEncrypter.new('password') +buffer = Zip::OutputStream.write_buffer(encrypter: enc) do |output| + output.put_next_entry("my_file.txt") + output.write my_data +end + +# Reading. +dec = Zip::TraditionalDecrypter.new('password') +Zip::InputStream.open(buffer, decrypter: dec) do |input| + entry = input.get_next_entry + puts "Contents of '#{entry.name}':" + puts input.read +end ``` -This is an experimental feature and the interface for encryption may change in future versions. +_This is an experimental feature and the interface for encryption may change in future versions._ ## Known issues From 8b87b0e20007c1b4ad8d4e6cc4e293ed8fa3db3d Mon Sep 17 00:00:00 2001 From: Finn Bacall Date: Mon, 13 Jun 2022 16:47:51 +0100 Subject: [PATCH 230/311] Implement `Zip::FileSystem::ZipFsFile#symlink?` --- lib/zip/filesystem/file.rb | 4 ++-- test/filesystem/file_nonmutating_test.rb | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/zip/filesystem/file.rb b/lib/zip/filesystem/file.rb index 7a93f7e2..d5597c2b 100644 --- a/lib/zip/filesystem/file.rb +++ b/lib/zip/filesystem/file.rb @@ -197,8 +197,8 @@ def chardev?(_filename) false end - def symlink?(_filename) - false + def symlink?(filename) + @mapped_zip.get_entry(filename).symlink? end def socket?(_filename) diff --git a/test/filesystem/file_nonmutating_test.rb b/test/filesystem/file_nonmutating_test.rb index 0e93b641..9c5d9d13 100644 --- a/test/filesystem/file_nonmutating_test.rb +++ b/test/filesystem/file_nonmutating_test.rb @@ -186,7 +186,9 @@ def test_blockdev? end def test_symlink? - assert_always_false(:symlink?) + zip_file = ::Zip::File.new('test/data/path_traversal/tuzovakaoff/symlink.zip') + assert(zip_file.file.symlink?('path')) + assert(!zip_file.file.symlink?('path/file.txt')) end def test_socket? From 451a04f7a26cb1166d142e2ba5641f1737162e75 Mon Sep 17 00:00:00 2001 From: Finn Bacall Date: Tue, 14 Jun 2022 08:36:19 +0100 Subject: [PATCH 231/311] Test for `Errno::ENOENT` --- test/filesystem/file_nonmutating_test.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/test/filesystem/file_nonmutating_test.rb b/test/filesystem/file_nonmutating_test.rb index 9c5d9d13..7124cce2 100644 --- a/test/filesystem/file_nonmutating_test.rb +++ b/test/filesystem/file_nonmutating_test.rb @@ -189,6 +189,7 @@ def test_symlink? zip_file = ::Zip::File.new('test/data/path_traversal/tuzovakaoff/symlink.zip') assert(zip_file.file.symlink?('path')) assert(!zip_file.file.symlink?('path/file.txt')) + assert_e_n_o_e_n_t(:symlink?) end def test_socket? From 513ce5e5f7c7a1f6886a9554f91537da6a647475 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sat, 18 Jun 2022 12:45:59 +0100 Subject: [PATCH 232/311] Remove unnecessary encoding change in tests for `File`. --- test/file_test.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/test/file_test.rb b/test/file_test.rb index 5c04d1cb..83a6c556 100644 --- a/test/file_test.rb +++ b/test/file_test.rb @@ -199,7 +199,6 @@ def test_open_buffer_close_does_not_change_file def test_open_buffer_with_io_and_block File.open('test/data/rubycode.zip') do |io| - io.set_encoding(Encoding::BINARY) # not strictly required but can be set Zip::File.open_buffer(io) do |zip_io| # left empty on purpose end From 48d6acf9cac5a57b6315941c4412ec56d41bfc20 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sat, 18 Jun 2022 16:19:52 +0100 Subject: [PATCH 233/311] Ensure all streams passed to `File.new` are in `binmode`. Previously, only those streams that were passed to `new` by `open_buffer` were in the correct mode. --- lib/zip/file.rb | 5 ++--- test/file_test.rb | 10 ++++++++++ 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/lib/zip/file.rb b/lib/zip/file.rb index cae4f973..15c52ca4 100644 --- a/lib/zip/file.rb +++ b/lib/zip/file.rb @@ -83,6 +83,8 @@ def initialize(path_or_io, create: false, buffer: false, **options) @file_permissions = ::File.stat(@name).mode if buffer + # https://github.com/rubyzip/rubyzip/issues/119 + path_or_io.binmode if path_or_io.respond_to?(:binmode) @cdir.read_from_stream(path_or_io) else ::File.open(@name, 'rb') do |f| @@ -138,9 +140,6 @@ def open_buffer(io = ::StringIO.new, create: false, **options) io = ::StringIO.new(io) if io.kind_of?(::String) - # https://github.com/rubyzip/rubyzip/issues/119 - io.binmode if io.respond_to?(:binmode) - zf = ::Zip::File.new(io, create: create, buffer: true, **options) return zf unless block_given? diff --git a/test/file_test.rb b/test/file_test.rb index 83a6c556..82019f05 100644 --- a/test/file_test.rb +++ b/test/file_test.rb @@ -131,6 +131,15 @@ def test_get_output_stream_with_entry end end + def test_new_with_io_opened_non_binary_mode + File.open('test/data/test.xls') do |io| + refute(io.binmode?) # We open in non-binmode on purpose. + Zip::File.new(io, buffer: true) do |zip_io| + # left empty on purpose + end + end + end + def test_open_buffer_with_string data = File.read('test/data/rubycode.zip', mode: 'rb') string = data.dup @@ -199,6 +208,7 @@ def test_open_buffer_close_does_not_change_file def test_open_buffer_with_io_and_block File.open('test/data/rubycode.zip') do |io| + refute(io.binmode?) # We open in non-binmode on purpose. Zip::File.open_buffer(io) do |zip_io| # left empty on purpose end From e0e754ae65883ea63175c95f252493abf6e7d43e Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sun, 6 Feb 2022 16:46:41 +0000 Subject: [PATCH 234/311] Switch how the `Entry::dirty` flag is used. Set it to true by default - because a new `Entry` is dirty by definition, having not been written yet. Then make sure that an `Entry` that is created by reading from a zip file is set as not dirty. --- lib/zip/entry.rb | 9 ++++----- lib/zip/file.rb | 2 -- lib/zip/streamable_stream.rb | 1 + test/entry_set_test.rb | 6 +++--- 4 files changed, 8 insertions(+), 10 deletions(-) diff --git a/lib/zip/entry.rb b/lib/zip/entry.rb index 9764d91d..858b916e 100644 --- a/lib/zip/entry.rb +++ b/lib/zip/entry.rb @@ -49,9 +49,6 @@ def set_default_vars_values @unix_uid = nil @unix_gid = nil @unix_perms = nil - # @posix_acl = nil - # @ntfs_acl = nil - @dirty = false end def check_name(name) @@ -82,11 +79,11 @@ def initialize( @fstype = ::Zip::RUNNING_ON_WINDOWS ? ::Zip::FSTYPE_FAT : ::Zip::FSTYPE_UNIX @ftype = name_is_directory? ? :directory : :file + @dirty = true @zipfile = zipfile @comment = comment || '' @compression_method = compression_method || DEFLATED @compression_level = compression_level || ::Zip.default_compression - @compressed_size = compressed_size || 0 @crc = crc || 0 @size = size || 0 @@ -288,6 +285,7 @@ def unpack_local_entry(buf) end def read_local_entry(io) #:nodoc:all + @dirty = false # No changes at this point. @local_header_offset = io.tell static_sized_fields_buf = io.read(::Zip::LOCAL_ENTRY_STATIC_HEADER_LENGTH) || '' @@ -433,6 +431,7 @@ def read_extra_field(buf, local: false) end def read_c_dir_entry(io) #:nodoc:all + @dirty = false # No changes at this point. static_sized_fields_buf = io.read(::Zip::CDIR_ENTRY_STATIC_HEADER_LENGTH) check_c_dir_entry_static_header_length(static_sized_fields_buf) unpack_c_dir_entry(static_sized_fields_buf) @@ -655,7 +654,7 @@ def get_raw_input_stream(&block) end def clean_up - # By default, do nothing + @dirty = false # Any changes are written at this point. end private diff --git a/lib/zip/file.rb b/lib/zip/file.rb index 15c52ca4..116a940c 100644 --- a/lib/zip/file.rb +++ b/lib/zip/file.rb @@ -240,7 +240,6 @@ def add(entry, src_path, &continue_on_exists_proc) ) end new_entry.gather_fileinfo_from_srcpath(src_path) - new_entry.dirty = true @cdir << new_entry end @@ -291,7 +290,6 @@ def commit ::Zip::OutputStream.open(tmp_file) do |zos| @cdir.each do |e| e.write_to_zip_output_stream(zos) - e.dirty = false e.clean_up end zos.comment = comment diff --git a/lib/zip/streamable_stream.rb b/lib/zip/streamable_stream.rb index e823e002..2fea80c7 100644 --- a/lib/zip/streamable_stream.rb +++ b/lib/zip/streamable_stream.rb @@ -44,6 +44,7 @@ def write_to_zip_output_stream(output_stream) end def clean_up + super @temp_file.unlink end end diff --git a/test/entry_set_test.rb b/test/entry_set_test.rb index 42e93681..f653d836 100644 --- a/test/entry_set_test.rb +++ b/test/entry_set_test.rb @@ -62,14 +62,14 @@ def test_each count = 0 @zip_entry_set.each do |entry| assert(ZIP_ENTRIES.include?(entry)) - refute(entry.dirty) - entry.dirty = true # Check that entries can be changed in this block. + assert(entry.dirty) + entry.dirty = false # Check that entries can be changed in this block. count += 1 end assert_equal(ZIP_ENTRIES.size, count) @zip_entry_set.each do |entry| - assert(entry.dirty) + refute(entry.dirty) end end From 78a3cc596f217e18a98dc5fa2876295ed9714d61 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sun, 6 Feb 2022 17:06:06 +0000 Subject: [PATCH 235/311] Make `Entry::zipfile` private. No need for it to be public, and especially not writeable. --- lib/zip/entry.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/zip/entry.rb b/lib/zip/entry.rb index 858b916e..626eabb3 100644 --- a/lib/zip/entry.rb +++ b/lib/zip/entry.rb @@ -16,7 +16,7 @@ class Entry attr_accessor :comment, :compressed_size, :follow_symlinks, :name, :restore_ownership, :restore_permissions, :restore_times, - :size, :unix_gid, :unix_perms, :unix_uid, :zipfile + :size, :unix_gid, :unix_perms, :unix_uid attr_accessor :crc, :dirty, :external_file_attributes, :fstype, :gp_flags, :internal_file_attributes, :local_header_offset # :nodoc: From 30022510484922926e0d15ee768093b7ff9476c6 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sun, 6 Feb 2022 17:46:25 +0000 Subject: [PATCH 236/311] Mark certain methods in `Entry` as making it dirty. This allows us to track which entries have changed without keeping a copy of all entries. I hope. --- lib/zip/entry.rb | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/lib/zip/entry.rb b/lib/zip/entry.rb index 626eabb3..bc12664f 100644 --- a/lib/zip/entry.rb +++ b/lib/zip/entry.rb @@ -14,6 +14,20 @@ class Entry COMPRESSION_LEVEL_FAST_GPFLAG = 0b100 COMPRESSION_LEVEL_MAX_GPFLAG = 0b010 + # Mark this Entry as dirty if the supplied method is called. + def self.mark_dirty(*symbols) # :nodoc: + # Move the original method and call it after we've set the dirty flag. + symbols.each do |symbol| + orig_name = "orig_#{symbol}" + alias_method orig_name, symbol + + define_method(symbol) do |param| + @dirty = true + send(orig_name, param) + end + end + end + attr_accessor :comment, :compressed_size, :follow_symlinks, :name, :restore_ownership, :restore_permissions, :restore_times, :size, :unix_gid, :unix_perms, :unix_uid @@ -23,6 +37,9 @@ class Entry attr_reader :extra, :compression_level, :ftype, :filepath # :nodoc: + mark_dirty :comment=, :compressed_size=, :name=, :size=, + :unix_gid=, :unix_perms=, :unix_uid= + def set_default_vars_values @local_header_offset = 0 @local_header_size = nil # not known until local entry is created or read From 7b340d62a6eeb0b667fe7b49098f426d91bebadb Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Mon, 7 Feb 2022 15:24:55 +0000 Subject: [PATCH 237/311] Abstract marking as dirty into `Dirtyable` for reuse. --- lib/zip/dirtyable.rb | 32 ++++++++++++++++++++++++++++++++ lib/zip/entry.rb | 21 ++++++--------------- 2 files changed, 38 insertions(+), 15 deletions(-) create mode 100644 lib/zip/dirtyable.rb diff --git a/lib/zip/dirtyable.rb b/lib/zip/dirtyable.rb new file mode 100644 index 00000000..78b1c86a --- /dev/null +++ b/lib/zip/dirtyable.rb @@ -0,0 +1,32 @@ +# frozen_string_literal: true + +module Zip + module Dirtyable # :nodoc: + def initialize(dirty_on_create: true) + @dirty = dirty_on_create + end + + def dirty? + @dirty + end + + module ClassMethods + def mark_dirty(*symbols) # :nodoc: + # Move the original method and call it after we've set the dirty flag. + symbols.each do |symbol| + orig_name = "orig_#{symbol}" + alias_method orig_name, symbol + + define_method(symbol) do |param| + @dirty = true + send(orig_name, param) + end + end + end + end + + def self.included(base) + base.extend(ClassMethods) + end + end +end diff --git a/lib/zip/entry.rb b/lib/zip/entry.rb index bc12664f..27a40428 100644 --- a/lib/zip/entry.rb +++ b/lib/zip/entry.rb @@ -1,8 +1,13 @@ # frozen_string_literal: true require 'pathname' + +require_relative 'dirtyable' + module Zip class Entry + include Dirtyable + STORED = ::Zip::COMPRESSION_METHOD_STORE DEFLATED = ::Zip::COMPRESSION_METHOD_DEFLATE @@ -14,20 +19,6 @@ class Entry COMPRESSION_LEVEL_FAST_GPFLAG = 0b100 COMPRESSION_LEVEL_MAX_GPFLAG = 0b010 - # Mark this Entry as dirty if the supplied method is called. - def self.mark_dirty(*symbols) # :nodoc: - # Move the original method and call it after we've set the dirty flag. - symbols.each do |symbol| - orig_name = "orig_#{symbol}" - alias_method orig_name, symbol - - define_method(symbol) do |param| - @dirty = true - send(orig_name, param) - end - end - end - attr_accessor :comment, :compressed_size, :follow_symlinks, :name, :restore_ownership, :restore_permissions, :restore_times, :size, :unix_gid, :unix_perms, :unix_uid @@ -89,6 +80,7 @@ def initialize( compression_level: ::Zip.default_compression, time: ::Zip::DOSTime.now, extra: ::Zip::ExtraField.new ) + super() @name = name check_name(@name) @@ -96,7 +88,6 @@ def initialize( @fstype = ::Zip::RUNNING_ON_WINDOWS ? ::Zip::FSTYPE_FAT : ::Zip::FSTYPE_UNIX @ftype = name_is_directory? ? :directory : :file - @dirty = true @zipfile = zipfile @comment = comment || '' @compression_method = compression_method || DEFLATED From 08641db9f8be2bba7d13d351294a92d11228aea7 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Mon, 7 Feb 2022 22:05:19 +0000 Subject: [PATCH 238/311] Make `CentralDirectory` dirtyable. --- lib/zip/central_directory.rb | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/zip/central_directory.rb b/lib/zip/central_directory.rb index 171d0243..bad4100d 100644 --- a/lib/zip/central_directory.rb +++ b/lib/zip/central_directory.rb @@ -2,9 +2,12 @@ require 'forwardable' +require_relative 'dirtyable' + module Zip class CentralDirectory extend Forwardable + include Dirtyable END_OF_CD_SIG = 0x06054b50 ZIP64_END_OF_CD_SIG = 0x06064b50 @@ -23,8 +26,10 @@ class CentralDirectory :<<, :delete, :each, :entries, :find_entry, :glob, :include?, :size + mark_dirty :<<, :comment=, :delete + def initialize(entries = EntrySet.new, comment = '') #:nodoc: - super() + super(dirty_on_create: false) @entry_set = entries.kind_of?(EntrySet) ? entries : EntrySet.new(entries) @comment = comment end From 5cd1ef2910729009308cee747f1aeabfe0b850ef Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Mon, 7 Feb 2022 22:22:20 +0000 Subject: [PATCH 239/311] Use new dirty statuses to detect zip file changes. This also means that we no longer need to keep a copy of the original set of `Entry`s or the central directory comment to test for changes. For situations where a zip file has a lot of entries (e.g. #506) this means we save a lot of memory, and a lot of time constructing the zip file in memory. --- lib/zip/file.rb | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/zip/file.rb b/lib/zip/file.rb index 116a940c..25389db3 100644 --- a/lib/zip/file.rb +++ b/lib/zip/file.rb @@ -105,8 +105,6 @@ def initialize(path_or_io, create: false, buffer: false, **options) raise Error, "File #{@name} not found" end - @stored_entries = @cdir.entries.map(&:dup) - @stored_comment = @cdir.comment @restore_ownership = options[:restore_ownership] @restore_permissions = options[:restore_permissions] @restore_times = options[:restore_times] @@ -317,10 +315,13 @@ def close # Returns true if any changes has been made to this archive since # the previous commit def commit_required? + return true if @create || @cdir.dirty? + @cdir.each do |e| - return true if e.dirty + return true if e.dirty? end - comment != @stored_comment || entries != @stored_entries || @create + + false end # Searches for entry with the specified name. Returns nil if From 33dce510a6591f973682c498a6fab269c896ca18 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Mon, 7 Feb 2022 23:04:49 +0000 Subject: [PATCH 240/311] Remove `Entry#dirty=` as 'dirtyness' is now monitored internally. Had to round out some of the accessors that mark an `Entry` as dirty. --- lib/zip/entry.rb | 5 +++-- lib/zip/filesystem/file.rb | 1 - test/entry_set_test.rb | 8 +++++--- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/lib/zip/entry.rb b/lib/zip/entry.rb index 27a40428..786d65b3 100644 --- a/lib/zip/entry.rb +++ b/lib/zip/entry.rb @@ -23,12 +23,13 @@ class Entry :restore_ownership, :restore_permissions, :restore_times, :size, :unix_gid, :unix_perms, :unix_uid - attr_accessor :crc, :dirty, :external_file_attributes, :fstype, :gp_flags, + attr_accessor :crc, :external_file_attributes, :fstype, :gp_flags, :internal_file_attributes, :local_header_offset # :nodoc: attr_reader :extra, :compression_level, :ftype, :filepath # :nodoc: - mark_dirty :comment=, :compressed_size=, :name=, :size=, + mark_dirty :comment=, :compressed_size=, :external_file_attributes=, + :fstype=, :gp_flags=, :name=, :size=, :unix_gid=, :unix_perms=, :unix_uid= def set_default_vars_values diff --git a/lib/zip/filesystem/file.rb b/lib/zip/filesystem/file.rb index d5597c2b..8ee6d06b 100644 --- a/lib/zip/filesystem/file.rb +++ b/lib/zip/filesystem/file.rb @@ -124,7 +124,6 @@ def chmod(mode, *filenames) e.fstype = FSTYPE_UNIX # Force conversion filesystem type to unix. e.unix_perms = mode e.external_file_attributes = mode << 16 - e.dirty = true end filenames.size end diff --git a/test/entry_set_test.rb b/test/entry_set_test.rb index f653d836..c5b27b73 100644 --- a/test/entry_set_test.rb +++ b/test/entry_set_test.rb @@ -60,16 +60,18 @@ def test_delete def test_each # Used each instead each_with_index due the bug in jRuby count = 0 + new_size = 200 @zip_entry_set.each do |entry| assert(ZIP_ENTRIES.include?(entry)) - assert(entry.dirty) - entry.dirty = false # Check that entries can be changed in this block. + entry.clean_up # Start from a "saved" state. + entry.size = new_size # Check that entries can be changed in this block. count += 1 end assert_equal(ZIP_ENTRIES.size, count) @zip_entry_set.each do |entry| - refute(entry.dirty) + assert_equal(new_size, entry.size) + assert(entry.dirty?) # Size was changed. end end From 307fc6c6e95e89824f494afdd2a0d56d77c0f4e1 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Mon, 21 Feb 2022 09:15:50 +0000 Subject: [PATCH 241/311] Mark other mutating methods in `Entry` as dirty. Also, remove `Entry#extra=` as it makes no sense (and wasn't even being tested). And remove slightly odd test that was assuming an archive would not be changed if its utime was changed - even if it was being changed back immediately. This test was merely confirming that we weren't catching timestamp changes correctly. --- .rubocop_todo.yml | 2 +- lib/zip/entry.rb | 10 ++-------- test/filesystem/file_nonmutating_test.rb | 9 --------- 3 files changed, 3 insertions(+), 18 deletions(-) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 4406c97d..387a9cd6 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -20,7 +20,7 @@ Lint/MissingSuper: # Offense count: 5 # Configuration parameters: CountComments, CountAsOne. Metrics/ClassLength: - Max: 620 + Max: 630 # Offense count: 21 # Configuration parameters: IgnoredMethods. diff --git a/lib/zip/entry.rb b/lib/zip/entry.rb index 786d65b3..36bb2dea 100644 --- a/lib/zip/entry.rb +++ b/lib/zip/entry.rb @@ -118,14 +118,6 @@ def incomplete? gp_flags & 8 == 8 end - def extra=(field) - @extra = if field.nil? - ExtraField.new - else - field.kind_of?(ExtraField) ? field : ExtraField.new(field.to_s) - end - end - def time if @extra['UniversalTime'] && !@extra['UniversalTime'].mtime.nil? @extra['UniversalTime'].mtime @@ -141,6 +133,7 @@ def time alias mtime time def time=(value) + @dirty = true unless @extra.member?('UniversalTime') || @extra.member?('NTFS') @extra.create('UniversalTime') end @@ -157,6 +150,7 @@ def compression_method end def compression_method=(method) + @dirty = true @compression_method = (@ftype == :directory ? STORED : method) end diff --git a/test/filesystem/file_nonmutating_test.rb b/test/filesystem/file_nonmutating_test.rb index 7124cce2..e6b1f132 100644 --- a/test/filesystem/file_nonmutating_test.rb +++ b/test/filesystem/file_nonmutating_test.rb @@ -152,15 +152,6 @@ def test_join assert_equal('a/b/c/d', @zip_file.file.join('a', 'b', 'c', 'd')) end - def test_utime - t_now = ::Zip::DOSTime.now - t_bak = @zip_file.file.mtime('file1') - @zip_file.file.utime(t_now, 'file1') - assert_equal(t_now, @zip_file.file.mtime('file1')) - @zip_file.file.utime(t_bak, 'file1') - assert_equal(t_bak, @zip_file.file.mtime('file1')) - end - def assert_always_false(operation) assert(!@zip_file.file.send(operation, 'noSuchFile')) assert(!@zip_file.file.send(operation, 'file1')) From ae0262df2e75fab1ce1ecbc1095c33032e1241d6 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Wed, 23 Feb 2022 10:13:13 +0000 Subject: [PATCH 242/311] Add `Entry#zip64?` as a better way detect Zip64 entries. --- lib/zip/central_directory.rb | 4 ++-- lib/zip/entry.rb | 6 +++++- test/local_entry_test.rb | 4 ++-- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/lib/zip/central_directory.rb b/lib/zip/central_directory.rb index bad4100d..146c444f 100644 --- a/lib/zip/central_directory.rb +++ b/lib/zip/central_directory.rb @@ -47,7 +47,7 @@ def write_to_stream(io) #:nodoc: if ::Zip.write_zip64_support need_zip64_eocd = cdir_offset > 0xFFFFFFFF || cdir_size > 0xFFFFFFFF \ || @entry_set.size > 0xFFFF - need_zip64_eocd ||= @entry_set.any? { |entry| entry.extra['Zip64'] } + 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) @@ -185,7 +185,7 @@ def read_central_directory_entries(io) #:nodoc: entry = Entry.read_c_dir_entry(io) next unless entry - offset = if entry.extra['Zip64'] + offset = if entry.zip64? entry.extra['Zip64'].relative_header_offset else entry.local_header_offset diff --git a/lib/zip/entry.rb b/lib/zip/entry.rb index 36bb2dea..8f6714c6 100644 --- a/lib/zip/entry.rb +++ b/lib/zip/entry.rb @@ -154,6 +154,10 @@ def compression_method=(method) @compression_method = (@ftype == :directory ? STORED : method) end + def zip64? + !@extra['Zip64'].nil? + end + def file_type_is?(type) raise InternalError, "current filetype is unknown: #{inspect}" unless @ftype @@ -721,7 +725,7 @@ def create_symlink(dest_path) # apply missing data from the zip64 extra information field, if present # (required when file sizes exceed 2**32, but can be used for all files) def parse_zip64_extra(for_local_header) #:nodoc:all - return if @extra['Zip64'].nil? + return unless zip64? if for_local_header @size, @compressed_size = @extra['Zip64'].parse(@size, @compressed_size) diff --git a/test/local_entry_test.rb b/test/local_entry_test.rb index 3cf21e62..f934d49d 100644 --- a/test/local_entry_test.rb +++ b/test/local_entry_test.rb @@ -122,13 +122,13 @@ def test_rewrite_local_header64 buf1 = StringIO.new entry = ::Zip::Entry.new('file.zip', 'entry_name') entry.write_local_entry(buf1) - assert(entry.extra['Zip64'].nil?, 'zip64 extra is unnecessarily present') + refute(entry.zip64?, 'zip64 extra is unnecessarily present') buf2 = StringIO.new entry.size = 0x123456789ABCDEF0 entry.compressed_size = 0x0123456789ABCDEF entry.write_local_entry(buf2, rewrite: true) - refute_nil(entry.extra['Zip64']) + assert(entry.zip64?) refute_equal(buf1.size, 0) assert_equal(buf1.size, buf2.size) # it can't grow, or we'd clobber file data end From d6482bd56736683912258e632efdd88deadbd454 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Wed, 23 Feb 2022 12:39:01 +0000 Subject: [PATCH 243/311] Generalize `Entry#time`. So we can use it for `atime`, `ctime` and `utime` as well. --- lib/zip/entry.rb | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/lib/zip/entry.rb b/lib/zip/entry.rb index 8f6714c6..f32f71dd 100644 --- a/lib/zip/entry.rb +++ b/lib/zip/entry.rb @@ -118,16 +118,17 @@ def incomplete? gp_flags & 8 == 8 end - def time - if @extra['UniversalTime'] && !@extra['UniversalTime'].mtime.nil? - @extra['UniversalTime'].mtime - elsif @extra['NTFS'] && !@extra['NTFS'].mtime.nil? - @extra['NTFS'].mtime - else - # Standard time field in central directory has local time - # under archive creator. Then, we can't get timezone. - @time - end + def time(component: :mtime) + time = + if @extra['UniversalTime'] + @extra['UniversalTime'].send(component) + elsif @extra['NTFS'] + @extra['NTFS'].send(component) + end + + # Standard time field in central directory has local time + # under archive creator. Then, we can't get timezone. + time || (@time if component == :mtime) end alias mtime time From 62ed397b1afcb941c88c69836d841c2d70d2ddbb Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Wed, 23 Feb 2022 12:40:47 +0000 Subject: [PATCH 244/311] Generalize `Entry#time=`. So we can use it for `atime=`, `ctime=` and `utime=` as well. --- lib/zip/entry.rb | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/zip/entry.rb b/lib/zip/entry.rb index f32f71dd..92e2c2f5 100644 --- a/lib/zip/entry.rb +++ b/lib/zip/entry.rb @@ -133,15 +133,16 @@ def time(component: :mtime) alias mtime time - def time=(value) + def time=(value, component: :mtime) @dirty = true unless @extra.member?('UniversalTime') || @extra.member?('NTFS') @extra.create('UniversalTime') end value = DOSTime.from_time(value) - (@extra['UniversalTime'] || @extra['NTFS']).mtime = value - @time = value + comp = "#{component}=" unless component.to_s.end_with?('=') + (@extra['UniversalTime'] || @extra['NTFS']).send(comp, value) + @time = value if component == :mtime end def compression_method From fff1f8ea8ac472ff01538245749fdf1d5808de36 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Wed, 23 Feb 2022 12:54:56 +0000 Subject: [PATCH 245/311] Add `Entry#mtime=` as an alias of `Entry#time=`. --- lib/zip/entry.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/zip/entry.rb b/lib/zip/entry.rb index 92e2c2f5..28ce277f 100644 --- a/lib/zip/entry.rb +++ b/lib/zip/entry.rb @@ -145,6 +145,8 @@ def time=(value, component: :mtime) @time = value if component == :mtime end + alias mtime= time= + def compression_method return STORED if @ftype == :directory || @compression_level == 0 From 466383ff1a21e366c42192883de0edd778384174 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Wed, 23 Feb 2022 21:49:34 +0000 Subject: [PATCH 246/311] Add other `Entry` time methods and test them all. --- .rubocop_todo.yml | 2 +- lib/zip/entry.rb | 16 +++++++++++++++ test/entry_test.rb | 50 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 67 insertions(+), 1 deletion(-) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 387a9cd6..2b3f89b1 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -20,7 +20,7 @@ Lint/MissingSuper: # Offense count: 5 # Configuration parameters: CountComments, CountAsOne. Metrics/ClassLength: - Max: 630 + Max: 650 # Offense count: 21 # Configuration parameters: IgnoredMethods. diff --git a/lib/zip/entry.rb b/lib/zip/entry.rb index 28ce277f..732039ce 100644 --- a/lib/zip/entry.rb +++ b/lib/zip/entry.rb @@ -133,6 +133,14 @@ def time(component: :mtime) alias mtime time + def atime + time(component: :atime) + end + + def ctime + time(component: :ctime) + end + def time=(value, component: :mtime) @dirty = true unless @extra.member?('UniversalTime') || @extra.member?('NTFS') @@ -147,6 +155,14 @@ def time=(value, component: :mtime) alias mtime= time= + def atime=(value) + send(:time=, value, component: :atime) + end + + def ctime=(value) + send(:time=, value, component: :ctime) + end + def compression_method return STORED if @ftype == :directory || @compression_level == 0 diff --git a/test/entry_test.rb b/test/entry_test.rb index f0e423f4..0ecf2f4a 100644 --- a/test/entry_test.rb +++ b/test/entry_test.rb @@ -281,6 +281,56 @@ def test_set_time_as_dos_time assert(entry.time.kind_of?(::Zip::DOSTime)) end + def test_atime + entry = ::Zip::Entry.new + time = Time.new(1999, 12, 31, 23, 59, 59) + + entry.atime = time + assert(entry.dirty?) + assert_equal(::Zip::DOSTime.from_time(time), entry.atime) + refute_equal(entry.time, entry.atime) + assert(entry.atime.kind_of?(::Zip::DOSTime)) + assert_nil(entry.ctime) + end + + def test_ctime + entry = ::Zip::Entry.new + time = Time.new(1999, 12, 31, 23, 59, 59) + + entry.ctime = time + assert(entry.dirty?) + assert_equal(::Zip::DOSTime.from_time(time), entry.ctime) + refute_equal(entry.time, entry.ctime) + assert(entry.ctime.kind_of?(::Zip::DOSTime)) + assert_nil(entry.atime) + end + + def test_mtime + entry = ::Zip::Entry.new + time = Time.new(1999, 12, 31, 23, 59, 59) + + entry.mtime = time + assert(entry.dirty?) + assert_equal(::Zip::DOSTime.from_time(time), entry.mtime) + assert_equal(entry.time, entry.mtime) + assert(entry.mtime.kind_of?(::Zip::DOSTime)) + assert_nil(entry.atime) + assert_nil(entry.ctime) + end + + def test_time + entry = ::Zip::Entry.new + time = Time.new(1999, 12, 31, 23, 59, 59) + + entry.time = time + assert(entry.dirty?) + assert_equal(::Zip::DOSTime.from_time(time), entry.time) + assert_equal(entry.mtime, entry.time) + assert(entry.time.kind_of?(::Zip::DOSTime)) + assert_nil(entry.atime) + assert_nil(entry.ctime) + end + def test_ensure_entry_time_set_to_file_mtime entry = ::Zip::Entry.new entry.gather_fileinfo_from_srcpath('test/data/mimetype') From 6486047d5fdca6b331401ec487cff8ac65badcaf Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Wed, 23 Feb 2022 21:50:23 +0000 Subject: [PATCH 247/311] Use the new `Entry` time methods in `Filesystem::File`. --- lib/zip/filesystem/file.rb | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/lib/zip/filesystem/file.rb b/lib/zip/filesystem/file.rb index 8ee6d06b..cb094701 100644 --- a/lib/zip/filesystem/file.rb +++ b/lib/zip/filesystem/file.rb @@ -167,21 +167,11 @@ def mtime(filename) end def atime(filename) - e = find_entry(filename) - if e.extra.member? 'UniversalTime' - e.extra['UniversalTime'].atime - elsif e.extra.member? 'NTFS' - e.extra['NTFS'].atime - end + @mapped_zip.get_entry(filename).atime end def ctime(filename) - e = find_entry(filename) - if e.extra.member? 'UniversalTime' - e.extra['UniversalTime'].ctime - elsif e.extra.member? 'NTFS' - e.extra['NTFS'].ctime - end + @mapped_zip.get_entry(filename).ctime end def pipe?(_filename) From c243b4429af9a29521b5ca2e99654e127684eadb Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sat, 25 Jun 2022 08:48:23 +0100 Subject: [PATCH 248/311] Factor out the code in `File` to init the CDir. This allows us to reuse it without overwriting any options passed to `File`. --- lib/zip/file.rb | 61 ++++++++++++++++++++++++++----------------------- 1 file changed, 33 insertions(+), 28 deletions(-) diff --git a/lib/zip/file.rb b/lib/zip/file.rb index 25389db3..93a76d33 100644 --- a/lib/zip/file.rb +++ b/lib/zip/file.rb @@ -75,35 +75,8 @@ def initialize(path_or_io, create: false, buffer: false, **options) .merge(options) @name = path_or_io.respond_to?(:path) ? path_or_io.path : path_or_io @create = create ? true : false # allow any truthy value to mean true - @cdir = ::Zip::CentralDirectory.new - - if ::File.size?(@name.to_s) - # There is a file, which exists, that is associated with this zip. - @create = false - @file_permissions = ::File.stat(@name).mode - if buffer - # https://github.com/rubyzip/rubyzip/issues/119 - path_or_io.binmode if path_or_io.respond_to?(:binmode) - @cdir.read_from_stream(path_or_io) - else - ::File.open(@name, 'rb') do |f| - @cdir.read_from_stream(f) - end - end - elsif buffer && path_or_io.size > 0 - # This zip is probably a non-empty StringIO. - @create = false - @cdir.read_from_stream(path_or_io) - elsif !@create && ::File.zero?(@name) - # A file exists, but it is empty, and we've said we're - # NOT creating a new zip. - raise Error, "File #{@name} has zero size. Did you mean to pass the create flag?" - elsif !@create - # If we get here, and we're not creating a new zip, then - # everything is wrong. - raise Error, "File #{@name} not found" - end + initialize_cdir(path_or_io, buffer: buffer) @restore_ownership = options[:restore_ownership] @restore_permissions = options[:restore_permissions] @@ -356,6 +329,38 @@ def mkdir(entry_name, permission = 0o755) private + def initialize_cdir(path_or_io, buffer: false) + @cdir = ::Zip::CentralDirectory.new + + if ::File.size?(@name.to_s) + # There is a file, which exists, that is associated with this zip. + @create = false + @file_permissions = ::File.stat(@name).mode + + if buffer + # https://github.com/rubyzip/rubyzip/issues/119 + path_or_io.binmode if path_or_io.respond_to?(:binmode) + @cdir.read_from_stream(path_or_io) + else + ::File.open(@name, 'rb') do |f| + @cdir.read_from_stream(f) + end + end + elsif buffer && path_or_io.size > 0 + # This zip is probably a non-empty StringIO. + @create = false + @cdir.read_from_stream(path_or_io) + elsif !@create && ::File.zero?(@name) + # A file exists, but it is empty, and we've said we're + # NOT creating a new zip. + raise Error, "File #{@name} has zero size. Did you mean to pass the create flag?" + elsif !@create + # If we get here, and we're not creating a new zip, then + # everything is wrong. + raise Error, "File #{@name} not found" + end + end + def check_entry_exists(entry_name, continue_on_exists_proc, proc_name) continue_on_exists_proc ||= proc { Zip.continue_on_exists_proc } return unless @cdir.include?(entry_name) From 14ff11ba05795c289cb1ea18b32287aa654d3cb1 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sat, 25 Jun 2022 08:51:32 +0100 Subject: [PATCH 249/311] Re-initialize CDir after a `commit`. Using the factored-out code preserves options set in `File`. Fixes #529. --- lib/zip/file.rb | 2 +- test/file_test.rb | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/lib/zip/file.rb b/lib/zip/file.rb index 93a76d33..89a098f9 100644 --- a/lib/zip/file.rb +++ b/lib/zip/file.rb @@ -267,7 +267,7 @@ def commit end true end - initialize(name) + initialize_cdir(@name) end # Write buffer write changes to buffer and return diff --git a/test/file_test.rb b/test/file_test.rb index 82019f05..e78eb8a1 100644 --- a/test/file_test.rb +++ b/test/file_test.rb @@ -640,6 +640,16 @@ def test_commit assert_equal(res, true) end + def test_commit_preserves_options + zip_file = 'test/data/generated/preserve_options.zip' + ::Zip::File.open(zip_file, create: true, compression_level: 8) do |zf| + assert(zf.commit_required?) + zf.commit + assert_equal(8, zf.instance_variable_get(:@compression_level)) + refute(zf.commit_required?) + end + end + def test_double_commit(filename = 'test/data/generated/double_commit_test.zip') ::FileUtils.touch('test/data/generated/test_double_commit1.txt') ::FileUtils.touch('test/data/generated/test_double_commit2.txt') From 708b7f53932071b552b746b6d66b6c21b5549a6e Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sat, 25 Jun 2022 08:53:35 +0100 Subject: [PATCH 250/311] Add a couple more checks in the tests for double `commit`s. Just ensure that a `commit` really does stick with both new and edited zip files. --- test/file_test.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/file_test.rb b/test/file_test.rb index e78eb8a1..106e69ab 100644 --- a/test/file_test.rb +++ b/test/file_test.rb @@ -656,8 +656,11 @@ def test_double_commit(filename = 'test/data/generated/double_commit_test.zip') zf = ::Zip::File.open(filename, create: true) zf.add('test1.txt', 'test/data/generated/test_double_commit1.txt') zf.commit + refute(zf.commit_required?) zf.add('test2.txt', 'test/data/generated/test_double_commit2.txt') + assert(zf.commit_required?) zf.commit + refute(zf.commit_required?) zf.close zf2 = ::Zip::File.open(filename) refute_nil(zf2.entries.detect { |e| e.name == 'test1.txt' }) From 6f1ad8b37dd360770cc34c378f71b5f0f8caaeca Mon Sep 17 00:00:00 2001 From: Brian Williams Date: Thu, 4 Aug 2022 13:53:33 -0500 Subject: [PATCH 251/311] Fix unraised error on encrypted archives --- lib/zip/input_stream.rb | 2 +- test/input_stream_test.rb | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/lib/zip/input_stream.rb b/lib/zip/input_stream.rb index e292ff3d..e0739a89 100644 --- a/lib/zip/input_stream.rb +++ b/lib/zip/input_stream.rb @@ -144,7 +144,7 @@ def open_entry @current_entry = ::Zip::Entry.read_local_entry(@archive_io) return if @current_entry.nil? - if @current_entry.encrypted? && @decrypter.kind_of?(NullEncrypter) + if @current_entry.encrypted? && @decrypter.kind_of?(NullDecrypter) raise Error, 'A password is required to decode this zip file' end diff --git a/test/input_stream_test.rb b/test/input_stream_test.rb index 773381f8..293483ce 100644 --- a/test/input_stream_test.rb +++ b/test/input_stream_test.rb @@ -91,6 +91,14 @@ def test_open_split_archive_raises_error end end + def test_open_encrypted_archive_raises_error + ::Zip::InputStream.open('test/data/zipWithEncryption.zip') do |zis| + assert_raises(::Zip::Error) do + zis.get_next_entry + end + end + end + def test_size_no_entry zis = ::Zip::InputStream.open(TestZipFile::TEST_ZIP2.zip_name) assert_nil(zis.size) From 08391da4d51feac37862194c7a3f9c41f2ce2e14 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sat, 13 Aug 2022 22:09:55 +0100 Subject: [PATCH 252/311] Ensure that `Entry.ftype` is correct via `InputStream`. When reading an archive with `InputStream`, `Entry.ftype` was returning `:file` for all entries, even if they were a directory. This is due to various side-effects in many methods in `Entry`. This commit fixes the behaviour, but not the side-effects. Fixes #533. --- lib/zip/entry.rb | 29 +++++++++++++++++------------ test/input_stream_test.rb | 10 ++++++++++ 2 files changed, 27 insertions(+), 12 deletions(-) diff --git a/lib/zip/entry.rb b/lib/zip/entry.rb index 732039ce..bfbf4b8b 100644 --- a/lib/zip/entry.rb +++ b/lib/zip/entry.rb @@ -26,7 +26,7 @@ class Entry attr_accessor :crc, :external_file_attributes, :fstype, :gp_flags, :internal_file_attributes, :local_header_offset # :nodoc: - attr_reader :extra, :compression_level, :ftype, :filepath # :nodoc: + attr_reader :extra, :compression_level, :filepath # :nodoc: mark_dirty :comment=, :compressed_size=, :external_file_attributes=, :fstype=, :gp_flags=, :name=, :size=, @@ -87,7 +87,6 @@ def initialize( set_default_vars_values @fstype = ::Zip::RUNNING_ON_WINDOWS ? ::Zip::FSTYPE_FAT : ::Zip::FSTYPE_UNIX - @ftype = name_is_directory? ? :directory : :file @zipfile = zipfile @comment = comment || '' @@ -164,14 +163,14 @@ def ctime=(value) end def compression_method - return STORED if @ftype == :directory || @compression_level == 0 + return STORED if ftype == :directory || @compression_level == 0 @compression_method end def compression_method=(method) @dirty = true - @compression_method = (@ftype == :directory ? STORED : method) + @compression_method = (ftype == :directory ? STORED : method) end def zip64? @@ -179,9 +178,11 @@ def zip64? end def file_type_is?(type) - raise InternalError, "current filetype is unknown: #{inspect}" unless @ftype + ftype == type + end - @ftype == type + def ftype # :nodoc: + @ftype ||= name_is_directory? ? :directory : :file end # Dynamic checkers @@ -263,7 +264,7 @@ def extract(dest_path = nil, &block) raise "unknown file type #{inspect}" unless directory? || file? || symlink? - __send__("create_#{@ftype}", dest_path, &block) + __send__("create_#{ftype}", dest_path, &block) self end @@ -340,6 +341,10 @@ def read_local_entry(io) #:nodoc:all end @name.tr!('\\', '/') # Normalise filepath separators after encoding set. + # We need to do this here because `initialize` has so many side-effects. + # :-( + @ftype = name_is_directory? ? :directory : :file + extra = io.read(@extra_length) if extra && extra.bytesize != @extra_length raise ::Zip::Error, 'Truncated local zip entry header' @@ -554,7 +559,7 @@ def write_c_dir_entry(io) #:nodoc:all prep_zip64_extra(false) case @fstype when ::Zip::FSTYPE_UNIX - ft = case @ftype + ft = case ftype when :file @unix_perms ||= 0o644 ::Zip::FILE_TYPE_FILE @@ -594,11 +599,11 @@ def <=>(other) # Returns an IO like object for the given ZipEntry. # Warning: may behave weird with symlinks. def get_input_stream(&block) - if @ftype == :directory + if ftype == :directory yield ::Zip::NullInputStream if block ::Zip::NullInputStream elsif @filepath - case @ftype + case ftype when :file ::File.open(@filepath, 'rb', &block) when :symlink @@ -607,7 +612,7 @@ def get_input_stream(&block) yield(stringio) if block stringio else - raise "unknown @file_type #{@ftype}" + raise "unknown @file_type #{ftype}" end else zis = ::Zip::InputStream.new(@zipfile, offset: local_header_offset) @@ -654,7 +659,7 @@ def gather_fileinfo_from_srcpath(src_path) # :nodoc: end def write_to_zip_output_stream(zip_output_stream) #:nodoc:all - if @ftype == :directory + if ftype == :directory zip_output_stream.put_next_entry(self) elsif @filepath zip_output_stream.put_next_entry(self) diff --git a/test/input_stream_test.rb b/test/input_stream_test.rb index 293483ce..6500e5eb 100644 --- a/test/input_stream_test.rb +++ b/test/input_stream_test.rb @@ -111,6 +111,16 @@ def test_size_with_entry end end + def test_get_entry_ftypes + ::Zip::InputStream.open(TestZipFile::TEST_ZIP4.zip_name) do |zis| + entry = zis.get_next_entry + assert_equal(:file, entry.ftype) + + entry = zis.get_next_entry + assert_equal(:directory, entry.ftype) + end + end + def test_incomplete_reads ::Zip::InputStream.open(TestZipFile::TEST_ZIP2.zip_name) do |zis| entry = zis.get_next_entry # longAscii.txt From 2e4dd9e0aa6194784f608211928b7d16edb5b8e6 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sat, 27 Nov 2021 11:02:29 +0000 Subject: [PATCH 253/311] Improve the message for CompressionMethodError. Convert the compression method number into a meaningful text representation, e.g., "BZIP2" instead of "12". --- lib/zip/errors.rb | 14 +++++++++++++- lib/zip/input_stream.rb | 3 +-- lib/zip/output_stream.rb | 3 +-- test/bzip2_support_test.rb | 7 ++++++- 4 files changed, 21 insertions(+), 6 deletions(-) diff --git a/lib/zip/errors.rb b/lib/zip/errors.rb index 66ae5356..3e1895a1 100644 --- a/lib/zip/errors.rb +++ b/lib/zip/errors.rb @@ -4,11 +4,23 @@ module Zip class Error < StandardError; end class EntryExistsError < Error; end class DestinationFileExistsError < Error; end - class CompressionMethodError < Error; end class EntryNameError < Error; end class EntrySizeError < Error; end class InternalError < Error; end class GPFBit3Error < Error; end class DecompressionError < Error; end class SplitArchiveError < Error; end + + class CompressionMethodError < Error + attr_reader :compression_method + + def initialize(method) + super() + @compression_method = method + end + + def message + "Unsupported compression method: #{COMPRESSION_METHODS[@compression_method]}." + end + end end diff --git a/lib/zip/input_stream.rb b/lib/zip/input_stream.rb index e0739a89..a1f29294 100644 --- a/lib/zip/input_stream.rb +++ b/lib/zip/input_stream.rb @@ -185,8 +185,7 @@ def get_decompressor @current_entry.compression_method ) if decompressor_class.nil? - raise ::Zip::CompressionMethodError, - "Unsupported compression method #{@current_entry.compression_method}" + raise ::Zip::CompressionMethodError, @current_entry.compression_method end decompressor_class.new(@decrypted_io, decompressed_size) diff --git a/lib/zip/output_stream.rb b/lib/zip/output_stream.rb index a2290c5d..0c4e93c8 100644 --- a/lib/zip/output_stream.rb +++ b/lib/zip/output_stream.rb @@ -170,8 +170,7 @@ def get_compressor(entry) when Entry::STORED ::Zip::PassThruCompressor.new(@output_stream) else - raise ::Zip::CompressionMethodError, - "Invalid compression method: '#{entry.compression_method}'" + raise ::Zip::CompressionMethodError, entry.compression_method end end diff --git a/test/bzip2_support_test.rb b/test/bzip2_support_test.rb index 119d1c0d..91c954a2 100644 --- a/test/bzip2_support_test.rb +++ b/test/bzip2_support_test.rb @@ -7,7 +7,12 @@ class Bzip2SupportTest < MiniTest::Test def test_read Zip::InputStream.open(BZIP2_ZIP_TEST_FILE) do |zis| - assert_raises(Zip::CompressionMethodError) { zis.get_next_entry } + error = assert_raises(Zip::CompressionMethodError) do + zis.get_next_entry + end + + assert_equal(12, error.compression_method) + assert_match(/BZIP2/, error.message) end end end From 03a9ee6b8a74aca0c46f7ed0123ab1e4bb7d96d4 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sun, 28 Nov 2021 16:51:06 +0000 Subject: [PATCH 254/311] Rename `GPFBit3Error` to `StreamingError`. `GPFBit3Error` doesn't really mean anything to the general user, and it's not descriptive of the issue at hand. This error is raised when a zip file cannot be streamed via `InputStream`, so `StreamingError` makes more sense. Also standardize the error message while we're about it. --- README.md | 2 +- lib/zip/errors.rb | 16 +++++++++++++++- lib/zip/input_stream.rb | 12 ++---------- test/file_test.rb | 6 +++++- test/input_stream_test.rb | 8 ++++++-- 5 files changed, 29 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 12d18720..2dca73c5 100644 --- a/README.md +++ b/README.md @@ -185,7 +185,7 @@ There is one exception where it can not work however, and this is if the file do > If bit 3 (0x08) of the general-purpose flags field is set, then the CRC-32 and file sizes are not known when the header is written. The fields in the local header are filled with zero, and the CRC-32 and size are appended in a 12-byte structure (optionally preceded by a 4-byte signature) immediately after the compressed data. -If `Zip::InputStream` finds such an entry in the zip archive it will raise an exception (`Zip::GPFBit3Error`). +If `Zip::InputStream` finds such an entry in the zip archive it will raise an exception (`Zip::StreamingError`). `Zip::InputStream` is not designed to be used for random access in a zip file. When performing any operations on an entry that you are accessing via `Zip::InputStream.get_next_entry` then you should complete any such operations before the next call to `get_next_entry`. diff --git a/lib/zip/errors.rb b/lib/zip/errors.rb index 3e1895a1..fd10022d 100644 --- a/lib/zip/errors.rb +++ b/lib/zip/errors.rb @@ -7,7 +7,6 @@ class DestinationFileExistsError < Error; end class EntryNameError < Error; end class EntrySizeError < Error; end class InternalError < Error; end - class GPFBit3Error < Error; end class DecompressionError < Error; end class SplitArchiveError < Error; end @@ -23,4 +22,19 @@ def message "Unsupported compression method: #{COMPRESSION_METHODS[@compression_method]}." end end + + class StreamingError < Error + attr_reader :entry + + def initialize(entry) + super() + @entry = entry + end + + def message + "The local header of this entry ('#{@entry.name}') does not contain " \ + 'the correct metadata for `Zip::InputStream` to be able to ' \ + 'uncompress it. Please use `Zip::File` instead of `Zip::InputStream`.' + end + end end diff --git a/lib/zip/input_stream.rb b/lib/zip/input_stream.rb index a1f29294..f48466c0 100644 --- a/lib/zip/input_stream.rb +++ b/lib/zip/input_stream.rb @@ -70,12 +70,7 @@ def close # Returns nil when there are no more entries. def get_next_entry unless @current_entry.nil? - if @current_entry.incomplete? - raise GPFBit3Error, - 'It is not possible to get complete info from the local ' \ - 'header to extract this entry (GP flags bit 3 is set). ' \ - 'Please use `Zip::File` instead of `Zip::InputStream`.' - end + raise StreamingError, @current_entry if @current_entry.incomplete? @archive_io.seek(@current_entry.next_header_offset, IO::SEEK_SET) end @@ -151,10 +146,7 @@ def open_entry if @current_entry.incomplete? && @current_entry.compressed_size == 0 \ && !@complete_entry - raise GPFBit3Error, - 'It is not possible to get complete info from the local ' \ - 'header to extract this entry (GP flags bit 3 is set). ' \ - 'Please use `Zip::File` instead of `Zip::InputStream`.' + raise StreamingError, @current_entry end @decrypted_io = get_decrypted_io diff --git a/test/file_test.rb b/test/file_test.rb index 106e69ab..dfc48c81 100644 --- a/test/file_test.rb +++ b/test/file_test.rb @@ -46,9 +46,13 @@ def test_create_from_scratch def test_get_input_stream_stored_with_gpflag_bit3 ::Zip::File.open('test/data/gpbit3stored.zip') do |zf| zis = zf.get_input_stream('file1.txt') - assert_raises(::Zip::GPFBit3Error) do + + error = assert_raises(::Zip::StreamingError) do zis.get_next_entry end + assert_match(/file1\.txt/, error.message) + assert_equal('file1.txt', error.entry.name) + zf.get_input_stream('file2.txt') end end diff --git a/test/input_stream_test.rb b/test/input_stream_test.rb index 6500e5eb..becb9336 100644 --- a/test/input_stream_test.rb +++ b/test/input_stream_test.rb @@ -69,17 +69,21 @@ def test_open_io_like_with_block def test_open_file_with_gp3bit_set ::Zip::InputStream.open('test/data/gpbit3stored.zip') do |zis| - assert_raises(::Zip::GPFBit3Error) do + error = assert_raises(::Zip::StreamingError) do zis.get_next_entry end + assert_match(/file1\.txt/, error.message) + assert_equal('file1.txt', error.entry.name) end end def test_open_file_with_gp3bit_set_created_by_osx_archive ::Zip::InputStream.open('test/data/osx-archive.zip') do |zis| - assert_raises(::Zip::GPFBit3Error) do + error = assert_raises(::Zip::StreamingError) do zis.get_next_entry end + assert_match(/1\.txt/, error.message) + assert_equal('1.txt', error.entry.name) end end From 19fe79e31e336ffc92771149cbeb84c2cb646039 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sun, 14 Aug 2022 11:30:43 +0100 Subject: [PATCH 255/311] Define the SplitArchiveError message within the error class. --- lib/zip/entry.rb | 3 +-- lib/zip/errors.rb | 7 ++++++- test/input_stream_test.rb | 3 ++- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/lib/zip/entry.rb b/lib/zip/entry.rb index bfbf4b8b..879ae54a 100644 --- a/lib/zip/entry.rb +++ b/lib/zip/entry.rb @@ -326,8 +326,7 @@ def read_local_entry(io) #:nodoc:all unless @header_signature == LOCAL_ENTRY_SIGNATURE if @header_signature == SPLIT_FILE_SIGNATURE - raise SplitArchiveError, - 'Rubyzip cannot extract from split archives at this time' + raise SplitArchiveError end raise Error, "Zip local header magic not found at location '#{local_header_offset}'" diff --git a/lib/zip/errors.rb b/lib/zip/errors.rb index fd10022d..e8582b72 100644 --- a/lib/zip/errors.rb +++ b/lib/zip/errors.rb @@ -8,7 +8,6 @@ class EntryNameError < Error; end class EntrySizeError < Error; end class InternalError < Error; end class DecompressionError < Error; end - class SplitArchiveError < Error; end class CompressionMethodError < Error attr_reader :compression_method @@ -23,6 +22,12 @@ def message end end + class SplitArchiveError < Error + def message + 'Rubyzip cannot extract from split archives at this time.' + end + end + class StreamingError < Error attr_reader :entry diff --git a/test/input_stream_test.rb b/test/input_stream_test.rb index becb9336..e1b92073 100644 --- a/test/input_stream_test.rb +++ b/test/input_stream_test.rb @@ -89,9 +89,10 @@ def test_open_file_with_gp3bit_set_created_by_osx_archive def test_open_split_archive_raises_error ::Zip::InputStream.open('test/data/invalid-split.zip') do |zis| - assert_raises(::Zip::SplitArchiveError) do + error = assert_raises(::Zip::SplitArchiveError) do zis.get_next_entry end + refute(error.message.empty?) end end From 51231673a40742f6ad64fbde880a79cb5952ad62 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sun, 14 Aug 2022 16:31:03 +0100 Subject: [PATCH 256/311] Define the DecompressionError message within the error class. --- lib/zip/errors.rb | 14 +++++++++++++- lib/zip/inflater.rb | 3 +-- test/encryption_test.rb | 3 ++- 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/lib/zip/errors.rb b/lib/zip/errors.rb index e8582b72..d67c8c8e 100644 --- a/lib/zip/errors.rb +++ b/lib/zip/errors.rb @@ -7,7 +7,6 @@ class DestinationFileExistsError < Error; end class EntryNameError < Error; end class EntrySizeError < Error; end class InternalError < Error; end - class DecompressionError < Error; end class CompressionMethodError < Error attr_reader :compression_method @@ -22,6 +21,19 @@ def message end end + class DecompressionError < Error + attr_reader :zlib_error + + def initialize(zlib_error) + super() + @zlib_error = zlib_error + end + + def message + "Zlib error ('#{@zlib_error.message}') while inflating." + end + end + class SplitArchiveError < Error def message 'Rubyzip cannot extract from split archives at this time.' diff --git a/lib/zip/inflater.rb b/lib/zip/inflater.rb index c702da75..181603da 100644 --- a/lib/zip/inflater.rb +++ b/lib/zip/inflater.rb @@ -40,8 +40,7 @@ def produce_input retry end rescue Zlib::Error => e - raise ::Zip::DecompressionError, - "Zlib error ('#{e.message}') while inflating" + raise ::Zip::DecompressionError, e end def input_finished? diff --git a/test/encryption_test.rb b/test/encryption_test.rb index bed3fe68..6fd63950 100644 --- a/test/encryption_test.rb +++ b/test/encryption_test.rb @@ -38,7 +38,7 @@ def test_encrypt assert_equal content, zis.read end - assert_raises(Zip::DecompressionError) do + error = assert_raises(Zip::DecompressionError) do Zip::InputStream.open( encrypted_zip, decrypter: Zip::TraditionalDecrypter.new("#{password}wrong") @@ -47,6 +47,7 @@ def test_encrypt assert_equal content, zis.read end end + assert_match(/Zlib error \('.+'\) while inflating\./, error.message) end def test_decrypt From 04cc10a80fe788609d84aa0db48866a8c2a99b58 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sun, 14 Aug 2022 16:33:13 +0100 Subject: [PATCH 257/311] Remove the InternalError class (never used). --- lib/zip/errors.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/zip/errors.rb b/lib/zip/errors.rb index d67c8c8e..bb15b6b5 100644 --- a/lib/zip/errors.rb +++ b/lib/zip/errors.rb @@ -6,7 +6,6 @@ class EntryExistsError < Error; end class DestinationFileExistsError < Error; end class EntryNameError < Error; end class EntrySizeError < Error; end - class InternalError < Error; end class CompressionMethodError < Error attr_reader :compression_method From 7097492dc8c6975cedc303d0d66c8f264f71a9e7 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sun, 14 Aug 2022 22:05:42 +0100 Subject: [PATCH 258/311] Define the EntryExistsError message within the error class. --- lib/zip/errors.rb | 13 ++++++++++++- lib/zip/file.rb | 11 ++++------- test/case_sensitivity_test.rb | 3 ++- test/file_test.rb | 11 ++++++++--- test/settings_test.rb | 3 ++- 5 files changed, 28 insertions(+), 13 deletions(-) diff --git a/lib/zip/errors.rb b/lib/zip/errors.rb index bb15b6b5..d617645e 100644 --- a/lib/zip/errors.rb +++ b/lib/zip/errors.rb @@ -2,7 +2,6 @@ module Zip class Error < StandardError; end - class EntryExistsError < Error; end class DestinationFileExistsError < Error; end class EntryNameError < Error; end class EntrySizeError < Error; end @@ -33,6 +32,18 @@ def message end end + class EntryExistsError < Error + def initialize(source, name) + super() + @source = source + @name = name + end + + def message + "'#{@source}' failed. Entry #{@name} already exists." + end + end + class SplitArchiveError < Error def message 'Rubyzip cannot extract from split archives at this time.' diff --git a/lib/zip/file.rb b/lib/zip/file.rb index 89a098f9..04e1d802 100644 --- a/lib/zip/file.rb +++ b/lib/zip/file.rb @@ -362,15 +362,12 @@ def initialize_cdir(path_or_io, buffer: false) end def check_entry_exists(entry_name, continue_on_exists_proc, proc_name) - continue_on_exists_proc ||= proc { Zip.continue_on_exists_proc } return unless @cdir.include?(entry_name) - if continue_on_exists_proc.call - remove get_entry(entry_name) - else - raise ::Zip::EntryExistsError, - proc_name + " failed. Entry #{entry_name} already exists" - end + continue_on_exists_proc ||= proc { Zip.continue_on_exists_proc } + raise ::Zip::EntryExistsError.new proc_name, entry_name unless continue_on_exists_proc.call + + remove get_entry(entry_name) end def check_file(path) diff --git a/test/case_sensitivity_test.rb b/test/case_sensitivity_test.rb index 81ad2072..07e84579 100644 --- a/test/case_sensitivity_test.rb +++ b/test/case_sensitivity_test.rb @@ -40,9 +40,10 @@ def test_add_case_insensitive SRC_FILES.each { |fn, _en| assert(::File.exist?(fn)) } zf = ::Zip::File.new(EMPTY_FILENAME, create: true) - assert_raises Zip::EntryExistsError do + error = assert_raises Zip::EntryExistsError do SRC_FILES.each { |fn, en| zf.add(en, fn) } end + assert_match(/'add'/, error.message) end # Ensure that names are treated case insensitively when reading files and +case_insensitive_match = true+ diff --git a/test/file_test.rb b/test/file_test.rb index dfc48c81..225a1ab4 100644 --- a/test/file_test.rb +++ b/test/file_test.rb @@ -389,11 +389,12 @@ def test_recover_permissions_after_add_files_to_archive end def test_add_existing_entry_name - assert_raises(::Zip::EntryExistsError) do + error = assert_raises(::Zip::EntryExistsError) do ::Zip::File.open(TEST_ZIP.zip_name) do |zf| zf.add(zf.entries.first.name, 'test/data/file2.txt') end end + assert_match(/'add'/, error.message) end def test_add_existing_entry_name_replace @@ -538,11 +539,12 @@ def test_rename_to_existing_entry old_entries = nil ::Zip::File.open(TEST_ZIP.zip_name) { |zf| old_entries = zf.entries } - assert_raises(::Zip::EntryExistsError) do + error = assert_raises(::Zip::EntryExistsError) do ::Zip::File.open(TEST_ZIP.zip_name) do |zf| zf.rename(zf.entries[0], zf.entries[1].name) end end + assert_match(/'rename'/, error.message) ::Zip::File.open(TEST_ZIP.zip_name) do |zf| assert_equal(old_entries.sort.map(&:name), zf.entries.sort.map(&:name)) @@ -586,7 +588,10 @@ def test_rename_non_entry def test_rename_entry_to_existing_entry entry1, entry2, * = TEST_ZIP.entry_names zf = ::Zip::File.new(TEST_ZIP.zip_name) - assert_raises(::Zip::EntryExistsError) { zf.rename(entry1, entry2) } + error = assert_raises(::Zip::EntryExistsError) do + zf.rename(entry1, entry2) + end + assert_match(/'rename'/, error.message) ensure zf.close end diff --git a/test/settings_test.rb b/test/settings_test.rb index a0c6906d..9fb50f1e 100644 --- a/test/settings_test.rb +++ b/test/settings_test.rb @@ -46,11 +46,12 @@ def test_false_on_exists_proc def test_false_continue_on_exists_proc Zip.continue_on_exists_proc = false - assert_raises(::Zip::EntryExistsError) do + error = assert_raises(::Zip::EntryExistsError) do ::Zip::File.open(TEST_ZIP.zip_name) do |zf| zf.add(zf.entries.first.name, 'test/data/file2.txt') end end + assert_match(/'add'/, error.message) end def test_true_continue_on_exists_proc From 07eca2bae89a7f4d79d6c7bcdb8d28e4185c1fdb Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Mon, 15 Aug 2022 22:02:33 +0100 Subject: [PATCH 259/311] Define the EntrySizeError message within the error class. --- lib/zip/entry.rb | 6 +++--- lib/zip/errors.rb | 14 +++++++++++++- test/file_extract_test.rb | 5 +++-- 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/lib/zip/entry.rb b/lib/zip/entry.rb index 879ae54a..4d900171 100644 --- a/lib/zip/entry.rb +++ b/lib/zip/entry.rb @@ -711,10 +711,10 @@ def create_file(dest_path, _continue_on_exists_proc = proc { Zip.continue_on_exi bytes_written += buf.bytesize next unless bytes_written > size && !warned - message = "entry '#{name}' should be #{size}B, but is larger when inflated." - raise ::Zip::EntrySizeError, message if ::Zip.validate_entry_sizes + error = ::Zip::EntrySizeError.new(self) + raise error if ::Zip.validate_entry_sizes - warn "WARNING: #{message}" + warn "WARNING: #{error.message}" warned = true end end diff --git a/lib/zip/errors.rb b/lib/zip/errors.rb index d617645e..687a03de 100644 --- a/lib/zip/errors.rb +++ b/lib/zip/errors.rb @@ -4,7 +4,6 @@ module Zip class Error < StandardError; end class DestinationFileExistsError < Error; end class EntryNameError < Error; end - class EntrySizeError < Error; end class CompressionMethodError < Error attr_reader :compression_method @@ -44,6 +43,19 @@ def message end end + class EntrySizeError < Error + attr_reader :entry + + def initialize(entry) + super() + @entry = entry + end + + def message + "Entry '#{@entry.name}' should be #{@entry.size}B, but is larger when inflated." + end + end + class SplitArchiveError < Error def message 'Rubyzip cannot extract from split archives at this time.' diff --git a/test/file_extract_test.rb b/test/file_extract_test.rb index e5243cab..734351a8 100644 --- a/test/file_extract_test.rb +++ b/test/file_extract_test.rb @@ -139,9 +139,10 @@ def test_extract_incorrect_size error = assert_raises ::Zip::EntrySizeError do a_entry.extract end - assert_equal \ - "entry 'a' should be 1B, but is larger when inflated.", + assert_equal( + "Entry 'a' should be 1B, but is larger when inflated.", error.message + ) end end end From e3f0aecf93cd66a6c266c2e70b0183a286414530 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Tue, 16 Aug 2022 10:52:18 +0100 Subject: [PATCH 260/311] Define the EntryNameError message within the error class. --- lib/zip/entry.rb | 13 ++----------- lib/zip/errors.rb | 16 +++++++++++++++- test/entry_test.rb | 8 ++++++-- 3 files changed, 23 insertions(+), 14 deletions(-) diff --git a/lib/zip/entry.rb b/lib/zip/entry.rb index 4d900171..a563aa4d 100644 --- a/lib/zip/entry.rb +++ b/lib/zip/entry.rb @@ -61,17 +61,8 @@ def set_default_vars_values end def check_name(name) - error = - if name.start_with?('/') - "Illegal entry name '#{name}'. Names must not start with '/'" - elsif name.length > 65_535 - 'Illegal entry name. Names must have fewer than 65,536 characters' - else - '' - end - return if error.empty? - - raise EntryNameError, error + raise EntryNameError, name if name.start_with?('/') + raise EntryNameError if name.length > 65_535 end def initialize( diff --git a/lib/zip/errors.rb b/lib/zip/errors.rb index 687a03de..feac3b84 100644 --- a/lib/zip/errors.rb +++ b/lib/zip/errors.rb @@ -3,7 +3,6 @@ module Zip class Error < StandardError; end class DestinationFileExistsError < Error; end - class EntryNameError < Error; end class CompressionMethodError < Error attr_reader :compression_method @@ -43,6 +42,21 @@ def message end end + class EntryNameError < Error + def initialize(name = nil) + super() + @name = name + end + + def message + if @name.nil? + 'Illegal entry name. Names must have fewer than 65,536 characters.' + else + "Illegal entry name '#{@name}'. Names must not start with '/'." + end + end + end + class EntrySizeError < Error attr_reader :entry diff --git a/test/entry_test.rb b/test/entry_test.rb index 0ecf2f4a..0d88c874 100644 --- a/test/entry_test.rb +++ b/test/entry_test.rb @@ -150,16 +150,20 @@ def test_parent_as_string end def test_entry_name_cannot_start_with_slash - assert_raises(::Zip::EntryNameError) { ::Zip::Entry.new('zf.zip', '/hej/der') } + error = assert_raises(::Zip::EntryNameError) do + ::Zip::Entry.new('zf.zip', '/hej/der') + end + assert_match(/'\/hej\/der'/, error.message) end def test_entry_name_cannot_be_too_long name = 'a' * 65_535 ::Zip::Entry.new('', name) # Should not raise anything. - assert_raises(::Zip::EntryNameError) do + error = assert_raises(::Zip::EntryNameError) do ::Zip::Entry.new('', "a#{name}") end + assert_match(/65,536/, error.message) end def test_store_file_without_compression From 750d3723806a3bf8f8f9bd69cb98dfd1133493eb Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Tue, 16 Aug 2022 11:13:30 +0100 Subject: [PATCH 261/311] Rename DestinationFileExistsError -> DestinationExistsError. And define the error message within the class. --- lib/zip/entry.rb | 15 ++++++--------- lib/zip/errors.rb | 13 ++++++++++++- test/file_extract_directory_test.rb | 4 +++- test/file_extract_test.rb | 3 ++- test/settings_test.rb | 4 +++- 5 files changed, 26 insertions(+), 13 deletions(-) diff --git a/lib/zip/entry.rb b/lib/zip/entry.rb index a563aa4d..7c7f2e17 100644 --- a/lib/zip/entry.rb +++ b/lib/zip/entry.rb @@ -689,9 +689,9 @@ def set_time(binary_dos_date, binary_dos_time) def create_file(dest_path, _continue_on_exists_proc = proc { Zip.continue_on_exists_proc }) if ::File.exist?(dest_path) && !yield(self, dest_path) - raise ::Zip::DestinationFileExistsError, - "Destination '#{dest_path}' already exists" + raise ::Zip::DestinationExistsError, dest_path end + ::File.open(dest_path, 'wb') do |os| get_input_stream do |is| bytes_written = 0 @@ -718,14 +718,11 @@ def create_directory(dest_path) return if ::File.directory?(dest_path) if ::File.exist?(dest_path) - if block_given? && yield(self, dest_path) - ::FileUtils.rm_f dest_path - else - raise ::Zip::DestinationFileExistsError, - "Cannot create directory '#{dest_path}'. " \ - 'A file already exists with that name' - end + raise ::Zip::DestinationExistsError, dest_path unless block_given? && yield(self, dest_path) + + ::FileUtils.rm_f dest_path end + ::FileUtils.mkdir_p(dest_path) set_extra_attributes_on_path(dest_path) end diff --git a/lib/zip/errors.rb b/lib/zip/errors.rb index feac3b84..74bf3a3d 100644 --- a/lib/zip/errors.rb +++ b/lib/zip/errors.rb @@ -2,7 +2,6 @@ module Zip class Error < StandardError; end - class DestinationFileExistsError < Error; end class CompressionMethodError < Error attr_reader :compression_method @@ -30,6 +29,18 @@ def message end end + class DestinationExistsError < Error + def initialize(destination) + super() + @destination = destination + end + + def message + "Cannot create file or directory '#{@destination}'. " \ + 'A file already exists with that name.' + end + end + class EntryExistsError < Error def initialize(source, name) super() diff --git a/test/file_extract_directory_test.rb b/test/file_extract_directory_test.rb index fc10979d..26730a08 100644 --- a/test/file_extract_directory_test.rb +++ b/test/file_extract_directory_test.rb @@ -38,7 +38,9 @@ def test_extract_directory_exists_as_dir def test_extract_directory_exists_as_file File.open(TEST_OUT_NAME, 'w') { |f| f.puts 'something' } - assert_raises(::Zip::DestinationFileExistsError) { extract_test_dir } + assert_raises(::Zip::DestinationExistsError) do + extract_test_dir + end end def test_extract_directory_exists_as_file_overwrite diff --git a/test/file_extract_test.rb b/test/file_extract_test.rb index 734351a8..0bce8e1b 100644 --- a/test/file_extract_test.rb +++ b/test/file_extract_test.rb @@ -39,11 +39,12 @@ def test_extract_exists text = 'written text' ::File.open(EXTRACTED_FILENAME, 'w') { |f| f.write(text) } - assert_raises(::Zip::DestinationFileExistsError) do + assert_raises(::Zip::DestinationExistsError) do ::Zip::File.open(TEST_ZIP.zip_name) do |zf| zf.extract(zf.entries.first, EXTRACTED_FILENAME) end end + File.open(EXTRACTED_FILENAME, 'r') do |f| assert_equal(text, f.read) end diff --git a/test/settings_test.rb b/test/settings_test.rb index 9fb50f1e..3bf66e4e 100644 --- a/test/settings_test.rb +++ b/test/settings_test.rb @@ -40,7 +40,9 @@ def test_true_on_exists_proc def test_false_on_exists_proc Zip.on_exists_proc = false File.open(TEST_OUT_NAME, 'w') { |f| f.puts 'something' } - assert_raises(Zip::DestinationFileExistsError) { extract_test_dir } + assert_raises(Zip::DestinationExistsError) do + extract_test_dir + end end def test_false_continue_on_exists_proc From d6eb73566c2a5aed030d0c1dbfe3cb930e4c1de8 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Mon, 31 Jan 2022 20:43:55 -0800 Subject: [PATCH 262/311] Enable Zip64 by default Previously if RubyZip attempted to create an archive with more than 64K entries, the central directory would truncate the count. `unzip` and `zipinfo` would fail with an error message such as: ``` error: expected central file header signature not found (file #93272). (please check that you have transferred or created the zipfile in the appropriate BINARY mode and that you have compiled UnZip properly) ``` This generated a lot of confusion and a production issue since many tools fail to decode a RubyZip-created archive if Zip64 is not enabled for a large number of files. Since Zip64 support is now the norm, enable this by default. --- README.md | 4 ++-- lib/zip.rb | 2 +- test/local_entry_test.rb | 1 + 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 2dca73c5..232e7cb4 100644 --- a/README.md +++ b/README.md @@ -336,10 +336,10 @@ end ### Zip64 Support -By default, Zip64 support is disabled for writing. To enable it do this: +By default, Zip64 support is enabled for writing. To disable it do this: ```ruby -Zip.write_zip64_support = true +Zip.write_zip64_support = false ``` _NOTE_: If you will enable Zip64 writing then you will need zip extractor with Zip64 support to extract archive. diff --git a/lib/zip.rb b/lib/zip.rb index 87a236a8..82c52003 100644 --- a/lib/zip.rb +++ b/lib/zip.rb @@ -61,7 +61,7 @@ def reset! @continue_on_exists_proc = false @sort_entries = false @default_compression = ::Zlib::DEFAULT_COMPRESSION - @write_zip64_support = false + @write_zip64_support = true @warn_invalid_date = true @case_insensitive_match = false @force_entry_names_encoding = nil diff --git a/test/local_entry_test.rb b/test/local_entry_test.rb index f934d49d..a6a02f32 100644 --- a/test/local_entry_test.rb +++ b/test/local_entry_test.rb @@ -62,6 +62,7 @@ def test_read_local_entry_from_truncated_zip_file_returns_nil end def test_write_entry + ::Zip.write_zip64_support = false entry = ::Zip::Entry.new( 'file.zip', 'entry_name', comment: 'my little comment', size: 400, extra: 'thisIsSomeExtraInformation', compressed_size: 100, crc: 987_654 From f460da3afb0142f299b2987bbfc25a2ad5aa6724 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Fri, 11 Nov 2022 17:51:17 +0000 Subject: [PATCH 263/311] Prevent unnecessary Zip64 data being stored. With Zip64 write support enabled by default, it's important that we only store the extra data when we need to. This commit ensures that the Zip64 extra data is included for an entry if its size is over 4GB, or if we don't know how big it will be at the point of writing the local header data. This commit also removes the need for the Zip64Placeholder extra data field. Now we just use the Zip64 field itself and ensure it's filled in correctly. --- README.md | 2 +- lib/zip/entry.rb | 75 +++++++++++++----------- lib/zip/extra_field.rb | 1 - lib/zip/extra_field/zip64_placeholder.rb | 17 ------ test/central_directory_test.rb | 1 - test/file_extract_test.rb | 66 +++++++++++++++++++++ test/file_test.rb | 14 +++-- test/local_entry_test.rb | 44 +++++++++----- test/zip64_full_test.rb | 1 - 9 files changed, 146 insertions(+), 75 deletions(-) delete mode 100644 lib/zip/extra_field/zip64_placeholder.rb diff --git a/README.md b/README.md index 232e7cb4..3093e6b8 100644 --- a/README.md +++ b/README.md @@ -342,7 +342,7 @@ By default, Zip64 support is enabled for writing. To disable it do this: Zip.write_zip64_support = false ``` -_NOTE_: If you will enable Zip64 writing then you will need zip extractor with Zip64 support to extract archive. +_NOTE_: If Zip64 write support is enabled then any extractor subsequently used may also require Zip64 support to read from the resultant archive. ### Block Form diff --git a/lib/zip/entry.rb b/lib/zip/entry.rb index 7c7f2e17..36bc32c9 100644 --- a/lib/zip/entry.rb +++ b/lib/zip/entry.rb @@ -21,13 +21,15 @@ class Entry attr_accessor :comment, :compressed_size, :follow_symlinks, :name, :restore_ownership, :restore_permissions, :restore_times, - :size, :unix_gid, :unix_perms, :unix_uid + :unix_gid, :unix_perms, :unix_uid attr_accessor :crc, :external_file_attributes, :fstype, :gp_flags, :internal_file_attributes, :local_header_offset # :nodoc: attr_reader :extra, :compression_level, :filepath # :nodoc: + attr_writer :size # :nodoc: + mark_dirty :comment=, :compressed_size=, :external_file_attributes=, :fstype=, :gp_flags=, :name=, :size=, :unix_gid=, :unix_perms=, :unix_uid= @@ -67,7 +69,7 @@ def check_name(name) def initialize( zipfile = '', name = '', - comment: '', size: 0, compressed_size: 0, crc: 0, + comment: '', size: nil, compressed_size: 0, crc: 0, compression_method: DEFLATED, compression_level: ::Zip.default_compression, time: ::Zip::DOSTime.now, extra: ::Zip::ExtraField.new @@ -85,7 +87,7 @@ def initialize( @compression_level = compression_level || ::Zip.default_compression @compressed_size = compressed_size || 0 @crc = crc || 0 - @size = size || 0 + @size = size @time = case time when ::Zip::DOSTime time @@ -108,6 +110,10 @@ def incomplete? gp_flags & 8 == 8 end + def size + @size || 0 + end + def time(component: :mtime) time = if @extra['UniversalTime'] @@ -355,13 +361,13 @@ def pack_local_entry @time.to_binary_dos_date, # @last_mod_date @crc, zip64 && zip64.compressed_size ? 0xFFFFFFFF : @compressed_size, - zip64 && zip64.original_size ? 0xFFFFFFFF : @size, + zip64 && zip64.original_size ? 0xFFFFFFFF : (@size || 0), name_size, @extra ? @extra.local_size : 0].pack('VvvvvvVVVvv') end def write_local_entry(io, rewrite: false) #:nodoc:all - prep_zip64_extra(true) + prep_local_zip64_extra verify_local_header_size! if rewrite @local_header_offset = io.tell @@ -531,7 +537,7 @@ def pack_c_dir_entry @time.to_binary_dos_date, # @last_mod_date @crc, zip64 && zip64.compressed_size ? 0xFFFFFFFF : @compressed_size, - zip64 && zip64.original_size ? 0xFFFFFFFF : @size, + zip64 && zip64.original_size ? 0xFFFFFFFF : (@size || 0), name_size, @extra ? @extra.c_dir_size : 0, comment_size, @@ -546,7 +552,8 @@ def pack_c_dir_entry end def write_c_dir_entry(io) #:nodoc:all - prep_zip64_extra(false) + prep_cdir_zip64_extra + case @fstype when ::Zip::FSTYPE_UNIX ft = case ftype @@ -645,6 +652,7 @@ def gather_fileinfo_from_srcpath(src_path) # :nodoc: end @filepath = src_path + @size = stat.size get_extra_attributes_from_path(@filepath) end @@ -769,39 +777,38 @@ def set_compression_level_flags end end - # create a zip64 extra information field if we need one - def prep_zip64_extra(for_local_header) #:nodoc:all + # rubocop:disable Style/GuardClause + def prep_local_zip64_extra return unless ::Zip.write_zip64_support + return if (!zip64? && @size && @size < 0xFFFFFFFF) || !file? - need_zip64 = @size >= 0xFFFFFFFF || @compressed_size >= 0xFFFFFFFF - need_zip64 ||= @local_header_offset >= 0xFFFFFFFF unless for_local_header - if need_zip64 + # Might not know size here, so need ZIP64 just in case. + # If we already have a ZIP64 extra (placeholder) then we must fill it in. + if zip64? || @size.nil? || @size >= 0xFFFFFFFF || @compressed_size >= 0xFFFFFFFF @version_needed_to_extract = VERSION_NEEDED_TO_EXTRACT_ZIP64 - @extra.delete('Zip64Placeholder') - zip64 = @extra.create('Zip64') - if for_local_header - # local header always includes size and compressed size - zip64.original_size = @size - zip64.compressed_size = @compressed_size - else - # central directory entry entries include whichever fields are necessary - zip64.original_size = @size if @size >= 0xFFFFFFFF - zip64.compressed_size = @compressed_size if @compressed_size >= 0xFFFFFFFF - zip64.relative_header_offset = @local_header_offset if @local_header_offset >= 0xFFFFFFFF - end - else - @extra.delete('Zip64') + zip64 = @extra['Zip64'] || @extra.create('Zip64') - # if this is a local header entry, create a placeholder - # so we have room to write a zip64 extra field afterward - # (we won't know if it's needed until the file data is written) - if for_local_header - @extra.create('Zip64Placeholder') - else - @extra.delete('Zip64Placeholder') - end + # Local header always includes size and compressed size. + zip64.original_size = @size || 0 + zip64.compressed_size = @compressed_size end end + + def prep_cdir_zip64_extra + return unless ::Zip.write_zip64_support + + if (@size && @size >= 0xFFFFFFFF) || @compressed_size >= 0xFFFFFFFF || + @local_header_offset >= 0xFFFFFFFF + @version_needed_to_extract = VERSION_NEEDED_TO_EXTRACT_ZIP64 + zip64 = @extra['Zip64'] || @extra.create('Zip64') + + # Central directory entry entries include whichever fields are necessary. + zip64.original_size = @size if @size && @size >= 0xFFFFFFFF + zip64.compressed_size = @compressed_size if @compressed_size >= 0xFFFFFFFF + zip64.relative_header_offset = @local_header_offset if @local_header_offset >= 0xFFFFFFFF + end + end + # rubocop:enable Style/GuardClause end end diff --git a/lib/zip/extra_field.rb b/lib/zip/extra_field.rb index 658675b4..8a73c3c9 100644 --- a/lib/zip/extra_field.rb +++ b/lib/zip/extra_field.rb @@ -90,7 +90,6 @@ def local_size require 'zip/extra_field/old_unix' require 'zip/extra_field/unix' require 'zip/extra_field/zip64' -require 'zip/extra_field/zip64_placeholder' require 'zip/extra_field/ntfs' # Copyright (C) 2002, 2003 Thomas Sondergaard diff --git a/lib/zip/extra_field/zip64_placeholder.rb b/lib/zip/extra_field/zip64_placeholder.rb deleted file mode 100644 index 2619a68e..00000000 --- a/lib/zip/extra_field/zip64_placeholder.rb +++ /dev/null @@ -1,17 +0,0 @@ -# frozen_string_literal: true - -module Zip - # placeholder to reserve space for a Zip64 extra information record, for the - # local file header only, that we won't know if we'll need until after - # we write the file data - class ExtraField::Zip64Placeholder < ExtraField::Generic - HEADER_ID = ['9999'].pack('H*') # this ID is used by other libraries such as .NET's Ionic.zip - register_map - - def initialize(_binstr = nil); end - - def pack_for_local - "\x00" * 16 - end - end -end diff --git a/test/central_directory_test.rb b/test/central_directory_test.rb index 6197eb3b..37b5ca65 100644 --- a/test/central_directory_test.rb +++ b/test/central_directory_test.rb @@ -88,7 +88,6 @@ def test_write_to_stream end def test_write64_to_stream - ::Zip.write_zip64_support = true entries = [ ::Zip::Entry.new( 'file.zip', 'file1-little', comment: 'comment1', size: 200, diff --git a/test/file_extract_test.rb b/test/file_extract_test.rb index 0bce8e1b..3b4cbb97 100644 --- a/test/file_extract_test.rb +++ b/test/file_extract_test.rb @@ -89,6 +89,8 @@ def test_extract_another_non_entry end def test_extract_incorrect_size + Zip.write_zip64_support = false + # The uncompressed size fields in the zip file cannot be trusted. This makes # it harder for callers to validate the sizes of the files they are # extracting, which can lead to denial of service. See also @@ -148,4 +150,68 @@ def test_extract_incorrect_size end end end + + def test_extract_incorrect_size_zip64 + # The uncompressed size fields in the zip file cannot be trusted. This makes + # it harder for callers to validate the sizes of the files they are + # extracting, which can lead to denial of service. See also + # https://en.wikipedia.org/wiki/Zip_bomb + # + # This version of the test ensures that fraudulent sizes in the ZIP64 + # extensions are caught. + Dir.mktmpdir do |tmp| + real_zip = File.join(tmp, 'real.zip') + fake_zip = File.join(tmp, 'fake.zip') + file_name = 'a' + true_size = 500_000 + fake_size = 1 + + ::Zip::File.open(real_zip, create: true) do |zf| + zf.get_output_stream(file_name) do |os| + os.write 'a' * true_size + end + end + + compressed_size = nil + ::Zip::File.open(real_zip) do |zf| + a_entry = zf.find_entry(file_name) + compressed_size = a_entry.compressed_size + assert_equal true_size, a_entry.size + end + + true_size_bytes = [0x1, 16, true_size, compressed_size].pack('vvQ Date: Thu, 17 Nov 2022 18:02:32 +0000 Subject: [PATCH 264/311] Only use the Zip64 CDIR end locator if needed. 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. --- lib/zip/central_directory.rb | 12 ++++-------- test/central_directory_test.rb | 35 +++++++++------------------------- 2 files changed, 13 insertions(+), 34 deletions(-) diff --git a/lib/zip/central_directory.rb b/lib/zip/central_directory.rb index 146c444f..62de599b 100644 --- a/lib/zip/central_directory.rb +++ b/lib/zip/central_directory.rb @@ -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 diff --git a/test/central_directory_test.rb b/test/central_directory_test.rb index 37b5ca65..c36d2dc4 100644 --- a/test/central_directory_test.rb +++ b/test/central_directory_test.rb @@ -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 From 84087e57740c32ce24184cb91e4e0d443ebfd242 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Tue, 3 Jan 2023 15:37:58 +0000 Subject: [PATCH 265/311] Ensure that entries can be extracted safely without path traversal. This commit adds a parameter to the `File#extract` and `Entry#extract` methods so that a base destination directory can be specified for extracting archives in bulk to somewhere in the filesystem that isn't the current working directory. This directory is `.` by default. It is combined with the entry path - which shouldn't but could have relative directories (e.g. `..`) in it - and tested for safety before extracting. Resolves #540. --- lib/zip/entry.rb | 20 +++++---- lib/zip/file.rb | 11 +++-- test/file_extract_directory_test.rb | 2 +- test/file_extract_test.rb | 3 +- test/file_options_test.rb | 64 ++++++++++++++------------- test/path_traversal_test.rb | 68 ++++++++++++++++++++--------- 6 files changed, 103 insertions(+), 65 deletions(-) diff --git a/lib/zip/entry.rb b/lib/zip/entry.rb index 36bc32c9..9c42e079 100644 --- a/lib/zip/entry.rb +++ b/lib/zip/entry.rb @@ -247,21 +247,25 @@ def next_header_offset #:nodoc:all local_entry_offset + compressed_size end - # Extracts entry to file dest_path (defaults to @name). - # NB: The caller is responsible for making sure dest_path is safe, if it - # is passed. - def extract(dest_path = nil, &block) - if dest_path.nil? && !name_safe? - warn "WARNING: skipped '#{@name}' as unsafe." + # Extracts this entry to a file at `entry_path`, with + # `destination_directory` as the base location in the filesystem. + # + # NB: The caller is responsible for making sure `destination_directory` is + # safe, if it is passed. + def extract(entry_path = @name, destination_directory: '.', &block) + dest_dir = ::File.absolute_path(destination_directory || '.') + extract_path = ::File.absolute_path(::File.join(dest_dir, entry_path)) + + unless extract_path.start_with?(dest_dir) + warn "WARNING: skipped extracting '#{@name}' to '#{extract_path}' as unsafe." return self end - dest_path ||= @name block ||= proc { ::Zip.on_exists_proc } raise "unknown file type #{inspect}" unless directory? || file? || symlink? - __send__("create_#{ftype}", dest_path, &block) + __send__("create_#{ftype}", extract_path, &block) self end diff --git a/lib/zip/file.rb b/lib/zip/file.rb index 04e1d802..69c73f5e 100644 --- a/lib/zip/file.rb +++ b/lib/zip/file.rb @@ -245,11 +245,16 @@ def replace(entry, src_path) add(entry, src_path) end - # Extracts entry to file dest_path. - def extract(entry, dest_path, &block) + # Extracts `entry` to a file at `entry_path`, with `destination_directory` + # as the base location in the filesystem. + # + # NB: The caller is responsible for making sure `destination_directory` is + # safe, if it is passed. + def extract(entry, entry_path = nil, destination_directory: '.', &block) block ||= proc { ::Zip.on_exists_proc } found_entry = get_entry(entry) - found_entry.extract(dest_path, &block) + entry_path ||= found_entry.name + found_entry.extract(entry_path, destination_directory: destination_directory, &block) end # Commits changes that has been made since the previous commit to diff --git a/test/file_extract_directory_test.rb b/test/file_extract_directory_test.rb index 26730a08..17d0995a 100644 --- a/test/file_extract_directory_test.rb +++ b/test/file_extract_directory_test.rb @@ -48,7 +48,7 @@ def test_extract_directory_exists_as_file_overwrite called = false extract_test_dir do |entry, dest_path| called = true - assert_equal(TEST_OUT_NAME, dest_path) + assert_equal(File.absolute_path(TEST_OUT_NAME), dest_path) assert(entry.directory?) true end diff --git a/test/file_extract_test.rb b/test/file_extract_test.rb index 3b4cbb97..77da4a6d 100644 --- a/test/file_extract_test.rb +++ b/test/file_extract_test.rb @@ -5,6 +5,7 @@ class ZipFileExtractTest < MiniTest::Test include CommonZipFileFixture EXTRACTED_FILENAME = 'test/data/generated/extEntry' + EXTRACTED_FILENAME_ABS = ::File.absolute_path(EXTRACTED_FILENAME) ENTRY_TO_EXTRACT, *REMAINING_ENTRIES = TEST_ZIP.entry_names.reverse def setup @@ -58,7 +59,7 @@ def test_extract_exists_overwrite ::Zip::File.open(TEST_ZIP.zip_name) do |zf| zf.extract(zf.entries.first, EXTRACTED_FILENAME) do |entry, extract_loc| called_correctly = zf.entries.first == entry && - extract_loc == EXTRACTED_FILENAME + extract_loc == EXTRACTED_FILENAME_ABS true end end diff --git a/test/file_options_test.rb b/test/file_options_test.rb index c1a568a6..c6b7885a 100644 --- a/test/file_options_test.rb +++ b/test/file_options_test.rb @@ -7,12 +7,15 @@ class FileOptionsTest < MiniTest::Test TXTPATH = ::File.expand_path(::File.join('data', 'file1.txt'), __dir__).freeze TXTPATH_600 = ::File.join(Dir.tmpdir, 'file1.600.txt').freeze TXTPATH_755 = ::File.join(Dir.tmpdir, 'file1.755.txt').freeze - EXTPATH_1 = ::File.join(Dir.tmpdir, 'extracted_1.txt').freeze - EXTPATH_2 = ::File.join(Dir.tmpdir, 'extracted_2.txt').freeze - EXTPATH_3 = ::File.join(Dir.tmpdir, 'extracted_3.txt').freeze ENTRY_1 = 'entry_1.txt' ENTRY_2 = 'entry_2.txt' ENTRY_3 = 'entry_3.txt' + EXTRACT_1 = 'extracted_1.txt' + EXTRACT_2 = 'extracted_2.txt' + EXTRACT_3 = 'extracted_3.txt' + EXTPATH_1 = ::File.join(Dir.tmpdir, EXTRACT_1).freeze + EXTPATH_2 = ::File.join(Dir.tmpdir, EXTRACT_2).freeze + EXTPATH_3 = ::File.join(Dir.tmpdir, EXTRACT_3).freeze def teardown ::File.unlink(ZIPPATH) if ::File.exist?(ZIPPATH) @@ -37,9 +40,9 @@ def test_restore_permissions_true end ::Zip::File.open(ZIPPATH, restore_permissions: true) do |zip| - zip.extract(ENTRY_1, EXTPATH_1) - zip.extract(ENTRY_2, EXTPATH_2) - zip.extract(ENTRY_3, EXTPATH_3) + zip.extract(ENTRY_1, EXTRACT_1, destination_directory: Dir.tmpdir) + zip.extract(ENTRY_2, EXTRACT_2, destination_directory: Dir.tmpdir) + zip.extract(ENTRY_3, EXTRACT_3, destination_directory: Dir.tmpdir) end assert_equal(::File.stat(TXTPATH).mode, ::File.stat(EXTPATH_1).mode) @@ -61,9 +64,9 @@ def test_restore_permissions_false end ::Zip::File.open(ZIPPATH, restore_permissions: false) do |zip| - zip.extract(ENTRY_1, EXTPATH_1) - zip.extract(ENTRY_2, EXTPATH_2) - zip.extract(ENTRY_3, EXTPATH_3) + zip.extract(ENTRY_1, EXTRACT_1, destination_directory: Dir.tmpdir) + zip.extract(ENTRY_2, EXTRACT_2, destination_directory: Dir.tmpdir) + zip.extract(ENTRY_3, EXTRACT_3, destination_directory: Dir.tmpdir) end default_perms = (Zip::RUNNING_ON_WINDOWS ? 0o100_644 : 0o100_666) - ::File.umask @@ -86,9 +89,9 @@ def test_restore_permissions_as_default end ::Zip::File.open(ZIPPATH) do |zip| - zip.extract(ENTRY_1, EXTPATH_1) - zip.extract(ENTRY_2, EXTPATH_2) - zip.extract(ENTRY_3, EXTPATH_3) + zip.extract(ENTRY_1, EXTRACT_1, destination_directory: Dir.tmpdir) + zip.extract(ENTRY_2, EXTRACT_2, destination_directory: Dir.tmpdir) + zip.extract(ENTRY_3, EXTRACT_3, destination_directory: Dir.tmpdir) end assert_equal(::File.stat(TXTPATH).mode, ::File.stat(EXTPATH_1).mode) @@ -103,8 +106,8 @@ def test_restore_times_true end ::Zip::File.open(ZIPPATH, restore_times: true) do |zip| - zip.extract(ENTRY_1, EXTPATH_1) - zip.extract(ENTRY_2, EXTPATH_2) + zip.extract(ENTRY_1, EXTRACT_1, destination_directory: Dir.tmpdir) + zip.extract(ENTRY_2, EXTRACT_2, destination_directory: Dir.tmpdir) end assert_time_equal(::File.mtime(TXTPATH), ::File.mtime(EXTPATH_1)) @@ -118,8 +121,8 @@ def test_restore_times_false end ::Zip::File.open(ZIPPATH, restore_times: false) do |zip| - zip.extract(ENTRY_1, EXTPATH_1) - zip.extract(ENTRY_2, EXTPATH_2) + zip.extract(ENTRY_1, EXTRACT_1, destination_directory: Dir.tmpdir) + zip.extract(ENTRY_2, EXTRACT_2, destination_directory: Dir.tmpdir) end assert_time_equal(::Time.now, ::File.mtime(EXTPATH_1)) @@ -133,8 +136,8 @@ def test_restore_times_true_as_default end ::Zip::File.open(ZIPPATH) do |zip| - zip.extract(ENTRY_1, EXTPATH_1) - zip.extract(ENTRY_2, EXTPATH_2) + zip.extract(ENTRY_1, EXTRACT_1, destination_directory: Dir.tmpdir) + zip.extract(ENTRY_2, EXTRACT_2, destination_directory: Dir.tmpdir) end assert_time_equal(::File.mtime(TXTPATH), ::File.mtime(EXTPATH_1)) @@ -143,20 +146,19 @@ def test_restore_times_true_as_default def test_get_find_consistency testzip = ::File.expand_path(::File.join('data', 'globTest.zip'), __dir__) - file_f = ::File.expand_path('f_test.txt', Dir.tmpdir) - file_g = ::File.expand_path('g_test.txt', Dir.tmpdir) - - ::Zip::File.open(testzip) do |zip| - e1 = zip.find_entry('globTest/food.txt') - e1.extract(file_f) - e2 = zip.get_entry('globTest/food.txt') - e2.extract(file_g) + Dir.mktmpdir do |tmp| + file_f = ::File.expand_path('f_test.txt', tmp) + file_g = ::File.expand_path('g_test.txt', tmp) + + ::Zip::File.open(testzip) do |zip| + e1 = zip.find_entry('globTest/food.txt') + e1.extract('f_test.txt', destination_directory: tmp) + e2 = zip.get_entry('globTest/food.txt') + e2.extract('g_test.txt', destination_directory: tmp) + end + + assert_time_equal(::File.mtime(file_f), ::File.mtime(file_g)) end - - assert_time_equal(::File.mtime(file_f), ::File.mtime(file_g)) - ensure - ::File.unlink(file_f) - ::File.unlink(file_g) end private diff --git a/test/path_traversal_test.rb b/test/path_traversal_test.rb index 4b500191..e1ec9a74 100644 --- a/test/path_traversal_test.rb +++ b/test/path_traversal_test.rb @@ -39,23 +39,31 @@ def in_tmpdir end def test_leading_slash - entries = { '/tmp/moo' => /WARNING: skipped '\/tmp\/moo'/ } - in_tmpdir do + entries = { '/tmp/moo' => '' } + in_tmpdir do |test_path| + Dir.mkdir('tmp') # Create 'tmp' dir within test directory. extract_paths(['jwilk', 'absolute1.zip'], entries) + + # Check that only the relative file is created. refute File.exist?('/tmp/moo') + assert File.exist?(File.join(test_path, 'tmp', 'moo')) end end def test_multiple_leading_slashes - entries = { '//tmp/moo' => /WARNING: skipped '\/\/tmp\/moo'/ } - in_tmpdir do + entries = { '//tmp/moo' => '' } + in_tmpdir do |test_path| + Dir.mkdir('tmp') # Create 'tmp' dir within test directory. extract_paths(['jwilk', 'absolute2.zip'], entries) + + # Check that only the relative file is created. refute File.exist?('/tmp/moo') + assert File.exist?(File.join(test_path, 'tmp', 'moo')) end end def test_leading_dot_dot - entries = { '../moo' => /WARNING: skipped '\.\.\/moo'/ } + entries = { '../moo' => /WARNING: skipped extracting '\.\.\/moo'/ } in_tmpdir do extract_paths(['jwilk', 'relative0.zip'], entries) refute File.exist?('../moo') @@ -65,7 +73,7 @@ def test_leading_dot_dot def test_non_leading_dot_dot_with_existing_folder entries = { 'tmp/' => '', - 'tmp/../../moo' => /WARNING: skipped 'tmp\/\.\.\/\.\.\/moo'/ + 'tmp/../../moo' => /WARNING: skipped extracting 'tmp\/\.\.\/\.\.\/moo'/ } in_tmpdir do extract_paths('relative1.zip', entries) @@ -75,7 +83,7 @@ def test_non_leading_dot_dot_with_existing_folder end def test_non_leading_dot_dot_without_existing_folder - entries = { 'tmp/../../moo' => /WARNING: skipped 'tmp\/\.\.\/\.\.\/moo'/ } + entries = { 'tmp/../../moo' => /WARNING: skipped extracting 'tmp\/\.\.\/\.\.\/moo'/ } in_tmpdir do extract_paths(['jwilk', 'relative2.zip'], entries) refute File.exist?('../moo') @@ -94,7 +102,7 @@ def test_file_symlink def test_directory_symlink # Can't create tmp/moo, because the tmp symlink is skipped. entries = { - 'tmp' => /WARNING: skipped symlink 'tmp'/, + 'tmp' => /WARNING: skipped symlink '.*\/tmp'/, 'tmp/moo' => :error } in_tmpdir do @@ -106,8 +114,8 @@ def test_directory_symlink def test_two_directory_symlinks_a # Can't create par/moo because the symlinks are skipped. entries = { - 'cur' => /WARNING: skipped symlink 'cur'/, - 'par' => /WARNING: skipped symlink 'par'/, + 'cur' => /WARNING: skipped symlink '.*\/cur'/, + 'par' => /WARNING: skipped symlink '.*\/par'/, 'par/moo' => :error } in_tmpdir do @@ -121,8 +129,8 @@ def test_two_directory_symlinks_a def test_two_directory_symlinks_b # Can't create par/moo, because the symlinks are skipped. entries = { - 'cur' => /WARNING: skipped symlink 'cur'/, - 'cur/par' => /WARNING: skipped symlink 'cur\/par'/, + 'cur' => /WARNING: skipped symlink '.*\/cur'/, + 'cur/par' => /WARNING: skipped symlink '.*\/cur\/par'/, 'par/moo' => :error } in_tmpdir do @@ -132,14 +140,29 @@ def test_two_directory_symlinks_b end end - def test_entry_name_with_absolute_path_does_not_extract - entries = { - '/tmp/' => /WARNING: skipped '\/tmp\/'/, - '/tmp/file.txt' => /WARNING: skipped '\/tmp\/file.txt'/ - } - in_tmpdir do - extract_paths(['tuzovakaoff', 'absolutepath.zip'], entries) + def test_entry_name_with_absolute_path_does_not_extract_by_accident + in_tmpdir do |test_path| + zip_path = File.join(TEST_FILE_ROOT, 'tuzovakaoff', 'absolutepath.zip') + Zip::File.open(zip_path) do |zip_file| + zip_file.each do |entry| + entry.extract(entry.name, destination_directory: nil) + assert File.exist?(File.join(test_path, entry.name)) + refute File.exist?(entry.name) unless entry.name == '/tmp/' + end + end + end + end + + def test_entry_name_with_absolute_path_extracts_to_cwd_by_default + in_tmpdir do |test_path| + zip_path = File.join(TEST_FILE_ROOT, 'tuzovakaoff', 'absolutepath.zip') + Zip::File.open(zip_path) do |zip_file| + zip_file.each(&:extract) + end + + # Check that only the relative file is created. refute File.exist?('/tmp/file.txt') + assert File.exist?(File.join(test_path, 'tmp', 'file.txt')) end end @@ -148,17 +171,20 @@ def test_entry_name_with_absolute_path_extract_when_given_different_path zip_path = File.join(TEST_FILE_ROOT, 'tuzovakaoff', 'absolutepath.zip') Zip::File.open(zip_path) do |zip_file| zip_file.each do |entry| - entry.extract(File.join(test_path, entry.name)) + entry.extract(destination_directory: test_path) end end + + # Check that only the relative file is created. refute File.exist?('/tmp/file.txt') + assert File.exist?(File.join(test_path, 'tmp', 'file.txt')) end end def test_entry_name_with_relative_symlink # Doesn't create the symlink path, so can't create path/file.txt. entries = { - 'path' => /WARNING: skipped symlink 'path'/, + 'path' => /WARNING: skipped symlink '.*\/path'/, 'path/file.txt' => :error } in_tmpdir do From ccc3d4ff1aad10f848d346bd18b6ccd3f76888b4 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sun, 6 Nov 2022 15:02:57 +0000 Subject: [PATCH 266/311] Set version to be 3.0.0.alpha. --- lib/zip/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/zip/version.rb b/lib/zip/version.rb index 633899d6..dd203608 100644 --- a/lib/zip/version.rb +++ b/lib/zip/version.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true module Zip - VERSION = '3.0.0' + VERSION = '3.0.0.alpha' end From edeab0713c67e398af181b490ff2b1fc100b7ebc Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sun, 6 Nov 2022 17:46:29 +0000 Subject: [PATCH 267/311] Add RDoc and tasks to the Rakefile. --- .gitignore | 1 + Rakefile | 9 +++++++++ rubyzip.gemspec | 1 + 3 files changed, 11 insertions(+) diff --git a/.gitignore b/.gitignore index 08ebec6b..17c0cbd8 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ Gemfile.lock +samples/*.zip.* samples/zipdialogui.rb coverage +html/ pkg/ .ruby-gemset .ruby-version diff --git a/Rakefile b/Rakefile index 4326fdf9..73299026 100644 --- a/Rakefile +++ b/Rakefile @@ -2,6 +2,7 @@ require 'bundler/gem_tasks' require 'rake/testtask' +require 'rdoc/task' require 'rubocop/rake_task' task default: :test @@ -13,4 +14,12 @@ Rake::TestTask.new(:test) do |test| test.verbose = true end +RDoc::Task.new do |rdoc| + rdoc.main = 'README.md' + rdoc.rdoc_files.include('README.md', 'lib/**/*.rb') + rdoc.options << '--markup=markdown' + rdoc.options << '--tab-width=2' + rdoc.options << "-t Rubyzip version #{::Zip::VERSION}" +end + RuboCop::RakeTask.new diff --git a/rubyzip.gemspec b/rubyzip.gemspec index 5287d0cd..4c557675 100644 --- a/rubyzip.gemspec +++ b/rubyzip.gemspec @@ -29,6 +29,7 @@ Gem::Specification.new do |s| s.add_development_dependency 'minitest', '~> 5.4' s.add_development_dependency 'rake', '~> 12.3.3' + s.add_development_dependency 'rdoc', '~> 6.4.0' s.add_development_dependency 'rubocop', '~> 1.12.0' s.add_development_dependency 'rubocop-performance', '~> 1.10.0' s.add_development_dependency 'rubocop-rake', '~> 0.5.0' From 016e1000baff6ee8517f6b4395ca8fec9849cdb6 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sun, 6 Nov 2022 17:59:13 +0000 Subject: [PATCH 268/311] Suppress Rubocop extension notice. --- .rubocop.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.rubocop.yml b/.rubocop.yml index 1997fe39..056c5164 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -7,6 +7,7 @@ inherit_from: .rubocop_todo.yml # Set this to the minimum supported ruby in the gemspec. Otherwise # we get errors if our ruby version doesn't match. AllCops: + SuggestExtensions: false TargetRubyVersion: 2.5 NewCops: enable From 0aa10bf7d5f61aa3e51fc87cf1fe67c7f39c212a Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sun, 6 Nov 2022 17:59:34 +0000 Subject: [PATCH 269/311] Document or hide modules from the docs. --- lib/zip.rb | 5 ++++ lib/zip/crypto/null_encryption.rb | 2 +- lib/zip/crypto/traditional_encryption.rb | 2 +- lib/zip/dirtyable.rb | 4 +-- lib/zip/file_split.rb | 2 +- lib/zip/filesystem.rb | 32 ++++++++++++---------- lib/zip/ioextras.rb | 4 +-- lib/zip/ioextras/abstract_input_stream.rb | 4 +-- lib/zip/ioextras/abstract_output_stream.rb | 4 +-- lib/zip/null_decompressor.rb | 2 +- lib/zip/null_input_stream.rb | 2 +- 11 files changed, 35 insertions(+), 28 deletions(-) diff --git a/lib/zip.rb b/lib/zip.rb index 82c52003..b2ccc678 100644 --- a/lib/zip.rb +++ b/lib/zip.rb @@ -35,6 +35,11 @@ require 'zip/streamable_directory' require 'zip/errors' +# Rubyzip is a ruby module for reading and writing zip files. +# +# The main entry points are File, InputStream and OutputStream. For a +# file/directory interface in the style of the standard ruby ::File and +# ::Dir APIs then `require 'zip/filesystem'` and see FileSystem. module Zip extend self attr_accessor :unicode_names, diff --git a/lib/zip/crypto/null_encryption.rb b/lib/zip/crypto/null_encryption.rb index ae33c40c..7cc1d019 100644 --- a/lib/zip/crypto/null_encryption.rb +++ b/lib/zip/crypto/null_encryption.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module Zip - module NullEncryption + module NullEncryption # :nodoc: def header_bytesize 0 end diff --git a/lib/zip/crypto/traditional_encryption.rb b/lib/zip/crypto/traditional_encryption.rb index eef3b18d..dfc1bd3f 100644 --- a/lib/zip/crypto/traditional_encryption.rb +++ b/lib/zip/crypto/traditional_encryption.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module Zip - module TraditionalEncryption + module TraditionalEncryption # :nodoc: def initialize(password) @password = password reset_keys! diff --git a/lib/zip/dirtyable.rb b/lib/zip/dirtyable.rb index 78b1c86a..4e2104e4 100644 --- a/lib/zip/dirtyable.rb +++ b/lib/zip/dirtyable.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module Zip - module Dirtyable # :nodoc: + module Dirtyable # :nodoc:all def initialize(dirty_on_create: true) @dirty = dirty_on_create end @@ -10,7 +10,7 @@ def dirty? @dirty end - module ClassMethods + module ClassMethods # :nodoc: def mark_dirty(*symbols) # :nodoc: # Move the original method and call it after we've set the dirty flag. symbols.each do |symbol| diff --git a/lib/zip/file_split.rb b/lib/zip/file_split.rb index 606d2e6b..0f6c694a 100644 --- a/lib/zip/file_split.rb +++ b/lib/zip/file_split.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module Zip - module FileSplit #:nodoc: + module FileSplit # :nodoc: MAX_SEGMENT_SIZE = 3_221_225_472 MIN_SEGMENT_SIZE = 65_536 DATA_BUFFER_SIZE = 8192 diff --git a/lib/zip/filesystem.rb b/lib/zip/filesystem.rb index e223b3c0..5a65c974 100644 --- a/lib/zip/filesystem.rb +++ b/lib/zip/filesystem.rb @@ -19,25 +19,27 @@ module Zip # first.txt, a directory entry named mydir # and finally another normal entry named second.txt # - # require 'zip/filesystem' + # ``` + # require 'zip/filesystem' # - # Zip::File.open("my.zip", create: true) { - # |zipfile| - # zipfile.file.open("first.txt", "w") { |f| f.puts "Hello world" } - # zipfile.dir.mkdir("mydir") - # zipfile.file.open("mydir/second.txt", "w") { |f| f.puts "Hello again" } - # } + # Zip::File.open('my.zip', create: true) do |zipfile| + # zipfile.file.open('first.txt', 'w') { |f| f.puts 'Hello world' } + # zipfile.dir.mkdir('mydir') + # zipfile.file.open('mydir/second.txt', 'w') { |f| f.puts 'Hello again' } + # end + # ``` # # Reading is as easy as writing, as the following example shows. The # example writes the contents of first.txt from zip archive # my.zip to standard out. # - # require 'zip/filesystem' + # ``` + # require 'zip/filesystem' # - # Zip::File.open("my.zip") { - # |zipfile| - # puts zipfile.file.read("first.txt") - # } + # Zip::File.open('my.zip') do |zipfile| + # puts zipfile.file.read('first.txt') + # end + # ``` module FileSystem def initialize # :nodoc: mapped_zip = ZipFileNameMapper.new(self) @@ -47,14 +49,14 @@ def initialize # :nodoc: @zip_fs_file.dir = @zip_fs_dir end - # Returns a FileSystem::Dir which is much like ruby's builtin Dir (class) - # object, except it works on the Zip::File on which this method is + # Returns a Zip::FileSystem::Dir which is much like ruby's builtin Dir + # (class) object, except it works on the Zip::File on which this method is # invoked def dir @zip_fs_dir end - # Returns a FileSystem::File which is much like ruby's builtin File + # Returns a Zip::FileSystem::File which is much like ruby's builtin File # (class) object, except it works on the Zip::File on which this method is # invoked def file diff --git a/lib/zip/ioextras.rb b/lib/zip/ioextras.rb index df208da1..fbc77c6f 100644 --- a/lib/zip/ioextras.rb +++ b/lib/zip/ioextras.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module Zip - module IOExtras #:nodoc: + module IOExtras # :nodoc: CHUNK_SIZE = 131_072 class << self @@ -20,7 +20,7 @@ def copy_stream_n(ostream, istream, nbytes) end # Implements kind_of? in order to pretend to be an IO object - module FakeIO + module FakeIO # :nodoc: def kind_of?(object) object == IO || super end diff --git a/lib/zip/ioextras/abstract_input_stream.rb b/lib/zip/ioextras/abstract_input_stream.rb index d0ea6825..721e33af 100644 --- a/lib/zip/ioextras/abstract_input_stream.rb +++ b/lib/zip/ioextras/abstract_input_stream.rb @@ -1,11 +1,11 @@ # frozen_string_literal: true module Zip - module IOExtras + module IOExtras # :nodoc: # Implements many of the convenience methods of IO # such as gets, getc, readline and readlines # depends on: input_finished?, produce_input and read - module AbstractInputStream + module AbstractInputStream # :nodoc: include Enumerable include FakeIO diff --git a/lib/zip/ioextras/abstract_output_stream.rb b/lib/zip/ioextras/abstract_output_stream.rb index 28a09090..a71d24f6 100644 --- a/lib/zip/ioextras/abstract_output_stream.rb +++ b/lib/zip/ioextras/abstract_output_stream.rb @@ -1,10 +1,10 @@ # frozen_string_literal: true module Zip - module IOExtras + module IOExtras # :nodoc: # Implements many of the output convenience methods of IO. # relies on << - module AbstractOutputStream + module AbstractOutputStream # :nodoc: include FakeIO def write(data) diff --git a/lib/zip/null_decompressor.rb b/lib/zip/null_decompressor.rb index 7b2557d4..8eb79e67 100644 --- a/lib/zip/null_decompressor.rb +++ b/lib/zip/null_decompressor.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module Zip - module NullDecompressor #:nodoc:all + module NullDecompressor # :nodoc:all module_function def read(_length = nil, _outbuf = nil) diff --git a/lib/zip/null_input_stream.rb b/lib/zip/null_input_stream.rb index 69bc38d7..5c2c7077 100644 --- a/lib/zip/null_input_stream.rb +++ b/lib/zip/null_input_stream.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module Zip - module NullInputStream #:nodoc:all + module NullInputStream # :nodoc:all include ::Zip::NullDecompressor include ::Zip::IOExtras::AbstractInputStream end From 2ffbfebb885a37a2c4ff4f8e473e248d14f24c3a Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sun, 6 Nov 2022 21:26:37 +0000 Subject: [PATCH 270/311] Document or hide classes from the docs. --- lib/zip/central_directory.rb | 2 +- lib/zip/compressor.rb | 2 +- lib/zip/crypto/decrypted_io.rb | 2 +- lib/zip/crypto/encryption.rb | 4 +- lib/zip/crypto/null_encryption.rb | 4 +- lib/zip/crypto/traditional_encryption.rb | 4 +- lib/zip/decompressor.rb | 2 +- lib/zip/deflater.rb | 2 +- lib/zip/dos_time.rb | 2 +- lib/zip/entry.rb | 1 + lib/zip/entry_set.rb | 2 +- lib/zip/errors.rb | 14 ++++++ lib/zip/extra_field.rb | 2 +- lib/zip/extra_field/generic.rb | 2 +- lib/zip/extra_field/ntfs.rb | 2 +- lib/zip/extra_field/old_unix.rb | 2 +- lib/zip/extra_field/universal_time.rb | 2 +- lib/zip/extra_field/unix.rb | 2 +- lib/zip/extra_field/unknown.rb | 2 +- lib/zip/extra_field/zip64.rb | 2 +- lib/zip/file.rb | 57 ++++++++++++------------ lib/zip/inflater.rb | 2 +- lib/zip/null_compressor.rb | 2 +- lib/zip/pass_thru_compressor.rb | 2 +- lib/zip/pass_thru_decompressor.rb | 2 +- lib/zip/streamable_directory.rb | 2 +- 26 files changed, 70 insertions(+), 54 deletions(-) diff --git a/lib/zip/central_directory.rb b/lib/zip/central_directory.rb index 62de599b..53d213f5 100644 --- a/lib/zip/central_directory.rb +++ b/lib/zip/central_directory.rb @@ -5,7 +5,7 @@ require_relative 'dirtyable' module Zip - class CentralDirectory + class CentralDirectory # :nodoc: extend Forwardable include Dirtyable diff --git a/lib/zip/compressor.rb b/lib/zip/compressor.rb index 8c0680e0..3622da80 100644 --- a/lib/zip/compressor.rb +++ b/lib/zip/compressor.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module Zip - class Compressor #:nodoc:all + class Compressor # :nodoc:all def finish; end end end diff --git a/lib/zip/crypto/decrypted_io.rb b/lib/zip/crypto/decrypted_io.rb index 92ccde63..db844a24 100644 --- a/lib/zip/crypto/decrypted_io.rb +++ b/lib/zip/crypto/decrypted_io.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module Zip - class DecryptedIo #:nodoc:all + class DecryptedIo # :nodoc:all CHUNK_SIZE = 32_768 def initialize(io, decrypter) diff --git a/lib/zip/crypto/encryption.rb b/lib/zip/crypto/encryption.rb index c792e38b..b9c96c67 100644 --- a/lib/zip/crypto/encryption.rb +++ b/lib/zip/crypto/encryption.rb @@ -1,10 +1,10 @@ # frozen_string_literal: true module Zip - class Encrypter #:nodoc:all + class Encrypter # :nodoc:all end - class Decrypter + class Decrypter # :nodoc:all end end diff --git a/lib/zip/crypto/null_encryption.rb b/lib/zip/crypto/null_encryption.rb index 7cc1d019..2ae48b70 100644 --- a/lib/zip/crypto/null_encryption.rb +++ b/lib/zip/crypto/null_encryption.rb @@ -11,7 +11,7 @@ def gp_flags end end - class NullEncrypter < Encrypter + class NullEncrypter < Encrypter # :nodoc: include NullEncryption def header(_mtime) @@ -29,7 +29,7 @@ def data_descriptor(_crc32, _compressed_size, _uncomprssed_size) def reset!; end end - class NullDecrypter < Decrypter + class NullDecrypter < Decrypter # :nodoc: include NullEncryption def decrypt(data) diff --git a/lib/zip/crypto/traditional_encryption.rb b/lib/zip/crypto/traditional_encryption.rb index dfc1bd3f..0f609048 100644 --- a/lib/zip/crypto/traditional_encryption.rb +++ b/lib/zip/crypto/traditional_encryption.rb @@ -38,7 +38,7 @@ def decrypt_byte end end - class TraditionalEncrypter < Encrypter + class TraditionalEncrypter < Encrypter # :nodoc: include TraditionalEncryption def header(mtime) @@ -72,7 +72,7 @@ def encode(num) end end - class TraditionalDecrypter < Decrypter + class TraditionalDecrypter < Decrypter # :nodoc: include TraditionalEncryption def decrypt(data) diff --git a/lib/zip/decompressor.rb b/lib/zip/decompressor.rb index b6bb9cc8..e75aba92 100644 --- a/lib/zip/decompressor.rb +++ b/lib/zip/decompressor.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module Zip - class Decompressor #:nodoc:all + class Decompressor # :nodoc:all CHUNK_SIZE = 32_768 def self.decompressor_classes diff --git a/lib/zip/deflater.rb b/lib/zip/deflater.rb index 8f4827b4..a899e118 100644 --- a/lib/zip/deflater.rb +++ b/lib/zip/deflater.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module Zip - class Deflater < Compressor #:nodoc:all + class Deflater < Compressor # :nodoc:all def initialize(output_stream, level = Zip.default_compression, encrypter = NullEncrypter.new) super() @output_stream = output_stream diff --git a/lib/zip/dos_time.rb b/lib/zip/dos_time.rb index 9203d734..0d94f21c 100644 --- a/lib/zip/dos_time.rb +++ b/lib/zip/dos_time.rb @@ -3,7 +3,7 @@ require 'rubygems' module Zip - class DOSTime < Time #:nodoc:all + class DOSTime < Time # :nodoc:all # MS-DOS File Date and Time format as used in Interrupt 21H Function 57H: # Register CX, the Time: diff --git a/lib/zip/entry.rb b/lib/zip/entry.rb index 9c42e079..38ebcb41 100644 --- a/lib/zip/entry.rb +++ b/lib/zip/entry.rb @@ -5,6 +5,7 @@ require_relative 'dirtyable' module Zip + # Zip::Entry represents an entry in a Zip archive. class Entry include Dirtyable diff --git a/lib/zip/entry_set.rb b/lib/zip/entry_set.rb index 11db9518..941e92e2 100644 --- a/lib/zip/entry_set.rb +++ b/lib/zip/entry_set.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module Zip - class EntrySet #:nodoc:all + class EntrySet # :nodoc:all include Enumerable attr_reader :entry_set diff --git a/lib/zip/errors.rb b/lib/zip/errors.rb index 74bf3a3d..c757a8da 100644 --- a/lib/zip/errors.rb +++ b/lib/zip/errors.rb @@ -1,8 +1,11 @@ # frozen_string_literal: true module Zip + # The superclass for all rubyzip error types. Simply rescue this one if + # you don't need to know what sort of error has been raised. class Error < StandardError; end + # Error raised if an unsupported compression method is used. class CompressionMethodError < Error attr_reader :compression_method @@ -16,6 +19,7 @@ def message end end + # Error raised if there is a problem while decompressing an archive entry. class DecompressionError < Error attr_reader :zlib_error @@ -29,6 +33,8 @@ def message end end + # Error raised when trying to extract an archive entry over an + # existing file. class DestinationExistsError < Error def initialize(destination) super() @@ -41,6 +47,8 @@ def message end end + # Error raised when trying to add an entry to an archive where the + # entry name already exists. class EntryExistsError < Error def initialize(source, name) super() @@ -53,6 +61,7 @@ def message end end + # Error raised when an entry name is invalid. class EntryNameError < Error def initialize(name = nil) super() @@ -68,6 +77,8 @@ def message end end + # Error raised if an entry is larger on extraction than it is advertised + # to be. class EntrySizeError < Error attr_reader :entry @@ -81,12 +92,15 @@ def message end end + # Error raised if a split archive is read. Rubyzip does not support reading + # split archives. class SplitArchiveError < Error def message 'Rubyzip cannot extract from split archives at this time.' end end + # Error raised if there is not enough metadata for the entry to be streamed. class StreamingError < Error attr_reader :entry diff --git a/lib/zip/extra_field.rb b/lib/zip/extra_field.rb index 8a73c3c9..95dd5db2 100644 --- a/lib/zip/extra_field.rb +++ b/lib/zip/extra_field.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module Zip - class ExtraField < Hash + class ExtraField < Hash # :nodoc:all ID_MAP = {} def initialize(binstr = nil, local: false) diff --git a/lib/zip/extra_field/generic.rb b/lib/zip/extra_field/generic.rb index b3db791a..137efe90 100644 --- a/lib/zip/extra_field/generic.rb +++ b/lib/zip/extra_field/generic.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module Zip - class ExtraField::Generic + class ExtraField::Generic # :nodoc: def self.register_map return unless const_defined?(:HEADER_ID) diff --git a/lib/zip/extra_field/ntfs.rb b/lib/zip/extra_field/ntfs.rb index cd691b1d..be1541db 100644 --- a/lib/zip/extra_field/ntfs.rb +++ b/lib/zip/extra_field/ntfs.rb @@ -3,7 +3,7 @@ module Zip # PKWARE NTFS Extra Field (0x000a) # Only Tag 0x0001 is supported - class ExtraField::NTFS < ExtraField::Generic + class ExtraField::NTFS < ExtraField::Generic # :nodoc: HEADER_ID = [0x000A].pack('v') register_map diff --git a/lib/zip/extra_field/old_unix.rb b/lib/zip/extra_field/old_unix.rb index 351339fe..0407d5da 100644 --- a/lib/zip/extra_field/old_unix.rb +++ b/lib/zip/extra_field/old_unix.rb @@ -2,7 +2,7 @@ module Zip # Olf Info-ZIP Extra for UNIX uid/gid and file timestampes - class ExtraField::OldUnix < ExtraField::Generic + class ExtraField::OldUnix < ExtraField::Generic # :nodoc: HEADER_ID = 'UX' register_map diff --git a/lib/zip/extra_field/universal_time.rb b/lib/zip/extra_field/universal_time.rb index 63ef070e..b77b321b 100644 --- a/lib/zip/extra_field/universal_time.rb +++ b/lib/zip/extra_field/universal_time.rb @@ -2,7 +2,7 @@ module Zip # Info-ZIP Additional timestamp field - class ExtraField::UniversalTime < ExtraField::Generic + class ExtraField::UniversalTime < ExtraField::Generic # :nodoc: HEADER_ID = 'UT' register_map diff --git a/lib/zip/extra_field/unix.rb b/lib/zip/extra_field/unix.rb index f88f1355..59207a23 100644 --- a/lib/zip/extra_field/unix.rb +++ b/lib/zip/extra_field/unix.rb @@ -2,7 +2,7 @@ module Zip # Info-ZIP Extra for UNIX uid/gid - class ExtraField::IUnix < ExtraField::Generic + class ExtraField::IUnix < ExtraField::Generic # :nodoc: HEADER_ID = 'Ux' register_map diff --git a/lib/zip/extra_field/unknown.rb b/lib/zip/extra_field/unknown.rb index 84e87f34..46f1f03d 100644 --- a/lib/zip/extra_field/unknown.rb +++ b/lib/zip/extra_field/unknown.rb @@ -2,7 +2,7 @@ module Zip # A class to hold unknown extra fields so that they are preserved. - class ExtraField::Unknown + class ExtraField::Unknown # :nodoc: def initialize @local_bin = +'' @cdir_bin = +'' diff --git a/lib/zip/extra_field/zip64.rb b/lib/zip/extra_field/zip64.rb index ba7b5110..14778970 100644 --- a/lib/zip/extra_field/zip64.rb +++ b/lib/zip/extra_field/zip64.rb @@ -2,7 +2,7 @@ module Zip # Info-ZIP Extra for Zip64 size - class ExtraField::Zip64 < ExtraField::Generic + class ExtraField::Zip64 < ExtraField::Generic # :nodoc: attr_accessor :compressed_size, :disk_start_number, :original_size, :relative_header_offset diff --git a/lib/zip/file.rb b/lib/zip/file.rb index 69c73f5e..9c66dc13 100644 --- a/lib/zip/file.rb +++ b/lib/zip/file.rb @@ -5,48 +5,49 @@ require_relative 'file_split' module Zip - # ZipFile is modeled after java.util.zip.ZipFile from the Java SDK. - # The most important methods are those inherited from - # ZipCentralDirectory for accessing information about the entries in - # the archive and methods such as get_input_stream and - # get_output_stream for reading from and writing entries to the + # Zip::File is modeled after java.util.zip.ZipFile from the Java SDK. + # The most important methods are those for accessing information about + # the entries in + # the archive and methods such as `get_input_stream` and + # `get_output_stream` for reading from and writing entries to the # archive. The class includes a few convenience methods such as - # #extract for extracting entries to the filesystem, and #remove, - # #replace, #rename and #mkdir for making simple modifications to + # `extract` for extracting entries to the filesystem, and `remove`, + # `replace`, `rename` and `mkdir` for making simple modifications to # the archive. # - # Modifications to a zip archive are not committed until #commit or - # #close is called. The method #open accepts a block following - # the pattern from File.open offering a simple way to + # Modifications to a zip archive are not committed until `commit` or + # `close` is called. The method `open` accepts a block following + # the pattern from ::File.open offering a simple way to # automatically close the archive when the block returns. # - # The following example opens zip archive my.zip + # The following example opens zip archive `my.zip` # (creating it if it doesn't exist) and adds an entry - # first.txt and a directory entry a_dir + # `first.txt` and a directory entry `a_dir` # to it. # - # require 'zip' + # ``` + # require 'zip' # - # Zip::File.open("my.zip", create: true) { - # |zipfile| - # zipfile.get_output_stream("first.txt") { |f| f.puts "Hello from ZipFile" } - # zipfile.mkdir("a_dir") - # } + # Zip::File.open('my.zip', create: true) do |zipfile| + # zipfile.get_output_stream('first.txt') { |f| f.puts 'Hello from Zip::File' } + # zipfile.mkdir('a_dir') + # end + # ``` # - # The next example reopens my.zip writes the contents of - # first.txt to standard out and deletes the entry from + # The next example reopens `my.zip`, writes the contents of + # `first.txt` to standard out and deletes the entry from # the archive. # - # require 'zip' + # ``` + # require 'zip' # - # Zip::File.open("my.zip", create: true) { - # |zipfile| - # puts zipfile.read("first.txt") - # zipfile.remove("first.txt") - # } + # Zip::File.open('my.zip', create: true) do |zipfile| + # puts zipfile.read('first.txt') + # zipfile.remove('first.txt') + # end # - # ZipFileSystem offers an alternative API that emulates ruby's - # interface for accessing the filesystem, ie. the File and Dir classes. + # Zip::FileSystem offers an alternative API that emulates ruby's + # interface for accessing the filesystem, ie. the ::File and ::Dir classes. class File extend Forwardable extend FileSplit diff --git a/lib/zip/inflater.rb b/lib/zip/inflater.rb index 181603da..40f285f1 100644 --- a/lib/zip/inflater.rb +++ b/lib/zip/inflater.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module Zip - class Inflater < Decompressor #:nodoc:all + class Inflater < Decompressor # :nodoc:all def initialize(*args) super diff --git a/lib/zip/null_compressor.rb b/lib/zip/null_compressor.rb index 41da0c61..c2ce899e 100644 --- a/lib/zip/null_compressor.rb +++ b/lib/zip/null_compressor.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module Zip - class NullCompressor < Compressor #:nodoc:all + class NullCompressor < Compressor # :nodoc:all include Singleton def <<(_data) diff --git a/lib/zip/pass_thru_compressor.rb b/lib/zip/pass_thru_compressor.rb index 484d0da1..9b5bdf3b 100644 --- a/lib/zip/pass_thru_compressor.rb +++ b/lib/zip/pass_thru_compressor.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module Zip - class PassThruCompressor < Compressor #:nodoc:all + class PassThruCompressor < Compressor # :nodoc:all def initialize(output_stream) super() @output_stream = output_stream diff --git a/lib/zip/pass_thru_decompressor.rb b/lib/zip/pass_thru_decompressor.rb index c9973c8d..56e8bd75 100644 --- a/lib/zip/pass_thru_decompressor.rb +++ b/lib/zip/pass_thru_decompressor.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module Zip - class PassThruDecompressor < Decompressor #:nodoc:all + class PassThruDecompressor < Decompressor # :nodoc:all def initialize(*args) super @read_so_far = 0 diff --git a/lib/zip/streamable_directory.rb b/lib/zip/streamable_directory.rb index a3c7c93b..2c931190 100644 --- a/lib/zip/streamable_directory.rb +++ b/lib/zip/streamable_directory.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module Zip - class StreamableDirectory < Entry + class StreamableDirectory < Entry # :nodoc: def initialize(zipfile, entry, src_path = nil, permission = nil) super(zipfile, entry) From f5ea5a8708124866e13757da96056f1af9bc522f Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Fri, 18 Nov 2022 22:17:34 +0000 Subject: [PATCH 271/311] Update Actions to use checkout@v3. --- .github/workflows/lint.yml | 2 +- .github/workflows/tests.yml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 66c262fb..01d401e6 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -7,7 +7,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout rubyzip code - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Install and set up ruby uses: ruby/setup-ruby@v1 diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 49fa1a55..af30a8d9 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -18,7 +18,7 @@ jobs: continue-on-error: ${{ endsWith(matrix.ruby, 'head') || matrix.os == 'windows' }} steps: - name: Checkout rubyzip code - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Install and set up ruby uses: ruby/setup-ruby@v1 @@ -55,7 +55,7 @@ jobs: continue-on-error: true steps: - name: Checkout rubyzip code - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Install and set up ruby uses: ruby/setup-ruby@v1 From f4a1a099623bcf8eb891520f4c0250e370ba9b4c Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sat, 19 Nov 2022 07:30:47 +0000 Subject: [PATCH 272/311] Update the linter CI to use Ruby 2.6. --- .github/workflows/lint.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 01d401e6..927e273f 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -12,7 +12,7 @@ jobs: - name: Install and set up ruby uses: ruby/setup-ruby@v1 with: - ruby-version: '2.5' + ruby-version: '2.6' bundler-cache: true - name: Rubocop From e672c46895974fc3916e8ab1250a7dbf2ba153c3 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sat, 19 Nov 2022 07:32:46 +0000 Subject: [PATCH 273/311] Use an updated rubygems in the tests CI. This is needed for Ruby 2.5, which we need to support a bit longer. --- .github/workflows/tests.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index af30a8d9..a7de14a7 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -24,6 +24,7 @@ jobs: uses: ruby/setup-ruby@v1 with: ruby-version: ${{ matrix.ruby }} + rubygems: '3.2.3' bundler-cache: true - name: Install other dependencies From c0a3c95eee50be2873699dd2cc8b15daacc49dc8 Mon Sep 17 00:00:00 2001 From: Peter Goldstein Date: Sun, 25 Dec 2022 11:10:52 -0500 Subject: [PATCH 274/311] Add Ruby 3.2 to the CI matrix. --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index a7de14a7..6ae5ee3a 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -8,7 +8,7 @@ jobs: fail-fast: false matrix: os: [ubuntu] - ruby: ['2.5', '2.6', '2.7', '3.0', '3.1', head, jruby, jruby-head, truffleruby, truffleruby-head] + ruby: ['2.5', '2.6', '2.7', '3.0', '3.1', '3.2', head, jruby, jruby-head, truffleruby, truffleruby-head] include: - os: macos ruby: '2.5' From efa00356340e11a35ffaf6db8b399dfc45a3e90d Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Tue, 3 Jan 2023 15:47:18 +0000 Subject: [PATCH 275/311] Turn on yjit for Ruby 3.2 in CI. --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 6ae5ee3a..b2823a17 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -51,7 +51,7 @@ jobs: fail-fast: false matrix: os: [ubuntu, macos] - ruby: ['3.1', head] + ruby: ['3.1', '3.2', head] runs-on: ${{ matrix.os }}-latest continue-on-error: true steps: From 3ce504fcfb586fbd7aa260302ca50b956aba4063 Mon Sep 17 00:00:00 2001 From: Peter Boling Date: Tue, 31 Jan 2023 06:43:13 +0700 Subject: [PATCH 276/311] Fix typo know -> known --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3093e6b8..9dc3f73f 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ The public API of some classes has been modernized to use named parameters for o Version 3.x requires at least Ruby 2.5. -Version 2.x requires at least Ruby 2.4, and is know to work on Ruby 3.1. +Version 2.x requires at least Ruby 2.4, and is known to work on Ruby 3.1. It is not recommended to use any versions of Rubyzip earlier than 2.3 due to security issues. From a4f9ec64234ce8b41885643cb1fb19b68be17b4d Mon Sep 17 00:00:00 2001 From: OZAWA Sakuro <10973+sakuro@users.noreply.github.com> Date: Mon, 16 Jan 2023 11:06:59 +0900 Subject: [PATCH 277/311] Add compatibility test for Zip::InputStream#read(0) --- test/input_stream_test.rb | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/test/input_stream_test.rb b/test/input_stream_test.rb index e1b92073..3fd27985 100644 --- a/test/input_stream_test.rb +++ b/test/input_stream_test.rb @@ -189,6 +189,12 @@ def test_read_with_number_of_bytes_returns_nil_at_eof end end + def test_read_with_zero_returns_empty_string + ::Zip::InputStream.open(TestZipFile::TEST_ZIP2.zip_name) do |zis| + assert_equal('', zis.read(0)) + end + end + def test_rewind ::Zip::InputStream.open(TestZipFile::TEST_ZIP2.zip_name) do |zis| e = zis.get_next_entry From a2fc20db8f5b15f54dd656f11f2f5c6f2bc1514b Mon Sep 17 00:00:00 2001 From: OZAWA Sakuro <10973+sakuro@users.noreply.github.com> Date: Mon, 16 Jan 2023 11:19:03 +0900 Subject: [PATCH 278/311] Zip::InputStream#read returns '' with 0 --- lib/zip/ioextras/abstract_input_stream.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/zip/ioextras/abstract_input_stream.rb b/lib/zip/ioextras/abstract_input_stream.rb index 721e33af..c1327252 100644 --- a/lib/zip/ioextras/abstract_input_stream.rb +++ b/lib/zip/ioextras/abstract_input_stream.rb @@ -36,7 +36,7 @@ def read(number_of_bytes = nil, buf = +'') end if tbuf.nil? || tbuf.empty? - return nil if number_of_bytes + return nil if number_of_bytes&.positive? return '' end From 8cec9491b2cbdf730e8ee3293df9f7253f7c8099 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sun, 16 Apr 2023 16:13:30 +0100 Subject: [PATCH 279/311] README: add link to wiki for version 3 details. --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 9dc3f73f..cf2adb81 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,8 @@ The public API of some classes has been modernized to use named parameters for o * `InputStream` * `OutputStream` +**Please see [Updating to version 3.x](https://github.com/rubyzip/rubyzip/wiki/Updating-to-version-3.x) in the wiki for details.** + ## Requirements Version 3.x requires at least Ruby 2.5. From bb09f90ef963dbc92f125635425df34d3b030b99 Mon Sep 17 00:00:00 2001 From: MSP-Greg Date: Fri, 28 Jul 2023 17:27:06 -0500 Subject: [PATCH 280/311] Action - add 2 Windows head builds --- .github/workflows/tests.yml | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index b2823a17..9f03ac34 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -10,10 +10,11 @@ jobs: os: [ubuntu] ruby: ['2.5', '2.6', '2.7', '3.0', '3.1', '3.2', head, jruby, jruby-head, truffleruby, truffleruby-head] include: - - os: macos - ruby: '2.5' - - os: windows - ruby: '2.5' + - { os: macos , ruby: '2.5' } + - { os: windows, ruby: '2.5' } + # head builds + - { os: windows, ruby: ucrt } + - { os: windows, ruby: mswin } runs-on: ${{ matrix.os }}-latest continue-on-error: ${{ endsWith(matrix.ruby, 'head') || matrix.os == 'windows' }} steps: @@ -27,10 +28,6 @@ jobs: rubygems: '3.2.3' bundler-cache: true - - name: Install other dependencies - if: matrix.os == 'windows' - run: choco install zip - - name: Run the tests env: RUBYOPT: -v From f811b2d00e04d41d702398efe3fb54dbac892826 Mon Sep 17 00:00:00 2001 From: MSP-Greg Date: Fri, 28 Jul 2023 17:39:26 -0500 Subject: [PATCH 281/311] Update README.md --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index cf2adb81..1172a4cc 100644 --- a/README.md +++ b/README.md @@ -373,10 +373,10 @@ Please see the table below for what we think the current situation is. Note: an | OS/Ruby | 2.5 | 2.6 | 2.7 | 3.0 | 3.1 | 3.1 +YJIT | Head | Head +YJIT | JRuby 9.3.2.0 | JRuby Head | Truffleruby 21.3.0 | Truffleruby Head | |---------|-----|-----|-----|-----|-----|----------|------|-----------|----------------|------------|--------------------|------------------| -|Ubuntu 20.04.3| CI | CI | CI | CI | CI | ci | ci | ci | CI | ci | CI | ci | -|Mac OS 11.6.2| CI | x | x | x | x | ci | | ci | x | | x | | +|Ubuntu 22.04| CI | CI | CI | CI | CI | ci | ci | ci | CI | ci | CI | ci | +|Mac OS 12.6.7| CI | x | x | x | x | ci | | ci | x | | x | | |Windows 10| | | x | | | | | | | | | | -|Windows Server 2019| CI | | | | | | | | | | | | +|Windows Server 2022| CI | | | | | | CI mswin
CI ucrt | | | | | | Key: `CI` - tested in CI, should work; `ci` - tested in CI, might fail; `x` - known working; `o` - known failing. From 5e9a9cbf18c0564fa1e62d86643935f03b36bde5 Mon Sep 17 00:00:00 2001 From: m-nakamura145 Date: Thu, 1 Feb 2024 22:49:34 +0900 Subject: [PATCH 282/311] Add Ruby 3.3 to CI matrix --- .github/workflows/tests.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 9f03ac34..386de7a0 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -8,7 +8,7 @@ jobs: fail-fast: false matrix: os: [ubuntu] - ruby: ['2.5', '2.6', '2.7', '3.0', '3.1', '3.2', head, jruby, jruby-head, truffleruby, truffleruby-head] + ruby: ['2.5', '2.6', '2.7', '3.0', '3.1', '3.2', '3.3', head, jruby, jruby-head, truffleruby, truffleruby-head] include: - { os: macos , ruby: '2.5' } - { os: windows, ruby: '2.5' } @@ -48,7 +48,7 @@ jobs: fail-fast: false matrix: os: [ubuntu, macos] - ruby: ['3.1', '3.2', head] + ruby: ['3.1', '3.2', '3.3', head] runs-on: ${{ matrix.os }}-latest continue-on-error: true steps: From e3c173b0fc0d33155c7d161c24dc36a722804aaf Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sat, 24 Feb 2024 17:36:52 +0000 Subject: [PATCH 283/311] Update the compatibility matrix in the README. --- README.md | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 1172a4cc..17735648 100644 --- a/README.md +++ b/README.md @@ -371,15 +371,19 @@ Rubyzip 2.3 is known to work on MRI 2.4 to 3.1 on Linux and Mac, and JRuby and T Please see the table below for what we think the current situation is. Note: an empty cell means "unknown", not "does not work". -| OS/Ruby | 2.5 | 2.6 | 2.7 | 3.0 | 3.1 | 3.1 +YJIT | Head | Head +YJIT | JRuby 9.3.2.0 | JRuby Head | Truffleruby 21.3.0 | Truffleruby Head | -|---------|-----|-----|-----|-----|-----|----------|------|-----------|----------------|------------|--------------------|------------------| -|Ubuntu 22.04| CI | CI | CI | CI | CI | ci | ci | ci | CI | ci | CI | ci | -|Mac OS 12.6.7| CI | x | x | x | x | ci | | ci | x | | x | | +| OS/Ruby | 2.5 | 2.6 | 2.7 | 3.0 | 3.1 | 3.2 | 3.3 | Head | JRuby 9.4.6.0 | JRuby Head | Truffleruby 23.1.2 | Truffleruby Head | +|---------|-----|-----|-----|-----|-----|-----|-----|------|---------------|------------|--------------------|------------------| +|Ubuntu 22.04| CI | CI | CI | CI | CI | CI | CI | ci | CI | ci | CI | ci | +|Mac OS 12.7.3| CI | x | x | ci | ci | ci | ci | ci | x | | x | | |Windows 10| | | x | | | | | | | | | | |Windows Server 2022| CI | | | | | | CI mswin
CI ucrt | | | | | | Key: `CI` - tested in CI, should work; `ci` - tested in CI, might fail; `x` - known working; `o` - known failing. +Ruby 3.0+ are also tested separately with YJIT turned on. + +See [the Actions tab](https://github.com/rubyzip/rubyzip/actions) in GitHub for full details. + Please [raise a PR](https://github.com/rubyzip/rubyzip/pulls) if you know Rubyzip works on a platform/Ruby combination not listed here, or [raise an issue](https://github.com/rubyzip/rubyzip/issues) if you see a failure where we think it should work. ## Developing From fd0cf5443ea95c781750f4c0ec735e6cec92ce38 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sat, 24 Feb 2024 17:52:33 +0000 Subject: [PATCH 284/311] Update ZipCrypto instructions for 2.x versions. Suggested by @KamilDzierbicki in #568. --- README.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/README.md b/README.md index 17735648..c55dcd0f 100644 --- a/README.md +++ b/README.md @@ -205,6 +205,27 @@ Any attempt to move about in a zip file opened with `Zip::InputStream` could res Rubyzip supports reading/writing zip files with traditional zip encryption (a.k.a. "ZipCrypto"). AES encryption is not yet supported. It can be used with buffer streams, e.g.: +#### Version 2.x + +```ruby +# Writing. +enc = Zip::TraditionalEncrypter.new('password') +buffer = Zip::OutputStream.write_buffer(::StringIO.new(''), enc) do |output| + output.put_next_entry("my_file.txt") + output.write my_data +end + +# Reading. +dec = Zip::TraditionalDecrypter.new('password') +Zip::InputStream.open(buffer, 0, dec) do |input| + entry = input.get_next_entry + puts "Contents of '#{entry.name}':" + puts input.read +end +``` + +#### Version 3.x + ```ruby # Writing. enc = Zip::TraditionalEncrypter.new('password') From b1ee5cf27212622a1487370e21e4d7bf4067460b Mon Sep 17 00:00:00 2001 From: Kyle Huston Date: Mon, 16 Oct 2023 10:54:59 -0400 Subject: [PATCH 285/311] Note write_zip64_support default is false before 3.0 Improve accuracy of readme to note that write_zip64_support is enabled by default in versions 3.0 and later. --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index c55dcd0f..de6db5e6 100644 --- a/README.md +++ b/README.md @@ -359,12 +359,14 @@ end ### Zip64 Support -By default, Zip64 support is enabled for writing. To disable it do this: +Since version 3.0, Zip64 support is enabled for writing by default. To disable it do this: ```ruby Zip.write_zip64_support = false ``` +Prior to version 3.0, Zip64 support is disabled for writing by default. + _NOTE_: If Zip64 write support is enabled then any extractor subsequently used may also require Zip64 support to read from the resultant archive. ### Block Form From 1c06454985b05d40cfb80def7164d800aaf49d70 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Fri, 1 Mar 2024 22:14:48 +0000 Subject: [PATCH 286/311] Update minimum ruby version to 3.0. All rubies before 3.0 are EOL and this is a major version bump, so it's the right time to do this. --- .github/workflows/lint.yml | 2 +- .github/workflows/tests.yml | 6 ++-- .rubocop.yml | 2 +- .rubocop_todo.yml | 3 ++ Rakefile | 2 +- lib/zip.rb | 2 +- lib/zip/central_directory.rb | 20 ++++++------- lib/zip/crypto/traditional_encryption.rb | 2 +- lib/zip/entry.rb | 34 +++++++++++----------- lib/zip/entry_set.rb | 4 +-- lib/zip/errors.rb | 6 ++-- lib/zip/extra_field.rb | 4 +-- lib/zip/extra_field/generic.rb | 6 ++-- lib/zip/extra_field/ntfs.rb | 4 +-- lib/zip/file.rb | 2 +- lib/zip/file_split.rb | 8 +---- lib/zip/filesystem/dir.rb | 4 +-- lib/zip/filesystem/zip_file_name_mapper.rb | 2 +- lib/zip/input_stream.rb | 7 ++--- lib/zip/ioextras.rb | 2 +- lib/zip/ioextras/abstract_input_stream.rb | 4 +-- lib/zip/output_stream.rb | 4 +-- rubyzip.gemspec | 29 +++++++++--------- samples/example_filesystem.rb | 2 +- samples/write_simple.rb | 2 +- test/case_sensitivity_test.rb | 6 ++-- test/deflater_test.rb | 2 +- test/encryption_test.rb | 6 ++-- test/file_extract_directory_test.rb | 2 +- test/file_extract_test.rb | 12 +++----- test/file_split_test.rb | 6 ++-- test/file_test.rb | 8 ++--- test/filesystem/file_nonmutating_test.rb | 8 ++--- test/gentestfiles.rb | 2 +- test/output_stream_test.rb | 6 ++-- test/settings_test.rb | 2 +- test/stored_support_test.rb | 8 ++--- test/test_helper.rb | 4 +-- 38 files changed, 114 insertions(+), 121 deletions(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 927e273f..0ba0e48c 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -12,7 +12,7 @@ jobs: - name: Install and set up ruby uses: ruby/setup-ruby@v1 with: - ruby-version: '2.6' + ruby-version: '3.0' bundler-cache: true - name: Rubocop diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 386de7a0..46117a01 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -8,10 +8,10 @@ jobs: fail-fast: false matrix: os: [ubuntu] - ruby: ['2.5', '2.6', '2.7', '3.0', '3.1', '3.2', '3.3', head, jruby, jruby-head, truffleruby, truffleruby-head] + ruby: ['3.0', '3.1', '3.2', '3.3', head, jruby, jruby-head, truffleruby, truffleruby-head] include: - - { os: macos , ruby: '2.5' } - - { os: windows, ruby: '2.5' } + - { os: macos , ruby: '3.0' } + - { os: windows, ruby: '3.0' } # head builds - { os: windows, ruby: ucrt } - { os: windows, ruby: mswin } diff --git a/.rubocop.yml b/.rubocop.yml index 056c5164..49bed3dd 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -8,7 +8,7 @@ inherit_from: .rubocop_todo.yml # we get errors if our ruby version doesn't match. AllCops: SuggestExtensions: false - TargetRubyVersion: 2.5 + TargetRubyVersion: 3.0 NewCops: enable # Allow this in this file because adding the extra lines is pointless. diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 2b3f89b1..ff5238cd 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -6,6 +6,9 @@ # Note that changes in the inspected code, or installation of new # versions of RuboCop, may require this file to be generated again. +Gemspec/DevelopmentDependencies: + Enabled: false + # Offense count: 7 Lint/MissingSuper: Exclude: diff --git a/Rakefile b/Rakefile index 73299026..f1e81470 100644 --- a/Rakefile +++ b/Rakefile @@ -19,7 +19,7 @@ RDoc::Task.new do |rdoc| rdoc.rdoc_files.include('README.md', 'lib/**/*.rb') rdoc.options << '--markup=markdown' rdoc.options << '--tab-width=2' - rdoc.options << "-t Rubyzip version #{::Zip::VERSION}" + rdoc.options << "-t Rubyzip version #{Zip::VERSION}" end RuboCop::RakeTask.new diff --git a/lib/zip.rb b/lib/zip.rb index b2ccc678..5d421226 100644 --- a/lib/zip.rb +++ b/lib/zip.rb @@ -65,7 +65,7 @@ def reset! @on_exists_proc = false @continue_on_exists_proc = false @sort_entries = false - @default_compression = ::Zlib::DEFAULT_COMPRESSION + @default_compression = Zlib::DEFAULT_COMPRESSION @write_zip64_support = true @warn_invalid_date = true @case_insensitive_match = false diff --git a/lib/zip/central_directory.rb b/lib/zip/central_directory.rb index 53d213f5..61be6fd3 100644 --- a/lib/zip/central_directory.rb +++ b/lib/zip/central_directory.rb @@ -28,7 +28,7 @@ class CentralDirectory # :nodoc: mark_dirty :<<, :comment=, :delete - def initialize(entries = EntrySet.new, comment = '') #:nodoc: + def initialize(entries = EntrySet.new, comment = '') # :nodoc: super(dirty_on_create: false) @entry_set = entries.kind_of?(EntrySet) ? entries : EntrySet.new(entries) @comment = comment @@ -39,7 +39,7 @@ def read_from_stream(io) read_central_directory_entries(io) end - def write_to_stream(io) #:nodoc: + def write_to_stream(io) # :nodoc: cdir_offset = io.tell @entry_set.each { |entry| entry.write_c_dir_entry(io) } eocd_offset = io.tell @@ -61,7 +61,7 @@ def count_entries(io) @size end - def ==(other) #:nodoc: + def ==(other) # :nodoc: return false unless other.kind_of?(CentralDirectory) @entry_set.entries.sort == other.entries.sort && comment == other.comment @@ -69,7 +69,7 @@ def ==(other) #:nodoc: private - def write_e_o_c_d(io, offset, cdir_size) #:nodoc: + def write_e_o_c_d(io, offset, cdir_size) # :nodoc: tmp = [ END_OF_CD_SIG, 0, # @numberOfThisDisk @@ -84,7 +84,7 @@ def write_e_o_c_d(io, offset, cdir_size) #:nodoc: io << @comment end - def write_64_e_o_c_d(io, offset, cdir_size) #:nodoc: + def write_64_e_o_c_d(io, offset, cdir_size) # :nodoc: tmp = [ ZIP64_END_OF_CD_SIG, 44, # size of zip64 end of central directory record (excludes signature and field itself) @@ -110,7 +110,7 @@ def write_64_eocd_locator(io, zip64_eocd_offset) io << tmp.pack('VVQ> 24).chr, ~@key2) end diff --git a/lib/zip/entry.rb b/lib/zip/entry.rb index 38ebcb41..18959233 100644 --- a/lib/zip/entry.rb +++ b/lib/zip/entry.rb @@ -185,12 +185,12 @@ def ftype # :nodoc: # Dynamic checkers %w[directory file symlink].each do |k| - define_method "#{k}?" do + define_method :"#{k}?" do file_type_is?(k.to_sym) end end - def name_is_directory? #:nodoc:all + def name_is_directory? # :nodoc:all @name.end_with?('/') end @@ -207,7 +207,7 @@ def name_safe? ::File.absolute_path(cleanpath.to_s, root).match?(/([A-Z]:)?#{naive}/i) end - def local_entry_offset #:nodoc:all + def local_entry_offset # :nodoc:all local_header_offset + @local_header_size end @@ -223,7 +223,7 @@ def comment_size @comment ? @comment.bytesize : 0 end - def calculate_local_header_size #:nodoc:all + def calculate_local_header_size # :nodoc:all LOCAL_ENTRY_STATIC_HEADER_LENGTH + name_size + extra_size end @@ -239,12 +239,12 @@ def verify_local_header_size! "Local header size changed (#{@local_header_size} -> #{new_size})" end - def cdir_header_size #:nodoc:all + def cdir_header_size # :nodoc:all CDIR_ENTRY_STATIC_HEADER_LENGTH + name_size + (@extra ? @extra.c_dir_size : 0) + comment_size end - def next_header_offset #:nodoc:all + def next_header_offset # :nodoc:all local_entry_offset + compressed_size end @@ -266,7 +266,7 @@ def extract(entry_path = @name, destination_directory: '.', &block) raise "unknown file type #{inspect}" unless directory? || file? || symlink? - __send__("create_#{ftype}", extract_path, &block) + __send__(:"create_#{ftype}", extract_path, &block) self end @@ -275,7 +275,7 @@ def to_s end class << self - def read_c_dir_entry(io) #:nodoc:all + def read_c_dir_entry(io) # :nodoc:all path = if io.respond_to?(:path) io.path else @@ -314,7 +314,7 @@ def unpack_local_entry(buf) @extra_length = buf.unpack('VCCvvvvVVVvv') end - def read_local_entry(io) #:nodoc:all + def read_local_entry(io) # :nodoc:all @dirty = false # No changes at this point. @local_header_offset = io.tell @@ -371,7 +371,7 @@ def pack_local_entry @extra ? @extra.local_size : 0].pack('VvvvvvVVVvv') end - def write_local_entry(io, rewrite: false) #:nodoc:all + def write_local_entry(io, rewrite: false) # :nodoc:all prep_local_zip64_extra verify_local_header_size! if rewrite @local_header_offset = io.tell @@ -463,7 +463,7 @@ def read_extra_field(buf, local: false) end end - def read_c_dir_entry(io) #:nodoc:all + def read_c_dir_entry(io) # :nodoc:all @dirty = false # No changes at this point. static_sized_fields_buf = io.read(::Zip::CDIR_ENTRY_STATIC_HEADER_LENGTH) check_c_dir_entry_static_header_length(static_sized_fields_buf) @@ -556,7 +556,7 @@ def pack_c_dir_entry ].pack('VCCvvvvvVVVvvvvvVV') end - def write_c_dir_entry(io) #:nodoc:all + def write_c_dir_entry(io) # :nodoc:all prep_cdir_zip64_extra case @fstype @@ -574,7 +574,7 @@ def write_c_dir_entry(io) #:nodoc:all end unless ft.nil? - @external_file_attributes = (ft << 12 | (@unix_perms & 0o7777)) << 16 + @external_file_attributes = ((ft << 12) | (@unix_perms & 0o7777)) << 16 end end @@ -639,7 +639,7 @@ def gather_fileinfo_from_srcpath(src_path) # :nodoc: if name_is_directory? raise ArgumentError, "entry name '#{newEntry}' indicates directory entry, but " \ - "'#{src_path}' is not a directory" + "'#{src_path}' is not a directory" end :file when 'directory' @@ -649,7 +649,7 @@ def gather_fileinfo_from_srcpath(src_path) # :nodoc: if name_is_directory? raise ArgumentError, "entry name '#{newEntry}' indicates directory entry, but " \ - "'#{src_path}' is not a directory" + "'#{src_path}' is not a directory" end :symlink else @@ -661,7 +661,7 @@ def gather_fileinfo_from_srcpath(src_path) # :nodoc: get_extra_attributes_from_path(@filepath) end - def write_to_zip_output_stream(zip_output_stream) #:nodoc:all + def write_to_zip_output_stream(zip_output_stream) # :nodoc:all if ftype == :directory zip_output_stream.put_next_entry(self) elsif @filepath @@ -749,7 +749,7 @@ def create_symlink(dest_path) # apply missing data from the zip64 extra information field, if present # (required when file sizes exceed 2**32, but can be used for all files) - def parse_zip64_extra(for_local_header) #:nodoc:all + def parse_zip64_extra(for_local_header) # :nodoc:all return unless zip64? if for_local_header diff --git a/lib/zip/entry_set.rb b/lib/zip/entry_set.rb index 941e92e2..cb4700d6 100644 --- a/lib/zip/entry_set.rb +++ b/lib/zip/entry_set.rb @@ -61,12 +61,12 @@ def parent(entry) end def glob(pattern, flags = ::File::FNM_PATHNAME | ::File::FNM_DOTMATCH | ::File::FNM_EXTGLOB) - entries.map do |entry| + entries.filter_map do |entry| next nil unless ::File.fnmatch(pattern, entry.name.chomp('/'), flags) yield(entry) if block_given? entry - end.compact + end end protected diff --git a/lib/zip/errors.rb b/lib/zip/errors.rb index c757a8da..5b75e9a6 100644 --- a/lib/zip/errors.rb +++ b/lib/zip/errors.rb @@ -43,7 +43,7 @@ def initialize(destination) def message "Cannot create file or directory '#{@destination}'. " \ - 'A file already exists with that name.' + 'A file already exists with that name.' end end @@ -111,8 +111,8 @@ def initialize(entry) def message "The local header of this entry ('#{@entry.name}') does not contain " \ - 'the correct metadata for `Zip::InputStream` to be able to ' \ - 'uncompress it. Please use `Zip::File` instead of `Zip::InputStream`.' + 'the correct metadata for `Zip::InputStream` to be able to ' \ + 'uncompress it. Please use `Zip::File` instead of `Zip::InputStream`.' end end end diff --git a/lib/zip/extra_field.rb b/lib/zip/extra_field.rb index 95dd5db2..31d75d2c 100644 --- a/lib/zip/extra_field.rb +++ b/lib/zip/extra_field.rb @@ -21,8 +21,8 @@ def extra_field_type_exist(binstr, id, len, index) def extra_field_type_unknown(binstr, len, index, local) self['Unknown'] ||= Unknown.new - if !len || len + 4 > binstr[index..-1].bytesize - self['Unknown'].merge(binstr[index..-1], local: local) + if !len || len + 4 > binstr[index..].bytesize + self['Unknown'].merge(binstr[index..], local: local) return end diff --git a/lib/zip/extra_field/generic.rb b/lib/zip/extra_field/generic.rb index 137efe90..ddd8eed6 100644 --- a/lib/zip/extra_field/generic.rb +++ b/lib/zip/extra_field/generic.rb @@ -21,17 +21,17 @@ def initial_parse(binstr) return false end - [binstr[2, 2].unpack1('v'), binstr[4..-1]] + [binstr[2, 2].unpack1('v'), binstr[4..]] end def to_local_bin s = pack_for_local - self.class.const_get(:HEADER_ID) + [s.bytesize].pack('v') << s + (self.class.const_get(:HEADER_ID) + [s.bytesize].pack('v')) << s end def to_c_dir_bin s = pack_for_c_dir - self.class.const_get(:HEADER_ID) + [s.bytesize].pack('v') << s + (self.class.const_get(:HEADER_ID) + [s.bytesize].pack('v')) << s end end end diff --git a/lib/zip/extra_field/ntfs.rb b/lib/zip/extra_field/ntfs.rb index be1541db..eac99ad6 100644 --- a/lib/zip/extra_field/ntfs.rb +++ b/lib/zip/extra_field/ntfs.rb @@ -25,7 +25,7 @@ def merge(binstr) size, content = initial_parse(binstr) (size && content) || return - content = content[4..-1] + content = content[4..] tags = parse_tags(content) tag1 = tags[1] @@ -86,7 +86,7 @@ def parse_tags(content) end def from_ntfs_time(ntfs_time) - ::Zip::DOSTime.at(ntfs_time / WINDOWS_TICK - SEC_TO_UNIX_EPOCH) + ::Zip::DOSTime.at((ntfs_time / WINDOWS_TICK) - SEC_TO_UNIX_EPOCH) end def to_ntfs_time(time) diff --git a/lib/zip/file.rb b/lib/zip/file.rb index 9c66dc13..85a75035 100644 --- a/lib/zip/file.rb +++ b/lib/zip/file.rb @@ -356,7 +356,7 @@ def initialize_cdir(path_or_io, buffer: false) # This zip is probably a non-empty StringIO. @create = false @cdir.read_from_stream(path_or_io) - elsif !@create && ::File.zero?(@name) + elsif !@create && ::File.empty?(@name) # A file exists, but it is empty, and we've said we're # NOT creating a new zip. raise Error, "File #{@name} has zero size. Did you mean to pass the create flag?" diff --git a/lib/zip/file_split.rb b/lib/zip/file_split.rb index 0f6c694a..de5364a3 100644 --- a/lib/zip/file_split.rb +++ b/lib/zip/file_split.rb @@ -7,13 +7,7 @@ module FileSplit # :nodoc: DATA_BUFFER_SIZE = 8192 def get_segment_size_for_split(segment_size) - if MIN_SEGMENT_SIZE > segment_size - MIN_SEGMENT_SIZE - elsif MAX_SEGMENT_SIZE < segment_size - MAX_SEGMENT_SIZE - else - segment_size - end + segment_size.clamp(MIN_SEGMENT_SIZE, MAX_SEGMENT_SIZE) end def get_partial_zip_file_name(zip_file_name, partial_zip_file_name) diff --git a/lib/zip/filesystem/dir.rb b/lib/zip/filesystem/dir.rb index a5e1ef9e..5ae4ac45 100644 --- a/lib/zip/filesystem/dir.rb +++ b/lib/zip/filesystem/dir.rb @@ -45,8 +45,8 @@ def entries(directory_name) entries end - def glob(*args, &block) - @mapped_zip.glob(*args, &block) + def glob(...) + @mapped_zip.glob(...) end def foreach(directory_name) diff --git a/lib/zip/filesystem/zip_file_name_mapper.rb b/lib/zip/filesystem/zip_file_name_mapper.rb index 6b9c7f51..77a4e691 100644 --- a/lib/zip/filesystem/zip_file_name_mapper.rb +++ b/lib/zip/filesystem/zip_file_name_mapper.rb @@ -74,7 +74,7 @@ def expand_path(path) private def expand_to_entry(path) - expand_path(path)[1..-1] + expand_path(path)[1..] end end end diff --git a/lib/zip/input_stream.rb b/lib/zip/input_stream.rb index f48466c0..56ecf4f9 100644 --- a/lib/zip/input_stream.rb +++ b/lib/zip/input_stream.rb @@ -144,8 +144,7 @@ def open_entry 'A password is required to decode this zip file' end - if @current_entry.incomplete? && @current_entry.compressed_size == 0 \ - && !@complete_entry + if @current_entry.incomplete? && @current_entry.compressed_size == 0 && !@complete_entry raise StreamingError, @current_entry end @@ -166,8 +165,8 @@ def get_decompressor return ::Zip::NullDecompressor if @current_entry.nil? decompressed_size = - if @current_entry.incomplete? && @current_entry.crc == 0 \ - && @current_entry.size == 0 && @complete_entry + if @current_entry.incomplete? && @current_entry.crc == 0 && + @current_entry.size == 0 && @complete_entry @complete_entry.size else @current_entry.size diff --git a/lib/zip/ioextras.rb b/lib/zip/ioextras.rb index fbc77c6f..66688966 100644 --- a/lib/zip/ioextras.rb +++ b/lib/zip/ioextras.rb @@ -12,7 +12,7 @@ def copy_stream(ostream, istream) def copy_stream_n(ostream, istream, nbytes) toread = nbytes while toread > 0 && !istream.eof? - tr = toread > CHUNK_SIZE ? CHUNK_SIZE : toread + tr = [toread, CHUNK_SIZE].min ostream.write(istream.read(tr, +'')) toread -= tr end diff --git a/lib/zip/ioextras/abstract_input_stream.rb b/lib/zip/ioextras/abstract_input_stream.rb index c1327252..ed95e63d 100644 --- a/lib/zip/ioextras/abstract_input_stream.rb +++ b/lib/zip/ioextras/abstract_input_stream.rb @@ -76,13 +76,13 @@ def gets(a_sep_string = $INPUT_RECORD_SEPARATOR, number_of_bytes = nil) a_sep_string = "#{$INPUT_RECORD_SEPARATOR}#{$INPUT_RECORD_SEPARATOR}" if a_sep_string.empty? buffer_index = 0 - over_limit = (number_of_bytes && @output_buffer.bytesize >= number_of_bytes) + over_limit = number_of_bytes && @output_buffer.bytesize >= number_of_bytes while (match_index = @output_buffer.index(a_sep_string, buffer_index)).nil? && !over_limit buffer_index = [buffer_index, @output_buffer.bytesize - a_sep_string.bytesize].max return @output_buffer.empty? ? nil : flush if input_finished? @output_buffer << produce_input - over_limit = (number_of_bytes && @output_buffer.bytesize >= number_of_bytes) + over_limit = number_of_bytes && @output_buffer.bytesize >= number_of_bytes end sep_index = [ match_index + a_sep_string.bytesize, diff --git a/lib/zip/output_stream.rb b/lib/zip/output_stream.rb index 0c4e93c8..095126b5 100644 --- a/lib/zip/output_stream.rb +++ b/lib/zip/output_stream.rb @@ -139,8 +139,8 @@ def finalize_current_entry return unless @current_entry finish - @current_entry.compressed_size = @output_stream.tell - \ - @current_entry.local_header_offset - \ + @current_entry.compressed_size = @output_stream.tell - + @current_entry.local_header_offset - @current_entry.calculate_local_header_size @current_entry.size = @compressor.size @current_entry.crc = @compressor.crc diff --git a/rubyzip.gemspec b/rubyzip.gemspec index 4c557675..f9d85f1c 100644 --- a/rubyzip.gemspec +++ b/rubyzip.gemspec @@ -4,7 +4,7 @@ require_relative 'lib/zip/version' Gem::Specification.new do |s| s.name = 'rubyzip' - s.version = ::Zip::VERSION + s.version = Zip::VERSION s.authors = ['Robert Haines', 'John Lees-Miller', 'Alexander Simonov'] s.email = [ 'hainesr@gmail.com', 'jdleesmiller@gmail.com', 'alex@simonov.me' @@ -18,21 +18,22 @@ Gem::Specification.new do |s| s.license = 'BSD-2-Clause' s.metadata = { - 'bug_tracker_uri' => 'https://github.com/rubyzip/rubyzip/issues', - 'changelog_uri' => "https://github.com/rubyzip/rubyzip/blob/v#{s.version}/Changelog.md", - 'documentation_uri' => "https://www.rubydoc.info/gems/rubyzip/#{s.version}", - 'source_code_uri' => "https://github.com/rubyzip/rubyzip/tree/v#{s.version}", - 'wiki_uri' => 'https://github.com/rubyzip/rubyzip/wiki' + 'bug_tracker_uri' => 'https://github.com/rubyzip/rubyzip/issues', + 'changelog_uri' => "https://github.com/rubyzip/rubyzip/blob/v#{s.version}/Changelog.md", + 'documentation_uri' => "https://www.rubydoc.info/gems/rubyzip/#{s.version}", + 'source_code_uri' => "https://github.com/rubyzip/rubyzip/tree/v#{s.version}", + 'wiki_uri' => 'https://github.com/rubyzip/rubyzip/wiki', + 'rubygems_mfa_required' => 'true' } - s.required_ruby_version = '>= 2.5' + s.required_ruby_version = '>= 3.0' - s.add_development_dependency 'minitest', '~> 5.4' - s.add_development_dependency 'rake', '~> 12.3.3' - s.add_development_dependency 'rdoc', '~> 6.4.0' - s.add_development_dependency 'rubocop', '~> 1.12.0' - s.add_development_dependency 'rubocop-performance', '~> 1.10.0' - s.add_development_dependency 'rubocop-rake', '~> 0.5.0' - s.add_development_dependency 'simplecov', '~> 0.18.0' + s.add_development_dependency 'minitest', '~> 5.22.0' + s.add_development_dependency 'rake', '~> 13.1.0' + s.add_development_dependency 'rdoc', '~> 6.6.2' + s.add_development_dependency 'rubocop', '~> 1.61.0' + s.add_development_dependency 'rubocop-performance', '~> 1.20.0' + s.add_development_dependency 'rubocop-rake', '~> 0.6.0' + s.add_development_dependency 'simplecov', '~> 0.22.0' s.add_development_dependency 'simplecov-lcov', '~> 0.8' end diff --git a/samples/example_filesystem.rb b/samples/example_filesystem.rb index 6909848f..ec0075ee 100755 --- a/samples/example_filesystem.rb +++ b/samples/example_filesystem.rb @@ -7,7 +7,7 @@ EXAMPLE_ZIP = 'filesystem.zip' -File.delete(EXAMPLE_ZIP) if File.exist?(EXAMPLE_ZIP) +FileUtils.rm_f(EXAMPLE_ZIP) Zip::File.open(EXAMPLE_ZIP, create: true) do |zf| zf.file.open('file1.txt', 'w') { |os| os.write 'first file1.txt' } diff --git a/samples/write_simple.rb b/samples/write_simple.rb index f7939200..84ac1387 100755 --- a/samples/write_simple.rb +++ b/samples/write_simple.rb @@ -5,7 +5,7 @@ require 'zip' -::Zip::OutputStream.open('simple.zip') do |zos| +Zip::OutputStream.open('simple.zip') do |zos| zos.put_next_entry 'entry.txt' zos.puts 'Hello world' end diff --git a/test/case_sensitivity_test.rb b/test/case_sensitivity_test.rb index 07e84579..9a4d84f4 100644 --- a/test/case_sensitivity_test.rb +++ b/test/case_sensitivity_test.rb @@ -18,7 +18,7 @@ def teardown def test_add_case_sensitive ::Zip.case_insensitive_match = false - SRC_FILES.each { |fn, _en| assert(::File.exist?(fn)) } + SRC_FILES.each { |(fn, _en)| assert(::File.exist?(fn)) } zf = ::Zip::File.new(EMPTY_FILENAME, create: true) SRC_FILES.each { |fn, en| zf.add(en, fn) } @@ -37,7 +37,7 @@ def test_add_case_sensitive def test_add_case_insensitive ::Zip.case_insensitive_match = true - SRC_FILES.each { |fn, _en| assert(::File.exist?(fn)) } + SRC_FILES.each { |(fn, _en)| assert(::File.exist?(fn)) } zf = ::Zip::File.new(EMPTY_FILENAME, create: true) error = assert_raises Zip::EntryExistsError do @@ -50,7 +50,7 @@ def test_add_case_insensitive def test_add_case_sensitive_read_case_insensitive ::Zip.case_insensitive_match = false - SRC_FILES.each { |fn, _en| assert(::File.exist?(fn)) } + SRC_FILES.each { |(fn, _en)| assert(::File.exist?(fn)) } zf = ::Zip::File.new(EMPTY_FILENAME, create: true) SRC_FILES.each { |fn, en| zf.add(en, fn) } diff --git a/test/deflater_test.rb b/test/deflater_test.rb index fb782205..9e6b1e2e 100644 --- a/test/deflater_test.rb +++ b/test/deflater_test.rb @@ -49,7 +49,7 @@ def test_data_error private def load_file(filename) - File.open(filename, 'rb', &:read) + File.binread(filename) end def deflate(data, filename) diff --git a/test/encryption_test.rb b/test/encryption_test.rb index 6fd63950..05612817 100644 --- a/test/encryption_test.rb +++ b/test/encryption_test.rb @@ -16,7 +16,7 @@ def teardown end def test_encrypt - content = File.open(INPUT_FILE1, 'r').read + content = File.read(INPUT_FILE1) test_filename = 'top_secret_file.txt' password = 'swordfish' @@ -58,11 +58,11 @@ def test_decrypt entry = zis.get_next_entry assert_equal 'file1.txt', entry.name assert_equal 1_327, entry.size - assert_equal ::File.open(INPUT_FILE1, 'r').read, zis.read + assert_equal ::File.read(INPUT_FILE1), zis.read entry = zis.get_next_entry assert_equal 'file2.txt', entry.name assert_equal 41_234, entry.size - assert_equal ::File.open(INPUT_FILE2, 'r').read, zis.read + assert_equal ::File.read(INPUT_FILE2), zis.read end end end diff --git a/test/file_extract_directory_test.rb b/test/file_extract_directory_test.rb index 17d0995a..8c70d759 100644 --- a/test/file_extract_directory_test.rb +++ b/test/file_extract_directory_test.rb @@ -22,7 +22,7 @@ def setup super Dir.rmdir(TEST_OUT_NAME) if File.directory? TEST_OUT_NAME - File.delete(TEST_OUT_NAME) if File.exist? TEST_OUT_NAME + FileUtils.rm_f(TEST_OUT_NAME) end def test_extract_directory diff --git a/test/file_extract_test.rb b/test/file_extract_test.rb index 77da4a6d..9e353d1c 100644 --- a/test/file_extract_test.rb +++ b/test/file_extract_test.rb @@ -38,7 +38,7 @@ def test_extract def test_extract_exists text = 'written text' - ::File.open(EXTRACTED_FILENAME, 'w') { |f| f.write(text) } + ::File.write(EXTRACTED_FILENAME, text) assert_raises(::Zip::DestinationExistsError) do ::Zip::File.open(TEST_ZIP.zip_name) do |zf| @@ -53,7 +53,7 @@ def test_extract_exists def test_extract_exists_overwrite text = 'written text' - ::File.open(EXTRACTED_FILENAME, 'w') { |f| f.write(text) } + ::File.write(EXTRACTED_FILENAME, text) called_correctly = false ::Zip::File.open(TEST_ZIP.zip_name) do |zf| @@ -123,9 +123,7 @@ def test_extract_incorrect_size assert data.include?(true_size_bytes) data.gsub! true_size_bytes, fake_size_bytes - File.open(fake_zip, 'wb') do |file| - file.write data - end + File.binwrite(fake_zip, data) Dir.chdir tmp do ::Zip::File.open(fake_zip) do |zf| @@ -187,9 +185,7 @@ def test_extract_incorrect_size_zip64 assert data.include?(true_size_bytes) data.gsub! true_size_bytes, fake_size_bytes - File.open(fake_zip, 'wb') do |file| - file.write data - end + File.binwrite(fake_zip, data) Dir.chdir tmp do ::Zip::File.open(fake_zip) do |zf| diff --git a/test/file_split_test.rb b/test/file_split_test.rb index 0e54cc99..859bf935 100644 --- a/test/file_split_test.rb +++ b/test/file_split_test.rb @@ -15,10 +15,10 @@ def setup def teardown File.delete(TEST_ZIP.zip_name) - File.delete(UNSPLITTED_FILENAME) if File.exist?(UNSPLITTED_FILENAME) + FileUtils.rm_f(UNSPLITTED_FILENAME) Dir["#{TEST_ZIP.zip_name}.*"].each do |zip_file_name| - File.delete(zip_file_name) if File.exist?(zip_file_name) + FileUtils.rm_f(zip_file_name) end end @@ -33,7 +33,7 @@ def test_split return if result.nil? - Dir["#{TEST_ZIP.zip_name}.*"].sort.each_with_index do |zip_file_name, index| + Dir["#{TEST_ZIP.zip_name}.*"].each_with_index do |zip_file_name, index| File.open(zip_file_name, 'rb') do |zip_file| zip_file.read([::Zip::SPLIT_FILE_SIGNATURE].pack('V').size) if index.zero? File.open(UNSPLITTED_FILENAME, 'ab') do |file| diff --git a/test/file_test.rb b/test/file_test.rb index b6dd766f..b09f78bd 100644 --- a/test/file_test.rb +++ b/test/file_test.rb @@ -22,7 +22,7 @@ def test_create_from_scratch_to_buffer zf.comment = comment end - ::File.open(EMPTY_FILENAME, 'wb') { |file| file.write buffer.string } + ::File.binwrite(EMPTY_FILENAME, buffer.string) zf_read = ::Zip::File.new(EMPTY_FILENAME) assert_equal(comment, zf_read.comment) @@ -105,7 +105,7 @@ def test_get_output_stream assert_equal(entry.time, custom_entry_args[:time]) zf.get_output_stream('entry.bin') do |os| - os.write(::File.open('test/data/generated/5entry.zip', 'rb').read) + os.write(::File.binread('test/data/generated/5entry.zip')) end end @@ -113,7 +113,7 @@ def test_get_output_stream assert_equal(count + 3, zf.size) assert_equal('Putting stuff in new_entry.txt', zf.read('new_entry.txt')) assert_equal('Putting stuff in data/generated/empty.txt', zf.read('test/data/generated/empty.txt')) - assert_equal(File.open('test/data/generated/5entry.zip', 'rb').read, zf.read('entry.bin')) + assert_equal(File.binread('test/data/generated/5entry.zip'), zf.read('entry.bin')) end end @@ -693,7 +693,7 @@ def test_write_buffer old_name = zf.entries.first zf.rename(old_name, new_name) buffer = zf.write_buffer(::StringIO.new) - File.open(TEST_ZIP.zip_name, 'wb') { |f| f.write buffer.string } + File.binwrite(TEST_ZIP.zip_name, buffer.string) zf_read = ::Zip::File.new(TEST_ZIP.zip_name) refute_nil(zf_read.entries.detect { |e| e.name == new_name }) assert_nil(zf_read.entries.detect { |e| e.name == old_name }) diff --git a/test/filesystem/file_nonmutating_test.rb b/test/filesystem/file_nonmutating_test.rb index e6b1f132..46d700b0 100644 --- a/test/filesystem/file_nonmutating_test.rb +++ b/test/filesystem/file_nonmutating_test.rb @@ -406,7 +406,7 @@ def test_foreach zf.file.foreach('test/data/file1.txt') do |l| # Ruby replaces \n with \r\n automatically on windows - newline = Zip::RUNNING_ON_WINDOWS ? l.gsub(/\r\n/, "\n") : l + newline = Zip::RUNNING_ON_WINDOWS ? l.gsub("\r\n", "\n") : l assert_equal(ref[index], newline) index = index.next end @@ -420,7 +420,7 @@ def test_foreach zf.file.foreach('test/data/file1.txt', ' ') do |l| # Ruby replaces \n with \r\n automatically on windows - newline = Zip::RUNNING_ON_WINDOWS ? l.gsub(/\r\n/, "\n") : l + newline = Zip::RUNNING_ON_WINDOWS ? l.gsub("\r\n", "\n") : l assert_equal(ref[index], newline) index = index.next end @@ -486,7 +486,7 @@ def test_readlines zip_file = zf.file.readlines('test/data/file1.txt') # Ruby replaces \n with \r\n automatically on windows - zip_file.each { |l| l.gsub!(/\r\n/, "\n") } if Zip::RUNNING_ON_WINDOWS + zip_file.each { |l| l.gsub!("\r\n", "\n") } if Zip::RUNNING_ON_WINDOWS assert_equal(orig_file, zip_file) end @@ -498,7 +498,7 @@ def test_read # Ruby replaces \n with \r\n automatically on windows zip_file = if Zip::RUNNING_ON_WINDOWS - zf.file.read('test/data/file1.txt').gsub(/\r\n/, "\n") + zf.file.read('test/data/file1.txt').gsub("\r\n", "\n") else zf.file.read('test/data/file1.txt') end diff --git a/test/gentestfiles.rb b/test/gentestfiles.rb index 50820427..75ce4d8f 100755 --- a/test/gentestfiles.rb +++ b/test/gentestfiles.rb @@ -20,7 +20,7 @@ class TestFiles class << self def create_test_files - Dir.mkdir 'test/data/generated' unless Dir.exist?('test/data/generated') + FileUtils.mkdir_p 'test/data/generated' ASCII_TEST_FILES.each_with_index do |filename, index| create_random_ascii(filename, 1E4 * (index + 1)) diff --git a/test/output_stream_test.rb b/test/output_stream_test.rb index b87f64fc..16254f13 100644 --- a/test/output_stream_test.rb +++ b/test/output_stream_test.rb @@ -29,7 +29,7 @@ def test_write_buffer zos.comment = TEST_ZIP.comment write_test_zip(zos) end - File.open(TEST_ZIP.zip_name, 'wb') { |f| f.write buffer.string } + File.binwrite(TEST_ZIP.zip_name, buffer.string) assert_test_zip_contents(TEST_ZIP) end @@ -50,7 +50,7 @@ def test_write_buffer_with_temp_file end tmp_file.rewind - File.open(TEST_ZIP.zip_name, 'wb') { |f| f.write(tmp_file.read) } + File.binwrite(TEST_ZIP.zip_name, tmp_file.read) tmp_file.unlink assert_test_zip_contents(TEST_ZIP) @@ -75,7 +75,7 @@ def test_write_buffer_with_default_io zos.comment = TEST_ZIP.comment write_test_zip(zos) end - File.open(TEST_ZIP.zip_name, 'wb') { |f| f.write buffer.string } + File.binwrite(TEST_ZIP.zip_name, buffer.string) assert_test_zip_contents(TEST_ZIP) end diff --git a/test/settings_test.rb b/test/settings_test.rb index 3bf66e4e..9e280aab 100644 --- a/test/settings_test.rb +++ b/test/settings_test.rb @@ -12,7 +12,7 @@ def setup super Dir.rmdir(TEST_OUT_NAME) if File.directory? TEST_OUT_NAME - File.delete(TEST_OUT_NAME) if File.exist? TEST_OUT_NAME + FileUtils.rm_f(TEST_OUT_NAME) end def teardown diff --git a/test/stored_support_test.rb b/test/stored_support_test.rb index 4e0cdad6..e1cd1813 100644 --- a/test/stored_support_test.rb +++ b/test/stored_support_test.rb @@ -14,11 +14,11 @@ def test_read entry = zis.get_next_entry assert_equal 'file1.txt', entry.name assert_equal 1_327, entry.size - assert_equal ::File.open(INPUT_FILE1, 'r').read, zis.read + assert_equal ::File.read(INPUT_FILE1), zis.read entry = zis.get_next_entry assert_equal 'file2.txt', entry.name assert_equal 41_234, entry.size - assert_equal ::File.open(INPUT_FILE2, 'r').read, zis.read + assert_equal ::File.read(INPUT_FILE2), zis.read end end @@ -29,11 +29,11 @@ def test_encrypted_read entry = zis.get_next_entry assert_equal 'file1.txt', entry.name assert_equal 1_327, entry.size - assert_equal ::File.open(INPUT_FILE1, 'r').read, zis.read + assert_equal ::File.read(INPUT_FILE1), zis.read entry = zis.get_next_entry assert_equal 'file2.txt', entry.name assert_equal 41_234, entry.size - assert_equal ::File.open(INPUT_FILE2, 'r').read, zis.read + assert_equal ::File.read(INPUT_FILE2), zis.read end end end diff --git a/test/test_helper.rb b/test/test_helper.rb index c3ab2bb7..8f665b1c 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -12,7 +12,7 @@ TestFiles.create_test_files TestZipFile.create_test_zips -::MiniTest.after_run do +MiniTest.after_run do FileUtils.rm_rf('test/data/generated') end @@ -137,7 +137,7 @@ module CommonZipFileFixture TEST_ZIP.zip_name = 'test/data/generated/5entry_copy.zip' def setup - File.delete(EMPTY_FILENAME) if File.exist?(EMPTY_FILENAME) + FileUtils.rm_f(EMPTY_FILENAME) FileUtils.cp(TestZipFile::TEST_ZIP2.zip_name, TEST_ZIP.zip_name) end end From 04376dc2bc41e06bfa1dab943f991e332b403554 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Fri, 1 Mar 2024 22:28:41 +0000 Subject: [PATCH 287/311] Update GitHub actions to Node.js 20 versions. --- .github/workflows/lint.yml | 2 +- .github/workflows/tests.yml | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 0ba0e48c..b777aaa8 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -7,7 +7,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout rubyzip code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Install and set up ruby uses: ruby/setup-ruby@v1 diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 46117a01..024cdd7f 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -19,7 +19,7 @@ jobs: continue-on-error: ${{ endsWith(matrix.ruby, 'head') || matrix.os == 'windows' }} steps: - name: Checkout rubyzip code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Install and set up ruby uses: ruby/setup-ruby@v1 @@ -37,7 +37,7 @@ jobs: - name: Coveralls if: matrix.os == 'ubuntu' && !endsWith(matrix.ruby, 'head') - uses: coverallsapp/github-action@master + uses: coverallsapp/github-action@v2 with: github-token: ${{ secrets.github_token }} flag-name: ${{ matrix.ruby }} @@ -53,7 +53,7 @@ jobs: continue-on-error: true steps: - name: Checkout rubyzip code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Install and set up ruby uses: ruby/setup-ruby@v1 @@ -72,7 +72,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Coveralls Finished - uses: coverallsapp/github-action@master + uses: coverallsapp/github-action@v2 with: github-token: ${{ secrets.github_token }} parallel-finished: true From 9cfa01a479e765920c60dae65346e84287f04ffc Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sat, 2 Mar 2024 14:51:25 +0000 Subject: [PATCH 288/311] Update note about minimum Ruby version in the README. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index de6db5e6..f555b77b 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ The public API of some classes has been modernized to use named parameters for o ## Requirements -Version 3.x requires at least Ruby 2.5. +Version 3.x requires at least Ruby 3.0. Version 2.x requires at least Ruby 2.4, and is known to work on Ruby 3.1. From e83bec471bedcb3308ca38a29b6a599933ad46ac Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sat, 2 Mar 2024 14:52:24 +0000 Subject: [PATCH 289/311] Update API documentation. Mostly move things out of the public API by removing them from the docs, but also add and correct docs where appropriate. --- lib/zip.rb | 5 +- lib/zip/constants.rb | 4 ++ lib/zip/entry.rb | 106 +++++++++++++++++++++++---------------- lib/zip/errors.rb | 21 ++++++++ lib/zip/file.rb | 3 +- lib/zip/filesystem.rb | 2 +- lib/zip/input_stream.rb | 21 ++++---- lib/zip/output_stream.rb | 12 ++--- lib/zip/version.rb | 2 +- 9 files changed, 113 insertions(+), 63 deletions(-) diff --git a/lib/zip.rb b/lib/zip.rb index 5d421226..f85e7fb0 100644 --- a/lib/zip.rb +++ b/lib/zip.rb @@ -57,9 +57,9 @@ module Zip restore_ownership: false, restore_permissions: true, restore_times: true - }.freeze + }.freeze # :nodoc: - def reset! + def reset! # :nodoc: @_ran_once = false @unicode_names = false @on_exists_proc = false @@ -73,6 +73,7 @@ def reset! @validate_entry_sizes = true end + # Set options for RubyZip in one block. def setup yield self unless @_ran_once @_ran_once = true diff --git a/lib/zip/constants.rb b/lib/zip/constants.rb index d0c5927e..2fcaff46 100644 --- a/lib/zip/constants.rb +++ b/lib/zip/constants.rb @@ -1,6 +1,8 @@ # frozen_string_literal: true module Zip + # :stopdoc: + RUNNING_ON_WINDOWS = RbConfig::CONFIG['host_os'] =~ /mswin|mingw|cygwin/i CENTRAL_DIRECTORY_ENTRY_SIGNATURE = 0x02014b50 @@ -116,4 +118,6 @@ module Zip COMPRESSION_METHOD_PPMD => 'PPMd version I, Rev 1', COMPRESSION_METHOD_AES => 'AES encryption' }.freeze + + # :startdoc: end diff --git a/lib/zip/entry.rb b/lib/zip/entry.rb index 18959233..fe3b9f11 100644 --- a/lib/zip/entry.rb +++ b/lib/zip/entry.rb @@ -9,16 +9,19 @@ module Zip class Entry include Dirtyable + # Constant used to specify that the entry is stored (i.e., not compressed). STORED = ::Zip::COMPRESSION_METHOD_STORE + + # Constant used to specify that the entry is deflated (i.e., compressed). DEFLATED = ::Zip::COMPRESSION_METHOD_DEFLATE # Language encoding flag (EFS) bit - EFS = 0b100000000000 + EFS = 0b100000000000 # :nodoc: # Compression level flags (used as part of the gp flags). - COMPRESSION_LEVEL_SUPERFAST_GPFLAG = 0b110 - COMPRESSION_LEVEL_FAST_GPFLAG = 0b100 - COMPRESSION_LEVEL_MAX_GPFLAG = 0b010 + COMPRESSION_LEVEL_SUPERFAST_GPFLAG = 0b110 # :nodoc: + COMPRESSION_LEVEL_FAST_GPFLAG = 0b100 # :nodoc: + COMPRESSION_LEVEL_MAX_GPFLAG = 0b010 # :nodoc: attr_accessor :comment, :compressed_size, :follow_symlinks, :name, :restore_ownership, :restore_permissions, :restore_times, @@ -35,7 +38,7 @@ class Entry :fstype=, :gp_flags=, :name=, :size=, :unix_gid=, :unix_perms=, :unix_uid= - def set_default_vars_values + def set_default_vars_values # :nodoc: @local_header_offset = 0 @local_header_size = nil # not known until local entry is created or read @internal_file_attributes = 1 @@ -63,11 +66,12 @@ def set_default_vars_values @unix_perms = nil end - def check_name(name) + def check_name(name) # :nodoc: raise EntryNameError, name if name.start_with?('/') raise EntryNameError if name.length > 65_535 end + # Create a new Zip::Entry. def initialize( zipfile = '', name = '', comment: '', size: nil, compressed_size: 0, crc: 0, @@ -103,18 +107,23 @@ def initialize( set_compression_level_flags end + # Is this entry encrypted? def encrypted? gp_flags & 1 == 1 end - def incomplete? + def incomplete? # :nodoc: gp_flags & 8 == 8 end + # The uncompressed size of the entry. def size @size || 0 end + # Get a timestamp component of this entry. + # + # Returns modification time by default. def time(component: :mtime) time = if @extra['UniversalTime'] @@ -130,14 +139,19 @@ def time(component: :mtime) alias mtime time + # Get the last access time of this entry, if available. def atime time(component: :atime) end + # Get the creation time of this entry, if available. def ctime time(component: :ctime) end + # Set a timestamp component of this entry. + # + # Sets modification time by default. def time=(value, component: :mtime) @dirty = true unless @extra.member?('UniversalTime') || @extra.member?('NTFS') @@ -152,30 +166,38 @@ def time=(value, component: :mtime) alias mtime= time= + # Set the last access time of this entry. def atime=(value) send(:time=, value, component: :atime) end + # Set the creation time of this entry. def ctime=(value) send(:time=, value, component: :ctime) end + # Return the compression method for this entry. + # + # Returns STORED if the entry is a directory or if the compression + # level is 0. def compression_method return STORED if ftype == :directory || @compression_level == 0 @compression_method end + # Set the compression method for this entry. def compression_method=(method) @dirty = true @compression_method = (ftype == :directory ? STORED : method) end + # Does this entry use the ZIP64 extensions? def zip64? !@extra['Zip64'].nil? end - def file_type_is?(type) + def file_type_is?(type) # :nodoc: ftype == type end @@ -190,14 +212,14 @@ def ftype # :nodoc: end end - def name_is_directory? # :nodoc:all + def name_is_directory? # :nodoc: @name.end_with?('/') end # Is the name a relative path, free of `..` patterns that could lead to # path traversal attacks? This does NOT handle symlinks; if the path # contains symlinks, this check is NOT enough to guarantee safety. - def name_safe? + def name_safe? # :nodoc: cleanpath = Pathname.new(@name).cleanpath return false unless cleanpath.relative? @@ -207,29 +229,29 @@ def name_safe? ::File.absolute_path(cleanpath.to_s, root).match?(/([A-Z]:)?#{naive}/i) end - def local_entry_offset # :nodoc:all + def local_entry_offset # :nodoc: local_header_offset + @local_header_size end - def name_size + def name_size # :nodoc: @name ? @name.bytesize : 0 end - def extra_size + def extra_size # :nodoc: @extra ? @extra.local_size : 0 end - def comment_size + def comment_size # :nodoc: @comment ? @comment.bytesize : 0 end - def calculate_local_header_size # :nodoc:all + def calculate_local_header_size # :nodoc: LOCAL_ENTRY_STATIC_HEADER_LENGTH + name_size + extra_size end # check before rewriting an entry (after file sizes are known) # that we didn't change the header size (and thus clobber file data or something) - def verify_local_header_size! + def verify_local_header_size! # :nodoc: return if @local_header_size.nil? new_size = calculate_local_header_size @@ -239,12 +261,12 @@ def verify_local_header_size! "Local header size changed (#{@local_header_size} -> #{new_size})" end - def cdir_header_size # :nodoc:all + def cdir_header_size # :nodoc: CDIR_ENTRY_STATIC_HEADER_LENGTH + name_size + (@extra ? @extra.c_dir_size : 0) + comment_size end - def next_header_offset # :nodoc:all + def next_header_offset # :nodoc: local_entry_offset + compressed_size end @@ -270,12 +292,12 @@ def extract(entry_path = @name, destination_directory: '.', &block) self end - def to_s + def to_s # :nodoc: @name end class << self - def read_c_dir_entry(io) # :nodoc:all + def read_c_dir_entry(io) # :nodoc: path = if io.respond_to?(:path) io.path else @@ -288,7 +310,7 @@ def read_c_dir_entry(io) # :nodoc:all nil end - def read_local_entry(io) + def read_local_entry(io) # :nodoc: entry = new(io) entry.read_local_entry(io) entry @@ -299,7 +321,7 @@ def read_local_entry(io) end end - def unpack_local_entry(buf) + def unpack_local_entry(buf) # :nodoc: @header_signature, @version, @fstype, @@ -314,7 +336,7 @@ def unpack_local_entry(buf) @extra_length = buf.unpack('VCCvvvvVVVvv') end - def read_local_entry(io) # :nodoc:all + def read_local_entry(io) # :nodoc: @dirty = false # No changes at this point. @local_header_offset = io.tell @@ -356,7 +378,7 @@ def read_local_entry(io) # :nodoc:all @local_header_size = calculate_local_header_size end - def pack_local_entry + def pack_local_entry # :nodoc: zip64 = @extra['Zip64'] [::Zip::LOCAL_ENTRY_SIGNATURE, @version_needed_to_extract, # version needed to extract @@ -371,7 +393,7 @@ def pack_local_entry @extra ? @extra.local_size : 0].pack('VvvvvvVVVvv') end - def write_local_entry(io, rewrite: false) # :nodoc:all + def write_local_entry(io, rewrite: false) # :nodoc: prep_local_zip64_extra verify_local_header_size! if rewrite @local_header_offset = io.tell @@ -383,7 +405,7 @@ def write_local_entry(io, rewrite: false) # :nodoc:all @local_header_size = io.tell - @local_header_offset end - def unpack_c_dir_entry(buf) + def unpack_c_dir_entry(buf) # :nodoc: @header_signature, @version, # version of encoding software @fstype, # filesystem type @@ -407,7 +429,7 @@ def unpack_c_dir_entry(buf) @comment = buf.unpack('VCCvvvvvVVVvvvvvVV') end - def set_ftype_from_c_dir_entry + def set_ftype_from_c_dir_entry # :nodoc: @ftype = case @fstype when ::Zip::FSTYPE_UNIX @unix_perms = (@external_file_attributes >> 16) & 0o7777 @@ -437,25 +459,25 @@ def set_ftype_from_c_dir_entry end end - def check_c_dir_entry_static_header_length(buf) + def check_c_dir_entry_static_header_length(buf) # :nodoc: return unless buf.nil? || buf.bytesize != ::Zip::CDIR_ENTRY_STATIC_HEADER_LENGTH raise Error, 'Premature end of file. Not enough data for zip cdir entry header' end - def check_c_dir_entry_signature + def check_c_dir_entry_signature # :nodoc: return if @header_signature == ::Zip::CENTRAL_DIRECTORY_ENTRY_SIGNATURE raise Error, "Zip local header magic not found at location '#{local_header_offset}'" end - def check_c_dir_entry_comment_size + def check_c_dir_entry_comment_size # :nodoc: return if @comment && @comment.bytesize == @comment_length raise ::Zip::Error, 'Truncated cdir zip entry header' end - def read_extra_field(buf, local: false) + def read_extra_field(buf, local: false) # :nodoc: if @extra.kind_of?(::Zip::ExtraField) @extra.merge(buf, local: local) if buf else @@ -463,7 +485,7 @@ def read_extra_field(buf, local: false) end end - def read_c_dir_entry(io) # :nodoc:all + def read_c_dir_entry(io) # :nodoc: @dirty = false # No changes at this point. static_sized_fields_buf = io.read(::Zip::CDIR_ENTRY_STATIC_HEADER_LENGTH) check_c_dir_entry_static_header_length(static_sized_fields_buf) @@ -503,7 +525,7 @@ def get_extra_attributes_from_path(path) # :nodoc: end # rubocop:disable Style/GuardClause - def set_unix_attributes_on_path(dest_path) + def set_unix_attributes_on_path(dest_path) # :nodoc: # Ignore setuid/setgid bits by default. Honour if @restore_ownership. unix_perms_mask = (@restore_ownership ? 0o7777 : 0o1777) if @restore_permissions && @unix_perms @@ -529,7 +551,7 @@ def set_extra_attributes_on_path(dest_path) # :nodoc: ::FileUtils.touch(dest_path, mtime: time) if @restore_times end - def pack_c_dir_entry + def pack_c_dir_entry # :nodoc: zip64 = @extra['Zip64'] [ @header_signature, @@ -556,7 +578,7 @@ def pack_c_dir_entry ].pack('VCCvvvvvVVVvvvvvVV') end - def write_c_dir_entry(io) # :nodoc:all + def write_c_dir_entry(io) # :nodoc: prep_cdir_zip64_extra case @fstype @@ -585,7 +607,7 @@ def write_c_dir_entry(io) # :nodoc:all io << @comment end - def ==(other) + def ==(other) # :nodoc: return false unless other.class == self.class # Compares contents of local entry and exposed fields @@ -594,7 +616,7 @@ def ==(other) end end - def <=>(other) + def <=>(other) # :nodoc: to_s <=> other.to_s end @@ -661,7 +683,7 @@ def gather_fileinfo_from_srcpath(src_path) # :nodoc: get_extra_attributes_from_path(@filepath) end - def write_to_zip_output_stream(zip_output_stream) # :nodoc:all + def write_to_zip_output_stream(zip_output_stream) # :nodoc: if ftype == :directory zip_output_stream.put_next_entry(self) elsif @filepath @@ -674,13 +696,13 @@ def write_to_zip_output_stream(zip_output_stream) # :nodoc:all end end - def parent_as_string + def parent_as_string # :nodoc: entry_name = name.chomp('/') slash_index = entry_name.rindex('/') slash_index ? entry_name.slice(0, slash_index + 1) : nil end - def get_raw_input_stream(&block) + def get_raw_input_stream(&block) # :nodoc: if @zipfile.respond_to?(:seek) && @zipfile.respond_to?(:read) yield @zipfile else @@ -688,7 +710,7 @@ def get_raw_input_stream(&block) end end - def clean_up + def clean_up # :nodoc: @dirty = false # Any changes are written at this point. end @@ -749,7 +771,7 @@ def create_symlink(dest_path) # apply missing data from the zip64 extra information field, if present # (required when file sizes exceed 2**32, but can be used for all files) - def parse_zip64_extra(for_local_header) # :nodoc:all + def parse_zip64_extra(for_local_header) # :nodoc: return unless zip64? if for_local_header diff --git a/lib/zip/errors.rb b/lib/zip/errors.rb index 5b75e9a6..f172a77f 100644 --- a/lib/zip/errors.rb +++ b/lib/zip/errors.rb @@ -7,13 +7,17 @@ class Error < StandardError; end # Error raised if an unsupported compression method is used. class CompressionMethodError < Error + # The compression method that has caused this error. attr_reader :compression_method + # Create a new CompressionMethodError with the specified incorrect + # compression method. def initialize(method) super() @compression_method = method end + # The message returned by this error. def message "Unsupported compression method: #{COMPRESSION_METHODS[@compression_method]}." end @@ -21,13 +25,17 @@ def message # Error raised if there is a problem while decompressing an archive entry. class DecompressionError < Error + # The error from the underlying Zlib library that caused this error. attr_reader :zlib_error + # Create a new DecompressionError with the specified underlying Zlib + # error. def initialize(zlib_error) super() @zlib_error = zlib_error end + # The message returned by this error. def message "Zlib error ('#{@zlib_error.message}') while inflating." end @@ -36,11 +44,13 @@ def message # Error raised when trying to extract an archive entry over an # existing file. class DestinationExistsError < Error + # Create a new DestinationExistsError with the clashing destination. def initialize(destination) super() @destination = destination end + # The message returned by this error. def message "Cannot create file or directory '#{@destination}'. " \ 'A file already exists with that name.' @@ -50,12 +60,14 @@ def message # Error raised when trying to add an entry to an archive where the # entry name already exists. class EntryExistsError < Error + # Create a new EntryExistsError with the specified source and name. def initialize(source, name) super() @source = source @name = name end + # The message returned by this error. def message "'#{@source}' failed. Entry #{@name} already exists." end @@ -63,11 +75,13 @@ def message # Error raised when an entry name is invalid. class EntryNameError < Error + # Create a new EntryNameError with the specified name. def initialize(name = nil) super() @name = name end + # The message returned by this error. def message if @name.nil? 'Illegal entry name. Names must have fewer than 65,536 characters.' @@ -80,13 +94,16 @@ def message # Error raised if an entry is larger on extraction than it is advertised # to be. class EntrySizeError < Error + # The entry that has caused this error. attr_reader :entry + # Create a new EntrySizeError with the specified entry. def initialize(entry) super() @entry = entry end + # The message returned by this error. def message "Entry '#{@entry.name}' should be #{@entry.size}B, but is larger when inflated." end @@ -95,6 +112,7 @@ def message # Error raised if a split archive is read. Rubyzip does not support reading # split archives. class SplitArchiveError < Error + # The message returned by this error. def message 'Rubyzip cannot extract from split archives at this time.' end @@ -102,13 +120,16 @@ def message # Error raised if there is not enough metadata for the entry to be streamed. class StreamingError < Error + # The entry that has caused this error. attr_reader :entry + # Create a new StreamingError with the specified entry. def initialize(entry) super() @entry = entry end + # The message returned by this error. def message "The local header of this entry ('#{@entry.name}') does not contain " \ 'the correct metadata for `Zip::InputStream` to be able to ' \ diff --git a/lib/zip/file.rb b/lib/zip/file.rb index 85a75035..754b9575 100644 --- a/lib/zip/file.rb +++ b/lib/zip/file.rb @@ -52,8 +52,9 @@ class File extend Forwardable extend FileSplit - IO_METHODS = [:tell, :seek, :read, :eof, :close].freeze + IO_METHODS = [:tell, :seek, :read, :eof, :close].freeze # :nodoc: + # The name of this zip archive. attr_reader :name # default -> false. diff --git a/lib/zip/filesystem.rb b/lib/zip/filesystem.rb index 5a65c974..55369e92 100644 --- a/lib/zip/filesystem.rb +++ b/lib/zip/filesystem.rb @@ -64,7 +64,7 @@ def file end end - class File + class File # :nodoc: include FileSystem end end diff --git a/lib/zip/input_stream.rb b/lib/zip/input_stream.rb index 56ecf4f9..7cb18ca5 100644 --- a/lib/zip/input_stream.rb +++ b/lib/zip/input_stream.rb @@ -1,5 +1,6 @@ # frozen_string_literal: true +## module Zip # InputStream is the basic class for reading zip entries in a # zip file. It is possible to create a InputStream object directly, @@ -39,9 +40,8 @@ module Zip # # java.util.zip.ZipInputStream is the original inspiration for this # class. - class InputStream - CHUNK_SIZE = 32_768 + CHUNK_SIZE = 32_768 # :nodoc: include ::Zip::IOExtras::AbstractInputStream @@ -60,6 +60,7 @@ def initialize(context, offset: 0, decrypter: nil) @complete_entry = nil end + # Close this InputStream. All further IO will raise an IOError. def close @archive_io.close end @@ -78,7 +79,7 @@ def get_next_entry open_entry end - # Rewinds the stream to the beginning of the current entry + # Rewinds the stream to the beginning of the current entry. def rewind return if @current_entry.nil? @@ -115,7 +116,7 @@ def open(filename_or_io, offset: 0, decrypter: nil) end end - def open_buffer(filename_or_io, offset: 0) + def open_buffer(filename_or_io, offset: 0) # :nodoc: warn 'open_buffer is deprecated!!! Use open instead!' ::Zip::InputStream.open(filename_or_io, offset: offset) end @@ -123,7 +124,7 @@ def open_buffer(filename_or_io, offset: 0) protected - def get_io(io_or_file, offset = 0) + def get_io(io_or_file, offset = 0) # :nodoc: if io_or_file.respond_to?(:seek) io = io_or_file.dup io.seek(offset, ::IO::SEEK_SET) @@ -135,7 +136,7 @@ def get_io(io_or_file, offset = 0) end end - def open_entry + def open_entry # :nodoc: @current_entry = ::Zip::Entry.read_local_entry(@archive_io) return if @current_entry.nil? @@ -154,14 +155,14 @@ def open_entry @current_entry end - def get_decrypted_io + def get_decrypted_io # :nodoc: header = @archive_io.read(@decrypter.header_bytesize) @decrypter.reset!(header) ::Zip::DecryptedIo.new(@archive_io, @decrypter) end - def get_decompressor + def get_decompressor # :nodoc: return ::Zip::NullDecompressor if @current_entry.nil? decompressed_size = @@ -182,11 +183,11 @@ def get_decompressor decompressor_class.new(@decrypted_io, decompressed_size) end - def produce_input + def produce_input # :nodoc: @decompressor.read(CHUNK_SIZE) end - def input_finished? + def input_finished? # :nodoc: @decompressor.eof end end diff --git a/lib/zip/output_stream.rb b/lib/zip/output_stream.rb index 095126b5..0609f89f 100644 --- a/lib/zip/output_stream.rb +++ b/lib/zip/output_stream.rb @@ -2,6 +2,7 @@ require 'forwardable' +## module Zip # ZipOutputStream is the basic class for writing zip files. It is # possible to create a ZipOutputStream object directly, passing @@ -20,7 +21,6 @@ module Zip # # java.util.zip.ZipOutputStream is the original inspiration for this # class. - class OutputStream extend Forwardable include ::Zip::IOExtras::AbstractOutputStream @@ -47,10 +47,10 @@ def initialize(file_name, stream: false, encrypter: nil) @current_entry = nil end - # Same as #initialize but if a block is passed the opened - # stream is passed to the block and closed when the block - # returns. class << self + # Same as #initialize but if a block is passed the opened + # stream is passed to the block and closed when the block + # returns. def open(file_name, encrypter: nil) return new(file_name) unless block_given? @@ -114,7 +114,7 @@ def put_next_entry( @current_entry = new_entry end - def copy_raw_entry(entry) + def copy_raw_entry(entry) # :nodoc: entry = entry.dup raise Error, 'zip stream is closed' if @closed raise Error, 'entry is not a ZipEntry' unless entry.kind_of?(Entry) @@ -185,7 +185,7 @@ def update_local_headers protected - def finish + def finish # :nodoc: @compressor.finish end diff --git a/lib/zip/version.rb b/lib/zip/version.rb index dd203608..8878fc45 100644 --- a/lib/zip/version.rb +++ b/lib/zip/version.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true module Zip - VERSION = '3.0.0.alpha' + VERSION = '3.0.0.alpha' # :nodoc: end From bfc9324a75b26b583c6d4337db85f4c40a3a8c84 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sat, 2 Mar 2024 14:55:46 +0000 Subject: [PATCH 290/311] Remove deprecated InputStream::open_buffer method. --- lib/zip/input_stream.rb | 5 ----- 1 file changed, 5 deletions(-) diff --git a/lib/zip/input_stream.rb b/lib/zip/input_stream.rb index 7cb18ca5..defccabc 100644 --- a/lib/zip/input_stream.rb +++ b/lib/zip/input_stream.rb @@ -115,11 +115,6 @@ def open(filename_or_io, offset: 0, decrypter: nil) zio.close if zio end end - - def open_buffer(filename_or_io, offset: 0) # :nodoc: - warn 'open_buffer is deprecated!!! Use open instead!' - ::Zip::InputStream.open(filename_or_io, offset: offset) - end end protected From c6229cc8c20f107a8d7afa8215f468d41d25ae89 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Tue, 5 Mar 2024 20:35:56 +0000 Subject: [PATCH 291/311] Remove TODO file. Out of date and we use GitHub issues now anyway. --- TODO | 15 --------------- 1 file changed, 15 deletions(-) delete mode 100644 TODO diff --git a/TODO b/TODO deleted file mode 100644 index 16b9a2e7..00000000 --- a/TODO +++ /dev/null @@ -1,15 +0,0 @@ - -* ZipInputStream: Support zip-files with trailing data descriptors -* Adjust rdoc stylesheet to advertise inherited methods if possible -* Suggestion: Add ZipFile/ZipInputStream example that demonstrates extracting all entries. -* Suggestion: ZipFile#extract destination should default to "." -* Suggestion: ZipEntry should have extract(), get_input_stream() methods etc -* (is buffering used anywhere with write?) -* Inflater.sysread should pass the buffer to produce_input. -* Implement ZipFsDir.glob -* ZipFile.checkIntegrity method -* non-MSDOS permission attributes -** See mail from Ned Konz to ruby-talk subj. "Re: SV: [ANN] Archive 0.2" -* Packager version, required unpacker version in zip headers -** See mail from Ned Konz to ruby-talk subj. "Re: SV: [ANN] Archive 0.2" -* implement storing attributes and ownership information From 0e4fc83b2516e8ed093348727ab45527e3016944 Mon Sep 17 00:00:00 2001 From: Oleksii Leonov Date: Sat, 13 Jan 2024 22:47:56 +0000 Subject: [PATCH 292/311] Add LICENSE file --- LICENSE.md | 24 ++++++++++++++++++++++++ rubyzip.gemspec | 2 +- 2 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 LICENSE.md diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 00000000..16e431c1 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,24 @@ +BSD 2-Clause License + +Copyright (c) 2002-2024, The Rubyzip Developers + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/rubyzip.gemspec b/rubyzip.gemspec index f9d85f1c..7392d538 100644 --- a/rubyzip.gemspec +++ b/rubyzip.gemspec @@ -13,7 +13,7 @@ Gem::Specification.new do |s| s.platform = Gem::Platform::RUBY s.summary = 'rubyzip is a ruby module for reading and writing zip files' s.files = Dir.glob('{samples,lib}/**/*.rb') + - %w[README.md Changelog.md Rakefile rubyzip.gemspec] + %w[LICENSE.md README.md Changelog.md Rakefile rubyzip.gemspec] s.require_paths = ['lib'] s.license = 'BSD-2-Clause' From 73c8e110ed1dbcff08ffa48bb1b094abd0348502 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Wed, 6 Mar 2024 20:53:48 +0000 Subject: [PATCH 293/311] Update README with up-to-date licence information. Fixes: #556 --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index f555b77b..700a2dd9 100644 --- a/README.md +++ b/README.md @@ -449,8 +449,7 @@ See https://github.com/rubyzip/rubyzip/graphs/contributors for a comprehensive l ## License -Rubyzip is distributed under the same license as ruby. See -http://www.ruby-lang.org/en/LICENSE.txt +Rubyzip is distributed under the same license as Ruby. In practice this means you can use it under the terms of the Ruby License or the 2-Clause BSD License. See https://www.ruby-lang.org/en/about/license.txt and LICENSE.md for details. ## Research notice Please note that this repository is participating in a study into sustainability From 8afc2514f7e54d5dbdf89363efd89b093e532319 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Thu, 7 Mar 2024 16:57:50 +0000 Subject: [PATCH 294/311] Use explicit named parameters for `File` methods. Stop using implicit options (`**options`). --- lib/zip/file.rb | 46 ++++++++++++++++++++++++++++++++++------------ 1 file changed, 34 insertions(+), 12 deletions(-) diff --git a/lib/zip/file.rb b/lib/zip/file.rb index 754b9575..5ea27ea2 100644 --- a/lib/zip/file.rb +++ b/lib/zip/file.rb @@ -70,28 +70,40 @@ class File # Opens a zip archive. Pass create: true to create # a new archive if it doesn't exist already. - def initialize(path_or_io, create: false, buffer: false, **options) + def initialize(path_or_io, create: false, buffer: false, + restore_ownership: DEFAULT_RESTORE_OPTIONS[:restore_ownership], + restore_permissions: DEFAULT_RESTORE_OPTIONS[:restore_permissions], + restore_times: DEFAULT_RESTORE_OPTIONS[:restore_times], + compression_level: ::Zip.default_compression) super() - options = DEFAULT_RESTORE_OPTIONS - .merge(compression_level: ::Zip.default_compression) - .merge(options) + @name = path_or_io.respond_to?(:path) ? path_or_io.path : path_or_io @create = create ? true : false # allow any truthy value to mean true initialize_cdir(path_or_io, buffer: buffer) - @restore_ownership = options[:restore_ownership] - @restore_permissions = options[:restore_permissions] - @restore_times = options[:restore_times] - @compression_level = options[:compression_level] + @restore_ownership = restore_ownership + @restore_permissions = restore_permissions + @restore_times = restore_times + @compression_level = compression_level end class << self # Similar to ::new. If a block is passed the Zip::File object is passed # to the block and is automatically closed afterwards, just as with # ruby's builtin File::open method. - def open(file_name, create: false, **options) - zf = ::Zip::File.new(file_name, create: create, **options) + def open(file_name, create: false, + restore_ownership: DEFAULT_RESTORE_OPTIONS[:restore_ownership], + restore_permissions: DEFAULT_RESTORE_OPTIONS[:restore_permissions], + restore_times: DEFAULT_RESTORE_OPTIONS[:restore_times], + compression_level: ::Zip.default_compression) + + zf = ::Zip::File.new(file_name, create: create, + restore_ownership: restore_ownership, + restore_permissions: restore_permissions, + restore_times: restore_times, + compression_level: compression_level) + return zf unless block_given? begin @@ -105,7 +117,12 @@ def open(file_name, create: false, **options) # stream, and outputs data to a buffer. # (This can be used to extract data from a # downloaded zip archive without first saving it to disk.) - def open_buffer(io = ::StringIO.new, create: false, **options) + def open_buffer(io = ::StringIO.new, create: false, + restore_ownership: DEFAULT_RESTORE_OPTIONS[:restore_ownership], + restore_permissions: DEFAULT_RESTORE_OPTIONS[:restore_permissions], + restore_times: DEFAULT_RESTORE_OPTIONS[:restore_times], + compression_level: ::Zip.default_compression) + unless IO_METHODS.map { |method| io.respond_to?(method) }.all? || io.kind_of?(String) raise 'Zip::File.open_buffer expects a String or IO-like argument' \ "(responds to #{IO_METHODS.join(', ')}). Found: #{io.class}" @@ -113,7 +130,12 @@ def open_buffer(io = ::StringIO.new, create: false, **options) io = ::StringIO.new(io) if io.kind_of?(::String) - zf = ::Zip::File.new(io, create: create, buffer: true, **options) + zf = ::Zip::File.new(io, create: create, buffer: true, + restore_ownership: restore_ownership, + restore_permissions: restore_permissions, + restore_times: restore_times, + compression_level: compression_level) + return zf unless block_given? yield zf From d53f046bc7960a4913ef028f8881c8e22ae62ca8 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Thu, 7 Mar 2024 21:31:24 +0000 Subject: [PATCH 295/311] Add `Entry#absolute_time?`. This method returns `true` if an entry has timezone information in its timestamps, `false` otherwise. --- lib/zip/entry.rb | 5 +++++ test/entry_test.rb | 8 ++++++++ 2 files changed, 13 insertions(+) diff --git a/lib/zip/entry.rb b/lib/zip/entry.rb index fe3b9f11..9e1a904c 100644 --- a/lib/zip/entry.rb +++ b/lib/zip/entry.rb @@ -176,6 +176,11 @@ def ctime=(value) send(:time=, value, component: :ctime) end + # Does this entry return time fields with accurate timezone information? + def absolute_time? + @extra.member?('UniversalTime') || @extra.member?('NTFS') + end + # Return the compression method for this entry. # # Returns STORED if the entry is a directory or if the compression diff --git a/test/entry_test.rb b/test/entry_test.rb index 0d88c874..b2d4c14e 100644 --- a/test/entry_test.rb +++ b/test/entry_test.rb @@ -340,4 +340,12 @@ def test_ensure_entry_time_set_to_file_mtime entry.gather_fileinfo_from_srcpath('test/data/mimetype') assert_equal(entry.time, File.stat('test/data/mimetype').mtime) end + + def test_absolute_time + entry = ::Zip::Entry.new + refute(entry.absolute_time?) + + entry.time = Time.now + assert(entry.absolute_time?) + end end From 0c0003cfda19e5c3726b29c46d5f75de802af547 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Fri, 8 Mar 2024 21:41:20 +0000 Subject: [PATCH 296/311] Add `DOSTime#absolute_time?`. This method returns `true` if the time instance was created with accurate timezone information. Ultimately, only those times parsed from binary DOS format are missing accurate timezone information, but we need this flag because ruby `Time` objects (from which `DOSTime` is decended) always have a timezone set (usually whatever is local at the time). --- lib/zip/dos_time.rb | 12 +++++- test/dos_time_test.rb | 86 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 97 insertions(+), 1 deletion(-) create mode 100644 test/dos_time_test.rb diff --git a/lib/zip/dos_time.rb b/lib/zip/dos_time.rb index 0d94f21c..ac80fac5 100644 --- a/lib/zip/dos_time.rb +++ b/lib/zip/dos_time.rb @@ -16,6 +16,14 @@ class DOSTime < Time # :nodoc:all # bits 5-8 month (1-12) # bits 9-15 year (four digit year minus 1980) + attr_writer :absolute_time # :nodoc: + + def absolute_time? + # If absolute time is not set, we can assume it is an absolute time + # because times do have timezone information by default. + @absolute_time.nil? ? true : @absolute_time + end + def to_binary_dos_time (sec / 2) + (min << 5) + @@ -53,7 +61,9 @@ def self.parse_binary_dos_format(bin_dos_date, bin_dos_time) month = (0b111100000 & bin_dos_date) >> 5 year = ((0b1111111000000000 & bin_dos_date) >> 9) + 1980 - local(year, month, day, hour, minute, second) + time = local(year, month, day, hour, minute, second) + time.absolute_time = false + time end if defined? JRUBY_VERSION && Gem::Version.new(JRUBY_VERSION) < '9.2.18.0' diff --git a/test/dos_time_test.rb b/test/dos_time_test.rb new file mode 100644 index 00000000..70a38a9f --- /dev/null +++ b/test/dos_time_test.rb @@ -0,0 +1,86 @@ +# frozen_string_literal: true + +require 'test_helper' +require 'zip/dos_time' + +class DOSTimeTest < MiniTest::Test + def setup + @dos_time = Zip::DOSTime.new(2022, 1, 1, 12, 0, 0) + end + + def test_new + dos_time = Zip::DOSTime.new + assert(dos_time.absolute_time?) + + dos_time = Zip::DOSTime.new(2022, 1, 1, 12, 0, 0) + assert(dos_time.absolute_time?) + + dos_time = Zip::DOSTime.new(2022, 1, 1, 12, 0, 0, 0) + assert(dos_time.absolute_time?) + end + + def test_now + dos_time = Zip::DOSTime.now + assert(dos_time.absolute_time?) + end + + def test_utc + dos_time = Zip::DOSTime.utc(2022, 1, 1, 12, 0, 0) + assert(dos_time.absolute_time?) + end + + def test_gm + dos_time = Zip::DOSTime.gm(2022, 1, 1, 12, 0, 0) + assert(dos_time.absolute_time?) + end + + def test_mktime + dos_time = Zip::DOSTime.mktime(2022, 1, 1, 12, 0, 0) + assert(dos_time.absolute_time?) + end + + def test_from_time + time = Time.new(2022, 1, 1, 12, 0, 0) + dos_time = Zip::DOSTime.from_time(time) + assert_equal(@dos_time, dos_time) + assert(dos_time.absolute_time?) + end + + def test_parse_binary_dos_format + bin_dos_date = 0b101010000100001 + bin_dos_time = 0b110000000000000 + dos_time = Zip::DOSTime.parse_binary_dos_format(bin_dos_date, bin_dos_time) + assert_equal(@dos_time, dos_time) + refute(dos_time.absolute_time?) + end + + def test_at + time = Time.at(1_641_038_400) + dos_time = Zip::DOSTime.at(1_641_038_400) + assert_equal(time, dos_time) + assert(dos_time.absolute_time?) + end + + def test_local + dos_time = Zip::DOSTime.local(2022, 1, 1, 12, 0, 0) + assert(dos_time.absolute_time?) + end + + def test_comparison + time = Time.new(2022, 1, 1, 12, 0, 0) + assert_equal(0, @dos_time <=> time) + end + + def test_jruby_cmp + return unless defined? JRUBY_VERSION && Gem::Version.new(JRUBY_VERSION) < '9.2.18.0' + + time = Time.new(2022, 1, 1, 12, 0, 0) + assert(@dos_time == time) + assert(@dos_time <= time) + assert(@dos_time >= time) + + time = Time.new(2022, 1, 1, 12, 1, 1) + assert(time > @dos_time) + assert(@dos_time < time) + end +end From 5c6a7c9ad967b128905b0ecacfd9f5dc1b12a9c7 Mon Sep 17 00:00:00 2001 From: Jean Boussier Date: Tue, 9 Apr 2024 09:10:04 +0200 Subject: [PATCH 297/311] Fix `File#write_buffer` to always return the given `io` Ref: https://github.com/rubyzip/rubyzip/commit/ef89a62b70d0b0c43a9579d414d5a917fef0cdc3 This fixes a regression in 2.4.rc1. Cherry-picked into 3.0 for consistency. --- lib/zip/file.rb | 2 +- test/file_test.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/zip/file.rb b/lib/zip/file.rb index 5ea27ea2..5c89606b 100644 --- a/lib/zip/file.rb +++ b/lib/zip/file.rb @@ -301,7 +301,7 @@ def commit # Write buffer write changes to buffer and return def write_buffer(io = ::StringIO.new) - return unless commit_required? + return io unless commit_required? ::Zip::OutputStream.write_buffer(io) do |zos| @cdir.each { |e| e.write_to_zip_output_stream(zos) } diff --git a/test/file_test.rb b/test/file_test.rb index b09f78bd..8179ce97 100644 --- a/test/file_test.rb +++ b/test/file_test.rb @@ -233,7 +233,7 @@ def test_open_buffer_without_block_write_buffer_does_nothing assert zf.entries.map(&:name).include?('zippedruby1.rb') # Ensure the buffer isn't changed. - zf.write_buffer(string_io) + assert_same(string_io, zf.write_buffer(string_io)) assert_equal(data, string_io.string) end From f81175b3d3b1e1355a3782e5c8eb049ce09a4d66 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sat, 26 Oct 2024 19:16:55 +0100 Subject: [PATCH 298/311] Update Changlog. --- Changelog.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/Changelog.md b/Changelog.md index 84b45492..ce1c0b99 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,5 +1,21 @@ # 3.0.0 (Next) +- Fix `File#write_buffer` to always return the given `io`. +- Add `Entry#absolute_time?` and `DOSTime#absolute_time?` methods. +- Use explicit named parameters for `File` methods. +- Ensure that entries can be extracted safely without path traversal. [#540](https://github.com/rubyzip/rubyzip/issues/540) +- Enable Zip64 by default. +- Rename `GPFBit3Error` to `StreamingError`. +- Ensure that `Entry.ftype` is correct via `InputStream`. [#533](https://github.com/rubyzip/rubyzip/issues/533) +- Add `Entry#zip64?` as a better way detect Zip64 entries. +- Implement `Zip::FileSystem::ZipFsFile#symlink?`. +- Remove `File::add_buffer` from the API. +- Fix `OutputStream#put_next_entry` to preserve `StreamableStream`s. [#503](https://github.com/rubyzip/rubyzip/issues/503) +- Ensure `File.open_buffer` doesn't rewrite unchanged data. +- Add `CentralDirectory#count_entries` and `File::count_entries`. +- Fix reading unknown extra fields. [#505](https://github.com/rubyzip/rubyzip/issues/505) +- Fix reading zip files with max length file comment. [#508](https://github.com/rubyzip/rubyzip/issues/508) +- Fix reading zip64 files with max length file comment. [#509](https://github.com/rubyzip/rubyzip/issues/509) - Don't silently alter zip files opened with `Zip::sort_entries`. [#329](https://github.com/rubyzip/rubyzip/issues/329) - Use named parameters for optional arguments in the public API. - Raise an error if entry names exceed 65,535 characters. [#247](https://github.com/rubyzip/rubyzip/issues/247) @@ -23,6 +39,13 @@ Tooling/internal: +- Only use the Zip64 CDIR end locator if needed. +- Prevent unnecessary Zip64 data being stored. +- Abstract marking various things as 'dirty' into `Dirtyable` for reuse. +- Properly test `File#mkdir`. +- Remove unused private method `File#directory?`. +- Expose the `EntrySet` more cleanly through `CentralDirectory`. +- `Zip::File` no longer subclasses `Zip::CentralDirectory`. - Configure Coveralls to not report a failure on minor decreases of test coverage. [#491](https://github.com/rubyzip/rubyzip/issues/491) - Extract the file splitting code out into its own module. - Refactor, and tidy up, the `Zip::Filesystem` classes for improved maintainability. From 5b0d25e416814beb062c707c1319eb79b9d4272f Mon Sep 17 00:00:00 2001 From: Bastien Date: Fri, 26 Apr 2024 17:16:40 +0200 Subject: [PATCH 299/311] Fix misspell --- lib/zip/crypto/null_encryption.rb | 2 +- lib/zip/crypto/traditional_encryption.rb | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/zip/crypto/null_encryption.rb b/lib/zip/crypto/null_encryption.rb index 2ae48b70..97764b73 100644 --- a/lib/zip/crypto/null_encryption.rb +++ b/lib/zip/crypto/null_encryption.rb @@ -22,7 +22,7 @@ def encrypt(data) data end - def data_descriptor(_crc32, _compressed_size, _uncomprssed_size) + def data_descriptor(_crc32, _compressed_size, _uncompressed_size) '' end diff --git a/lib/zip/crypto/traditional_encryption.rb b/lib/zip/crypto/traditional_encryption.rb index f035895d..dc92c822 100644 --- a/lib/zip/crypto/traditional_encryption.rb +++ b/lib/zip/crypto/traditional_encryption.rb @@ -55,8 +55,8 @@ def encrypt(data) data.unpack('C*').map { |x| encode x }.pack('C*') end - def data_descriptor(crc32, compressed_size, uncomprssed_size) - [0x08074b50, crc32, compressed_size, uncomprssed_size].pack('VVVV') + def data_descriptor(crc32, compressed_size, uncompressed_size) + [0x08074b50, crc32, compressed_size, uncompressed_size].pack('VVVV') end def reset! From d908e72819a552f7235a4a756a56fdc4ed5f55c3 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sat, 4 Jan 2025 16:17:25 +0000 Subject: [PATCH 300/311] Add Ruby 3.4 to the tests. --- .github/workflows/tests.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 024cdd7f..31564eb8 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -8,7 +8,7 @@ jobs: fail-fast: false matrix: os: [ubuntu] - ruby: ['3.0', '3.1', '3.2', '3.3', head, jruby, jruby-head, truffleruby, truffleruby-head] + ruby: ['3.0', '3.1', '3.2', '3.3', '3.4', head, jruby, jruby-head, truffleruby, truffleruby-head] include: - { os: macos , ruby: '3.0' } - { os: windows, ruby: '3.0' } @@ -48,7 +48,7 @@ jobs: fail-fast: false matrix: os: [ubuntu, macos] - ruby: ['3.1', '3.2', '3.3', head] + ruby: ['3.1', '3.2', '3.3', '3.4', head] runs-on: ${{ matrix.os }}-latest continue-on-error: true steps: From 7412338c2e616680b93b670591d0cd5420e709aa Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sat, 4 Jan 2025 16:18:27 +0000 Subject: [PATCH 301/311] Fix variable name typo in `DecryptedIo`. Fixes: #580 --- lib/zip/crypto/decrypted_io.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/zip/crypto/decrypted_io.rb b/lib/zip/crypto/decrypted_io.rb index db844a24..a38004fa 100644 --- a/lib/zip/crypto/decrypted_io.rb +++ b/lib/zip/crypto/decrypted_io.rb @@ -18,7 +18,7 @@ def read(length = nil, outbuf = +'') buffer << produce_input end - outbuf.replace(buffer.slice!(0...(length || output_buffer.bytesize))) + outbuf.replace(buffer.slice!(0...(length || buffer.bytesize))) end private From b3186d7ad19618cdcdde9d52095b8903fe599335 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sat, 4 Jan 2025 16:37:02 +0000 Subject: [PATCH 302/311] Update the version of RubyGems in the actions. --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 31564eb8..e20d5f94 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -25,7 +25,7 @@ jobs: uses: ruby/setup-ruby@v1 with: ruby-version: ${{ matrix.ruby }} - rubygems: '3.2.3' + rubygems: '3.5.23' bundler-cache: true - name: Run the tests From 89cdf82a7794dbbd4d4bcfd4726eb42641257d5b Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sun, 5 Jan 2025 19:24:16 +0000 Subject: [PATCH 303/311] Copy the 2.4 branch changelog into the main branch. --- Changelog.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/Changelog.md b/Changelog.md index ce1c0b99..ea6e39c1 100644 --- a/Changelog.md +++ b/Changelog.md @@ -61,6 +61,29 @@ Tooling/internal: - Update rubocop again and run it in CI. [#444](https://github.com/rubyzip/rubyzip/pull/444) - Fix a test that was incorrect on big-endian architectures. [#445](https://github.com/rubyzip/rubyzip/pull/445) +# 2.4.1 (2025-01-05) + +*This is a re-release of version 2.4 with a full version number string. We need to move to version 2.4.1 due to the canonical version number 2.4 now being taken in Rubygems.* + +Tooling: + +- Opt-in for MFA requirement explicitly on 2.4 branch. + +# 2.4 (2025-01-04) - Yanked + +*Yanked due to incorrect version number format (2.4 vs 2.4.0).* + +- Ensure compatibility with `--enable-frozen-string-literal`. +- Ensure `File.open_buffer` doesn't rewrite unchanged data. This is a backport of the fix on the 3.x branch. +- Enable use of the version 3 calling style (mainly named parameters) wherever possible, while retaining version 2.x compatibility. +- Add (switchable) warning messages to methods that are changed or removed in version 3.x. + +Tooling: + +- Switch to using GitHub Actions (from Travis). +- Update Rubocop versions and configuration. +- Update actions with latest rubies. + # 2.3.2 (2021-07-05) - A "dummy" release to warn about breaking changes coming in version 3.0. This updated version uses the Gem `post_install_message` instead of printing to `STDERR`. From 0d920d552f809a261c31345b8009752f65a01fdb Mon Sep 17 00:00:00 2001 From: Geremia Taglialatela Date: Tue, 7 Jan 2025 08:40:56 +0100 Subject: [PATCH 304/311] Update README with Ruby version compatibility This commit updates the README to reflect the Ruby versions that are known to work with version 2.x of the library. Specifically, it documents that version 2.x works on Ruby 3.x. Ref: 0001864cfe0a1e76879179dfa1ba7b9e60d5a991 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 700a2dd9..067717e1 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ The public API of some classes has been modernized to use named parameters for o Version 3.x requires at least Ruby 3.0. -Version 2.x requires at least Ruby 2.4, and is known to work on Ruby 3.1. +Version 2.x requires at least Ruby 2.4, and is known to work on Ruby 3.x. It is not recommended to use any versions of Rubyzip earlier than 2.3 due to security issues. From 8ed666289129ffe2b826ccec4824290520b843f0 Mon Sep 17 00:00:00 2001 From: Geremia Taglialatela Date: Mon, 6 Jan 2025 23:03:49 +0100 Subject: [PATCH 305/311] Fix JRuby CI tests Pin `jar-dependencies` to `0.4.1`. Ref: jruby/jruby#7262 Close #626 --- Gemfile | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Gemfile b/Gemfile index 7f4f5e95..93106acb 100644 --- a/Gemfile +++ b/Gemfile @@ -3,3 +3,7 @@ source 'https://rubygems.org' gemspec + +# TODO: remove when JRuby 9.4.10.0 will be released and available on CI +# Ref: https://github.com/jruby/jruby/issues/7262 +gem 'jar-dependencies', '0.4.1' if RUBY_PLATFORM.include?('java') From f7c6b79256f99311231747ff9cc6a169a2014628 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sat, 25 Jan 2025 09:55:27 +0000 Subject: [PATCH 306/311] Update some dev dependency gems and relax version matching. For gems such as rake, minitest and rdoc it is appropriate to allow version matches against major versions, allowing us to get the latest minor and patch versions automatically. Also update the minimum minor versions while we are at it. We'll leave rubocop* and simplecov* gems pinned to minor versions. --- rubyzip.gemspec | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/rubyzip.gemspec b/rubyzip.gemspec index 7392d538..66ec3657 100644 --- a/rubyzip.gemspec +++ b/rubyzip.gemspec @@ -28,9 +28,9 @@ Gem::Specification.new do |s| s.required_ruby_version = '>= 3.0' - s.add_development_dependency 'minitest', '~> 5.22.0' - s.add_development_dependency 'rake', '~> 13.1.0' - s.add_development_dependency 'rdoc', '~> 6.6.2' + s.add_development_dependency 'minitest', '~> 5.25' + s.add_development_dependency 'rake', '~> 13.2' + s.add_development_dependency 'rdoc', '~> 6.11' s.add_development_dependency 'rubocop', '~> 1.61.0' s.add_development_dependency 'rubocop-performance', '~> 1.20.0' s.add_development_dependency 'rubocop-rake', '~> 0.6.0' From 3f909b2bdc89e406ce26fd864b110f4b1b90197b Mon Sep 17 00:00:00 2001 From: Geremia Taglialatela Date: Sat, 11 Jan 2025 11:21:42 +0100 Subject: [PATCH 307/311] Fix CI against JRuby, JRuby-head, and Windows Additionally: - Use `latest` rubygems, instead of specifying a version and keeping it up to date - Bump `rake` dependency to `~> 13.2.0` to allow tests to pass against Windows --- .github/workflows/tests.yml | 2 +- Gemfile | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index e20d5f94..a09e80d8 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -25,7 +25,7 @@ jobs: uses: ruby/setup-ruby@v1 with: ruby-version: ${{ matrix.ruby }} - rubygems: '3.5.23' + rubygems: latest bundler-cache: true - name: Run the tests diff --git a/Gemfile b/Gemfile index 93106acb..bdfaab79 100644 --- a/Gemfile +++ b/Gemfile @@ -6,4 +6,7 @@ gemspec # TODO: remove when JRuby 9.4.10.0 will be released and available on CI # Ref: https://github.com/jruby/jruby/issues/7262 -gem 'jar-dependencies', '0.4.1' if RUBY_PLATFORM.include?('java') +if RUBY_PLATFORM.include?('java') + gem 'jar-dependencies', '0.4.1' + gem 'ruby-maven', '3.3.13' +end From 43d845c2cbe989068952132e0d7b23c91e01d1d7 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sun, 26 Jan 2025 17:17:11 +0000 Subject: [PATCH 308/311] Update version number, README and Changelog for RC1. Also update the year to 2025 in the licence. --- Changelog.md | 5 +++++ LICENSE.md | 2 +- README.md | 15 +++++++-------- lib/zip/version.rb | 2 +- 4 files changed, 14 insertions(+), 10 deletions(-) diff --git a/Changelog.md b/Changelog.md index ea6e39c1..06b40c17 100644 --- a/Changelog.md +++ b/Changelog.md @@ -39,6 +39,11 @@ Tooling/internal: +- Update the README with new Ruby version compatability information. +- Fix various issues with JRuby tests. +- Update gem dependency versions. +- Add Ruby 3.4 to the CI. +- Fix mispelled variable names in the crypto classes. - Only use the Zip64 CDIR end locator if needed. - Prevent unnecessary Zip64 data being stored. - Abstract marking various things as 'dirty' into `Dirtyable` for reuse. diff --git a/LICENSE.md b/LICENSE.md index 16e431c1..5673f4bc 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,6 +1,6 @@ BSD 2-Clause License -Copyright (c) 2002-2024, The Rubyzip Developers +Copyright (c) 2002-2025, The Rubyzip Developers Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/README.md b/README.md index 067717e1..dcf01eda 100644 --- a/README.md +++ b/README.md @@ -388,22 +388,21 @@ Rubyzip is known to run on a number of platforms and under a number of different ### Version 2.3.x -Rubyzip 2.3 is known to work on MRI 2.4 to 3.1 on Linux and Mac, and JRuby and Truffleruby on Linux. There are known issues with Windows which have been fixed on the development branch. Please [let us know](https://github.com/rubyzip/rubyzip/pulls) if you know Rubyzip 2.3 works on a platform/Ruby combination not listed here, or [raise an issue](https://github.com/rubyzip/rubyzip/issues) if you see a failure where we think it should work. +Rubyzip 2.3 is known to work on MRI 2.4 to 3.4 on Linux and Mac, and JRuby and Truffleruby on Linux. There are known issues with Windows which have been fixed on the development branch. Please [let us know](https://github.com/rubyzip/rubyzip/pulls) if you know Rubyzip 2.3 works on a platform/Ruby combination not listed here, or [raise an issue](https://github.com/rubyzip/rubyzip/issues) if you see a failure where we think it should work. ### Next (version 3.0.0) Please see the table below for what we think the current situation is. Note: an empty cell means "unknown", not "does not work". -| OS/Ruby | 2.5 | 2.6 | 2.7 | 3.0 | 3.1 | 3.2 | 3.3 | Head | JRuby 9.4.6.0 | JRuby Head | Truffleruby 23.1.2 | Truffleruby Head | -|---------|-----|-----|-----|-----|-----|-----|-----|------|---------------|------------|--------------------|------------------| -|Ubuntu 22.04| CI | CI | CI | CI | CI | CI | CI | ci | CI | ci | CI | ci | -|Mac OS 12.7.3| CI | x | x | ci | ci | ci | ci | ci | x | | x | | -|Windows 10| | | x | | | | | | | | | | -|Windows Server 2022| CI | | | | | | CI mswin
CI ucrt | | | | | | +| OS/Ruby | 3.0 | 3.1 | 3.2 | 3.3 | 3.4 | Head | JRuby 9.4.9.0 | JRuby Head | Truffleruby 24.1.1 | Truffleruby Head | +|---------|-----|-----|-----|-----|-----|------|---------------|------------|--------------------|------------------| +|Ubuntu 22.04| CI | CI | CI | CI | CI | ci | CI | ci | CI | ci | +|Mac OS 14.7.2| CI | CI | CI | CI | CI | ci | x | | x | | +|Windows Server 2022| CI | | | | CI mswin
CI ucrt | | | | | | Key: `CI` - tested in CI, should work; `ci` - tested in CI, might fail; `x` - known working; `o` - known failing. -Ruby 3.0+ are also tested separately with YJIT turned on. +Rubies 3.1+ are also tested separately with YJIT turned on (Ubuntu and Mac OS). See [the Actions tab](https://github.com/rubyzip/rubyzip/actions) in GitHub for full details. diff --git a/lib/zip/version.rb b/lib/zip/version.rb index 8878fc45..12555e21 100644 --- a/lib/zip/version.rb +++ b/lib/zip/version.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true module Zip - VERSION = '3.0.0.alpha' # :nodoc: + VERSION = '3.0.0.rc1' # :nodoc: end From 98881e23d1e5f43b104533f752a9e177509714c2 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sat, 1 Feb 2025 16:31:20 +0000 Subject: [PATCH 309/311] Add a test to ensure correct version number format. Hopefully this will avoid a repeat of the '2.4' debacle... --- test/version_test.rb | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 test/version_test.rb diff --git a/test/version_test.rb b/test/version_test.rb new file mode 100644 index 00000000..3df7662b --- /dev/null +++ b/test/version_test.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +require 'test_helper' +require 'zip/version' + +class VersionTest < MiniTest::Test + def test_version + # Ensure all our versions numbers have at least MAJOR.MINOR.PATCH + # elements separated by dots, to comply with Semantic Versioning. + assert_match(/^\d+\.\d+\.\d+/, Zip::VERSION) + end +end From deca4d5aeb0f662bbe6cb8c11017dd524f008fb8 Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sat, 8 Feb 2025 16:51:30 +0000 Subject: [PATCH 310/311] Fix de facto regression for input streams. A close reading of the ZIP spec insists that if bit 3 of the GP flags is set then the archive cannot be read via `Zip::InputStream`. But in most cases the correct information is present to be able to do so, both safely and reliably, and v2.4 does allow this. This commit ensures that behaviour is present in v3.0. --- lib/zip/entry.rb | 35 +++++++++++++++++++++++------------ test/file_split_test.rb | 9 +++++++++ 2 files changed, 32 insertions(+), 12 deletions(-) diff --git a/lib/zip/entry.rb b/lib/zip/entry.rb index 9e1a904c..394f9190 100644 --- a/lib/zip/entry.rb +++ b/lib/zip/entry.rb @@ -113,7 +113,7 @@ def encrypted? end def incomplete? # :nodoc: - gp_flags & 8 == 8 + (gp_flags & 8 == 8) && (crc == 0 || size == 0 || compressed_size == 0) end # The uncompressed size of the entry. @@ -343,24 +343,25 @@ def unpack_local_entry(buf) # :nodoc: def read_local_entry(io) # :nodoc: @dirty = false # No changes at this point. - @local_header_offset = io.tell + current_offset = io.tell - static_sized_fields_buf = io.read(::Zip::LOCAL_ENTRY_STATIC_HEADER_LENGTH) || '' + read_local_header_fields(io) - unless static_sized_fields_buf.bytesize == ::Zip::LOCAL_ENTRY_STATIC_HEADER_LENGTH - raise Error, 'Premature end of file. Not enough data for zip entry local header' - end + if @header_signature == SPLIT_FILE_SIGNATURE + raise SplitArchiveError if current_offset.zero? - unpack_local_entry(static_sized_fields_buf) + # Rewind, skipping the data descriptor, then try to read the local header again. + current_offset += 16 + io.seek(current_offset) + read_local_header_fields(io) + end unless @header_signature == LOCAL_ENTRY_SIGNATURE - if @header_signature == SPLIT_FILE_SIGNATURE - raise SplitArchiveError - end - - raise Error, "Zip local header magic not found at location '#{local_header_offset}'" + raise Error, "Zip local header magic not found at location '#{current_offset}'" end + @local_header_offset = current_offset + set_time(@last_mod_date, @last_mod_time) @name = io.read(@name_length) @@ -721,6 +722,16 @@ def clean_up # :nodoc: private + def read_local_header_fields(io) # :nodoc: + static_sized_fields_buf = io.read(::Zip::LOCAL_ENTRY_STATIC_HEADER_LENGTH) || '' + + unless static_sized_fields_buf.bytesize == ::Zip::LOCAL_ENTRY_STATIC_HEADER_LENGTH + raise Error, 'Premature end of file. Not enough data for zip entry local header' + end + + unpack_local_entry(static_sized_fields_buf) + end + def set_time(binary_dos_date, binary_dos_time) @time = ::Zip::DOSTime.parse_binary_dos_format(binary_dos_date, binary_dos_time) rescue ArgumentError diff --git a/test/file_split_test.rb b/test/file_split_test.rb index 859bf935..0c5a5eeb 100644 --- a/test/file_split_test.rb +++ b/test/file_split_test.rb @@ -5,6 +5,7 @@ class ZipFileSplitTest < MiniTest::Test TEST_ZIP = TestZipFile::TEST_ZIP2.clone TEST_ZIP.zip_name = 'large_zip_file.zip' + TEST_ZIP_FIRST_SEGMENT = 'large_zip_file.zip.001' EXTRACTED_FILENAME = 'test/data/generated/extEntrySplit' UNSPLITTED_FILENAME = 'test/data/generated/unsplitted.zip' ENTRY_TO_EXTRACT = TEST_ZIP.entry_names.first @@ -59,4 +60,12 @@ def test_split entry.get_input_stream(&:read)) end end + + def test_raise_error_on_open_split_zip + ::Zip::File.split(TEST_ZIP.zip_name, segment_size: 65_536, delete_original: false) + + assert_raises(::Zip::SplitArchiveError) do + ::Zip::InputStream.open(TEST_ZIP_FIRST_SEGMENT, &:get_next_entry) + end + end end From 1f3f84c88914b2b3c77c18b73f2ecb42225a54af Mon Sep 17 00:00:00 2001 From: Robert Haines Date: Sat, 8 Feb 2025 17:18:43 +0000 Subject: [PATCH 311/311] Update version number and Changelog for RC2. --- Changelog.md | 2 ++ lib/zip/version.rb | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index 06b40c17..7e0b9052 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,5 +1,6 @@ # 3.0.0 (Next) +- Fix de facto regression for input streams. - Fix `File#write_buffer` to always return the given `io`. - Add `Entry#absolute_time?` and `DOSTime#absolute_time?` methods. - Use explicit named parameters for `File` methods. @@ -39,6 +40,7 @@ Tooling/internal: +- Add a test to ensure correct version number format. - Update the README with new Ruby version compatability information. - Fix various issues with JRuby tests. - Update gem dependency versions. diff --git a/lib/zip/version.rb b/lib/zip/version.rb index 12555e21..7957e0de 100644 --- a/lib/zip/version.rb +++ b/lib/zip/version.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true module Zip - VERSION = '3.0.0.rc1' # :nodoc: + VERSION = '3.0.0.rc2' # :nodoc: end

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