zip_tricks 3.1.0 → 3.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3434ce881bfe7fdf3494ed494f8e251082d94510
|
4
|
+
data.tar.gz: 30062193918dacf4a018d7bde923303f0a14c47f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 88be7f1bebd8e0faa8906203b594e022b173390039732a3eaafa53ec903770b0a619f7a0895a8203b75c6c09f5908ca400d86611f443e34fdead53554edd8e14
|
7
|
+
data.tar.gz: 5c4362394b3666ce528e16c657b019a8185875555af2202a8c976df523c309017ac060ec61082617b3573722cb1d2e4eb002502ef9c8b770227859756e0afe5f
|
@@ -188,26 +188,36 @@ class ZipTricks::FileReader
|
|
188
188
|
# Parse an IO handle to a ZIP archive into an array of Entry objects.
|
189
189
|
#
|
190
190
|
# @param io[#tell, #seek, #read, #size] an IO-ish object
|
191
|
+
# @param read_local_headers[Boolean] whether to proceed to read the local headers in addition to the central directory
|
191
192
|
# @return [Array<Entry>] an array of entries within the ZIP being parsed
|
192
|
-
def read_zip_structure(io)
|
193
|
+
def read_zip_structure(io, read_local_headers: true)
|
193
194
|
zip_file_size = io.size
|
194
195
|
eocd_offset = get_eocd_offset(io, zip_file_size)
|
195
196
|
|
196
|
-
zip64_end_of_cdir_location =
|
197
|
+
zip64_end_of_cdir_location = get_zip64_eocd_location(io, eocd_offset)
|
197
198
|
num_files, cdir_location, cdir_size = if zip64_end_of_cdir_location
|
198
199
|
num_files_and_central_directory_offset_zip64(io, zip64_end_of_cdir_location)
|
199
200
|
else
|
200
201
|
num_files_and_central_directory_offset(io, eocd_offset)
|
201
202
|
end
|
203
|
+
log { 'Located the central directory start at %d' % cdir_location }
|
202
204
|
seek(io, cdir_location)
|
203
205
|
|
204
206
|
# Read the entire central directory in one fell swoop
|
205
207
|
central_directory_str = read_n(io, cdir_size)
|
206
208
|
central_directory_io = StringIO.new(central_directory_str)
|
209
|
+
log { 'Read %d bytes with central directory entries' % cdir_size }
|
207
210
|
|
208
|
-
entries = (
|
209
|
-
|
210
|
-
|
211
|
+
entries = (0...num_files).map do |entry_n|
|
212
|
+
log { 'Reading the central directory entry %d starting at offset %d' % [entry_n, cdir_location + central_directory_io.tell] }
|
213
|
+
read_cdir_entry(central_directory_io)
|
214
|
+
end
|
215
|
+
|
216
|
+
entries.each_with_index do |entry, i|
|
217
|
+
if read_local_headers
|
218
|
+
log { 'Reading the local header for entry %d at offset %d' % [i, entry.local_file_header_offset] }
|
219
|
+
entry.compressed_data_offset = find_compressed_data_start_offset(io, entry.local_file_header_offset)
|
220
|
+
end
|
211
221
|
end
|
212
222
|
end
|
213
223
|
|
@@ -349,9 +359,20 @@ class ZipTricks::FileReader
|
|
349
359
|
# ...of which we really only need the Zip64 extra
|
350
360
|
if zip64_extra_contents = extra_table[1] # Zip64 extra
|
351
361
|
zip64_extra = StringIO.new(zip64_extra_contents)
|
352
|
-
e.
|
353
|
-
|
354
|
-
|
362
|
+
log { 'Will read Zip64 extra data for %s, %d bytes' % [e.filename, zip64_extra.size] }
|
363
|
+
# Now here be dragons. The APPNOTE specifies that
|
364
|
+
#
|
365
|
+
# > The order of the fields in the ZIP64 extended
|
366
|
+
# > information record is fixed, but the fields will
|
367
|
+
# > only appear if the corresponding Local or Central
|
368
|
+
# > directory record field is set to 0xFFFF or 0xFFFFFFFF.
|
369
|
+
#
|
370
|
+
# It means that before we read this stuff we need to check if the previously-read
|
371
|
+
# values are at overflow, and only _then_ proceed to read them. Bah.
|
372
|
+
e.uncompressed_size = read_8b(zip64_extra) if e.uncompressed_size == 0xFFFFFFFF
|
373
|
+
e.compressed_size = read_8b(zip64_extra) if e.compressed_size == 0xFFFFFFFF
|
374
|
+
e.local_file_header_offset = read_8b(zip64_extra) if e.local_file_header_offset == 0xFFFFFFFF
|
375
|
+
# Disk number comes last and we can skip it anyway, since we do not support multi-disk archives
|
355
376
|
end
|
356
377
|
end
|
357
378
|
end
|
@@ -369,8 +390,11 @@ class ZipTricks::FileReader
|
|
369
390
|
eocd_idx_in_buf = locate_eocd_signature(str_containing_eocd_record)
|
370
391
|
|
371
392
|
raise "Could not find the EOCD signature in the buffer - maybe a malformed ZIP file" unless eocd_idx_in_buf
|
372
|
-
|
373
|
-
implied_position_of_eocd_record + eocd_idx_in_buf
|
393
|
+
|
394
|
+
eocd_offset = implied_position_of_eocd_record + eocd_idx_in_buf
|
395
|
+
log { 'Found EOCD signature at offset %d' % eocd_offset }
|
396
|
+
|
397
|
+
eocd_offset
|
374
398
|
end
|
375
399
|
|
376
400
|
# This is tricky. Essentially, we have to scan the maximum possible number of bytes (that the EOCD can
|
@@ -394,7 +418,6 @@ class ZipTricks::FileReader
|
|
394
418
|
|
395
419
|
window_location = in_str.bytesize + end_location
|
396
420
|
unpacked = window.unpack(unpack_pattern)
|
397
|
-
|
398
421
|
# If we found the signarue, pick up the comment size, and check if the size of the window
|
399
422
|
# plus that comment size is where we are in the string. If we are - bingo.
|
400
423
|
if unpacked[0] == 0x06054b50 && comment_size = unpacked[-1]
|
@@ -409,18 +432,23 @@ class ZipTricks::FileReader
|
|
409
432
|
|
410
433
|
# Find the Zip64 EOCD locator segment offset. Do this by seeking backwards from the
|
411
434
|
# EOCD record in the archive by fixed offsets
|
412
|
-
def
|
435
|
+
def get_zip64_eocd_location(file_io, eocd_offset)
|
413
436
|
zip64_eocd_loc_offset = eocd_offset
|
414
437
|
zip64_eocd_loc_offset -= 4 # The signature
|
415
438
|
zip64_eocd_loc_offset -= 4 # Which disk has the Zip64 end of central directory record
|
416
439
|
zip64_eocd_loc_offset -= 8 # Offset of the zip64 central directory record
|
417
440
|
zip64_eocd_loc_offset -= 4 # Total number of disks
|
418
441
|
|
442
|
+
log { 'Will look for the Zip64 EOCD locator signature at offset %d' % zip64_eocd_loc_offset }
|
443
|
+
|
419
444
|
# If the offset is negative there is certainly no Zip64 EOCD locator here
|
420
445
|
return unless zip64_eocd_loc_offset >= 0
|
421
446
|
|
422
447
|
file_io.seek(zip64_eocd_loc_offset, IO::SEEK_SET)
|
423
448
|
assert_signature(file_io, 0x07064b50)
|
449
|
+
|
450
|
+
log { 'Found Zip64 EOCD locator at offset %d' % zip64_eocd_loc_offset }
|
451
|
+
|
424
452
|
disk_num = read_4b(file_io) # number of the disk
|
425
453
|
raise UnsupportedFeature, "The archive spans multiple disks" if disk_num != 0
|
426
454
|
read_8b(file_io)
|
@@ -445,9 +473,10 @@ class ZipTricks::FileReader
|
|
445
473
|
|
446
474
|
num_files_this_disk = read_8b(zip64_eocdr) # number of files on this disk
|
447
475
|
num_files_total = read_8b(zip64_eocdr) # files total in the central directory
|
448
|
-
|
449
476
|
raise UnsupportedFeature, "The archive spans multiple disks" if num_files_this_disk != num_files_total
|
450
477
|
|
478
|
+
log { 'Zip64 EOCD record states there are %d files in the archive' % num_files_total }
|
479
|
+
|
451
480
|
central_dir_size = read_8b(zip64_eocdr) # Size of the central directory
|
452
481
|
central_dir_offset = read_8b(zip64_eocdr) # Where the central directory starts
|
453
482
|
|
@@ -519,4 +548,12 @@ class ZipTricks::FileReader
|
|
519
548
|
|
520
549
|
private_constant :C_V, :C_v, :C_Qe, :MAX_END_OF_CENTRAL_DIRECTORY_RECORD_SIZE,
|
521
550
|
:MAX_LOCAL_HEADER_SIZE, :SIZE_OF_USABLE_EOCD_RECORD
|
551
|
+
|
552
|
+
# Is provided as a stub to be overridden in a subclass if you need it. Will report
|
553
|
+
# during various stages of reading. The log message is contained in the return value
|
554
|
+
# of `yield` in the method (the log messages are lazy-evaluated).
|
555
|
+
def log
|
556
|
+
# The most minimal implementation for the method is just this:
|
557
|
+
# $stderr.puts(yield)
|
558
|
+
end
|
522
559
|
end
|
data/lib/zip_tricks.rb
CHANGED
@@ -66,4 +66,17 @@ describe ZipTricks::FileReader do
|
|
66
66
|
entries = described_class.read_zip_structure(z)
|
67
67
|
expect(entries.length).to eq(1)
|
68
68
|
end
|
69
|
+
|
70
|
+
it 'can handle a Zip64 central directory fields that only contains the required fields (substitutes for standard fields)' do
|
71
|
+
# In this example central directory, 2 entries contain Zip64 extra where only the local header offset is set (8 bytes each)
|
72
|
+
# This is the exceptional case where we have to poke at a private method directly
|
73
|
+
File.open(__dir__ + '/cdir_entry_with_partial_use_of_zip64_extra_fields.bin', 'rb') do |f|
|
74
|
+
reader = described_class.new
|
75
|
+
entry = reader.send(:read_cdir_entry, f)
|
76
|
+
expect(entry.local_file_header_offset).to eq(4312401349)
|
77
|
+
expect(entry.filename).to eq('Motorhead - Ace Of Spades.srt')
|
78
|
+
expect(entry.compressed_size).to eq(69121)
|
79
|
+
expect(entry.uncompressed_size).to eq(69121)
|
80
|
+
end
|
81
|
+
end
|
69
82
|
end
|
data/zip_tricks.gemspec
CHANGED
@@ -2,16 +2,16 @@
|
|
2
2
|
# DO NOT EDIT THIS FILE DIRECTLY
|
3
3
|
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
4
|
# -*- encoding: utf-8 -*-
|
5
|
-
# stub: zip_tricks 3.1.
|
5
|
+
# stub: zip_tricks 3.1.1 ruby lib
|
6
6
|
|
7
7
|
Gem::Specification.new do |s|
|
8
8
|
s.name = "zip_tricks"
|
9
|
-
s.version = "3.1.
|
9
|
+
s.version = "3.1.1"
|
10
10
|
|
11
11
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
12
12
|
s.require_paths = ["lib"]
|
13
13
|
s.authors = ["Julik Tarkhanov"]
|
14
|
-
s.date = "2016-08-
|
14
|
+
s.date = "2016-08-17"
|
15
15
|
s.description = "Makes rubyzip stream, for real"
|
16
16
|
s.email = "me@julik.nl"
|
17
17
|
s.extra_rdoc_files = [
|
@@ -52,6 +52,7 @@ Gem::Specification.new do |s|
|
|
52
52
|
"spec/spec_helper.rb",
|
53
53
|
"spec/zip_tricks/block_deflate_spec.rb",
|
54
54
|
"spec/zip_tricks/block_write_spec.rb",
|
55
|
+
"spec/zip_tricks/cdir_entry_with_partial_use_of_zip64_extra_fields.bin",
|
55
56
|
"spec/zip_tricks/file_reader_spec.rb",
|
56
57
|
"spec/zip_tricks/rack_body_spec.rb",
|
57
58
|
"spec/zip_tricks/remote_io_spec.rb",
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: zip_tricks
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.1.
|
4
|
+
version: 3.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Julik Tarkhanov
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-08-
|
11
|
+
date: 2016-08-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rubyzip
|
@@ -203,6 +203,7 @@ files:
|
|
203
203
|
- spec/spec_helper.rb
|
204
204
|
- spec/zip_tricks/block_deflate_spec.rb
|
205
205
|
- spec/zip_tricks/block_write_spec.rb
|
206
|
+
- spec/zip_tricks/cdir_entry_with_partial_use_of_zip64_extra_fields.bin
|
206
207
|
- spec/zip_tricks/file_reader_spec.rb
|
207
208
|
- spec/zip_tricks/rack_body_spec.rb
|
208
209
|
- spec/zip_tricks/remote_io_spec.rb
|