rubyzip 1.2.0 → 2.3.0
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 +5 -5
- data/README.md +89 -43
- data/Rakefile +3 -0
- data/lib/zip.rb +14 -3
- data/lib/zip/central_directory.rb +12 -8
- data/lib/zip/compressor.rb +1 -2
- data/lib/zip/constants.rb +55 -3
- data/lib/zip/crypto/decrypted_io.rb +40 -0
- data/lib/zip/crypto/null_encryption.rb +2 -4
- data/lib/zip/crypto/traditional_encryption.rb +9 -9
- data/lib/zip/decompressor.rb +20 -2
- data/lib/zip/dos_time.rb +13 -8
- data/lib/zip/entry.rb +115 -80
- data/lib/zip/entry_set.rb +6 -4
- data/lib/zip/errors.rb +2 -0
- data/lib/zip/extra_field.rb +12 -10
- 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/extra_field/zip64_placeholder.rb +1 -2
- data/lib/zip/file.rb +136 -100
- data/lib/zip/filesystem.rb +199 -179
- data/lib/zip/inflater.rb +24 -36
- data/lib/zip/input_stream.rb +37 -27
- data/lib/zip/ioextras.rb +1 -1
- data/lib/zip/ioextras/abstract_input_stream.rb +20 -9
- data/lib/zip/ioextras/abstract_output_stream.rb +4 -4
- data/lib/zip/null_decompressor.rb +1 -9
- data/lib/zip/output_stream.rb +19 -10
- data/lib/zip/pass_thru_compressor.rb +2 -2
- data/lib/zip/pass_thru_decompressor.rb +14 -23
- 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/example_recursive.rb +15 -18
- data/samples/gtk_ruby_zip.rb +20 -20
- data/samples/qtzip.rb +7 -7
- data/samples/write_simple.rb +2 -4
- data/samples/zipfind.rb +23 -22
- metadata +41 -130
- data/test/basic_zip_file_test.rb +0 -60
- data/test/case_sensitivity_test.rb +0 -69
- data/test/central_directory_entry_test.rb +0 -69
- data/test/central_directory_test.rb +0 -100
- data/test/crypto/null_encryption_test.rb +0 -53
- data/test/crypto/traditional_encryption_test.rb +0 -80
- data/test/data/WarnInvalidDate.zip +0 -0
- data/test/data/file1.txt +0 -46
- data/test/data/file1.txt.deflatedData +0 -0
- data/test/data/file2.txt +0 -1504
- data/test/data/globTest.zip +0 -0
- data/test/data/globTest/foo.txt +0 -0
- data/test/data/globTest/foo/bar/baz/foo.txt +0 -0
- data/test/data/globTest/food.txt +0 -0
- data/test/data/mimetype +0 -1
- data/test/data/notzippedruby.rb +0 -7
- data/test/data/ntfs.zip +0 -0
- data/test/data/oddExtraField.zip +0 -0
- data/test/data/rubycode.zip +0 -0
- data/test/data/rubycode2.zip +0 -0
- data/test/data/test.xls +0 -0
- data/test/data/testDirectory.bin +0 -0
- data/test/data/zip64-sample.zip +0 -0
- data/test/data/zipWithDirs.zip +0 -0
- data/test/data/zipWithEncryption.zip +0 -0
- data/test/deflater_test.rb +0 -65
- data/test/encryption_test.rb +0 -42
- data/test/entry_set_test.rb +0 -152
- data/test/entry_test.rb +0 -163
- data/test/errors_test.rb +0 -34
- data/test/extra_field_test.rb +0 -76
- data/test/file_extract_directory_test.rb +0 -54
- data/test/file_extract_test.rb +0 -83
- data/test/file_permissions_test.rb +0 -69
- data/test/file_split_test.rb +0 -57
- data/test/file_test.rb +0 -563
- data/test/filesystem/dir_iterator_test.rb +0 -58
- data/test/filesystem/directory_test.rb +0 -121
- data/test/filesystem/file_mutating_test.rb +0 -88
- data/test/filesystem/file_nonmutating_test.rb +0 -508
- data/test/filesystem/file_stat_test.rb +0 -64
- data/test/gentestfiles.rb +0 -122
- data/test/inflater_test.rb +0 -14
- data/test/input_stream_test.rb +0 -182
- data/test/ioextras/abstract_input_stream_test.rb +0 -102
- data/test/ioextras/abstract_output_stream_test.rb +0 -106
- data/test/ioextras/fake_io_test.rb +0 -18
- data/test/local_entry_test.rb +0 -154
- data/test/output_stream_test.rb +0 -128
- data/test/pass_thru_compressor_test.rb +0 -30
- data/test/pass_thru_decompressor_test.rb +0 -14
- data/test/samples/example_recursive_test.rb +0 -37
- data/test/settings_test.rb +0 -95
- data/test/test_helper.rb +0 -221
- data/test/unicode_file_names_and_comments_test.rb +0 -50
- data/test/zip64_full_test.rb +0 -51
- data/test/zip64_support_test.rb +0 -14
@@ -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
|
-
class Decompressor
|
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
@@ -19,7 +19,7 @@ module Zip
|
|
19
19
|
end
|
20
20
|
|
21
21
|
def to_binary_dos_date
|
22
|
-
|
22
|
+
day +
|
23
23
|
(month << 5) +
|
24
24
|
((year - 1980) << 9)
|
25
25
|
end
|
@@ -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
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'pathname'
|
1
2
|
module Zip
|
2
3
|
class Entry
|
3
4
|
STORED = 0
|
@@ -7,6 +8,7 @@ module Zip
|
|
7
8
|
|
8
9
|
attr_accessor :comment, :compressed_size, :crc, :extra, :compression_method,
|
9
10
|
:name, :size, :local_header_offset, :zipfile, :fstype, :external_file_attributes,
|
11
|
+
:internal_file_attributes,
|
10
12
|
:gp_flags, :header_signature, :follow_symlinks,
|
11
13
|
:restore_times, :restore_permissions, :restore_ownership,
|
12
14
|
:unix_uid, :unix_gid, :unix_perms,
|
@@ -32,7 +34,7 @@ module Zip
|
|
32
34
|
end
|
33
35
|
@follow_symlinks = false
|
34
36
|
|
35
|
-
@restore_times =
|
37
|
+
@restore_times = false
|
36
38
|
@restore_permissions = false
|
37
39
|
@restore_ownership = false
|
38
40
|
# BUG: need an extra field to support uid/gid's
|
@@ -46,6 +48,7 @@ module Zip
|
|
46
48
|
|
47
49
|
def check_name(name)
|
48
50
|
return unless name.start_with?('/')
|
51
|
+
|
49
52
|
raise ::Zip::EntryNameError, "Illegal ZipEntry name '#{name}', name must not start with /"
|
50
53
|
end
|
51
54
|
|
@@ -67,7 +70,15 @@ module Zip
|
|
67
70
|
@time = args[8] || ::Zip::DOSTime.now
|
68
71
|
|
69
72
|
@ftype = name_is_directory? ? :directory : :file
|
70
|
-
@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
|
71
82
|
end
|
72
83
|
|
73
84
|
def time
|
@@ -89,16 +100,17 @@ module Zip
|
|
89
100
|
@extra.create('UniversalTime')
|
90
101
|
end
|
91
102
|
(@extra['UniversalTime'] || @extra['NTFS']).mtime = value
|
92
|
-
@time
|
103
|
+
@time = value
|
93
104
|
end
|
94
105
|
|
95
106
|
def file_type_is?(type)
|
96
107
|
raise InternalError, "current filetype is unknown: #{inspect}" unless @ftype
|
108
|
+
|
97
109
|
@ftype == type
|
98
110
|
end
|
99
111
|
|
100
112
|
# Dynamic checkers
|
101
|
-
%w
|
113
|
+
%w[directory file symlink].each do |k|
|
102
114
|
define_method "#{k}?" do
|
103
115
|
file_type_is?(k.to_sym)
|
104
116
|
end
|
@@ -108,6 +120,18 @@ module Zip
|
|
108
120
|
@name.end_with?('/')
|
109
121
|
end
|
110
122
|
|
123
|
+
# Is the name a relative path, free of `..` patterns that could lead to
|
124
|
+
# path traversal attacks? This does NOT handle symlinks; if the path
|
125
|
+
# contains symlinks, this check is NOT enough to guarantee safety.
|
126
|
+
def name_safe?
|
127
|
+
cleanpath = Pathname.new(@name).cleanpath
|
128
|
+
return false unless cleanpath.relative?
|
129
|
+
|
130
|
+
root = ::File::SEPARATOR
|
131
|
+
naive_expanded_path = ::File.join(root, cleanpath.to_s)
|
132
|
+
::File.absolute_path(cleanpath.to_s, root) == naive_expanded_path
|
133
|
+
end
|
134
|
+
|
111
135
|
def local_entry_offset #:nodoc:all
|
112
136
|
local_header_offset + @local_header_size
|
113
137
|
end
|
@@ -132,6 +156,7 @@ module Zip
|
|
132
156
|
# that we didn't change the header size (and thus clobber file data or something)
|
133
157
|
def verify_local_header_size!
|
134
158
|
return if @local_header_size.nil?
|
159
|
+
|
135
160
|
new_size = calculate_local_header_size
|
136
161
|
raise Error, "local header size changed (#{@local_header_size} -> #{new_size})" if @local_header_size != new_size
|
137
162
|
end
|
@@ -146,15 +171,20 @@ module Zip
|
|
146
171
|
end
|
147
172
|
|
148
173
|
# Extracts entry to file dest_path (defaults to @name).
|
149
|
-
|
174
|
+
# NB: The caller is responsible for making sure dest_path is safe, if it
|
175
|
+
# is passed.
|
176
|
+
def extract(dest_path = nil, &block)
|
177
|
+
if dest_path.nil? && !name_safe?
|
178
|
+
warn "WARNING: skipped '#{@name}' as unsafe."
|
179
|
+
return self
|
180
|
+
end
|
181
|
+
|
182
|
+
dest_path ||= @name
|
150
183
|
block ||= proc { ::Zip.on_exists_proc }
|
151
184
|
|
152
|
-
|
153
|
-
__send__("create_#{@ftype}", dest_path, &block)
|
154
|
-
else
|
155
|
-
raise "unknown file type #{inspect}"
|
156
|
-
end
|
185
|
+
raise "unknown file type #{inspect}" unless directory? || file? || symlink?
|
157
186
|
|
187
|
+
__send__("create_#{@ftype}", dest_path, &block)
|
158
188
|
self
|
159
189
|
end
|
160
190
|
|
@@ -164,15 +194,15 @@ module Zip
|
|
164
194
|
|
165
195
|
class << self
|
166
196
|
def read_zip_short(io) # :nodoc:
|
167
|
-
io.read(2).
|
197
|
+
io.read(2).unpack1('v')
|
168
198
|
end
|
169
199
|
|
170
200
|
def read_zip_long(io) # :nodoc:
|
171
|
-
io.read(4).
|
201
|
+
io.read(4).unpack1('V')
|
172
202
|
end
|
173
203
|
|
174
204
|
def read_zip_64_long(io) # :nodoc:
|
175
|
-
io.read(8).
|
205
|
+
io.read(8).unpack1('Q<')
|
176
206
|
end
|
177
207
|
|
178
208
|
def read_c_dir_entry(io) #:nodoc:all
|
@@ -197,8 +227,6 @@ module Zip
|
|
197
227
|
end
|
198
228
|
end
|
199
229
|
|
200
|
-
public
|
201
|
-
|
202
230
|
def unpack_local_entry(buf)
|
203
231
|
@header_signature,
|
204
232
|
@version,
|
@@ -228,22 +256,27 @@ module Zip
|
|
228
256
|
unless @header_signature == ::Zip::LOCAL_ENTRY_SIGNATURE
|
229
257
|
raise ::Zip::Error, "Zip local header magic not found at location '#{local_header_offset}'"
|
230
258
|
end
|
259
|
+
|
231
260
|
set_time(@last_mod_date, @last_mod_time)
|
232
261
|
|
233
262
|
@name = io.read(@name_length)
|
234
263
|
extra = io.read(@extra_length)
|
235
264
|
|
236
|
-
@name.
|
265
|
+
@name.tr!('\\', '/')
|
266
|
+
if ::Zip.force_entry_names_encoding
|
267
|
+
@name.force_encoding(::Zip.force_entry_names_encoding)
|
268
|
+
end
|
237
269
|
|
238
270
|
if extra && extra.bytesize != @extra_length
|
239
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
|
240
276
|
else
|
241
|
-
|
242
|
-
@extra.merge(extra) if extra
|
243
|
-
else
|
244
|
-
@extra = ::Zip::ExtraField.new(extra)
|
245
|
-
end
|
277
|
+
@extra = ::Zip::ExtraField.new(extra)
|
246
278
|
end
|
279
|
+
|
247
280
|
parse_zip64_extra(true)
|
248
281
|
@local_header_size = calculate_local_header_size
|
249
282
|
end
|
@@ -252,13 +285,13 @@ module Zip
|
|
252
285
|
zip64 = @extra['Zip64']
|
253
286
|
[::Zip::LOCAL_ENTRY_SIGNATURE,
|
254
287
|
@version_needed_to_extract, # version needed to extract
|
255
|
-
@gp_flags, # @gp_flags
|
288
|
+
@gp_flags, # @gp_flags
|
256
289
|
@compression_method,
|
257
|
-
@time.to_binary_dos_time, # @last_mod_time
|
258
|
-
@time.to_binary_dos_date, # @last_mod_date
|
290
|
+
@time.to_binary_dos_time, # @last_mod_time
|
291
|
+
@time.to_binary_dos_date, # @last_mod_date
|
259
292
|
@crc,
|
260
|
-
|
261
|
-
|
293
|
+
zip64 && zip64.compressed_size ? 0xFFFFFFFF : @compressed_size,
|
294
|
+
zip64 && zip64.original_size ? 0xFFFFFFFF : @size,
|
262
295
|
name_size,
|
263
296
|
@extra ? @extra.local_size : 0].pack('VvvvvvVVVvv')
|
264
297
|
end
|
@@ -302,7 +335,7 @@ module Zip
|
|
302
335
|
def set_ftype_from_c_dir_entry
|
303
336
|
@ftype = case @fstype
|
304
337
|
when ::Zip::FSTYPE_UNIX
|
305
|
-
@unix_perms = (@external_file_attributes >> 16) &
|
338
|
+
@unix_perms = (@external_file_attributes >> 16) & 0o7777
|
306
339
|
case (@external_file_attributes >> 28)
|
307
340
|
when ::Zip::FILE_TYPE_DIR
|
308
341
|
:directory
|
@@ -330,21 +363,24 @@ module Zip
|
|
330
363
|
|
331
364
|
def check_c_dir_entry_static_header_length(buf)
|
332
365
|
return if buf.bytesize == ::Zip::CDIR_ENTRY_STATIC_HEADER_LENGTH
|
366
|
+
|
333
367
|
raise Error, 'Premature end of file. Not enough data for zip cdir entry header'
|
334
368
|
end
|
335
369
|
|
336
370
|
def check_c_dir_entry_signature
|
337
371
|
return if header_signature == ::Zip::CENTRAL_DIRECTORY_ENTRY_SIGNATURE
|
372
|
+
|
338
373
|
raise Error, "Zip local header magic not found at location '#{local_header_offset}'"
|
339
374
|
end
|
340
375
|
|
341
376
|
def check_c_dir_entry_comment_size
|
342
377
|
return if @comment && @comment.bytesize == @comment_length
|
378
|
+
|
343
379
|
raise ::Zip::Error, 'Truncated cdir zip entry header'
|
344
380
|
end
|
345
381
|
|
346
382
|
def read_c_dir_extra_field(io)
|
347
|
-
if @extra.
|
383
|
+
if @extra.kind_of?(::Zip::ExtraField)
|
348
384
|
@extra.merge(io.read(@extra_length))
|
349
385
|
else
|
350
386
|
@extra = ::Zip::ExtraField.new(io.read(@extra_length))
|
@@ -357,7 +393,10 @@ module Zip
|
|
357
393
|
unpack_c_dir_entry(static_sized_fields_buf)
|
358
394
|
check_c_dir_entry_signature
|
359
395
|
set_time(@last_mod_date, @last_mod_time)
|
360
|
-
@name = io.read(@name_length)
|
396
|
+
@name = io.read(@name_length)
|
397
|
+
if ::Zip.force_entry_names_encoding
|
398
|
+
@name.force_encoding(::Zip.force_entry_names_encoding)
|
399
|
+
end
|
361
400
|
read_c_dir_extra_field(io)
|
362
401
|
@comment = io.read(@comment_length)
|
363
402
|
check_c_dir_entry_comment_size
|
@@ -375,20 +414,25 @@ module Zip
|
|
375
414
|
|
376
415
|
def get_extra_attributes_from_path(path) # :nodoc:
|
377
416
|
return if Zip::RUNNING_ON_WINDOWS
|
417
|
+
|
378
418
|
stat = file_stat(path)
|
379
419
|
@unix_uid = stat.uid
|
380
420
|
@unix_gid = stat.gid
|
381
|
-
@unix_perms = stat.mode &
|
421
|
+
@unix_perms = stat.mode & 0o7777
|
422
|
+
@time = ::Zip::DOSTime.from_time(stat.mtime)
|
382
423
|
end
|
383
424
|
|
384
|
-
def
|
385
|
-
# BUG: does not update timestamps into account
|
425
|
+
def set_unix_attributes_on_path(dest_path)
|
386
426
|
# ignore setuid/setgid bits by default. honor if @restore_ownership
|
387
|
-
unix_perms_mask =
|
388
|
-
unix_perms_mask =
|
427
|
+
unix_perms_mask = 0o1777
|
428
|
+
unix_perms_mask = 0o7777 if @restore_ownership
|
389
429
|
::FileUtils.chmod(@unix_perms & unix_perms_mask, dest_path) if @restore_permissions && @unix_perms
|
390
430
|
::FileUtils.chown(@unix_uid, @unix_gid, dest_path) if @restore_ownership && @unix_uid && @unix_gid && ::Process.egid == 0
|
391
|
-
|
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
|
392
436
|
end
|
393
437
|
|
394
438
|
def set_extra_attributes_on_path(dest_path) # :nodoc:
|
@@ -396,7 +440,7 @@ module Zip
|
|
396
440
|
|
397
441
|
case @fstype
|
398
442
|
when ::Zip::FSTYPE_UNIX
|
399
|
-
|
443
|
+
set_unix_attributes_on_path(dest_path)
|
400
444
|
end
|
401
445
|
end
|
402
446
|
|
@@ -406,21 +450,21 @@ module Zip
|
|
406
450
|
@header_signature,
|
407
451
|
@version, # version of encoding software
|
408
452
|
@fstype, # filesystem type
|
409
|
-
@version_needed_to_extract, # @versionNeededToExtract
|
410
|
-
@gp_flags, # @gp_flags
|
453
|
+
@version_needed_to_extract, # @versionNeededToExtract
|
454
|
+
@gp_flags, # @gp_flags
|
411
455
|
@compression_method,
|
412
|
-
@time.to_binary_dos_time, # @last_mod_time
|
413
|
-
@time.to_binary_dos_date, # @last_mod_date
|
456
|
+
@time.to_binary_dos_time, # @last_mod_time
|
457
|
+
@time.to_binary_dos_date, # @last_mod_date
|
414
458
|
@crc,
|
415
|
-
|
416
|
-
|
459
|
+
zip64 && zip64.compressed_size ? 0xFFFFFFFF : @compressed_size,
|
460
|
+
zip64 && zip64.original_size ? 0xFFFFFFFF : @size,
|
417
461
|
name_size,
|
418
462
|
@extra ? @extra.c_dir_size : 0,
|
419
463
|
comment_size,
|
420
|
-
|
464
|
+
zip64 && zip64.disk_start_number ? 0xFFFF : 0, # disk number start
|
421
465
|
@internal_file_attributes, # file type (binary=0, text=1)
|
422
466
|
@external_file_attributes, # native filesystem attributes
|
423
|
-
|
467
|
+
zip64 && zip64.relative_header_offset ? 0xFFFFFFFF : @local_header_offset,
|
424
468
|
@name,
|
425
469
|
@extra,
|
426
470
|
@comment
|
@@ -433,18 +477,18 @@ module Zip
|
|
433
477
|
when ::Zip::FSTYPE_UNIX
|
434
478
|
ft = case @ftype
|
435
479
|
when :file
|
436
|
-
@unix_perms ||=
|
480
|
+
@unix_perms ||= 0o644
|
437
481
|
::Zip::FILE_TYPE_FILE
|
438
482
|
when :directory
|
439
|
-
@unix_perms ||=
|
483
|
+
@unix_perms ||= 0o755
|
440
484
|
::Zip::FILE_TYPE_DIR
|
441
485
|
when :symlink
|
442
|
-
@unix_perms ||=
|
486
|
+
@unix_perms ||= 0o755
|
443
487
|
::Zip::FILE_TYPE_SYMLINK
|
444
488
|
end
|
445
489
|
|
446
490
|
unless ft.nil?
|
447
|
-
@external_file_attributes = (ft << 12 | (@unix_perms &
|
491
|
+
@external_file_attributes = (ft << 12 | (@unix_perms & 0o7777)) << 16
|
448
492
|
end
|
449
493
|
end
|
450
494
|
|
@@ -457,8 +501,9 @@ module Zip
|
|
457
501
|
|
458
502
|
def ==(other)
|
459
503
|
return false unless other.class == self.class
|
504
|
+
|
460
505
|
# Compares contents of local entry and exposed fields
|
461
|
-
keys_equal = %w
|
506
|
+
keys_equal = %w[compression_method crc compressed_size size name extra filepath].all? do |k|
|
462
507
|
other.__send__(k.to_sym) == __send__(k.to_sym)
|
463
508
|
end
|
464
509
|
keys_equal && time.dos_equals(other.time)
|
@@ -488,7 +533,7 @@ module Zip
|
|
488
533
|
end
|
489
534
|
else
|
490
535
|
zis = ::Zip::InputStream.new(@zipfile, local_header_offset)
|
491
|
-
zis.instance_variable_set(:@
|
536
|
+
zis.instance_variable_set(:@complete_entry, self)
|
492
537
|
zis.get_next_entry
|
493
538
|
if block_given?
|
494
539
|
begin
|
@@ -564,7 +609,7 @@ module Zip
|
|
564
609
|
def set_time(binary_dos_date, binary_dos_time)
|
565
610
|
@time = ::Zip::DOSTime.parse_binary_dos_format(binary_dos_date, binary_dos_time)
|
566
611
|
rescue ArgumentError
|
567
|
-
warn '
|
612
|
+
warn 'WARNING: invalid date/time in zip entry.' if ::Zip.warn_invalid_date
|
568
613
|
end
|
569
614
|
|
570
615
|
def create_file(dest_path, _continue_on_exists_proc = proc { Zip.continue_on_exists_proc })
|
@@ -574,18 +619,29 @@ module Zip
|
|
574
619
|
end
|
575
620
|
::File.open(dest_path, 'wb') do |os|
|
576
621
|
get_input_stream do |is|
|
577
|
-
|
578
|
-
|
579
|
-
buf = ''
|
622
|
+
bytes_written = 0
|
623
|
+
warned = false
|
624
|
+
buf = +''
|
580
625
|
while (buf = is.sysread(::Zip::Decompressor::CHUNK_SIZE, buf))
|
581
626
|
os << buf
|
627
|
+
bytes_written += buf.bytesize
|
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
|
582
635
|
end
|
583
636
|
end
|
584
637
|
end
|
638
|
+
|
639
|
+
set_extra_attributes_on_path(dest_path)
|
585
640
|
end
|
586
641
|
|
587
642
|
def create_directory(dest_path)
|
588
643
|
return if ::File.directory?(dest_path)
|
644
|
+
|
589
645
|
if ::File.exist?(dest_path)
|
590
646
|
if block_given? && yield(self, dest_path)
|
591
647
|
::FileUtils.rm_f dest_path
|
@@ -601,38 +657,16 @@ module Zip
|
|
601
657
|
|
602
658
|
# BUG: create_symlink() does not use &block
|
603
659
|
def create_symlink(dest_path)
|
604
|
-
|
605
|
-
|
606
|
-
|
607
|
-
rescue Errno::ENOENT
|
608
|
-
end
|
609
|
-
|
610
|
-
io = get_input_stream
|
611
|
-
linkto = io.read
|
612
|
-
|
613
|
-
if stat
|
614
|
-
if stat.symlink?
|
615
|
-
if ::File.readlink(dest_path) == linkto
|
616
|
-
return
|
617
|
-
else
|
618
|
-
raise ::Zip::DestinationFileExistsError,
|
619
|
-
"Cannot create symlink '#{dest_path}'. " \
|
620
|
-
'A symlink already exists with that name'
|
621
|
-
end
|
622
|
-
else
|
623
|
-
raise ::Zip::DestinationFileExistsError,
|
624
|
-
"Cannot create symlink '#{dest_path}'. " \
|
625
|
-
'A file already exists with that name'
|
626
|
-
end
|
627
|
-
end
|
628
|
-
|
629
|
-
::File.symlink(linkto, dest_path)
|
660
|
+
# TODO: Symlinks pose security challenges. Symlink support temporarily
|
661
|
+
# removed in view of https://github.com/rubyzip/rubyzip/issues/369 .
|
662
|
+
warn "WARNING: skipped symlink '#{dest_path}'."
|
630
663
|
end
|
631
664
|
|
632
665
|
# apply missing data from the zip64 extra information field, if present
|
633
666
|
# (required when file sizes exceed 2**32, but can be used for all files)
|
634
667
|
def parse_zip64_extra(for_local_header) #:nodoc:all
|
635
668
|
return if @extra['Zip64'].nil?
|
669
|
+
|
636
670
|
if for_local_header
|
637
671
|
@size, @compressed_size = @extra['Zip64'].parse(@size, @compressed_size)
|
638
672
|
else
|
@@ -647,6 +681,7 @@ module Zip
|
|
647
681
|
# create a zip64 extra information field if we need one
|
648
682
|
def prep_zip64_extra(for_local_header) #:nodoc:all
|
649
683
|
return unless ::Zip.write_zip64_support
|
684
|
+
|
650
685
|
need_zip64 = @size >= 0xFFFFFFFF || @compressed_size >= 0xFFFFFFFF
|
651
686
|
need_zip64 ||= @local_header_offset >= 0xFFFFFFFF unless for_local_header
|
652
687
|
if need_zip64
|