rubyzip 1.0.0 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of rubyzip might be problematic. Click here for more details.
- checksums.yaml +6 -14
- data/README.md +173 -42
- data/Rakefile +10 -5
- data/TODO +0 -1
- data/lib/zip/central_directory.rb +55 -24
- data/lib/zip/compressor.rb +0 -0
- data/lib/zip/constants.rb +4 -2
- data/lib/zip/crypto/encryption.rb +11 -0
- data/lib/zip/crypto/null_encryption.rb +45 -0
- data/lib/zip/crypto/traditional_encryption.rb +99 -0
- data/lib/zip/decompressor.rb +2 -2
- data/lib/zip/deflater.rb +11 -6
- data/lib/zip/dos_time.rb +4 -5
- data/lib/zip/entry.rb +159 -97
- data/lib/zip/entry_set.rb +18 -18
- data/lib/zip/errors.rb +15 -6
- data/lib/zip/extra_field/generic.rb +8 -8
- data/lib/zip/extra_field/ntfs.rb +90 -0
- data/lib/zip/extra_field/old_unix.rb +44 -0
- data/lib/zip/extra_field/universal_time.rb +14 -14
- data/lib/zip/extra_field/unix.rb +8 -9
- data/lib/zip/extra_field/zip64.rb +44 -6
- data/lib/zip/extra_field/zip64_placeholder.rb +16 -0
- data/lib/zip/extra_field.rb +20 -8
- data/lib/zip/file.rb +126 -114
- data/lib/zip/filesystem.rb +140 -139
- data/lib/zip/inflater.rb +10 -9
- data/lib/zip/input_stream.rb +105 -80
- data/lib/zip/ioextras/abstract_input_stream.rb +15 -12
- data/lib/zip/ioextras/abstract_output_stream.rb +0 -2
- data/lib/zip/ioextras.rb +1 -3
- data/lib/zip/null_compressor.rb +2 -2
- data/lib/zip/null_decompressor.rb +4 -4
- data/lib/zip/null_input_stream.rb +2 -1
- data/lib/zip/output_stream.rb +57 -43
- data/lib/zip/pass_thru_compressor.rb +4 -4
- data/lib/zip/pass_thru_decompressor.rb +4 -5
- data/lib/zip/streamable_directory.rb +2 -2
- data/lib/zip/streamable_stream.rb +22 -13
- data/lib/zip/version.rb +1 -1
- data/lib/zip.rb +11 -2
- data/samples/example.rb +30 -40
- data/samples/example_filesystem.rb +16 -18
- data/samples/example_recursive.rb +35 -27
- data/samples/{gtkRubyzip.rb → gtk_ruby_zip.rb} +25 -27
- data/samples/qtzip.rb +19 -28
- data/samples/write_simple.rb +12 -13
- data/samples/zipfind.rb +29 -37
- data/test/basic_zip_file_test.rb +60 -0
- data/test/case_sensitivity_test.rb +69 -0
- data/test/central_directory_entry_test.rb +69 -0
- data/test/central_directory_test.rb +100 -0
- data/test/crypto/null_encryption_test.rb +53 -0
- data/test/crypto/traditional_encryption_test.rb +80 -0
- data/test/data/WarnInvalidDate.zip +0 -0
- data/test/data/file1.txt +46 -0
- data/test/data/file1.txt.deflatedData +0 -0
- data/test/data/file2.txt +1504 -0
- data/test/data/globTest/foo/bar/baz/foo.txt +0 -0
- data/test/data/globTest/foo.txt +0 -0
- data/test/data/globTest/food.txt +0 -0
- data/test/data/globTest.zip +0 -0
- data/test/data/mimetype +1 -0
- data/test/data/notzippedruby.rb +7 -0
- 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 +65 -0
- data/test/encryption_test.rb +42 -0
- data/test/entry_set_test.rb +152 -0
- data/test/entry_test.rb +163 -0
- data/test/errors_test.rb +34 -0
- data/test/extra_field_test.rb +76 -0
- data/test/file_extract_directory_test.rb +54 -0
- data/test/file_extract_test.rb +83 -0
- data/test/file_permissions_test.rb +69 -0
- data/test/file_split_test.rb +57 -0
- data/test/file_test.rb +563 -0
- data/test/filesystem/dir_iterator_test.rb +58 -0
- data/test/filesystem/directory_test.rb +121 -0
- data/test/filesystem/file_mutating_test.rb +88 -0
- data/test/filesystem/file_nonmutating_test.rb +508 -0
- data/test/filesystem/file_stat_test.rb +64 -0
- data/test/gentestfiles.rb +122 -0
- data/test/inflater_test.rb +14 -0
- data/test/input_stream_test.rb +182 -0
- data/test/ioextras/abstract_input_stream_test.rb +102 -0
- data/test/ioextras/abstract_output_stream_test.rb +106 -0
- data/test/ioextras/fake_io_test.rb +18 -0
- data/test/local_entry_test.rb +154 -0
- data/test/output_stream_test.rb +128 -0
- data/test/pass_thru_compressor_test.rb +30 -0
- data/test/pass_thru_decompressor_test.rb +14 -0
- data/test/samples/example_recursive_test.rb +37 -0
- data/test/settings_test.rb +95 -0
- data/test/test_helper.rb +221 -0
- data/test/unicode_file_names_and_comments_test.rb +50 -0
- data/test/zip64_full_test.rb +51 -0
- data/test/zip64_support_test.rb +14 -0
- metadata +198 -22
- data/NEWS +0 -182
data/lib/zip/deflater.rb
CHANGED
@@ -1,23 +1,28 @@
|
|
1
1
|
module Zip
|
2
2
|
class Deflater < Compressor #:nodoc:all
|
3
|
-
|
4
|
-
def initialize(output_stream, level = ::Zlib::DEFAULT_COMPRESSION)
|
3
|
+
def initialize(output_stream, level = Zip.default_compression, encrypter = NullEncrypter.new)
|
5
4
|
super()
|
6
5
|
@output_stream = output_stream
|
7
6
|
@zlib_deflater = ::Zlib::Deflate.new(level, -::Zlib::MAX_WBITS)
|
8
7
|
@size = 0
|
9
8
|
@crc = ::Zlib.crc32
|
9
|
+
@encrypter = encrypter
|
10
10
|
end
|
11
11
|
|
12
|
-
def <<
|
12
|
+
def <<(data)
|
13
13
|
val = data.to_s
|
14
|
-
@crc = Zlib
|
14
|
+
@crc = Zlib.crc32(val, @crc)
|
15
15
|
@size += val.bytesize
|
16
|
-
|
16
|
+
buffer = @zlib_deflater.deflate(data)
|
17
|
+
if buffer.empty?
|
18
|
+
@output_stream
|
19
|
+
else
|
20
|
+
@output_stream << @encrypter.encrypt(buffer)
|
21
|
+
end
|
17
22
|
end
|
18
23
|
|
19
24
|
def finish
|
20
|
-
@output_stream << @zlib_deflater.finish until @zlib_deflater.finished?
|
25
|
+
@output_stream << @encrypter.encrypt(@zlib_deflater.finish) until @zlib_deflater.finished?
|
21
26
|
end
|
22
27
|
|
23
28
|
attr_reader :size, :crc
|
data/lib/zip/dos_time.rb
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
module Zip
|
2
2
|
class DOSTime < Time #:nodoc:all
|
3
|
-
|
4
|
-
#MS-DOS File Date and Time format as used in Interrupt 21H Function 57H:
|
3
|
+
# MS-DOS File Date and Time format as used in Interrupt 21H Function 57H:
|
5
4
|
|
6
5
|
# Register CX, the Time:
|
7
6
|
# Bits 0-4 2 second increments (0-29)
|
@@ -14,7 +13,7 @@ module Zip
|
|
14
13
|
# bits 9-15 year (four digit year minus 1980)
|
15
14
|
|
16
15
|
def to_binary_dos_time
|
17
|
-
(sec/2) +
|
16
|
+
(sec / 2) +
|
18
17
|
(min << 5) +
|
19
18
|
(hour << 11)
|
20
19
|
end
|
@@ -27,7 +26,7 @@ module Zip
|
|
27
26
|
|
28
27
|
# Dos time is only stored with two seconds accuracy
|
29
28
|
def dos_equals(other)
|
30
|
-
to_i/2 == other.to_i/2
|
29
|
+
to_i / 2 == other.to_i / 2
|
31
30
|
end
|
32
31
|
|
33
32
|
def self.parse_binary_dos_format(binaryDosDate, binaryDosTime)
|
@@ -38,7 +37,7 @@ module Zip
|
|
38
37
|
month = (0b111100000 & binaryDosDate) >> 5
|
39
38
|
year = ((0b1111111000000000 & binaryDosDate) >> 9) + 1980
|
40
39
|
begin
|
41
|
-
|
40
|
+
local(year, month, day, hour, minute, second)
|
42
41
|
end
|
43
42
|
end
|
44
43
|
end
|
data/lib/zip/entry.rb
CHANGED
@@ -15,13 +15,13 @@ module Zip
|
|
15
15
|
|
16
16
|
def set_default_vars_values
|
17
17
|
@local_header_offset = 0
|
18
|
-
@local_header_size =
|
18
|
+
@local_header_size = nil # not known until local entry is created or read
|
19
19
|
@internal_file_attributes = 1
|
20
20
|
@external_file_attributes = 0
|
21
21
|
@header_signature = ::Zip::CENTRAL_DIRECTORY_ENTRY_SIGNATURE
|
22
22
|
|
23
23
|
@version_needed_to_extract = VERSION_NEEDED_TO_EXTRACT
|
24
|
-
@version =
|
24
|
+
@version = VERSION_MADE_BY
|
25
25
|
|
26
26
|
@ftype = nil # unspecified or unknown
|
27
27
|
@filepath = nil
|
@@ -39,15 +39,14 @@ module Zip
|
|
39
39
|
@unix_uid = nil
|
40
40
|
@unix_gid = nil
|
41
41
|
@unix_perms = nil
|
42
|
-
|
43
|
-
|
42
|
+
# @posix_acl = nil
|
43
|
+
# @ntfs_acl = nil
|
44
44
|
@dirty = false
|
45
45
|
end
|
46
46
|
|
47
47
|
def check_name(name)
|
48
|
-
|
49
|
-
|
50
|
-
end
|
48
|
+
return unless name.start_with?('/')
|
49
|
+
raise ::Zip::EntryNameError, "Illegal ZipEntry name '#{name}', name must not start with /"
|
51
50
|
end
|
52
51
|
|
53
52
|
def initialize(*args)
|
@@ -68,12 +67,14 @@ module Zip
|
|
68
67
|
@time = args[8] || ::Zip::DOSTime.now
|
69
68
|
|
70
69
|
@ftype = name_is_directory? ? :directory : :file
|
71
|
-
@extra = ::Zip::ExtraField.new(@extra.to_s) unless ::Zip::ExtraField
|
70
|
+
@extra = ::Zip::ExtraField.new(@extra.to_s) unless @extra.is_a?(::Zip::ExtraField)
|
72
71
|
end
|
73
72
|
|
74
73
|
def time
|
75
74
|
if @extra['UniversalTime']
|
76
75
|
@extra['UniversalTime'].mtime
|
76
|
+
elsif @extra['NTFS']
|
77
|
+
@extra['NTFS'].mtime
|
77
78
|
else
|
78
79
|
# Standard time field in central directory has local time
|
79
80
|
# under archive creator. Then, we can't get timezone.
|
@@ -81,18 +82,18 @@ module Zip
|
|
81
82
|
end
|
82
83
|
end
|
83
84
|
|
84
|
-
alias
|
85
|
+
alias mtime time
|
85
86
|
|
86
87
|
def time=(value)
|
87
|
-
unless @extra.member?('UniversalTime')
|
88
|
+
unless @extra.member?('UniversalTime') || @extra.member?('NTFS')
|
88
89
|
@extra.create('UniversalTime')
|
89
90
|
end
|
90
|
-
@extra['UniversalTime'].mtime = value
|
91
|
+
(@extra['UniversalTime'] || @extra['NTFS']).mtime = value
|
91
92
|
@time = value
|
92
93
|
end
|
93
94
|
|
94
95
|
def file_type_is?(type)
|
95
|
-
raise
|
96
|
+
raise InternalError, "current filetype is unknown: #{inspect}" unless @ftype
|
96
97
|
@ftype == type
|
97
98
|
end
|
98
99
|
|
@@ -124,17 +125,24 @@ module Zip
|
|
124
125
|
end
|
125
126
|
|
126
127
|
def calculate_local_header_size #:nodoc:all
|
127
|
-
fix_zip64_sizes!
|
128
128
|
LOCAL_ENTRY_STATIC_HEADER_LENGTH + name_size + extra_size
|
129
129
|
end
|
130
130
|
|
131
|
+
# check before rewriting an entry (after file sizes are known)
|
132
|
+
# that we didn't change the header size (and thus clobber file data or something)
|
133
|
+
def verify_local_header_size!
|
134
|
+
return if @local_header_size.nil?
|
135
|
+
new_size = calculate_local_header_size
|
136
|
+
raise Error, "local header size changed (#{@local_header_size} -> #{new_size})" if @local_header_size != new_size
|
137
|
+
end
|
138
|
+
|
131
139
|
def cdir_header_size #:nodoc:all
|
132
140
|
CDIR_ENTRY_STATIC_HEADER_LENGTH + name_size +
|
133
141
|
(@extra ? @extra.c_dir_size : 0) + comment_size
|
134
142
|
end
|
135
143
|
|
136
144
|
def next_header_offset #:nodoc:all
|
137
|
-
local_entry_offset +
|
145
|
+
local_entry_offset + compressed_size + data_descriptor_size
|
138
146
|
end
|
139
147
|
|
140
148
|
# Extracts entry to file dest_path (defaults to @name).
|
@@ -142,9 +150,9 @@ module Zip
|
|
142
150
|
block ||= proc { ::Zip.on_exists_proc }
|
143
151
|
|
144
152
|
if directory? || file? || symlink?
|
145
|
-
|
153
|
+
__send__("create_#{@ftype}", dest_path, &block)
|
146
154
|
else
|
147
|
-
raise
|
155
|
+
raise "unknown file type #{inspect}"
|
148
156
|
end
|
149
157
|
|
150
158
|
self
|
@@ -154,8 +162,6 @@ module Zip
|
|
154
162
|
@name
|
155
163
|
end
|
156
164
|
|
157
|
-
protected
|
158
|
-
|
159
165
|
class << self
|
160
166
|
def read_zip_short(io) # :nodoc:
|
161
167
|
io.read(2).unpack('v')[0]
|
@@ -166,25 +172,29 @@ module Zip
|
|
166
172
|
end
|
167
173
|
|
168
174
|
def read_zip_64_long(io) # :nodoc:
|
169
|
-
io.read(8).unpack('
|
175
|
+
io.read(8).unpack('Q<')[0]
|
170
176
|
end
|
171
177
|
|
172
178
|
def read_c_dir_entry(io) #:nodoc:all
|
173
|
-
|
179
|
+
path = if io.respond_to?(:path)
|
180
|
+
io.path
|
181
|
+
else
|
182
|
+
io
|
183
|
+
end
|
184
|
+
entry = new(path)
|
174
185
|
entry.read_c_dir_entry(io)
|
175
186
|
entry
|
176
|
-
rescue
|
187
|
+
rescue Error
|
177
188
|
nil
|
178
189
|
end
|
179
190
|
|
180
191
|
def read_local_entry(io)
|
181
|
-
entry = new(io
|
192
|
+
entry = new(io)
|
182
193
|
entry.read_local_entry(io)
|
183
194
|
entry
|
184
|
-
rescue
|
195
|
+
rescue Error
|
185
196
|
nil
|
186
197
|
end
|
187
|
-
|
188
198
|
end
|
189
199
|
|
190
200
|
public
|
@@ -207,16 +217,16 @@ module Zip
|
|
207
217
|
def read_local_entry(io) #:nodoc:all
|
208
218
|
@local_header_offset = io.tell
|
209
219
|
|
210
|
-
static_sized_fields_buf = io.read(::Zip::LOCAL_ENTRY_STATIC_HEADER_LENGTH)
|
220
|
+
static_sized_fields_buf = io.read(::Zip::LOCAL_ENTRY_STATIC_HEADER_LENGTH) || ''
|
211
221
|
|
212
222
|
unless static_sized_fields_buf.bytesize == ::Zip::LOCAL_ENTRY_STATIC_HEADER_LENGTH
|
213
|
-
raise
|
223
|
+
raise Error, 'Premature end of file. Not enough data for zip entry local header'
|
214
224
|
end
|
215
225
|
|
216
226
|
unpack_local_entry(static_sized_fields_buf)
|
217
227
|
|
218
228
|
unless @header_signature == ::Zip::LOCAL_ENTRY_SIGNATURE
|
219
|
-
raise ::Zip::
|
229
|
+
raise ::Zip::Error, "Zip local header magic not found at location '#{local_header_offset}'"
|
220
230
|
end
|
221
231
|
set_time(@last_mod_date, @last_mod_time)
|
222
232
|
|
@@ -226,18 +236,20 @@ module Zip
|
|
226
236
|
@name.gsub!('\\', '/')
|
227
237
|
|
228
238
|
if extra && extra.bytesize != @extra_length
|
229
|
-
raise ::Zip::
|
239
|
+
raise ::Zip::Error, 'Truncated local zip entry header'
|
230
240
|
else
|
231
|
-
if ::Zip::ExtraField
|
232
|
-
@extra.merge(extra)
|
241
|
+
if @extra.is_a?(::Zip::ExtraField)
|
242
|
+
@extra.merge(extra) if extra
|
233
243
|
else
|
234
244
|
@extra = ::Zip::ExtraField.new(extra)
|
235
245
|
end
|
236
246
|
end
|
247
|
+
parse_zip64_extra(true)
|
237
248
|
@local_header_size = calculate_local_header_size
|
238
249
|
end
|
239
250
|
|
240
251
|
def pack_local_entry
|
252
|
+
zip64 = @extra['Zip64']
|
241
253
|
[::Zip::LOCAL_ENTRY_SIGNATURE,
|
242
254
|
@version_needed_to_extract, # version needed to extract
|
243
255
|
@gp_flags, # @gp_flags ,
|
@@ -245,19 +257,22 @@ module Zip
|
|
245
257
|
@time.to_binary_dos_time, # @last_mod_time ,
|
246
258
|
@time.to_binary_dos_date, # @last_mod_date ,
|
247
259
|
@crc,
|
248
|
-
@compressed_size,
|
249
|
-
@size,
|
260
|
+
(zip64 && zip64.compressed_size) ? 0xFFFFFFFF : @compressed_size,
|
261
|
+
(zip64 && zip64.original_size) ? 0xFFFFFFFF : @size,
|
250
262
|
name_size,
|
251
263
|
@extra ? @extra.local_size : 0].pack('VvvvvvVVVvv')
|
252
264
|
end
|
253
265
|
|
254
|
-
def write_local_entry(io) #:nodoc:all
|
266
|
+
def write_local_entry(io, rewrite = false) #:nodoc:all
|
267
|
+
prep_zip64_extra(true)
|
268
|
+
verify_local_header_size! if rewrite
|
255
269
|
@local_header_offset = io.tell
|
256
270
|
|
257
271
|
io << pack_local_entry
|
258
272
|
|
259
273
|
io << @name
|
260
|
-
io <<
|
274
|
+
io << @extra.to_local_bin if @extra
|
275
|
+
@local_header_size = io.tell - @local_header_offset
|
261
276
|
end
|
262
277
|
|
263
278
|
def unpack_c_dir_entry(buf)
|
@@ -296,8 +311,8 @@ module Zip
|
|
296
311
|
when ::Zip::FILE_TYPE_SYMLINK
|
297
312
|
:symlink
|
298
313
|
else
|
299
|
-
#best case guess for whether it is a file or not
|
300
|
-
#Otherwise this would be set to unknown and that entry would never be able to extracted
|
314
|
+
# best case guess for whether it is a file or not
|
315
|
+
# Otherwise this would be set to unknown and that entry would never be able to extracted
|
301
316
|
if name_is_directory?
|
302
317
|
:directory
|
303
318
|
else
|
@@ -314,21 +329,18 @@ module Zip
|
|
314
329
|
end
|
315
330
|
|
316
331
|
def check_c_dir_entry_static_header_length(buf)
|
317
|
-
|
318
|
-
|
319
|
-
end
|
332
|
+
return if buf.bytesize == ::Zip::CDIR_ENTRY_STATIC_HEADER_LENGTH
|
333
|
+
raise Error, 'Premature end of file. Not enough data for zip cdir entry header'
|
320
334
|
end
|
321
335
|
|
322
336
|
def check_c_dir_entry_signature
|
323
|
-
|
324
|
-
|
325
|
-
end
|
337
|
+
return if header_signature == ::Zip::CENTRAL_DIRECTORY_ENTRY_SIGNATURE
|
338
|
+
raise Error, "Zip local header magic not found at location '#{local_header_offset}'"
|
326
339
|
end
|
327
340
|
|
328
341
|
def check_c_dir_entry_comment_size
|
329
|
-
|
330
|
-
|
331
|
-
end
|
342
|
+
return if @comment && @comment.bytesize == @comment_length
|
343
|
+
raise ::Zip::Error, 'Truncated cdir zip entry header'
|
332
344
|
end
|
333
345
|
|
334
346
|
def read_c_dir_extra_field(io)
|
@@ -345,29 +357,28 @@ module Zip
|
|
345
357
|
unpack_c_dir_entry(static_sized_fields_buf)
|
346
358
|
check_c_dir_entry_signature
|
347
359
|
set_time(@last_mod_date, @last_mod_time)
|
348
|
-
@name = io.read(@name_length).
|
360
|
+
@name = io.read(@name_length).tr('\\', '/')
|
349
361
|
read_c_dir_extra_field(io)
|
350
362
|
@comment = io.read(@comment_length)
|
351
363
|
check_c_dir_entry_comment_size
|
352
364
|
set_ftype_from_c_dir_entry
|
353
|
-
|
365
|
+
parse_zip64_extra(false)
|
354
366
|
end
|
355
367
|
|
356
368
|
def file_stat(path) # :nodoc:
|
357
369
|
if @follow_symlinks
|
358
|
-
::File
|
370
|
+
::File.stat(path)
|
359
371
|
else
|
360
|
-
::File
|
372
|
+
::File.lstat(path)
|
361
373
|
end
|
362
374
|
end
|
363
375
|
|
364
376
|
def get_extra_attributes_from_path(path) # :nodoc:
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
end
|
377
|
+
return if Zip::RUNNING_ON_WINDOWS
|
378
|
+
stat = file_stat(path)
|
379
|
+
@unix_uid = stat.uid
|
380
|
+
@unix_gid = stat.gid
|
381
|
+
@unix_perms = stat.mode & 07777
|
371
382
|
end
|
372
383
|
|
373
384
|
def set_unix_permissions_on_path(dest_path)
|
@@ -381,7 +392,7 @@ module Zip
|
|
381
392
|
end
|
382
393
|
|
383
394
|
def set_extra_attributes_on_path(dest_path) # :nodoc:
|
384
|
-
return unless
|
395
|
+
return unless file? || directory?
|
385
396
|
|
386
397
|
case @fstype
|
387
398
|
when ::Zip::FSTYPE_UNIX
|
@@ -390,6 +401,7 @@ module Zip
|
|
390
401
|
end
|
391
402
|
|
392
403
|
def pack_c_dir_entry
|
404
|
+
zip64 = @extra['Zip64']
|
393
405
|
[
|
394
406
|
@header_signature,
|
395
407
|
@version, # version of encoding software
|
@@ -400,15 +412,15 @@ module Zip
|
|
400
412
|
@time.to_binary_dos_time, # @last_mod_time ,
|
401
413
|
@time.to_binary_dos_date, # @last_mod_date ,
|
402
414
|
@crc,
|
403
|
-
@compressed_size,
|
404
|
-
@size,
|
415
|
+
(zip64 && zip64.compressed_size) ? 0xFFFFFFFF : @compressed_size,
|
416
|
+
(zip64 && zip64.original_size) ? 0xFFFFFFFF : @size,
|
405
417
|
name_size,
|
406
418
|
@extra ? @extra.c_dir_size : 0,
|
407
419
|
comment_size,
|
408
|
-
0, # disk number start
|
420
|
+
(zip64 && zip64.disk_start_number) ? 0xFFFF : 0, # disk number start
|
409
421
|
@internal_file_attributes, # file type (binary=0, text=1)
|
410
422
|
@external_file_attributes, # native filesystem attributes
|
411
|
-
@local_header_offset,
|
423
|
+
(zip64 && zip64.relative_header_offset) ? 0xFFFFFFFF : @local_header_offset,
|
412
424
|
@name,
|
413
425
|
@extra,
|
414
426
|
@comment
|
@@ -416,6 +428,7 @@ module Zip
|
|
416
428
|
end
|
417
429
|
|
418
430
|
def write_c_dir_entry(io) #:nodoc:all
|
431
|
+
prep_zip64_extra(false)
|
419
432
|
case @fstype
|
420
433
|
when ::Zip::FSTYPE_UNIX
|
421
434
|
ft = case @ftype
|
@@ -446,21 +459,21 @@ module Zip
|
|
446
459
|
return false unless other.class == self.class
|
447
460
|
# Compares contents of local entry and exposed fields
|
448
461
|
keys_equal = %w(compression_method crc compressed_size size name extra filepath).all? do |k|
|
449
|
-
other.__send__(k.to_sym) ==
|
462
|
+
other.__send__(k.to_sym) == __send__(k.to_sym)
|
450
463
|
end
|
451
|
-
keys_equal &&
|
464
|
+
keys_equal && time.dos_equals(other.time)
|
452
465
|
end
|
453
466
|
|
454
|
-
def <=>
|
455
|
-
|
467
|
+
def <=>(other)
|
468
|
+
to_s <=> other.to_s
|
456
469
|
end
|
457
470
|
|
458
471
|
# Returns an IO like object for the given ZipEntry.
|
459
472
|
# Warning: may behave weird with symlinks.
|
460
473
|
def get_input_stream(&block)
|
461
474
|
if @ftype == :directory
|
462
|
-
yield
|
463
|
-
::Zip::NullInputStream
|
475
|
+
yield ::Zip::NullInputStream if block_given?
|
476
|
+
::Zip::NullInputStream
|
464
477
|
elsif @filepath
|
465
478
|
case @ftype
|
466
479
|
when :file
|
@@ -475,6 +488,7 @@ module Zip
|
|
475
488
|
end
|
476
489
|
else
|
477
490
|
zis = ::Zip::InputStream.new(@zipfile, local_header_offset)
|
491
|
+
zis.instance_variable_set(:@internal, true)
|
478
492
|
zis.get_next_entry
|
479
493
|
if block_given?
|
480
494
|
begin
|
@@ -494,22 +508,22 @@ module Zip
|
|
494
508
|
when 'file'
|
495
509
|
if name_is_directory?
|
496
510
|
raise ArgumentError,
|
497
|
-
"entry name '#{newEntry}' indicates directory entry, but "
|
498
|
-
|
511
|
+
"entry name '#{newEntry}' indicates directory entry, but " \
|
512
|
+
"'#{src_path}' is not a directory"
|
499
513
|
end
|
500
514
|
:file
|
501
515
|
when 'directory'
|
502
|
-
@name +=
|
516
|
+
@name += '/' unless name_is_directory?
|
503
517
|
:directory
|
504
518
|
when 'link'
|
505
519
|
if name_is_directory?
|
506
520
|
raise ArgumentError,
|
507
|
-
"entry name '#{newEntry}' indicates directory entry, but "
|
508
|
-
|
521
|
+
"entry name '#{newEntry}' indicates directory entry, but " \
|
522
|
+
"'#{src_path}' is not a directory"
|
509
523
|
end
|
510
524
|
:symlink
|
511
525
|
else
|
512
|
-
raise
|
526
|
+
raise "unknown file type: #{src_path.inspect} #{stat.inspect}"
|
513
527
|
end
|
514
528
|
|
515
529
|
@filepath = src_path
|
@@ -518,9 +532,9 @@ module Zip
|
|
518
532
|
|
519
533
|
def write_to_zip_output_stream(zip_output_stream) #:nodoc:all
|
520
534
|
if @ftype == :directory
|
521
|
-
zip_output_stream.put_next_entry(self)
|
535
|
+
zip_output_stream.put_next_entry(self, nil, nil, ::Zip::Entry::STORED)
|
522
536
|
elsif @filepath
|
523
|
-
zip_output_stream.put_next_entry(self, nil, nil,
|
537
|
+
zip_output_stream.put_next_entry(self, nil, nil, compression_method || ::Zip::Entry::DEFLATED)
|
524
538
|
get_input_stream { |is| ::Zip::IOExtras.copy_stream(zip_output_stream, is) }
|
525
539
|
else
|
526
540
|
zip_output_stream.copy_raw_entry(self)
|
@@ -530,11 +544,19 @@ module Zip
|
|
530
544
|
def parent_as_string
|
531
545
|
entry_name = name.chomp('/')
|
532
546
|
slash_index = entry_name.rindex('/')
|
533
|
-
slash_index ? entry_name.slice(0, slash_index+1) : nil
|
547
|
+
slash_index ? entry_name.slice(0, slash_index + 1) : nil
|
534
548
|
end
|
535
549
|
|
536
550
|
def get_raw_input_stream(&block)
|
537
|
-
|
551
|
+
if @zipfile.respond_to?(:seek) && @zipfile.respond_to?(:read)
|
552
|
+
yield @zipfile
|
553
|
+
else
|
554
|
+
::File.open(@zipfile, 'rb', &block)
|
555
|
+
end
|
556
|
+
end
|
557
|
+
|
558
|
+
def clean_up
|
559
|
+
# By default, do nothing
|
538
560
|
end
|
539
561
|
|
540
562
|
private
|
@@ -542,20 +564,20 @@ module Zip
|
|
542
564
|
def set_time(binary_dos_date, binary_dos_time)
|
543
565
|
@time = ::Zip::DOSTime.parse_binary_dos_format(binary_dos_date, binary_dos_time)
|
544
566
|
rescue ArgumentError
|
545
|
-
|
567
|
+
warn 'Invalid date/time in zip entry' if ::Zip.warn_invalid_date
|
546
568
|
end
|
547
569
|
|
548
|
-
def create_file(dest_path,
|
549
|
-
if ::File.
|
550
|
-
raise ::Zip::
|
570
|
+
def create_file(dest_path, _continue_on_exists_proc = proc { Zip.continue_on_exists_proc })
|
571
|
+
if ::File.exist?(dest_path) && !yield(self, dest_path)
|
572
|
+
raise ::Zip::DestinationFileExistsError,
|
551
573
|
"Destination '#{dest_path}' already exists"
|
552
574
|
end
|
553
|
-
::File.open(dest_path,
|
575
|
+
::File.open(dest_path, 'wb') do |os|
|
554
576
|
get_input_stream do |is|
|
555
577
|
set_extra_attributes_on_path(dest_path)
|
556
578
|
|
557
579
|
buf = ''
|
558
|
-
while buf = is.sysread(::Zip::Decompressor::CHUNK_SIZE, buf)
|
580
|
+
while (buf = is.sysread(::Zip::Decompressor::CHUNK_SIZE, buf))
|
559
581
|
os << buf
|
560
582
|
end
|
561
583
|
end
|
@@ -564,13 +586,13 @@ module Zip
|
|
564
586
|
|
565
587
|
def create_directory(dest_path)
|
566
588
|
return if ::File.directory?(dest_path)
|
567
|
-
if ::File.
|
589
|
+
if ::File.exist?(dest_path)
|
568
590
|
if block_given? && yield(self, dest_path)
|
569
|
-
::FileUtils
|
591
|
+
::FileUtils.rm_f dest_path
|
570
592
|
else
|
571
|
-
raise ::Zip::
|
572
|
-
"Cannot create directory '#{dest_path}'. "
|
573
|
-
|
593
|
+
raise ::Zip::DestinationFileExistsError,
|
594
|
+
"Cannot create directory '#{dest_path}'. " \
|
595
|
+
'A file already exists with that name'
|
574
596
|
end
|
575
597
|
end
|
576
598
|
::FileUtils.mkdir_p(dest_path)
|
@@ -593,27 +615,67 @@ module Zip
|
|
593
615
|
if ::File.readlink(dest_path) == linkto
|
594
616
|
return
|
595
617
|
else
|
596
|
-
raise
|
597
|
-
"Cannot create symlink '#{dest_path}'. "
|
598
|
-
|
618
|
+
raise ::Zip::DestinationFileExistsError,
|
619
|
+
"Cannot create symlink '#{dest_path}'. " \
|
620
|
+
'A symlink already exists with that name'
|
599
621
|
end
|
600
622
|
else
|
601
|
-
raise
|
602
|
-
"Cannot create symlink '#{dest_path}'. "
|
603
|
-
|
623
|
+
raise ::Zip::DestinationFileExistsError,
|
624
|
+
"Cannot create symlink '#{dest_path}'. " \
|
625
|
+
'A file already exists with that name'
|
604
626
|
end
|
605
627
|
end
|
606
628
|
|
607
629
|
::File.symlink(linkto, dest_path)
|
608
630
|
end
|
609
631
|
|
610
|
-
|
611
|
-
|
612
|
-
|
613
|
-
|
632
|
+
# apply missing data from the zip64 extra information field, if present
|
633
|
+
# (required when file sizes exceed 2**32, but can be used for all files)
|
634
|
+
def parse_zip64_extra(for_local_header) #:nodoc:all
|
635
|
+
return if @extra['Zip64'].nil?
|
636
|
+
if for_local_header
|
637
|
+
@size, @compressed_size = @extra['Zip64'].parse(@size, @compressed_size)
|
638
|
+
else
|
639
|
+
@size, @compressed_size, @local_header_offset = @extra['Zip64'].parse(@size, @compressed_size, @local_header_offset)
|
614
640
|
end
|
615
641
|
end
|
616
642
|
|
643
|
+
def data_descriptor_size
|
644
|
+
(@gp_flags & 0x0008) > 0 ? 16 : 0
|
645
|
+
end
|
646
|
+
|
647
|
+
# create a zip64 extra information field if we need one
|
648
|
+
def prep_zip64_extra(for_local_header) #:nodoc:all
|
649
|
+
return unless ::Zip.write_zip64_support
|
650
|
+
need_zip64 = @size >= 0xFFFFFFFF || @compressed_size >= 0xFFFFFFFF
|
651
|
+
need_zip64 ||= @local_header_offset >= 0xFFFFFFFF unless for_local_header
|
652
|
+
if need_zip64
|
653
|
+
@version_needed_to_extract = VERSION_NEEDED_TO_EXTRACT_ZIP64
|
654
|
+
@extra.delete('Zip64Placeholder')
|
655
|
+
zip64 = @extra.create('Zip64')
|
656
|
+
if for_local_header
|
657
|
+
# local header always includes size and compressed size
|
658
|
+
zip64.original_size = @size
|
659
|
+
zip64.compressed_size = @compressed_size
|
660
|
+
else
|
661
|
+
# central directory entry entries include whichever fields are necessary
|
662
|
+
zip64.original_size = @size if @size >= 0xFFFFFFFF
|
663
|
+
zip64.compressed_size = @compressed_size if @compressed_size >= 0xFFFFFFFF
|
664
|
+
zip64.relative_header_offset = @local_header_offset if @local_header_offset >= 0xFFFFFFFF
|
665
|
+
end
|
666
|
+
else
|
667
|
+
@extra.delete('Zip64')
|
668
|
+
|
669
|
+
# if this is a local header entry, create a placeholder
|
670
|
+
# so we have room to write a zip64 extra field afterward
|
671
|
+
# (we won't know if it's needed until the file data is written)
|
672
|
+
if for_local_header
|
673
|
+
@extra.create('Zip64Placeholder')
|
674
|
+
else
|
675
|
+
@extra.delete('Zip64Placeholder')
|
676
|
+
end
|
677
|
+
end
|
678
|
+
end
|
617
679
|
end
|
618
680
|
end
|
619
681
|
|