rubyzip 1.2.1 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/README.md +83 -35
- data/lib/zip.rb +11 -1
- data/lib/zip/central_directory.rb +3 -3
- data/lib/zip/compressor.rb +1 -2
- data/lib/zip/constants.rb +3 -3
- data/lib/zip/crypto/null_encryption.rb +2 -4
- data/lib/zip/decompressor.rb +1 -1
- data/lib/zip/dos_time.rb +1 -1
- data/lib/zip/entry.rb +67 -57
- data/lib/zip/entry_set.rb +3 -3
- data/lib/zip/errors.rb +1 -0
- data/lib/zip/extra_field.rb +2 -2
- data/lib/zip/extra_field/generic.rb +1 -1
- data/lib/zip/extra_field/zip64_placeholder.rb +1 -2
- data/lib/zip/file.rb +47 -27
- data/lib/zip/filesystem.rb +17 -13
- data/lib/zip/inflater.rb +2 -2
- data/lib/zip/input_stream.rb +10 -7
- data/lib/zip/ioextras/abstract_input_stream.rb +1 -1
- data/lib/zip/ioextras/abstract_output_stream.rb +1 -1
- data/lib/zip/output_stream.rb +5 -5
- data/lib/zip/pass_thru_decompressor.rb +1 -1
- data/lib/zip/streamable_stream.rb +1 -1
- data/lib/zip/version.rb +1 -1
- data/samples/example_recursive.rb +14 -15
- data/samples/gtk_ruby_zip.rb +1 -1
- data/samples/qtzip.rb +1 -1
- data/samples/zipfind.rb +2 -2
- metadata +25 -121
- 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 -57
- 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 -163
- data/test/entry_test.rb +0 -154
- 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 -583
- 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 -126
- 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 -234
- 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
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 4e2be8aa3dea85f1e99fa26b5b2530ede874228f35379181d5219eb3864a0e33
|
4
|
+
data.tar.gz: '0985b8cd0b421a0bdf83953ba907ddad52d236948a81a242f322f0f5fa2145a6'
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c870bef8352ddb4e706a847f66da596fe556938d1b1422c3541f8ab24aa10bbdb6be577cd031853459101e24a4290aa816436834d1cae66dd26238b451f41d15
|
7
|
+
data.tar.gz: bb5c8ca7d286346bfad64a6e1bd637cb8a40a99974bc62fbd76ea1a5ee139d16dcf52a8480cc219e48e311d84046feafab2c497cfbeb0721940a06a5c7a163a2
|
data/README.md
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
# rubyzip
|
2
|
+
|
2
3
|
[![Gem Version](https://badge.fury.io/rb/rubyzip.svg)](http://badge.fury.io/rb/rubyzip)
|
3
4
|
[![Build Status](https://secure.travis-ci.org/rubyzip/rubyzip.svg)](http://travis-ci.org/rubyzip/rubyzip)
|
4
5
|
[![Code Climate](https://codeclimate.com/github/rubyzip/rubyzip.svg)](https://codeclimate.com/github/rubyzip/rubyzip)
|
@@ -19,9 +20,10 @@ gem 'zip-zip' # will load compatibility for old rubyzip API.
|
|
19
20
|
|
20
21
|
## Requirements
|
21
22
|
|
22
|
-
|
23
|
+
- Ruby 2.4 or greater (for rubyzip 2.0; use 1.x for older rubies)
|
23
24
|
|
24
25
|
## Installation
|
26
|
+
|
25
27
|
Rubyzip is available on RubyGems:
|
26
28
|
|
27
29
|
```
|
@@ -52,14 +54,15 @@ Zip::File.open(zipfile_name, Zip::File::CREATE) do |zipfile|
|
|
52
54
|
# Two arguments:
|
53
55
|
# - The name of the file as it will appear in the archive
|
54
56
|
# - The original file, including the path to find it
|
55
|
-
zipfile.add(filename, folder
|
57
|
+
zipfile.add(filename, File.join(folder, filename))
|
56
58
|
end
|
57
|
-
zipfile.get_output_stream("myFile") { |
|
59
|
+
zipfile.get_output_stream("myFile") { |f| f.write "myFile contains just this" }
|
58
60
|
end
|
59
61
|
```
|
60
62
|
|
61
63
|
### Zipping a directory recursively
|
62
|
-
|
64
|
+
|
65
|
+
Copy from [here](https://github.com/rubyzip/rubyzip/blob/9d891f7353e66052283562d3e252fe380bb4b199/samples/example_recursive.rb)
|
63
66
|
|
64
67
|
```ruby
|
65
68
|
require 'zip'
|
@@ -83,40 +86,37 @@ class ZipFileGenerator
|
|
83
86
|
|
84
87
|
# Zip the input directory.
|
85
88
|
def write
|
86
|
-
entries = Dir.entries(@input_dir) - %w
|
89
|
+
entries = Dir.entries(@input_dir) - %w[. ..]
|
87
90
|
|
88
|
-
::Zip::File.open(@output_file, ::Zip::File::CREATE) do |
|
89
|
-
write_entries entries, '',
|
91
|
+
::Zip::File.open(@output_file, ::Zip::File::CREATE) do |zipfile|
|
92
|
+
write_entries entries, '', zipfile
|
90
93
|
end
|
91
94
|
end
|
92
95
|
|
93
96
|
private
|
94
97
|
|
95
98
|
# A helper method to make the recursion work.
|
96
|
-
def write_entries(entries, path,
|
99
|
+
def write_entries(entries, path, zipfile)
|
97
100
|
entries.each do |e|
|
98
|
-
|
99
|
-
disk_file_path = File.join(@input_dir,
|
100
|
-
puts "Deflating #{disk_file_path}"
|
101
|
+
zipfile_path = path == '' ? e : File.join(path, e)
|
102
|
+
disk_file_path = File.join(@input_dir, zipfile_path)
|
101
103
|
|
102
104
|
if File.directory? disk_file_path
|
103
|
-
recursively_deflate_directory(disk_file_path,
|
105
|
+
recursively_deflate_directory(disk_file_path, zipfile, zipfile_path)
|
104
106
|
else
|
105
|
-
put_into_archive(disk_file_path,
|
107
|
+
put_into_archive(disk_file_path, zipfile, zipfile_path)
|
106
108
|
end
|
107
109
|
end
|
108
110
|
end
|
109
111
|
|
110
|
-
def recursively_deflate_directory(disk_file_path,
|
111
|
-
|
112
|
-
subdir = Dir.entries(disk_file_path) - %w
|
113
|
-
write_entries subdir,
|
112
|
+
def recursively_deflate_directory(disk_file_path, zipfile, zipfile_path)
|
113
|
+
zipfile.mkdir zipfile_path
|
114
|
+
subdir = Dir.entries(disk_file_path) - %w[. ..]
|
115
|
+
write_entries subdir, zipfile_path, zipfile
|
114
116
|
end
|
115
117
|
|
116
|
-
def put_into_archive(disk_file_path,
|
117
|
-
|
118
|
-
f.write(File.open(disk_file_path, 'rb').read)
|
119
|
-
end
|
118
|
+
def put_into_archive(disk_file_path, zipfile, zipfile_path)
|
119
|
+
zipfile.add(zipfile_path, disk_file_path)
|
120
120
|
end
|
121
121
|
end
|
122
122
|
```
|
@@ -152,12 +152,15 @@ When modifying a zip archive the file permissions of the archive are preserved.
|
|
152
152
|
### Reading a Zip file
|
153
153
|
|
154
154
|
```ruby
|
155
|
+
MAX_SIZE = 1024**2 # 1MiB (but of course you can increase this)
|
155
156
|
Zip::File.open('foo.zip') do |zip_file|
|
156
157
|
# Handle entries one by one
|
157
158
|
zip_file.each do |entry|
|
158
|
-
# Extract to file/directory/symlink
|
159
159
|
puts "Extracting #{entry.name}"
|
160
|
-
entry.
|
160
|
+
raise 'File too large when extracted' if entry.size > MAX_SIZE
|
161
|
+
|
162
|
+
# Extract to file or directory based on name in the archive
|
163
|
+
entry.extract
|
161
164
|
|
162
165
|
# Read into memory
|
163
166
|
content = entry.get_input_stream.read
|
@@ -165,6 +168,7 @@ Zip::File.open('foo.zip') do |zip_file|
|
|
165
168
|
|
166
169
|
# Find specific entry
|
167
170
|
entry = zip_file.glob('*.csv').first
|
171
|
+
raise 'File too large when extracted' if entry.size > MAX_SIZE
|
168
172
|
puts entry.get_input_stream.read
|
169
173
|
end
|
170
174
|
```
|
@@ -175,9 +179,7 @@ end
|
|
175
179
|
|
176
180
|
But there is one exception when it is not working - General Purpose Flag Bit 3.
|
177
181
|
|
178
|
-
|
179
|
-
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
|
180
|
-
```
|
182
|
+
> 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
|
181
183
|
|
182
184
|
If `::Zip::InputStream` finds such entry in the zip archive it will raise an exception.
|
183
185
|
|
@@ -221,7 +223,9 @@ File.open(new_path, "wb") {|f| f.write(buffer.string) }
|
|
221
223
|
|
222
224
|
## Configuration
|
223
225
|
|
224
|
-
|
226
|
+
### Existing Files
|
227
|
+
|
228
|
+
By default, rubyzip will not overwrite files if they already exist inside of the extracted path. To change this behavior, you may specify a configuration option like so:
|
225
229
|
|
226
230
|
```ruby
|
227
231
|
Zip.on_exists_proc = true
|
@@ -235,25 +239,77 @@ Additionally, if you want to configure rubyzip to overwrite existing files while
|
|
235
239
|
Zip.continue_on_exists_proc = true
|
236
240
|
```
|
237
241
|
|
242
|
+
### Non-ASCII Names
|
243
|
+
|
238
244
|
If you want to store non-english names and want to open them on Windows(pre 7) you need to set this option:
|
239
245
|
|
240
246
|
```ruby
|
241
247
|
Zip.unicode_names = true
|
242
248
|
```
|
243
249
|
|
250
|
+
Sometimes file names inside zip contain non-ASCII characters. If you can assume which encoding was used for such names and want to be able to find such entries using `find_entry` then you can force assumed encoding like so:
|
251
|
+
|
252
|
+
```ruby
|
253
|
+
Zip.force_entry_names_encoding = 'UTF-8'
|
254
|
+
```
|
255
|
+
|
256
|
+
Allowed encoding names are the same as accepted by `String#force_encoding`
|
257
|
+
|
258
|
+
### Date Validation
|
259
|
+
|
244
260
|
Some zip files might have an invalid date format, which will raise a warning. You can hide this warning with the following setting:
|
245
261
|
|
246
262
|
```ruby
|
247
263
|
Zip.warn_invalid_date = false
|
248
264
|
```
|
249
265
|
|
266
|
+
### Size Validation
|
267
|
+
|
268
|
+
By default (in rubyzip >= 2.0), rubyzip's `extract` method checks that an entry's reported uncompressed size is not (significantly) smaller than its actual size. This is to help you protect your application against [zip bombs](https://en.wikipedia.org/wiki/Zip_bomb). Before `extract`ing an entry, you should check that its size is in the range you expect. For example, if your application supports processing up to 100 files at once, each up to 10MiB, your zip extraction code might look like:
|
269
|
+
|
270
|
+
```ruby
|
271
|
+
MAX_FILE_SIZE = 10 * 1024**2 # 10MiB
|
272
|
+
MAX_FILES = 100
|
273
|
+
Zip::File.open('foo.zip') do |zip_file|
|
274
|
+
num_files = 0
|
275
|
+
zip_file.each do |entry|
|
276
|
+
num_files += 1 if entry.file?
|
277
|
+
raise 'Too many extracted files' if num_files > MAX_FILES
|
278
|
+
raise 'File too large when extracted' if entry.size > MAX_FILE_SIZE
|
279
|
+
entry.extract
|
280
|
+
end
|
281
|
+
end
|
282
|
+
```
|
283
|
+
|
284
|
+
If you need to extract zip files that report incorrect uncompressed sizes and you really trust them not too be too large, you can disable this setting with
|
285
|
+
```ruby
|
286
|
+
Zip.validate_entry_sizes = false
|
287
|
+
```
|
288
|
+
|
289
|
+
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.
|
290
|
+
|
291
|
+
### Default Compression
|
292
|
+
|
250
293
|
You can set the default compression level like so:
|
251
294
|
|
252
295
|
```ruby
|
253
296
|
Zip.default_compression = Zlib::DEFAULT_COMPRESSION
|
254
297
|
```
|
298
|
+
|
255
299
|
It defaults to `Zlib::DEFAULT_COMPRESSION`. Possible values are `Zlib::BEST_COMPRESSION`, `Zlib::DEFAULT_COMPRESSION` and `Zlib::NO_COMPRESSION`
|
256
300
|
|
301
|
+
### Zip64 Support
|
302
|
+
|
303
|
+
By default, Zip64 support is disabled for writing. To enable it do this:
|
304
|
+
|
305
|
+
```ruby
|
306
|
+
Zip.write_zip64_support = true
|
307
|
+
```
|
308
|
+
|
309
|
+
_NOTE_: If you will enable Zip64 writing then you will need zip extractor with Zip64 support to extract archive.
|
310
|
+
|
311
|
+
### Block Form
|
312
|
+
|
257
313
|
You can set multiple settings at the same time by using a block:
|
258
314
|
|
259
315
|
```ruby
|
@@ -265,14 +321,6 @@ You can set multiple settings at the same time by using a block:
|
|
265
321
|
end
|
266
322
|
```
|
267
323
|
|
268
|
-
By default, Zip64 support is disabled for writing. To enable it do this:
|
269
|
-
|
270
|
-
```ruby
|
271
|
-
Zip.write_zip64_support = true
|
272
|
-
```
|
273
|
-
|
274
|
-
_NOTE_: If you will enable Zip64 writing then you will need zip extractor with Zip64 support to extract archive.
|
275
|
-
|
276
324
|
## Developing
|
277
325
|
|
278
326
|
To run the test you need to do this:
|
data/lib/zip.rb
CHANGED
@@ -34,7 +34,16 @@ require 'zip/errors'
|
|
34
34
|
|
35
35
|
module Zip
|
36
36
|
extend self
|
37
|
-
attr_accessor :unicode_names,
|
37
|
+
attr_accessor :unicode_names,
|
38
|
+
:on_exists_proc,
|
39
|
+
:continue_on_exists_proc,
|
40
|
+
:sort_entries,
|
41
|
+
:default_compression,
|
42
|
+
:write_zip64_support,
|
43
|
+
:warn_invalid_date,
|
44
|
+
:case_insensitive_match,
|
45
|
+
:force_entry_names_encoding,
|
46
|
+
:validate_entry_sizes
|
38
47
|
|
39
48
|
def reset!
|
40
49
|
@_ran_once = false
|
@@ -46,6 +55,7 @@ module Zip
|
|
46
55
|
@write_zip64_support = false
|
47
56
|
@warn_invalid_date = true
|
48
57
|
@case_insensitive_match = false
|
58
|
+
@validate_entry_sizes = true
|
49
59
|
end
|
50
60
|
|
51
61
|
def setup
|
@@ -96,7 +96,7 @@ module Zip
|
|
96
96
|
@size_in_bytes = Entry.read_zip_64_long(buf)
|
97
97
|
@cdir_offset = Entry.read_zip_64_long(buf)
|
98
98
|
@zip_64_extensible = buf.slice!(0, buf.bytesize)
|
99
|
-
raise Error, 'Zip consistency problem while reading eocd structure' unless buf.
|
99
|
+
raise Error, 'Zip consistency problem while reading eocd structure' unless buf.empty?
|
100
100
|
end
|
101
101
|
|
102
102
|
def read_e_o_c_d(buf) #:nodoc:
|
@@ -113,7 +113,7 @@ module Zip
|
|
113
113
|
else
|
114
114
|
buf.read(comment_length)
|
115
115
|
end
|
116
|
-
raise Error, 'Zip consistency problem while reading eocd structure' unless buf.
|
116
|
+
raise Error, 'Zip consistency problem while reading eocd structure' unless buf.empty?
|
117
117
|
end
|
118
118
|
|
119
119
|
def read_central_directory_entries(io) #:nodoc:
|
@@ -130,7 +130,7 @@ module Zip
|
|
130
130
|
|
131
131
|
def read_from_stream(io) #:nodoc:
|
132
132
|
buf = start_buf(io)
|
133
|
-
if
|
133
|
+
if zip64_file?(buf)
|
134
134
|
read_64_e_o_c_d(buf)
|
135
135
|
else
|
136
136
|
read_e_o_c_d(buf)
|
data/lib/zip/compressor.rb
CHANGED
data/lib/zip/constants.rb
CHANGED
@@ -11,9 +11,9 @@ module Zip
|
|
11
11
|
VERSION_NEEDED_TO_EXTRACT = 20
|
12
12
|
VERSION_NEEDED_TO_EXTRACT_ZIP64 = 45
|
13
13
|
|
14
|
-
FILE_TYPE_FILE =
|
15
|
-
FILE_TYPE_DIR =
|
16
|
-
FILE_TYPE_SYMLINK =
|
14
|
+
FILE_TYPE_FILE = 0o10
|
15
|
+
FILE_TYPE_DIR = 0o04
|
16
|
+
FILE_TYPE_SYMLINK = 0o12
|
17
17
|
|
18
18
|
FSTYPE_FAT = 0
|
19
19
|
FSTYPE_AMIGA = 1
|
data/lib/zip/decompressor.rb
CHANGED
data/lib/zip/dos_time.rb
CHANGED
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
|
@@ -99,7 +100,7 @@ module Zip
|
|
99
100
|
end
|
100
101
|
|
101
102
|
# Dynamic checkers
|
102
|
-
%w
|
103
|
+
%w[directory file symlink].each do |k|
|
103
104
|
define_method "#{k}?" do
|
104
105
|
file_type_is?(k.to_sym)
|
105
106
|
end
|
@@ -109,6 +110,17 @@ module Zip
|
|
109
110
|
@name.end_with?('/')
|
110
111
|
end
|
111
112
|
|
113
|
+
# Is the name a relative path, free of `..` patterns that could lead to
|
114
|
+
# path traversal attacks? This does NOT handle symlinks; if the path
|
115
|
+
# contains symlinks, this check is NOT enough to guarantee safety.
|
116
|
+
def name_safe?
|
117
|
+
cleanpath = Pathname.new(@name).cleanpath
|
118
|
+
return false unless cleanpath.relative?
|
119
|
+
root = ::File::SEPARATOR
|
120
|
+
naive_expanded_path = ::File.join(root, cleanpath.to_s)
|
121
|
+
::File.absolute_path(cleanpath.to_s, root) == naive_expanded_path
|
122
|
+
end
|
123
|
+
|
112
124
|
def local_entry_offset #:nodoc:all
|
113
125
|
local_header_offset + @local_header_size
|
114
126
|
end
|
@@ -147,14 +159,17 @@ module Zip
|
|
147
159
|
end
|
148
160
|
|
149
161
|
# Extracts entry to file dest_path (defaults to @name).
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
if
|
154
|
-
puts "WARNING: skipped
|
162
|
+
# NB: The caller is responsible for making sure dest_path is safe, if it
|
163
|
+
# is passed.
|
164
|
+
def extract(dest_path = nil, &block)
|
165
|
+
if dest_path.nil? && !name_safe?
|
166
|
+
puts "WARNING: skipped #{@name} as unsafe"
|
155
167
|
return self
|
156
168
|
end
|
157
169
|
|
170
|
+
dest_path ||= @name
|
171
|
+
block ||= proc { ::Zip.on_exists_proc }
|
172
|
+
|
158
173
|
if directory? || file? || symlink?
|
159
174
|
__send__("create_#{@ftype}", dest_path, &block)
|
160
175
|
else
|
@@ -239,7 +254,10 @@ module Zip
|
|
239
254
|
@name = io.read(@name_length)
|
240
255
|
extra = io.read(@extra_length)
|
241
256
|
|
242
|
-
@name.
|
257
|
+
@name.tr!('\\', '/')
|
258
|
+
if ::Zip.force_entry_names_encoding
|
259
|
+
@name.force_encoding(::Zip.force_entry_names_encoding)
|
260
|
+
end
|
243
261
|
|
244
262
|
if extra && extra.bytesize != @extra_length
|
245
263
|
raise ::Zip::Error, 'Truncated local zip entry header'
|
@@ -258,13 +276,13 @@ module Zip
|
|
258
276
|
zip64 = @extra['Zip64']
|
259
277
|
[::Zip::LOCAL_ENTRY_SIGNATURE,
|
260
278
|
@version_needed_to_extract, # version needed to extract
|
261
|
-
@gp_flags, # @gp_flags
|
279
|
+
@gp_flags, # @gp_flags
|
262
280
|
@compression_method,
|
263
|
-
@time.to_binary_dos_time, # @last_mod_time
|
264
|
-
@time.to_binary_dos_date, # @last_mod_date
|
281
|
+
@time.to_binary_dos_time, # @last_mod_time
|
282
|
+
@time.to_binary_dos_date, # @last_mod_date
|
265
283
|
@crc,
|
266
|
-
|
267
|
-
|
284
|
+
zip64 && zip64.compressed_size ? 0xFFFFFFFF : @compressed_size,
|
285
|
+
zip64 && zip64.original_size ? 0xFFFFFFFF : @size,
|
268
286
|
name_size,
|
269
287
|
@extra ? @extra.local_size : 0].pack('VvvvvvVVVvv')
|
270
288
|
end
|
@@ -308,7 +326,7 @@ module Zip
|
|
308
326
|
def set_ftype_from_c_dir_entry
|
309
327
|
@ftype = case @fstype
|
310
328
|
when ::Zip::FSTYPE_UNIX
|
311
|
-
@unix_perms = (@external_file_attributes >> 16) &
|
329
|
+
@unix_perms = (@external_file_attributes >> 16) & 0o7777
|
312
330
|
case (@external_file_attributes >> 28)
|
313
331
|
when ::Zip::FILE_TYPE_DIR
|
314
332
|
:directory
|
@@ -364,6 +382,9 @@ module Zip
|
|
364
382
|
check_c_dir_entry_signature
|
365
383
|
set_time(@last_mod_date, @last_mod_time)
|
366
384
|
@name = io.read(@name_length)
|
385
|
+
if ::Zip.force_entry_names_encoding
|
386
|
+
@name.force_encoding(::Zip.force_entry_names_encoding)
|
387
|
+
end
|
367
388
|
read_c_dir_extra_field(io)
|
368
389
|
@comment = io.read(@comment_length)
|
369
390
|
check_c_dir_entry_comment_size
|
@@ -384,14 +405,14 @@ module Zip
|
|
384
405
|
stat = file_stat(path)
|
385
406
|
@unix_uid = stat.uid
|
386
407
|
@unix_gid = stat.gid
|
387
|
-
@unix_perms = stat.mode &
|
408
|
+
@unix_perms = stat.mode & 0o7777
|
388
409
|
end
|
389
410
|
|
390
411
|
def set_unix_permissions_on_path(dest_path)
|
391
412
|
# BUG: does not update timestamps into account
|
392
413
|
# ignore setuid/setgid bits by default. honor if @restore_ownership
|
393
|
-
unix_perms_mask =
|
394
|
-
unix_perms_mask =
|
414
|
+
unix_perms_mask = 0o1777
|
415
|
+
unix_perms_mask = 0o7777 if @restore_ownership
|
395
416
|
::FileUtils.chmod(@unix_perms & unix_perms_mask, dest_path) if @restore_permissions && @unix_perms
|
396
417
|
::FileUtils.chown(@unix_uid, @unix_gid, dest_path) if @restore_ownership && @unix_uid && @unix_gid && ::Process.egid == 0
|
397
418
|
# File::utimes()
|
@@ -412,21 +433,21 @@ module Zip
|
|
412
433
|
@header_signature,
|
413
434
|
@version, # version of encoding software
|
414
435
|
@fstype, # filesystem type
|
415
|
-
@version_needed_to_extract, # @versionNeededToExtract
|
416
|
-
@gp_flags, # @gp_flags
|
436
|
+
@version_needed_to_extract, # @versionNeededToExtract
|
437
|
+
@gp_flags, # @gp_flags
|
417
438
|
@compression_method,
|
418
|
-
@time.to_binary_dos_time, # @last_mod_time
|
419
|
-
@time.to_binary_dos_date, # @last_mod_date
|
439
|
+
@time.to_binary_dos_time, # @last_mod_time
|
440
|
+
@time.to_binary_dos_date, # @last_mod_date
|
420
441
|
@crc,
|
421
|
-
|
422
|
-
|
442
|
+
zip64 && zip64.compressed_size ? 0xFFFFFFFF : @compressed_size,
|
443
|
+
zip64 && zip64.original_size ? 0xFFFFFFFF : @size,
|
423
444
|
name_size,
|
424
445
|
@extra ? @extra.c_dir_size : 0,
|
425
446
|
comment_size,
|
426
|
-
|
447
|
+
zip64 && zip64.disk_start_number ? 0xFFFF : 0, # disk number start
|
427
448
|
@internal_file_attributes, # file type (binary=0, text=1)
|
428
449
|
@external_file_attributes, # native filesystem attributes
|
429
|
-
|
450
|
+
zip64 && zip64.relative_header_offset ? 0xFFFFFFFF : @local_header_offset,
|
430
451
|
@name,
|
431
452
|
@extra,
|
432
453
|
@comment
|
@@ -439,18 +460,18 @@ module Zip
|
|
439
460
|
when ::Zip::FSTYPE_UNIX
|
440
461
|
ft = case @ftype
|
441
462
|
when :file
|
442
|
-
@unix_perms ||=
|
463
|
+
@unix_perms ||= 0o644
|
443
464
|
::Zip::FILE_TYPE_FILE
|
444
465
|
when :directory
|
445
|
-
@unix_perms ||=
|
466
|
+
@unix_perms ||= 0o755
|
446
467
|
::Zip::FILE_TYPE_DIR
|
447
468
|
when :symlink
|
448
|
-
@unix_perms ||=
|
469
|
+
@unix_perms ||= 0o755
|
449
470
|
::Zip::FILE_TYPE_SYMLINK
|
450
471
|
end
|
451
472
|
|
452
473
|
unless ft.nil?
|
453
|
-
@external_file_attributes = (ft << 12 | (@unix_perms &
|
474
|
+
@external_file_attributes = (ft << 12 | (@unix_perms & 0o7777)) << 16
|
454
475
|
end
|
455
476
|
end
|
456
477
|
|
@@ -464,7 +485,7 @@ module Zip
|
|
464
485
|
def ==(other)
|
465
486
|
return false unless other.class == self.class
|
466
487
|
# Compares contents of local entry and exposed fields
|
467
|
-
keys_equal = %w
|
488
|
+
keys_equal = %w[compression_method crc compressed_size size name extra filepath].all? do |k|
|
468
489
|
other.__send__(k.to_sym) == __send__(k.to_sym)
|
469
490
|
end
|
470
491
|
keys_equal && time.dos_equals(other.time)
|
@@ -494,7 +515,7 @@ module Zip
|
|
494
515
|
end
|
495
516
|
else
|
496
517
|
zis = ::Zip::InputStream.new(@zipfile, local_header_offset)
|
497
|
-
zis.instance_variable_set(:@
|
518
|
+
zis.instance_variable_set(:@complete_entry, self)
|
498
519
|
zis.get_next_entry
|
499
520
|
if block_given?
|
500
521
|
begin
|
@@ -582,9 +603,21 @@ module Zip
|
|
582
603
|
get_input_stream do |is|
|
583
604
|
set_extra_attributes_on_path(dest_path)
|
584
605
|
|
585
|
-
|
606
|
+
bytes_written = 0
|
607
|
+
warned = false
|
608
|
+
buf = ''.dup
|
586
609
|
while (buf = is.sysread(::Zip::Decompressor::CHUNK_SIZE, buf))
|
587
610
|
os << buf
|
611
|
+
bytes_written += buf.bytesize
|
612
|
+
if bytes_written > size && !warned
|
613
|
+
message = "Entry #{name} should be #{size}B but is larger when inflated"
|
614
|
+
if ::Zip.validate_entry_sizes
|
615
|
+
raise ::Zip::EntrySizeError, message
|
616
|
+
else
|
617
|
+
puts "WARNING: #{message}"
|
618
|
+
warned = true
|
619
|
+
end
|
620
|
+
end
|
588
621
|
end
|
589
622
|
end
|
590
623
|
end
|
@@ -607,32 +640,9 @@ module Zip
|
|
607
640
|
|
608
641
|
# BUG: create_symlink() does not use &block
|
609
642
|
def create_symlink(dest_path)
|
610
|
-
|
611
|
-
|
612
|
-
|
613
|
-
rescue Errno::ENOENT
|
614
|
-
end
|
615
|
-
|
616
|
-
io = get_input_stream
|
617
|
-
linkto = io.read
|
618
|
-
|
619
|
-
if stat
|
620
|
-
if stat.symlink?
|
621
|
-
if ::File.readlink(dest_path) == linkto
|
622
|
-
return
|
623
|
-
else
|
624
|
-
raise ::Zip::DestinationFileExistsError,
|
625
|
-
"Cannot create symlink '#{dest_path}'. " \
|
626
|
-
'A symlink already exists with that name'
|
627
|
-
end
|
628
|
-
else
|
629
|
-
raise ::Zip::DestinationFileExistsError,
|
630
|
-
"Cannot create symlink '#{dest_path}'. " \
|
631
|
-
'A file already exists with that name'
|
632
|
-
end
|
633
|
-
end
|
634
|
-
|
635
|
-
::File.symlink(linkto, dest_path)
|
643
|
+
# TODO: Symlinks pose security challenges. Symlink support temporarily
|
644
|
+
# removed in view of https://github.com/rubyzip/rubyzip/issues/369 .
|
645
|
+
puts "WARNING: skipped symlink #{dest_path}"
|
636
646
|
end
|
637
647
|
|
638
648
|
# apply missing data from the zip64 extra information field, if present
|