rubyzip 2.0.0 → 2.3.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Rakefile +3 -0
- data/lib/zip.rb +3 -2
- data/lib/zip/central_directory.rb +9 -5
- data/lib/zip/constants.rb +52 -0
- data/lib/zip/crypto/decrypted_io.rb +40 -0
- data/lib/zip/crypto/traditional_encryption.rb +9 -9
- data/lib/zip/decompressor.rb +19 -1
- data/lib/zip/dos_time.rb +12 -7
- data/lib/zip/entry.rb +57 -38
- data/lib/zip/entry_set.rb +2 -0
- data/lib/zip/errors.rb +1 -0
- data/lib/zip/extra_field.rb +11 -9
- data/lib/zip/extra_field/generic.rb +10 -9
- data/lib/zip/extra_field/ntfs.rb +4 -0
- data/lib/zip/extra_field/old_unix.rb +3 -1
- data/lib/zip/extra_field/universal_time.rb +42 -12
- data/lib/zip/extra_field/unix.rb +3 -1
- data/lib/zip/extra_field/zip64.rb +4 -2
- data/lib/zip/file.rb +79 -54
- data/lib/zip/filesystem.rb +193 -177
- data/lib/zip/inflater.rb +24 -36
- data/lib/zip/input_stream.rb +33 -26
- data/lib/zip/ioextras.rb +1 -1
- data/lib/zip/ioextras/abstract_input_stream.rb +19 -8
- data/lib/zip/ioextras/abstract_output_stream.rb +1 -1
- data/lib/zip/null_decompressor.rb +1 -9
- data/lib/zip/output_stream.rb +14 -5
- data/lib/zip/pass_thru_compressor.rb +2 -2
- data/lib/zip/pass_thru_decompressor.rb +13 -22
- data/lib/zip/streamable_directory.rb +3 -3
- data/lib/zip/streamable_stream.rb +6 -10
- data/lib/zip/version.rb +1 -1
- data/samples/example.rb +2 -2
- data/samples/example_filesystem.rb +1 -1
- data/samples/gtk_ruby_zip.rb +19 -19
- data/samples/qtzip.rb +6 -6
- data/samples/write_simple.rb +2 -4
- data/samples/zipfind.rb +23 -22
- metadata +44 -21
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 35bd078119c42cd2250fadd127a0feae3299184b0bf90804c3ff0bc28d1c427f
|
4
|
+
data.tar.gz: 98e034b50a428ff970f25b28348993d938a88c5d5c93506561761f260062c059
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 421a8884fbfe6f720e2e0da35f34e4208b96f83529faf5cba03501aa2693d95d51b9c19e04e4567801de1822120a0e14faf1c4d0991a164b8f0d011eaa6c0f7b
|
7
|
+
data.tar.gz: 59f29c6b49a14c777605224b351d8d14f7fdfe88a2ffc75a5f2120f51152831ed9afa04d8daf1f971bad982c3bcdd86e17d5616c8c9d57ddff7509b7f59e58b1
|
data/Rakefile
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'bundler/gem_tasks'
|
2
2
|
require 'rake/testtask'
|
3
|
+
require 'rubocop/rake_task'
|
3
4
|
|
4
5
|
task default: :test
|
5
6
|
|
@@ -10,6 +11,8 @@ Rake::TestTask.new(:test) do |test|
|
|
10
11
|
test.verbose = true
|
11
12
|
end
|
12
13
|
|
14
|
+
RuboCop::RakeTask.new
|
15
|
+
|
13
16
|
# Rake::TestTask.new(:zip64_full_test) do |test|
|
14
17
|
# test.libs << File.join(File.dirname(__FILE__), 'lib')
|
15
18
|
# test.libs << File.join(File.dirname(__FILE__), 'test')
|
data/lib/zip.rb
CHANGED
@@ -1,10 +1,11 @@
|
|
1
|
+
require 'English'
|
1
2
|
require 'delegate'
|
2
3
|
require 'singleton'
|
3
4
|
require 'tempfile'
|
4
|
-
require 'tmpdir'
|
5
5
|
require 'fileutils'
|
6
6
|
require 'stringio'
|
7
7
|
require 'zlib'
|
8
|
+
require 'zip/constants'
|
8
9
|
require 'zip/dos_time'
|
9
10
|
require 'zip/ioextras'
|
10
11
|
require 'rbconfig'
|
@@ -22,6 +23,7 @@ require 'zip/null_compressor'
|
|
22
23
|
require 'zip/null_input_stream'
|
23
24
|
require 'zip/pass_thru_compressor'
|
24
25
|
require 'zip/pass_thru_decompressor'
|
26
|
+
require 'zip/crypto/decrypted_io'
|
25
27
|
require 'zip/crypto/encryption'
|
26
28
|
require 'zip/crypto/null_encryption'
|
27
29
|
require 'zip/crypto/traditional_encryption'
|
@@ -29,7 +31,6 @@ require 'zip/inflater'
|
|
29
31
|
require 'zip/deflater'
|
30
32
|
require 'zip/streamable_stream'
|
31
33
|
require 'zip/streamable_directory'
|
32
|
-
require 'zip/constants'
|
33
34
|
require 'zip/errors'
|
34
35
|
|
35
36
|
module Zip
|
@@ -65,7 +65,7 @@ module Zip
|
|
65
65
|
@entry_set ? @entry_set.size : 0, # number of entries on this disk
|
66
66
|
@entry_set ? @entry_set.size : 0, # number of entries total
|
67
67
|
cdir_size, # size of central directory
|
68
|
-
offset
|
68
|
+
offset # offset of start of central directory in its disk
|
69
69
|
]
|
70
70
|
io << tmp.pack('VQ<vvVVQ<Q<Q<Q<')
|
71
71
|
end
|
@@ -141,6 +141,7 @@ module Zip
|
|
141
141
|
def get_e_o_c_d(buf) #:nodoc:
|
142
142
|
sig_index = buf.rindex([END_OF_CDS].pack('V'))
|
143
143
|
raise Error, 'Zip end of central directory signature not found' unless sig_index
|
144
|
+
|
144
145
|
buf = buf.slice!((sig_index + 4)..(buf.bytesize))
|
145
146
|
|
146
147
|
def buf.read(count)
|
@@ -166,8 +167,10 @@ module Zip
|
|
166
167
|
def get_64_e_o_c_d(buf) #:nodoc:
|
167
168
|
zip_64_start = buf.rindex([ZIP64_END_OF_CDS].pack('V'))
|
168
169
|
raise Error, 'Zip64 end of central directory signature not found' unless zip_64_start
|
170
|
+
|
169
171
|
zip_64_locator = buf.rindex([ZIP64_EOCD_LOCATOR].pack('V'))
|
170
172
|
raise Error, 'Zip64 end of central directory signature locator not found' unless zip_64_locator
|
173
|
+
|
171
174
|
buf = buf.slice!((zip_64_start + 4)..zip_64_locator)
|
172
175
|
|
173
176
|
def buf.read(count)
|
@@ -178,8 +181,8 @@ module Zip
|
|
178
181
|
end
|
179
182
|
|
180
183
|
# For iterating over the entries.
|
181
|
-
def each(&
|
182
|
-
@entry_set.each(&
|
184
|
+
def each(&a_proc)
|
185
|
+
@entry_set.each(&a_proc)
|
183
186
|
end
|
184
187
|
|
185
188
|
# Returns the number of entries in the central directory (and
|
@@ -191,13 +194,14 @@ module Zip
|
|
191
194
|
def self.read_from_stream(io) #:nodoc:
|
192
195
|
cdir = new
|
193
196
|
cdir.read_from_stream(io)
|
194
|
-
|
197
|
+
cdir
|
195
198
|
rescue Error
|
196
|
-
|
199
|
+
nil
|
197
200
|
end
|
198
201
|
|
199
202
|
def ==(other) #:nodoc:
|
200
203
|
return false unless other.kind_of?(CentralDirectory)
|
204
|
+
|
201
205
|
@entry_set.entries.sort == other.entries.sort && comment == other.comment
|
202
206
|
end
|
203
207
|
end
|
data/lib/zip/constants.rb
CHANGED
@@ -60,4 +60,56 @@ module Zip
|
|
60
60
|
FSTYPE_MAC_OSX => 'Mac OS/X (Darwin)'.freeze,
|
61
61
|
FSTYPE_ATHEOS => 'AtheOS'.freeze
|
62
62
|
}.freeze
|
63
|
+
|
64
|
+
COMPRESSION_METHOD_STORE = 0
|
65
|
+
COMPRESSION_METHOD_SHRINK = 1
|
66
|
+
COMPRESSION_METHOD_REDUCE_1 = 2
|
67
|
+
COMPRESSION_METHOD_REDUCE_2 = 3
|
68
|
+
COMPRESSION_METHOD_REDUCE_3 = 4
|
69
|
+
COMPRESSION_METHOD_REDUCE_4 = 5
|
70
|
+
COMPRESSION_METHOD_IMPLODE = 6
|
71
|
+
# RESERVED = 7
|
72
|
+
COMPRESSION_METHOD_DEFLATE = 8
|
73
|
+
COMPRESSION_METHOD_DEFLATE_64 = 9
|
74
|
+
COMPRESSION_METHOD_PKWARE_DCLI = 10
|
75
|
+
# RESERVED = 11
|
76
|
+
COMPRESSION_METHOD_BZIP2 = 12
|
77
|
+
# RESERVED = 13
|
78
|
+
COMPRESSION_METHOD_LZMA = 14
|
79
|
+
# RESERVED = 15
|
80
|
+
COMPRESSION_METHOD_IBM_CMPSC = 16
|
81
|
+
# RESERVED = 17
|
82
|
+
COMPRESSION_METHOD_IBM_TERSE = 18
|
83
|
+
COMPRESSION_METHOD_IBM_LZ77 = 19
|
84
|
+
COMPRESSION_METHOD_JPEG = 96
|
85
|
+
COMPRESSION_METHOD_WAVPACK = 97
|
86
|
+
COMPRESSION_METHOD_PPMD = 98
|
87
|
+
COMPRESSION_METHOD_AES = 99
|
88
|
+
|
89
|
+
COMPRESSION_METHODS = {
|
90
|
+
COMPRESSION_METHOD_STORE => 'Store (no compression)',
|
91
|
+
COMPRESSION_METHOD_SHRINK => 'Shrink',
|
92
|
+
COMPRESSION_METHOD_REDUCE_1 => 'Reduce with compression factor 1',
|
93
|
+
COMPRESSION_METHOD_REDUCE_2 => 'Reduce with compression factor 2',
|
94
|
+
COMPRESSION_METHOD_REDUCE_3 => 'Reduce with compression factor 3',
|
95
|
+
COMPRESSION_METHOD_REDUCE_4 => 'Reduce with compression factor 4',
|
96
|
+
COMPRESSION_METHOD_IMPLODE => 'Implode',
|
97
|
+
# RESERVED = 7
|
98
|
+
COMPRESSION_METHOD_DEFLATE => 'Deflate',
|
99
|
+
COMPRESSION_METHOD_DEFLATE_64 => 'Deflate64(tm)',
|
100
|
+
COMPRESSION_METHOD_PKWARE_DCLI => 'PKWARE Data Compression Library Imploding (old IBM TERSE)',
|
101
|
+
# RESERVED = 11
|
102
|
+
COMPRESSION_METHOD_BZIP2 => 'BZIP2',
|
103
|
+
# RESERVED = 13
|
104
|
+
COMPRESSION_METHOD_LZMA => 'LZMA',
|
105
|
+
# RESERVED = 15
|
106
|
+
COMPRESSION_METHOD_IBM_CMPSC => 'IBM z/OS CMPSC Compression',
|
107
|
+
# RESERVED = 17
|
108
|
+
COMPRESSION_METHOD_IBM_TERSE => 'IBM TERSE (new)',
|
109
|
+
COMPRESSION_METHOD_IBM_LZ77 => 'IBM LZ77 z Architecture (PFS)',
|
110
|
+
COMPRESSION_METHOD_JPEG => 'JPEG variant',
|
111
|
+
COMPRESSION_METHOD_WAVPACK => 'WavPack compressed data',
|
112
|
+
COMPRESSION_METHOD_PPMD => 'PPMd version I, Rev 1',
|
113
|
+
COMPRESSION_METHOD_AES => 'AES encryption'
|
114
|
+
}.freeze
|
63
115
|
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Zip
|
2
|
+
class DecryptedIo #:nodoc:all
|
3
|
+
CHUNK_SIZE = 32_768
|
4
|
+
|
5
|
+
def initialize(io, decrypter)
|
6
|
+
@io = io
|
7
|
+
@decrypter = decrypter
|
8
|
+
end
|
9
|
+
|
10
|
+
def read(length = nil, outbuf = +'')
|
11
|
+
return (length.nil? || length.zero? ? '' : nil) if eof
|
12
|
+
|
13
|
+
while length.nil? || (buffer.bytesize < length)
|
14
|
+
break if input_finished?
|
15
|
+
|
16
|
+
buffer << produce_input
|
17
|
+
end
|
18
|
+
|
19
|
+
outbuf.replace(buffer.slice!(0...(length || output_buffer.bytesize)))
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def eof
|
25
|
+
buffer.empty? && input_finished?
|
26
|
+
end
|
27
|
+
|
28
|
+
def buffer
|
29
|
+
@buffer ||= +''
|
30
|
+
end
|
31
|
+
|
32
|
+
def input_finished?
|
33
|
+
@io.eof
|
34
|
+
end
|
35
|
+
|
36
|
+
def produce_input
|
37
|
+
@decrypter.decrypt(@io.read(CHUNK_SIZE))
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -24,8 +24,8 @@ module Zip
|
|
24
24
|
end
|
25
25
|
end
|
26
26
|
|
27
|
-
def update_keys(
|
28
|
-
@key0 = ~Zlib.crc32(
|
27
|
+
def update_keys(num)
|
28
|
+
@key0 = ~Zlib.crc32(num, ~@key0)
|
29
29
|
@key1 = ((@key1 + (@key0 & 0xff)) * 134_775_813 + 1) & 0xffffffff
|
30
30
|
@key2 = ~Zlib.crc32((@key1 >> 24).chr, ~@key2)
|
31
31
|
end
|
@@ -63,10 +63,10 @@ module Zip
|
|
63
63
|
|
64
64
|
private
|
65
65
|
|
66
|
-
def encode(
|
66
|
+
def encode(num)
|
67
67
|
t = decrypt_byte
|
68
|
-
update_keys(
|
69
|
-
t ^
|
68
|
+
update_keys(num.chr)
|
69
|
+
t ^ num
|
70
70
|
end
|
71
71
|
end
|
72
72
|
|
@@ -86,10 +86,10 @@ module Zip
|
|
86
86
|
|
87
87
|
private
|
88
88
|
|
89
|
-
def decode(
|
90
|
-
|
91
|
-
update_keys(
|
92
|
-
|
89
|
+
def decode(num)
|
90
|
+
num ^= decrypt_byte
|
91
|
+
update_keys(num.chr)
|
92
|
+
num
|
93
93
|
end
|
94
94
|
end
|
95
95
|
end
|
data/lib/zip/decompressor.rb
CHANGED
@@ -1,9 +1,27 @@
|
|
1
1
|
module Zip
|
2
2
|
class Decompressor #:nodoc:all
|
3
3
|
CHUNK_SIZE = 32_768
|
4
|
-
|
4
|
+
|
5
|
+
def self.decompressor_classes
|
6
|
+
@decompressor_classes ||= {}
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.register(compression_method, decompressor_class)
|
10
|
+
decompressor_classes[compression_method] = decompressor_class
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.find_by_compression_method(compression_method)
|
14
|
+
decompressor_classes[compression_method]
|
15
|
+
end
|
16
|
+
|
17
|
+
attr_reader :input_stream
|
18
|
+
attr_reader :decompressed_size
|
19
|
+
|
20
|
+
def initialize(input_stream, decompressed_size = nil)
|
5
21
|
super()
|
22
|
+
|
6
23
|
@input_stream = input_stream
|
24
|
+
@decompressed_size = decompressed_size
|
7
25
|
end
|
8
26
|
end
|
9
27
|
end
|
data/lib/zip/dos_time.rb
CHANGED
@@ -29,13 +29,18 @@ module Zip
|
|
29
29
|
to_i / 2 == other.to_i / 2
|
30
30
|
end
|
31
31
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
32
|
+
# Create a DOSTime instance from a vanilla Time instance.
|
33
|
+
def self.from_time(time)
|
34
|
+
local(time.year, time.month, time.day, time.hour, time.min, time.sec)
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.parse_binary_dos_format(bin_dos_date, bin_dos_time)
|
38
|
+
second = 2 * (0b11111 & bin_dos_time)
|
39
|
+
minute = (0b11111100000 & bin_dos_time) >> 5
|
40
|
+
hour = (0b1111100000000000 & bin_dos_time) >> 11
|
41
|
+
day = (0b11111 & bin_dos_date)
|
42
|
+
month = (0b111100000 & bin_dos_date) >> 5
|
43
|
+
year = ((0b1111111000000000 & bin_dos_date) >> 9) + 1980
|
39
44
|
begin
|
40
45
|
local(year, month, day, hour, minute, second)
|
41
46
|
end
|
data/lib/zip/entry.rb
CHANGED
@@ -34,7 +34,7 @@ module Zip
|
|
34
34
|
end
|
35
35
|
@follow_symlinks = false
|
36
36
|
|
37
|
-
@restore_times =
|
37
|
+
@restore_times = false
|
38
38
|
@restore_permissions = false
|
39
39
|
@restore_ownership = false
|
40
40
|
# BUG: need an extra field to support uid/gid's
|
@@ -48,6 +48,7 @@ module Zip
|
|
48
48
|
|
49
49
|
def check_name(name)
|
50
50
|
return unless name.start_with?('/')
|
51
|
+
|
51
52
|
raise ::Zip::EntryNameError, "Illegal ZipEntry name '#{name}', name must not start with /"
|
52
53
|
end
|
53
54
|
|
@@ -69,7 +70,15 @@ module Zip
|
|
69
70
|
@time = args[8] || ::Zip::DOSTime.now
|
70
71
|
|
71
72
|
@ftype = name_is_directory? ? :directory : :file
|
72
|
-
@extra = ::Zip::ExtraField.new(@extra.to_s) unless @extra.
|
73
|
+
@extra = ::Zip::ExtraField.new(@extra.to_s) unless @extra.kind_of?(::Zip::ExtraField)
|
74
|
+
end
|
75
|
+
|
76
|
+
def encrypted?
|
77
|
+
gp_flags & 1 == 1
|
78
|
+
end
|
79
|
+
|
80
|
+
def incomplete?
|
81
|
+
gp_flags & 8 == 8
|
73
82
|
end
|
74
83
|
|
75
84
|
def time
|
@@ -91,11 +100,12 @@ module Zip
|
|
91
100
|
@extra.create('UniversalTime')
|
92
101
|
end
|
93
102
|
(@extra['UniversalTime'] || @extra['NTFS']).mtime = value
|
94
|
-
@time
|
103
|
+
@time = value
|
95
104
|
end
|
96
105
|
|
97
106
|
def file_type_is?(type)
|
98
107
|
raise InternalError, "current filetype is unknown: #{inspect}" unless @ftype
|
108
|
+
|
99
109
|
@ftype == type
|
100
110
|
end
|
101
111
|
|
@@ -116,6 +126,7 @@ module Zip
|
|
116
126
|
def name_safe?
|
117
127
|
cleanpath = Pathname.new(@name).cleanpath
|
118
128
|
return false unless cleanpath.relative?
|
129
|
+
|
119
130
|
root = ::File::SEPARATOR
|
120
131
|
naive_expanded_path = ::File.join(root, cleanpath.to_s)
|
121
132
|
::File.absolute_path(cleanpath.to_s, root) == naive_expanded_path
|
@@ -145,6 +156,7 @@ module Zip
|
|
145
156
|
# that we didn't change the header size (and thus clobber file data or something)
|
146
157
|
def verify_local_header_size!
|
147
158
|
return if @local_header_size.nil?
|
159
|
+
|
148
160
|
new_size = calculate_local_header_size
|
149
161
|
raise Error, "local header size changed (#{@local_header_size} -> #{new_size})" if @local_header_size != new_size
|
150
162
|
end
|
@@ -163,19 +175,16 @@ module Zip
|
|
163
175
|
# is passed.
|
164
176
|
def extract(dest_path = nil, &block)
|
165
177
|
if dest_path.nil? && !name_safe?
|
166
|
-
|
178
|
+
warn "WARNING: skipped '#{@name}' as unsafe."
|
167
179
|
return self
|
168
180
|
end
|
169
181
|
|
170
182
|
dest_path ||= @name
|
171
183
|
block ||= proc { ::Zip.on_exists_proc }
|
172
184
|
|
173
|
-
|
174
|
-
__send__("create_#{@ftype}", dest_path, &block)
|
175
|
-
else
|
176
|
-
raise "unknown file type #{inspect}"
|
177
|
-
end
|
185
|
+
raise "unknown file type #{inspect}" unless directory? || file? || symlink?
|
178
186
|
|
187
|
+
__send__("create_#{@ftype}", dest_path, &block)
|
179
188
|
self
|
180
189
|
end
|
181
190
|
|
@@ -185,15 +194,15 @@ module Zip
|
|
185
194
|
|
186
195
|
class << self
|
187
196
|
def read_zip_short(io) # :nodoc:
|
188
|
-
io.read(2).
|
197
|
+
io.read(2).unpack1('v')
|
189
198
|
end
|
190
199
|
|
191
200
|
def read_zip_long(io) # :nodoc:
|
192
|
-
io.read(4).
|
201
|
+
io.read(4).unpack1('V')
|
193
202
|
end
|
194
203
|
|
195
204
|
def read_zip_64_long(io) # :nodoc:
|
196
|
-
io.read(8).
|
205
|
+
io.read(8).unpack1('Q<')
|
197
206
|
end
|
198
207
|
|
199
208
|
def read_c_dir_entry(io) #:nodoc:all
|
@@ -218,8 +227,6 @@ module Zip
|
|
218
227
|
end
|
219
228
|
end
|
220
229
|
|
221
|
-
public
|
222
|
-
|
223
230
|
def unpack_local_entry(buf)
|
224
231
|
@header_signature,
|
225
232
|
@version,
|
@@ -249,6 +256,7 @@ module Zip
|
|
249
256
|
unless @header_signature == ::Zip::LOCAL_ENTRY_SIGNATURE
|
250
257
|
raise ::Zip::Error, "Zip local header magic not found at location '#{local_header_offset}'"
|
251
258
|
end
|
259
|
+
|
252
260
|
set_time(@last_mod_date, @last_mod_time)
|
253
261
|
|
254
262
|
@name = io.read(@name_length)
|
@@ -261,13 +269,14 @@ module Zip
|
|
261
269
|
|
262
270
|
if extra && extra.bytesize != @extra_length
|
263
271
|
raise ::Zip::Error, 'Truncated local zip entry header'
|
272
|
+
end
|
273
|
+
|
274
|
+
if @extra.kind_of?(::Zip::ExtraField)
|
275
|
+
@extra.merge(extra) if extra
|
264
276
|
else
|
265
|
-
|
266
|
-
@extra.merge(extra) if extra
|
267
|
-
else
|
268
|
-
@extra = ::Zip::ExtraField.new(extra)
|
269
|
-
end
|
277
|
+
@extra = ::Zip::ExtraField.new(extra)
|
270
278
|
end
|
279
|
+
|
271
280
|
parse_zip64_extra(true)
|
272
281
|
@local_header_size = calculate_local_header_size
|
273
282
|
end
|
@@ -354,21 +363,24 @@ module Zip
|
|
354
363
|
|
355
364
|
def check_c_dir_entry_static_header_length(buf)
|
356
365
|
return if buf.bytesize == ::Zip::CDIR_ENTRY_STATIC_HEADER_LENGTH
|
366
|
+
|
357
367
|
raise Error, 'Premature end of file. Not enough data for zip cdir entry header'
|
358
368
|
end
|
359
369
|
|
360
370
|
def check_c_dir_entry_signature
|
361
371
|
return if header_signature == ::Zip::CENTRAL_DIRECTORY_ENTRY_SIGNATURE
|
372
|
+
|
362
373
|
raise Error, "Zip local header magic not found at location '#{local_header_offset}'"
|
363
374
|
end
|
364
375
|
|
365
376
|
def check_c_dir_entry_comment_size
|
366
377
|
return if @comment && @comment.bytesize == @comment_length
|
378
|
+
|
367
379
|
raise ::Zip::Error, 'Truncated cdir zip entry header'
|
368
380
|
end
|
369
381
|
|
370
382
|
def read_c_dir_extra_field(io)
|
371
|
-
if @extra.
|
383
|
+
if @extra.kind_of?(::Zip::ExtraField)
|
372
384
|
@extra.merge(io.read(@extra_length))
|
373
385
|
else
|
374
386
|
@extra = ::Zip::ExtraField.new(io.read(@extra_length))
|
@@ -402,20 +414,25 @@ module Zip
|
|
402
414
|
|
403
415
|
def get_extra_attributes_from_path(path) # :nodoc:
|
404
416
|
return if Zip::RUNNING_ON_WINDOWS
|
417
|
+
|
405
418
|
stat = file_stat(path)
|
406
419
|
@unix_uid = stat.uid
|
407
420
|
@unix_gid = stat.gid
|
408
421
|
@unix_perms = stat.mode & 0o7777
|
422
|
+
@time = ::Zip::DOSTime.from_time(stat.mtime)
|
409
423
|
end
|
410
424
|
|
411
|
-
def
|
412
|
-
# BUG: does not update timestamps into account
|
425
|
+
def set_unix_attributes_on_path(dest_path)
|
413
426
|
# ignore setuid/setgid bits by default. honor if @restore_ownership
|
414
427
|
unix_perms_mask = 0o1777
|
415
428
|
unix_perms_mask = 0o7777 if @restore_ownership
|
416
429
|
::FileUtils.chmod(@unix_perms & unix_perms_mask, dest_path) if @restore_permissions && @unix_perms
|
417
430
|
::FileUtils.chown(@unix_uid, @unix_gid, dest_path) if @restore_ownership && @unix_uid && @unix_gid && ::Process.egid == 0
|
418
|
-
|
431
|
+
|
432
|
+
# Restore the timestamp on a file. This will either have come from the
|
433
|
+
# original source file that was copied into the archive, or from the
|
434
|
+
# creation date of the archive if there was no original source file.
|
435
|
+
::FileUtils.touch(dest_path, mtime: time) if @restore_times
|
419
436
|
end
|
420
437
|
|
421
438
|
def set_extra_attributes_on_path(dest_path) # :nodoc:
|
@@ -423,7 +440,7 @@ module Zip
|
|
423
440
|
|
424
441
|
case @fstype
|
425
442
|
when ::Zip::FSTYPE_UNIX
|
426
|
-
|
443
|
+
set_unix_attributes_on_path(dest_path)
|
427
444
|
end
|
428
445
|
end
|
429
446
|
|
@@ -484,6 +501,7 @@ module Zip
|
|
484
501
|
|
485
502
|
def ==(other)
|
486
503
|
return false unless other.class == self.class
|
504
|
+
|
487
505
|
# Compares contents of local entry and exposed fields
|
488
506
|
keys_equal = %w[compression_method crc compressed_size size name extra filepath].all? do |k|
|
489
507
|
other.__send__(k.to_sym) == __send__(k.to_sym)
|
@@ -591,7 +609,7 @@ module Zip
|
|
591
609
|
def set_time(binary_dos_date, binary_dos_time)
|
592
610
|
@time = ::Zip::DOSTime.parse_binary_dos_format(binary_dos_date, binary_dos_time)
|
593
611
|
rescue ArgumentError
|
594
|
-
warn '
|
612
|
+
warn 'WARNING: invalid date/time in zip entry.' if ::Zip.warn_invalid_date
|
595
613
|
end
|
596
614
|
|
597
615
|
def create_file(dest_path, _continue_on_exists_proc = proc { Zip.continue_on_exists_proc })
|
@@ -601,30 +619,29 @@ module Zip
|
|
601
619
|
end
|
602
620
|
::File.open(dest_path, 'wb') do |os|
|
603
621
|
get_input_stream do |is|
|
604
|
-
set_extra_attributes_on_path(dest_path)
|
605
|
-
|
606
622
|
bytes_written = 0
|
607
623
|
warned = false
|
608
|
-
buf = ''
|
624
|
+
buf = +''
|
609
625
|
while (buf = is.sysread(::Zip::Decompressor::CHUNK_SIZE, buf))
|
610
626
|
os << buf
|
611
627
|
bytes_written += buf.bytesize
|
612
|
-
|
613
|
-
|
614
|
-
|
615
|
-
|
616
|
-
|
617
|
-
|
618
|
-
|
619
|
-
end
|
620
|
-
end
|
628
|
+
next unless bytes_written > size && !warned
|
629
|
+
|
630
|
+
message = "entry '#{name}' should be #{size}B, but is larger when inflated."
|
631
|
+
raise ::Zip::EntrySizeError, message if ::Zip.validate_entry_sizes
|
632
|
+
|
633
|
+
warn "WARNING: #{message}"
|
634
|
+
warned = true
|
621
635
|
end
|
622
636
|
end
|
623
637
|
end
|
638
|
+
|
639
|
+
set_extra_attributes_on_path(dest_path)
|
624
640
|
end
|
625
641
|
|
626
642
|
def create_directory(dest_path)
|
627
643
|
return if ::File.directory?(dest_path)
|
644
|
+
|
628
645
|
if ::File.exist?(dest_path)
|
629
646
|
if block_given? && yield(self, dest_path)
|
630
647
|
::FileUtils.rm_f dest_path
|
@@ -642,13 +659,14 @@ module Zip
|
|
642
659
|
def create_symlink(dest_path)
|
643
660
|
# TODO: Symlinks pose security challenges. Symlink support temporarily
|
644
661
|
# removed in view of https://github.com/rubyzip/rubyzip/issues/369 .
|
645
|
-
|
662
|
+
warn "WARNING: skipped symlink '#{dest_path}'."
|
646
663
|
end
|
647
664
|
|
648
665
|
# apply missing data from the zip64 extra information field, if present
|
649
666
|
# (required when file sizes exceed 2**32, but can be used for all files)
|
650
667
|
def parse_zip64_extra(for_local_header) #:nodoc:all
|
651
668
|
return if @extra['Zip64'].nil?
|
669
|
+
|
652
670
|
if for_local_header
|
653
671
|
@size, @compressed_size = @extra['Zip64'].parse(@size, @compressed_size)
|
654
672
|
else
|
@@ -663,6 +681,7 @@ module Zip
|
|
663
681
|
# create a zip64 extra information field if we need one
|
664
682
|
def prep_zip64_extra(for_local_header) #:nodoc:all
|
665
683
|
return unless ::Zip.write_zip64_support
|
684
|
+
|
666
685
|
need_zip64 = @size >= 0xFFFFFFFF || @compressed_size >= 0xFFFFFFFF
|
667
686
|
need_zip64 ||= @local_header_offset >= 0xFFFFFFFF unless for_local_header
|
668
687
|
if need_zip64
|